Files
project.linkedcorp.net/BASE_DE_DATOS.md
T
Milton Gonzalez bb6bd6865c refactor: renombra Subtareas a Tareas en todo el sistema
Actividad -> Tarea, sin nivel "sub" intermedio (no hay una entidad "Tarea"
de la que estas fueran sub-, asi que el prefijo sobraba). Tabla subtasks
-> tasks (RENAME TABLE idempotente en la migracion 005; 001_initial.sql
tambien actualizado para que una instalacion nueva cree la tabla ya con
el nombre final, sin pasar por el nombre viejo). Modulo backend, rutas,
servicio frontend, pestaña del detalle de actividad, i18n y documentacion
actualizados. Sin cambios de comportamiento, solo nombres.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-10 08:55:58 -05:00

7.7 KiB

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")
tasks checklist liviano dentro de una actividad ("Tareas" en la UI; renombrada desde subtasks en v1.8.0)
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
  • tasks.activity_id -> activities.id, tasks.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 tareas = % de tareas hechas; actividad sin tareas = 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/invited) es el ciclo de vida de la cuenta, 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).
  • invited: cuenta placeholder creada al agregar por correo a alguien que aún no es usuario desde Miembros de un proyecto (name=email, password_hash aleatorio inservible, reutiliza verification_token/verification_token_expires — 7 días en vez de 24h). Al aceptar la invitación (POST /api/auth/accept-invite) pasa directo a active sin pasar por pending_approval — a diferencia del autorregistro público, aquí quien lo invitó ya lo vetó al agregarlo a su proyecto. La fila de project_members se crea en el momento de invitar, no al aceptar, así que en cuanto acepta ya aparece como miembro sin pasos adicionales.
  • 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 /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:

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):

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):

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.