7557dffc52
- POST /api/auth/register: primer usuario del sistema -> superadmin con sesion directa (bootstrap, sin correo ni aprobacion); los siguientes quedan pending_verification y reciben un correo con enlace de un solo uso (24h). - GET /api/auth/verify-email: pasa a pending_approval y notifica in-app a los admin/superadmin activos. - admin/superadmin aprueban (POST /api/users/:id/approve, eligen rol y departamento) o rechazan (POST /api/users/:id/reject, borra y audita el email) desde Organizacion -> Usuarios (nuevo badge de estado + acciones). - login() da mensaje especifico segun el status de la cuenta en vez del generico "credenciales invalidas". - Recuperacion de clave (forgot/reset-password): respuesta siempre generica, token de 1 hora de un solo uso. - Nuevo modulo settings: SMTP configurable SOLO por superadmin desde Configuracion -> Correo (SMTP); clave cifrada AES-256-GCM (ENCRYPTION_KEY) en la tabla system_settings, nunca expuesta por la API. sendMail() degrada a modo dev (loguea en vez de enviar) si no hay SMTP configurado, y nunca deja que un fallo de envio tumbe al que lo llama. - Migracion 002_registration_and_smtp.sql: rol superadmin, columnas de ciclo de vida en users (status, tokens), tabla system_settings, y promueve al usuario mas antiguo del sistema a superadmin. - Fix: React.StrictMode disparaba el efecto de VerifyEmailPage dos veces; la segunda llamada (token ya consumido) mostraba "enlace invalido" pese a que la verificacion ya habia funcionado. Se agrega una guarda por token. - Fix (deuda tecnica v1.0.0): se reordena el montaje de rutas en app.js — subtasksRoutes (mount generico en /api) va al final, para no interceptar con su requireAuth interno una request sin token dirigida a un router mas especifico registrado despues. - Documentacion actualizada: AGENTS.md, SPEC.md, API.md, BASE_DE_DATOS.md, CONFIGURACION.md, ESTADO.md, CHANGELOG.md. Version 1.1.0. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
140 lines
6.2 KiB
Markdown
140 lines
6.2 KiB
Markdown
# 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).
|
|
- 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.
|