feat: autorregistro con verificacion de correo + aprobacion, superadmin y SMTP
- 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>
This commit is contained in:
+17
-25
@@ -20,8 +20,9 @@
|
||||
|
||||
| Tabla | Contenido |
|
||||
|---|---|
|
||||
| `roles` | catálogo fijo de 5 roles (admin, it_manager, project_lead, collaborator, viewer) |
|
||||
| `users` | usuarios, con rol y departamento |
|
||||
| `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) |
|
||||
@@ -58,7 +59,14 @@
|
||||
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.
|
||||
- Borrado lógico donde aplique (`users.is_active`); nunca DELETE físico de usuarios con historial.
|
||||
- `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.
|
||||
|
||||
@@ -105,36 +113,20 @@ node src/scripts/seed-db.js
|
||||
|
||||
---
|
||||
|
||||
## Setup de base de datos en producción (primera vez) — pendiente, no ejecutado aún
|
||||
## Setup de base de datos en producción — ejecutado el 2026-09-06
|
||||
|
||||
El servidor `.32` no tiene SSH accesible; el acceso admin es solo desde la máquina de desarrollo.
|
||||
Credenciales de acceso admin: ver `SERVIDORES.md` (project-md-templates/, no versionado).
|
||||
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`.
|
||||
|
||||
```bash
|
||||
mysql -h 192.168.222.32 -u mgonzalez -p --ssl=0
|
||||
```
|
||||
|
||||
```sql
|
||||
CREATE DATABASE IF NOT EXISTS project_linkedcorp
|
||||
CHARACTER SET utf8mb4
|
||||
COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
CREATE USER IF NOT EXISTS 'project_linkedcorp'@'192.168.222.3'
|
||||
IDENTIFIED BY '[DB_PASSWORD_PROD]';
|
||||
|
||||
GRANT ALL PRIVILEGES ON project_linkedcorp.* TO 'project_linkedcorp'@'192.168.222.3';
|
||||
|
||||
FLUSH PRIVILEGES;
|
||||
```
|
||||
|
||||
Migración en el servidor de aplicaciones (`.3`), cuando exista el deploy:
|
||||
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; NO recrear usuarios de prueba de dev
|
||||
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
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
Reference in New Issue
Block a user