Files
project.linkedcorp.net/ESTADO.md
T
2026-10-29 15:30:21 -05:00

278 lines
21 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ESTADO — project.linkedcorp.net
Ultima actualizacion: 2026-10-29 (v4.11.0 commit + push, sin 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)
- 2026-10-29 (v4.11.0): refactor de la pantalla en dev. Formularios cita/paciente/bloqueo con el modelo de "Nueva reunión" (`ClinicForms.jsx`), Pacientes y Bloqueos en `ListView`, Configuración como listados + popup lectura/Editar (`ClinicConfigTab.jsx`; se quitó el campo "ID de usuario"). Corregido: paciente/cédula que se arrastraba a la siguiente cita (borrador huérfano al cancelar "Nuevo paciente") y caída al "Descartar cambios" en un registro nuevo. Probado en navegador (recepción y admin temporal), sin errores de consola.
- 2026-10-29 (v4.11.0): aviso sin bloqueo al elegir paciente en una cita si tiene citas futuras agendadas/confirmadas (lista hasta 5; excluye la cita que se edita). Diálogo compartido "Cambios sin guardar": botones ya no se desbordan (`app.css`).
- 2026-10-29: BD de dev con datos de prueba de la agenda (15-oct a 28-nov): 7 médicos (`own`) y recepción `pruiz`, `jcabrera` (`all`) `@agenda.prueba` + mgonzalez, 60 pacientes (nota `[DATO DE PRUEBA]`), 4 tipos "(prueba)", 569 citas, 6 bloqueos. Script: `seed-agenda.cjs` del scratchpad de la sesión (con `--limpiar` los borra).
- 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.