d0f90542e9
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
275 lines
20 KiB
Markdown
275 lines
20 KiB
Markdown
# ESTADO — project.linkedcorp.net
|
||
|
||
Ultima actualizacion: 2026-10-29 (v4.10.0 commit local, sin push ni desplegar)
|
||
Fase actual: **En produccion** (`https://project.linkedcorp.net`, v1.1.0). MVP fase 1 operativo +
|
||
autorregistro/aprobacion/SMTP.
|
||
|
||
## Conexiones externas (2026-10-29, en curso, sin commit)
|
||
|
||
- Diseño y requisitos acordados en `docs/CONEXIONES-EXTERNAS.md`; decisiones #2–#4 en `DECISIONES.md`.
|
||
- Hecho: conector FoxPro 1.0.0 en `data/conectores/foxpro/` (API solo lectura, contrato v1 en
|
||
`data/conectores/README.md`). `npm test` 16/16 OK con DBF temporal. Sin probar con tablas reales
|
||
de SAGA ni los `.ps1` en Windows.
|
||
- Siguiente: con permiso del usuario, revisar por SSH `192.168.0.253` las carpetas de nómina y
|
||
clientes; instalar en `C:\api_project\foxpro`; luego el módulo del sistema.
|
||
|
||
## Agenda médica (v4.7.0, sin desplegar)
|
||
|
||
- v4.9.1: la agenda usa el componente compartido `CalendarBoard` (el mismo del Calendario); siempre
|
||
abre en Mes (`plc_clinic_view_v3`). En prod se veía vacía por la vista Día guardada y sin médicos.
|
||
- v4.9.0: clic en un día/hora libre abre la cita nueva con esa fecha; clic en una cita la abre en
|
||
lectura (Editar, Confirmar, Cancelar, Eliminar). Autor fijo de la cita (`created_by_kind`
|
||
user/ai_agent → "Agente IA" para el futuro WhatsApp). Borrado lógico de citas (con motivo) y
|
||
bloqueos; filtro Estado incluye Canceladas/Eliminadas. Migración 037 aplicada en dev (2 veces OK).
|
||
Probado en dev: 41/41 pruebas de API + navegador, datos temporales borrados.
|
||
- v4.8.1: abre en Mes con barra igual al Calendario; aviso si no hay médicos (en prod se veía vacío
|
||
porque la 4.8.0 abría en Día y aún no hay médicos configurados).
|
||
- v4.8.0: vistas Mes (calendario) y Semana con todos los médicos filtrados, color por médico. Sin
|
||
migración. Revisado en dev con datos temporales (ya borrados), sin errores de consola.
|
||
|
||
- Módulo `clinic` con permisos por `clinic_staff` (`all` recepción, `own` médico), pacientes, horarios, tipos, citas y bloqueos recurrentes.
|
||
- Migración `036_clinic.sql` **aplicada en dev** (2026-10-28), no en producción (la aplica el actualizador).
|
||
- Probado en dev con usuarios/pacientes temporales (ya borrados), 29/29: configuración por admin que no es
|
||
personal, 403 sin módulo o sin staff, horario de atención, choque médico, choque paciente con otro médico,
|
||
cita contigua permitida, quirófano recurrente (también la semana siguiente), bloqueo sobre cita pide
|
||
confirmación, disponibilidad, concurrencia (6 intentos simultáneos de 2 recepcionistas al mismo hueco →
|
||
1 creada, 5 × 409), médico solo ve/gestiona lo suyo, cancelar exige motivo, auditoría. UI Día revisada.
|
||
- La migración crea el primer staff `all` para el superadmin más antiguo. Configurar el resto del personal, tipos y horarios desde la pestaña de configuración antes de agendar. `clinic` está desactivado por defecto para otros usuarios y el superadmin lo habilita en Organización.
|
||
- Sin commit, push ni despliegue.
|
||
|
||
## Cambio de correo con aprobación (v4.6.0, sin desplegar)
|
||
|
||
- Implementado en Mi cuenta y Organización → Usuarios, con enlaces públicos de aprobación propia del superadmin y confirmación del correo nuevo.
|
||
- Migración `035_email_change_requests.sql` **aplicada en dev** (2026-10-28), no en producción (la aplica
|
||
el actualizador). Probado en dev con usuarios temporales (ya borrados): colaborador→admin aprueba,
|
||
admin→solo superadmin, superadmin→se autoaprueba por enlace al correo actual; sesiones siguen vivas
|
||
hasta confirmar desde el correo nuevo y ahí se cierran todas; login viejo falla y nuevo funciona;
|
||
tokens de un solo uso y nunca en auditoría. Pendiente: envío real con SMTP (dev → log).
|
||
- No se hizo commit ni push.
|
||
|
||
## Calendario de reuniones (v4.4.0, sin desplegar)
|
||
|
||
- v4.5.0: calendario personal para todos los roles (ver SPEC §7), capas Proyectos/Actividades/
|
||
Tareas/Recurrentes/Reuniones con colores, filtros Ver (mío/compartido) y Mostrar (tipos); recurrentes compartidas en solo
|
||
lectura. Probado con un usuario temporal (ya borrado): lo compartido aparece a ambos y lo ajeno
|
||
no. Sin migración nueva. Probado en dev con tareas de prueba (fin de mes 31→30, corte por
|
||
`end_date`, vencida en rojo), ya borradas.
|
||
|
||
- Menú y API de reuniones, recurrencia, RSVP, vencimientos de actividades, correo iCalendar y
|
||
recordatorios in-app implementados en código. `DELETE` marca cancelada toda la serie.
|
||
- Migración `034_meetings.sql` **aplicada en dev** (2026-10-27), **no en producción**. Aplicarla
|
||
antes de arrancar el backend 4.4.0 en prod. El backend de dev en :4019 sigue con el código
|
||
anterior hasta reiniciarlo.
|
||
- Probado en dev con instancia temporal (4099/5197, datos de prueba ya borrados): alta, choque con
|
||
confirmación, validaciones, expansión semanal, rango máx. 93 días, edición con versión (409),
|
||
cancelar ocurrencia (EXDATE/RECURRENCE-ID), cancelar serie (METHOD:CANCEL), RSVP solo
|
||
participante (aviso in-app al organizador), recordatorio in-app a tiempo, UI escritorio/móvil sin
|
||
errores de consola. Pendiente: probar envío real con SMTP (dev no tiene SMTP → log).
|
||
|
||
## Auditoría de seguridad y calidad (v3.0.0, sin desplegar)
|
||
|
||
- Se corrigieron controles de roles, alcance de búsqueda y tareas recurrentes, bootstrap
|
||
concurrente, subida de logo/adjuntos, censura de tokens en logs de la app, revocación JWT,
|
||
límites de ingesta, correo, paginación y accesibilidad base.
|
||
- Migraciones nuevas `028_session_version.sql` y `029_optimistic_versions.sql`: **no aplicadas**.
|
||
El backend nuevo requiere ambas antes de arrancar con una base existente. Tras `028`, los JWT
|
||
anteriores dejan de funcionar y se debe iniciar sesión otra vez.
|
||
- `GET /api/projects` ahora devuelve `{items,total,page,pageSize}`. El frontend de esta versión
|
||
se adaptó; otros clientes deben hacerlo. `POST /api/attachments` requiere entidad en query.
|
||
- Pendiente: vinculación token de inventario a equipo (instaladores actuales comparten token),
|
||
medición SQL `EXPLAIN` con datos reales. Hallazgo 7: falta verificar los logs del proxy NPM
|
||
para confirmar la censura de tokens; pendiente operativo, sin tocar NPM. Hallazgo 11:
|
||
vincular token de inventario a equipo requiere migrar las instalaciones que comparten token;
|
||
pendiente operativo. No se consultó la BD ni producción durante esta tarea.
|
||
- Hallazgo 16 completo en código: edición con versión optimista para proyectos, comentarios,
|
||
actividades, tareas, departamentos, usuarios, los cuatro catálogos, notas, tareas recurrentes,
|
||
equipos, empresa y SMTP. La migración 029 se amplió; sigue **sin aplicar**.
|
||
|
||
## Sesiones activas y Mi cuenta (v4.3.0, sin desplegar)
|
||
|
||
- Migración `033_user_sessions.sql` **aplicada en dev** (2026-10-26), **no en producción**. Aplicarla
|
||
antes de arrancar el backend nuevo; al desplegar, todos inician sesión una vez (JWT sin `sid`).
|
||
- Probado en dev contra una instancia temporal (puerto 4099, usuario de prueba ya borrado): listar,
|
||
cerrar una/las demás/todas, cambio de clave (mantiene la actual), logout real, token sin `sid`,
|
||
admin vs superadmin. El backend de dev en :4019 sigue con el código anterior hasta reiniciarlo.
|
||
|
||
## Como retomar
|
||
|
||
Actualizador v3.0.1: el script publica `logs/update.status`; el panel espera su fin y comprueba
|
||
las versiones activas de backend y `version.json` del frontend antes de activar el Service Worker
|
||
y recargar. Pendiente verificar el flujo completo en un despliegue autorizado; no se tocó producción.
|
||
|
||
1. Leer primero el orquestador: `/home/mgonzalez/desarrollo/project-md-templates/leeme_primero.txt`.
|
||
2. Leer `AGENTS.md`, `SPEC.md`, `PRODUCCION.md` y este archivo.
|
||
3. Backend dev: `cd backend && npm install && npm run dev` (puerto 4019). Requiere `backend/.env`
|
||
(no versionado — completar con las credenciales de la BD dev `project_linkedcorp` en
|
||
`192.168.222.20`; la clave la tiene el usuario).
|
||
4. Frontend dev: `cd frontend && npm install && npm run dev` (puerto 5196, proxy `/api` al backend).
|
||
5. Login dev: `mgonzalez@linkedcorp.net` / clave `mgonzalez` (rol Administrador). Login de
|
||
**producción**: mismo email, clave fuerte generada por `create-admin.js` (no está en este repo,
|
||
se entregó al usuario al desplegar).
|
||
6. Produccion: ver `PRODUCCION.md` y `DEPLOY_WEB.md` para topología, comandos y el flujo de deploy
|
||
de actualizaciones (arquetipo B, dos servicios systemd en `192.168.222.3`).
|
||
|
||
## Hecho y verificado
|
||
|
||
### Menú Equipos (v2.15.0, 2026-10-19) — EN DESARROLLO, NO DESPLEGADO
|
||
|
||
- Inventario de TI con agente GLPI oficial; diseño traído de linkedtrack (que sigue aparte, sin
|
||
tocar): `docs/ARQUITECTURA-INVENTARIO.md`. Backend `modules/devices/` (ingest, normalizer,
|
||
repository, service, routes), migración `023` aplicada en dev, frontend `pages/Equipos/`.
|
||
- Verificado con inventarios sintéticos JSON y XML (alta, actualización, diff, 401 sin token,
|
||
handshake `contact`). **Pendiente: probar con un agente GLPI real** (ver "Pendientes" del doc de
|
||
arquitectura) y ajustar `normalizer.js`/handshake según el JSON real.
|
||
- Antes de producción: migración 023 en `.32`, `npm install` (fast-xml-parser), confirmar que NPM
|
||
deja pasar `POST /api/inventory` desde la red de los equipos y subir versión/deploy.
|
||
|
||
### Fase 1 — MVP en desarrollo (2026-09-03)
|
||
|
||
- Backend completo: auth (JWT + rate limit), users, departments, catalogs, projects (+members),
|
||
activities (+dependencies), subtasks, comments, attachments, notifications, dashboard, audit.
|
||
Recalculo automatico de `progress_pct` verificado.
|
||
- Frontend completo: identidad Fluent/Windows adaptada a web — NavigationView, ContentDialog,
|
||
InfoBar, Toast, Flyout, ListView, ComboBox, Treeview, Tabs, Expander. Paginas: Login, Dashboard,
|
||
Proyectos (listado + detalle), Organizacion, Configuracion. i18n es-EC/en-US, tema claro/oscuro.
|
||
- Verificacion end-to-end con Playwright sin errores de consola.
|
||
|
||
### Iteraciones de UI/UX (2026-09-03 al 2026-09-05)
|
||
|
||
- `DetailPopup`: reemplaza el antipatron "formulario apilado debajo/al costado del listado" en
|
||
TODOS los listados de gestion (Proyectos, Actividades, Departamentos, Usuarios, Miembros,
|
||
Catalogos) — popup de tamaño fijo por instancia, listado inactivo detras, nunca se cierra con
|
||
clic afuera. Regla documentada en `project-md-templates/UI-UX-WINDOWS.md`.
|
||
- Bug de stacking corregido: `ContentDialog` ahora usa portal a `document.body` (antes quedaba
|
||
tapado detras de un `DetailPopup` abierto). Jerarquia de z-index: Flyout(100) < DetailPopup(200)
|
||
< ContentDialog(250) < Toast(300).
|
||
- Bug de remount corregido: las guardas `if (loading || !data) return <Cargando/>` desmontaban
|
||
TODA la pantalla (y el popup abierto dentro) en cada recarga en segundo plano. Cambiadas a
|
||
`if (!data)` — solo bloquean la carga inicial.
|
||
- Bug de clipping corregido: `ComboBox` (y `Flyout`) se posicionan con portal + `position:fixed`
|
||
calculado desde el trigger — antes un ancestro con `overflow:hidden` (Expander, DetailPopup) los
|
||
recortaba. Regla documentada en `UI-UX-WINDOWS.md`.
|
||
- "Miembros" del proyecto es su propia pestaña (antes vivia dentro de "Info").
|
||
- Logo real de LinkedCorp (`/desarrollo/imgLinkedCorp/logoLinkedCorpTxt(Black).png`) en sidebar y
|
||
login, theme-aware, tamaño duplicado respecto a la primera version.
|
||
|
||
### Deploy a produccion (2026-09-06)
|
||
|
||
- BD `project_linkedcorp` creada en `192.168.222.32` (prod) con `crear-db-prod.sh`.
|
||
- Servidor `192.168.222.3`: `/opt/apps/project.linkedcorp.net/{source,backend,frontend,storage,logs}`.
|
||
- Migraciones aplicadas; catalogos sembrados (`seed-db.js`, sin datos de ejemplo en produccion —
|
||
ver decision abajo); usuario Administrador inicial creado con `create-admin.js`.
|
||
- Servicios systemd `project-linkedcorp-backend` (4019) y `project-linkedcorp-frontend` (5196,
|
||
`serve -s dist`), ambos `enabled` + `active`.
|
||
- NPM: Proxy Host id 77 → frontend, Custom Location `/api` → backend, SSL Let's Encrypt forzado.
|
||
- Verificado end-to-end via curl contra `https://project.linkedcorp.net`: health check, login,
|
||
token valido. Detalle completo en `PRODUCCION.md`.
|
||
- **Pendiente no bloqueante**: limpiar `.md` de `/opt/apps/project.linkedcorp.net/source` (paso de
|
||
higiene de `DEPLOY_WEB.md`) — bloqueado por el clasificador de auto-mode del agente durante el
|
||
deploy; requiere ejecutarlo a mano o con autorizacion explicita la proxima vez.
|
||
|
||
### Autorregistro, aprobación, superadmin y SMTP (2026-09-06)
|
||
|
||
- Migración `002_registration_and_smtp.sql`: rol `superadmin`, columnas de ciclo de vida en `users`
|
||
(`status`, tokens de verificación/recuperación), tabla `system_settings`. El usuario más antiguo
|
||
(id=1, creado por `create-admin.js` antes de este feature) se promovió solo a `superadmin`.
|
||
- `POST /api/auth/register`: primer usuario del sistema → `superadmin` con sesión directa
|
||
(bootstrap); los siguientes → `pending_verification`, correo con enlace de un solo uso (24h).
|
||
- `GET /api/auth/verify-email` → `pending_approval` + notificación in-app a `admin`/`superadmin`.
|
||
- `admin`/`superadmin` aprueban (`POST /api/users/:id/approve`, eligen rol/departamento) o rechazan
|
||
(`POST /api/users/:id/reject`, borra y audita el email) desde Organización → Usuarios (nuevo badge
|
||
de estado + acciones de fila).
|
||
- `login()` da mensaje específico según `status` (pendiente de verificar / pendiente de aprobar)
|
||
en vez del genérico "credenciales inválidas".
|
||
- Recuperación de clave (`/olvide-clave` → `/restablecer-clave`): respuesta siempre genérica, token
|
||
de 1 hora de un solo uso.
|
||
- SMTP configurable desde Configuración → Correo (SMTP), **solo superadmin** (oculto para el resto);
|
||
clave cifrada AES-256-GCM (`ENCRYPTION_KEY`) en `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 envío tumbe al que lo llama (crítico para `forgot-password`, que debe responder
|
||
genérico siempre).
|
||
- Bug real corregido: `React.StrictMode` disparaba el efecto de `VerifyEmailPage` dos veces; la
|
||
segunda llamada (token ya consumido) mostraba "enlace inválido" pese a que la verificación había
|
||
funcionado. Se agregó una guarda por token ya solicitado.
|
||
- Deuda técnica de la v1.0.0 resuelta de paso: se reordenó el montaje de rutas en `app.js` —
|
||
`subtasksRoutes` (mount genérico en `/api`) ahora va al final, así ya no intercepta con su
|
||
`requireAuth` interno una request sin token dirigida a un router más específico (`/api/settings`,
|
||
etc.) registrado después.
|
||
- Verificado end-to-end en dev con Playwright: registro → correo logueado (modo dev) → login
|
||
rechazado (pendiente de verificar) → verificar correo → login rechazado (pendiente de aprobar) →
|
||
aprobar desde Usuarios → login exitoso → recuperar clave → login con clave nueva → panel SMTP
|
||
visible solo para superadmin. Sin errores de consola.
|
||
- **Desplegado a producción el mismo día**: migración `002` aplicada sobre `.32`, ambos servicios
|
||
reconstruidos y reiniciados (PID confirmado), verificado via curl contra
|
||
`https://project.linkedcorp.net` (login reporta `superadmin`, `GET /api/settings/smtp` responde
|
||
`configured:false`). Detalle en `PRODUCCION.md` → "Actualización v1.1.0".
|
||
- **Pendiente real**: el superadmin de producción debe cargar el SMTP real desde Configuración →
|
||
Correo (SMTP) — sin eso, nadie externo puede completar el autorregistro en producción (el enlace
|
||
de verificación solo queda logueado en el servidor, no llega por correo de verdad).
|
||
|
||
### Cambiar clave desde la app (v1.2.0, 2026-09-06)
|
||
|
||
- `PUT /api/auth/change-password` (exige la clave actual, valida clave nueva ≥ 8 caracteres) +
|
||
opción "Cambiar clave" en el menú de usuario (popup), para cualquier usuario logueado —
|
||
independiente de "olvidé mi clave", que es para cuando no se puede iniciar sesión.
|
||
- Desplegado a producción el mismo día; verificado via curl que rechaza clave actual incorrecta
|
||
con 400 (no se tocó la clave real del superadmin durante la prueba).
|
||
|
||
### Boton de prueba real en Correo (SMTP) (v1.3.0, 2026-09-06)
|
||
|
||
- El usuario reportó, con razón, que la pantalla de SMTP no tenía forma de confirmar que la
|
||
configuración guardada funcionara de verdad (sin botón de prueba ni campo de destinatario) — una
|
||
omisión básica para una pantalla de config de un servicio externo.
|
||
- `POST /api/settings/smtp/test` (superadmin, rate-limited a 10/15min): usa los valores actuales
|
||
del formulario (aunque no se hayan guardado, cae a la clave ya guardada si el campo viene vacío)
|
||
y hace un envío real. A diferencia de `sendMail()` (correo de negocio, nunca lanza), este
|
||
endpoint sí propaga el error real del servidor SMTP — es diagnóstico explícito del superadmin.
|
||
- Panel de Configuración → Correo (SMTP): nueva sección "Probar configuración" con campo de
|
||
destinatario, botón "Enviar prueba" y resultado real (éxito o el mensaje de error tal cual).
|
||
- Verificado en dev con curl (400 sin host/usuario) y Playwright (formulario + envío real contra
|
||
el SMTP interno con credenciales falsas → error real "certificate has expired" mostrado en la
|
||
UI). Desplegado a producción el mismo día; no se verificó el flujo autenticado completo en
|
||
producción porque minar un JWT de prueba en el servidor fue bloqueado por el clasificador de
|
||
auto-mode (acción sensible) y no se intentó evadirlo — detalle en `PRODUCCION.md`.
|
||
- Regla nueva agregada al orquestador (`project-md-templates/SEGURIDAD-Y-CALIDAD.md`, sección
|
||
1.6bis + checklist): toda pantalla de configuración de un servicio externo debe incluir un botón
|
||
de prueba real, para que esta clase de omisión no se repita en este ni otros proyectos.
|
||
|
||
## Pendiente (fase 2, sin comprometer lo ya construido)
|
||
|
||
- Riesgos e Incidencias como modulos propios (hoy: `risk_level` simple en el proyecto).
|
||
- Solicitudes/requerimientos formales de otras areas.
|
||
- Recursos (servidores/licencias/software), Proveedores, Presupuesto y costos.
|
||
- Registro de horas trabajadas detallado (bitacora diaria).
|
||
- Reportes exportables (PDF/Excel/CSV) y vista ejecutiva separada.
|
||
- Permisos por accion dentro de cada modulo (hoy: acceso por modulo y usuario desde v3.1.0 +
|
||
roles fijos codificados).
|
||
- IA (hecho hasta v4.2.0: config, "Plan con IA", "Nuevo con IA" con duplicados, consumo por usuario,
|
||
"Informe con IA"). Ideas siguientes: resumen semanal del Panel, detector de riesgos, resumir
|
||
comentarios en tareas, dividir tareas grandes, responsables por actividad entre miembros.
|
||
- Notificaciones por email/WhatsApp (hoy: solo in-app).
|
||
- Job programado para detectar actividades vencidas/por vencer (hoy: solo se notifica al asignar).
|
||
- DatePicker: sigue usando `<input type="date">` nativo (simplificacion deliberada del MVP).
|
||
- En producción, crear los departamentos/usuarios reales de la empresa desde la UI (arranca limpia
|
||
a proposito, ver decision abajo).
|
||
|
||
## Decisiones clave
|
||
|
||
- MVP simplificado respecto al listado original de 30 areas — detalle en `SPEC.md`.
|
||
- Identidad UI/UX: `UI-UX-WINDOWS.md` (Fluent/Windows 11), no la identidad web estandar del grupo.
|
||
- Empresa unica (single-company): no existe tabla `companies`.
|
||
- Arquetipo de despliegue B (dos servicios separados).
|
||
- Produccion arranca sin datos de ejemplo: `seed-db.js` solo carga catalogos universales
|
||
(roles/prioridades/estados/tipos) cuando `NODE_ENV=production`; los departamentos y usuarios de
|
||
ejemplo (TI/Finanzas/RRHH/Comercial, admin con clave=usuario) son SOLO de dev. El admin real de
|
||
produccion se crea con `create-admin.js` (clave fuerte generada, nunca clave=usuario).
|
||
- Autorregistro con doble candado (correo + aprobacion admin) — decision explicita del usuario,
|
||
revierte la regla inicial "sin autosuscripcion". El primer usuario del sistema es `superadmin`
|
||
automatico; el resto pasa por verificacion + aprobacion. SMTP vive en BD (cifrado), configurable
|
||
solo por el superadmin desde la UI, no en `.env`.
|
||
|
||
## Deuda tecnica
|
||
|
||
- `npm audit`: 3 vulnerabilidades moderadas en `qs`/`body-parser`, transitivas de Express 4.x (sin
|
||
fix no-breaking disponible al 2026-09-06). No critico; revisar cuando Express publique el fix.
|