# Base de Datos - project.linkedcorp.net ## Motor - Motor: MariaDB - Base principal: `project_linkedcorp` - Host desarrollo: `192.168.222.20` - Host producción: `192.168.222.32` - Puerto: `3306` - Charset: `utf8mb4` - Collation: `utf8mb4_unicode_ci` ## Fuente de verdad - La base ya existe: sí (creada en dev el 2026-09-03, usuario `project_linkedcorp`). - Archivo SQL base: `backend/database/migrations/` (SQL idempotente, numerado, `CREATE TABLE IF NOT EXISTS`). - Seeds: `backend/database/seeds/` (roles, catálogos, usuario admin de dev). ## Tablas principales (MVP) | Tabla | Contenido | |---|---| | `roles` | catálogo fijo de 6 roles (superadmin, admin, it_manager, project_lead, collaborator, viewer) | | `users` | usuarios, con rol, departamento y ciclo de vida de registro (`status`, tokens) | | `system_settings` | clave/valor de configuración del sistema (hoy: SMTP, clave cifrada) | | `departments` | departamentos/áreas, jerarquía simple vía `parent_id` | | `project_types` | catálogo de tipos de proyecto | | `priorities` | catálogo de prioridades (baja/media/alta/crítica) | | `project_statuses` | catálogo de estados de proyecto | | `activity_statuses` | catálogo de estados de actividad | | `projects` | proyectos | | `project_members` | usuarios asignados a un proyecto (reemplaza "equipos" como módulo aparte) | | `activities` | actividades de un proyecto | | `activity_dependencies` | dependencias simples entre actividades ("depende de") | | `subtasks` | checklist liviano dentro de una actividad | | `comments` | comentarios sobre proyecto o actividad (`entity_type` + `entity_id`) | | `attachments` | archivos adjuntos a proyecto o actividad | | `notifications` | notificaciones in-app por usuario | | `audit_log` | auditoría central (quién, qué, sobre qué, cuándo) | ## Relaciones clave - `users.role_id -> roles.id`, `users.department_id -> departments.id` - `departments.parent_id -> departments.id` (jerarquía opcional, ej. TI → Desarrollo) - `projects.status_id -> project_statuses.id`, `projects.priority_id -> priorities.id`, `projects.project_type_id -> project_types.id`, `projects.owner_user_id -> users.id`, `projects.requesting_department_id / responsible_department_id -> departments.id` - `project_members.project_id -> projects.id`, `project_members.user_id -> users.id` - `activities.project_id -> projects.id`, `activities.status_id -> activity_statuses.id`, `activities.priority_id -> priorities.id`, `activities.responsible_user_id -> users.id` - `activity_dependencies.activity_id / depends_on_activity_id -> activities.id` - `subtasks.activity_id -> activities.id`, `subtasks.responsible_user_id -> users.id` (nullable) - `comments.entity_id` / `attachments.entity_id` apuntan a `projects.id` o `activities.id` según `entity_type` (sin FK física cruzada — se valida en el servicio) ## Reglas de datos - `projects.progress_pct` y `activities.progress_pct` son **calculados por el servicio**, no editables directo por el cliente: actividad con subtareas = % de subtareas hechas; actividad sin subtareas = manual (0-100); proyecto = promedio del avance de sus actividades. - `users.password_hash` con bcrypt, nunca texto plano ni reversible. - `users.status` (`pending_verification`/`pending_approval`/`active`) es el ciclo de vida del autorregistro, independiente de `is_active` (que sigue siendo el interruptor manual del admin para desactivar a alguien ya activo). Un usuario `pending_approval` rechazado se **borra** físicamente (no hay estado "rechazado" — se audita el email en `audit_log` antes de borrar). - `system_settings.value` para `smtp_pass_enc` va **cifrado** (AES-256-GCM, `ENCRYPTION_KEY` del `.env`) — nunca en texto plano, nunca se devuelve por la API (solo `configured: true/false`). - Borrado lógico donde aplique (`users.is_active`); nunca DELETE físico de usuarios **activos** con historial (los `pending_approval` rechazados sí se borran, ver arriba). - `projects.deleted_at`/`deleted_by`: eliminación lógica (quien puede editar el proyecto puede "eliminarlo" — se oculta de listados/dashboard, sigue en BD). Borrado físico (`DELETE`, arrastra actividades/comentarios/adjuntos por `ON DELETE CASCADE`) solo vía `permanentlyDeleteProject`, solo superadmin, y solo sobre un proyecto que ya tiene `deleted_at` (dos pasos, nunca directo). - `project_members.role_in_project`: `'owner'` (dueño, se asigna solo al crear el proyecto, nunca se toca desde el endpoint de rol), `'admin'` (administrador **de ese proyecto puntual** — un proyecto puede tener varios, otorgado por quien ya puede escribir el proyecto, sin importar el rol global de a quién se lo otorgan), `NULL`/otro = miembro sin permisos de escritura (salvo que además sea `project_lead` global, comportamiento previo sin cambios). - Empresa asumida única (no hay tabla `companies` en el MVP). - Producción siempre usa `192.168.222.32`. Desarrollo siempre usa `192.168.222.20`. No mezclar. ## Riesgos o decisiones - Sin triggers de BD para recalcular avances: toda la lógica vive en la capa `service/` del backend (más simple de depurar y de extender en fase 2). - `risk_level` es un campo simple en `projects` (bajo/medio/alto) — el módulo completo de Riesgos (probabilidad/impacto/mitigación) queda para fase 2, sin necesitar migrar este campo. ## Archivos relacionados - `backend/.env.example` - `backend/database/migrations/*.sql` - `backend/database/seeds/*.sql` --- ## Crear la base de datos en DESARROLLO (.20) Ya ejecutado para este proyecto (2026-09-03): ```bash bash /home/mgonzalez/desarrollo/project-md-templates/crear-db-dev.sh project_linkedcorp project_linkedcorp ``` BD `project_linkedcorp` y usuario `project_linkedcorp`@`192.168.222.40` creados en `192.168.222.20`. Las credenciales están en `backend/.env` (no versionado). Verificar: ```bash mysql --defaults-extra-file=~/.config/mysql/dev20-admin.cnf -e "SHOW DATABASES LIKE 'project_linkedcorp';" ``` Aplicar migraciones/seeds (una vez creado `backend/src/scripts/migrate-db.js`): ```bash cd backend set -a && source .env && set +a node src/scripts/migrate-db.js node src/scripts/seed-db.js ``` --- ## Setup de base de datos en producción — ejecutado el 2026-09-06 BD `project_linkedcorp` creada en `.32` con `crear-db-prod.sh` (grant a `192.168.222.3`). Detalle del proceso y estado real en `PRODUCCION.md`. Migración en el servidor de aplicaciones (`.3`): ```bash ssh deploy@192.168.222.3 -p 2203 cd /opt/apps/project.linkedcorp.net/source/backend set -a && source /opt/apps/project.linkedcorp.net/backend/.env && set +a node src/scripts/migrate-db.js node src/scripts/seed-db.js # solo catálogos/roles; sin datos de ejemplo en produccion node src/scripts/create-admin.js "Nombre" correo@dominio # solo la primera vez, bootstrap manual ``` --- ## Actualizaciones de esquema en producción Método: archivos SQL idempotentes en `backend/database/migrations/00X-nombre.sql` (`CREATE TABLE IF NOT EXISTS` / `information_schema` guards), aplicados con `node src/scripts/migrate-db.js`. Siempre backup (`mysqldump --single-transaction`) antes de migrar en producción.