Saltar a contenido

ADR-010 · cis-admin · rewrite frontend a Vite+React+TS (backend FastAPI preservado)

Fecha: 2026-04-27 Source: /srv/projects/cis/cis-plan/DECISIONS.md (do not edit here — re-split desde la fuente)


Contexto: admin.innovacionsantiago.cl tiene hoy tres stacks coexistentes en el mismo repo (/srv/projects/cds/cis/cis-admin/):

  1. Django legacy (cds_admin.settings, manage.py, ~10 años de UI productiva — remanente del cds-admin original migrado en 2026-04-09).
  2. FastAPI en :8244 — backend nuevo con 30+ routers, modelos SQLAlchemy async, integración Authentik OIDC, Claudia v1 embebida (services/claudia_*.py + 3 timers systemd).
  3. Next.js 14 app router en :8245 — frontend reemplazando al Django UI, ~15 páginas ((authenticated)/usuarios, /contabilidad, /documentos, /tributario, /finanzas, /nomina, etc.), middleware de cookies + tanstack-query + zustand + tailwind 3.4.

El parking lot de este DECISIONS.md (item #1) lleva abierta la pregunta desde el 2026-04-08: ¿refactor Django in-place o rewrite FastAPI+Next.js? — esa pregunta ya tiene respuesta parcial (FastAPI ganó como backend, Next.js como frontend nuevo). Pero el frontend Next.js, sin haber alcanzado paridad funcional con Django, ya muestra fricciones que duelen:

  • Bundle pesado para una SPA interna: Next.js 14 (RSC + App Router + Image opt + middleware edge) está pensado para sites SEO con mezcla server/client. El admin no necesita SSR ni SEO (es noindex, nofollow); 100% de las pantallas requieren login. Pagamos el costo de Next sin usar nada de su valor agregado.
  • Build time: cada next build toca tsc + webpack + middleware compile + page compile; ~25–40s en vps-cis. Vite hace lo mismo en ~3–5s.
  • Dos servicios systemd en lugar de uno: cis-admin-frontend.service (Node :8245) corre 24/7 sólo para servir el SSR de páginas que igual son client-side. Vite genera estático → Caddy sirve → cero proceso Node en runtime.
  • Inconsistencia con cis-infra: el panel hermano infra.innovacionsantiago.cl (operaciones técnicas) ya está en Vite+React+TS y consume el shell federado de style.innovacionsantiago.cl/v6/ (cis.min.css + shell.min.js con <cis-navbar>). El admin no puede reusar ese pattern fácilmente porque Next.js inyecta su propio runtime.
  • Auth en frontend duplica al gate de Caddy: el middleware.ts actual lee cookies session+authed desde el browser para hacer un fast-path. Esa lógica ya la cubre la combinación Caddy auth-forward-only + Authentik outpost que usa cis-infra (gate al borde, headers X-Authentik-*, SPA solo lee /api/_me). El stack legacy mantiene cookies firmadas de Starlette por compat con Django (cookie session httponly de Starlette + cookie redundante authed post-fix 2026-04-15).
  • Complejidad de cutover: con shell federado y SPA estática, una nueva versión del admin se publica con un cp -r dist/* /srv/projects/cis/cis-admin/dist/ + reload Caddy. No hay rolling de un service Node.

Este ADR no toca el backend FastAPI (:8244 — 30+ routers, modelos, tests, OIDC, Authentik integration). Se preserva tal cual; el rewrite es solo frontend.

Alternativas consideradas:

  1. Refactor in-place del Next.js (mantener stack) — corregir los pain points (eject de RSC, cleanup de middleware, optimización de bundle). Costo: trabajo continuo de mantenimiento; sigue requiriendo el service Node :8245 y next build lento. Beneficio: cero riesgo de migración, los devs siguen escribiendo en el mismo framework.
  2. Refactor in-place del Django legacy (la pregunta original del parking lot, dirección opuesta) — descartada implícitamente desde 2026-04-08 cuando FastAPI ganó como backend; el Django UI quedó en modo cascarón pendiente de decommission. Reactivar Django ahora sería retroceder.
  3. Rewrite a Vite+React+TS preservando FastAPIelegida. Alinea con cis-infra, elimina un service systemd, build 5–8× más rápido, bundle ~3× más chico, auth se reduce a leer /api/_me (gate real en Caddy). Costo: hay que migrar ~15 páginas, pero se hace por fase y ambas apps coexisten durante la transición. La SPA nueva consume los mismos endpoints /api/v1/* del FastAPI sin cambios en backend.
  4. Rewrite a otro framework (Remix, SolidStart, SvelteKit, Astro). Descartada: cis-infra ya estableció Vite+React como el stack de paneles internos; agregar un framework distinto fragmenta el ecosistema y requiere un segundo conjunto de design tokens / componentes.

Decisión: el frontend de cis-admin se reescribe como SPA Vite + React 18 + TypeScript servida estática desde Caddy (/srv/projects/cis/cis-admin/dist). Stack:

Capa Tooling
Build Vite 5+ con @vitejs/plugin-react
Lenguaje TypeScript 5+, JSX react-jsx
UI runtime React 18 (no 19 todavía — alinear con tanstack-query estable)
Routing react-router-dom v7
Server state @tanstack/react-query v5
Estilos Tailwind 3.4 + cis.min.css v6 federado de style.innovacionsantiago.cl
Shell global <cis-navbar> + <cis-footer> vía shell.min.js v6
Auth Lectura de /api/_me (Caddy outpost forward_auth + headers X-Authentik-*)
Tests vitest + @testing-library/react + msw
Path /srv/projects/cis/cis-admin/frontend/ (sibling del backend/ legacy)
Build out /srv/projects/cis/cis-admin/dist/

El backend FastAPI :8244 se preserva sin cambios. Los endpoints /api/v1/* que la SPA consume son los mismos que ya consume el frontend Next.js — la SPA es un cliente nuevo del mismo backend.

El cis-admin-legacy (Django + Next.js) sigue corriendo en producción durante toda la migración. La SPA nueva es opt-in mediante feature flag ?next=1 (cookie cis-admin-next=1 persistida) hasta que la paridad funcional sea suficiente para invertir el default.

Plan de migración por fases:

  • Fase A · Bootstrap (esta sesión, 2026-04-27) — esqueleto Vite+React+TS+Tailwind+react-router-dom+tanstack-query+vitest+msw en /srv/projects/cis/cis-admin/frontend/. AuthContext que lee /api/_me. Pantalla / con dashboard placeholder + ping a /api/v1/health del backend. Build genera /srv/projects/cis/cis-admin/dist/index.html listo para Caddy. Site block diseñado pero NO aplicado: el cutover de Caddy requiere OK del user. Tests vitest + msw verde. Commits + cis-note al cerrar.
  • Fase B · /usuarios + /monitoring — migrar las dos rutas con backend más nuevo y mejor probado: /usuarios (panel unificado con /api/v1/users/unified, agregado 2026-04-22 — ver AGENT-CONTEXT.md) y /monitoring (snapshots vía /api/v1/monitoring/ingest + monitoring_snapshots agregado por agent-m-monitoreo, 2026-04-22). Son las mejores candidatas porque tienen contratos limpios y tests backend.
  • Fase C · /documentos + /firmas — migrar la sección legal (PDF preview, firmas pendientes, plantillas) que ya está parcialmente en cis-docs (docs.innovacionsantiago.cl, 2026-04-17). Decidir si /documentos del admin se fusiona con cis-docs o si comparten endpoints.
  • Fase D · /contabilidad + /tributario + /finanzas — el bloque más grande (datos tributarios reales en cis_admin). Migración con feature parity 1:1 antes de invertir el flag default.
  • Fase E · Deprecar legacy — invertir el default (?next=1 deja de ser opt-in, el legacy queda detrás de ?legacy=1). Después: detener cis-admin-frontend.service, archivar cds_admin/ Django, mover puerto :8245 a libre. Backend FastAPI :8244 se mantiene.

Los timers Claudia v1 (cis-admin-claudia-followup.timer, cis-admin-heartbeat.timer, cis-admin-pulse.timer) son ortogonales a este ADR — están dentro del backend FastAPI, no del frontend. Su decommission depende del rewrite Claudia v1→v2 (ADR pendiente, separado).

Consecuencias:

  • Bundle: ~150–250 KB raw (~80–95 KB gz) esperado original. Bumped a 280 KB raw (2026-05-04, post A.5 panel surfaces) tras agregar 6 surfaces nuevas (/sudo /vault /alerts /plans /agents /approvals) en Bloque A. Todo en lazy chunks separados de 2.86–4.68KB cada uno, core 252.24KB raw / 77.93KB gz. Si supera 280KB raw, considerar code-splitting más agresivo o extracción de libs comunes.
  • Build: ~3–5s para vite build + ~2–3s para tsc -b. Ganancia ~5–8× sobre Next.
  • Service systemd: en Fase E, baja cis-admin-frontend.service (-1 service de los 22+ del repo). El monitoreo (cis-monitoreo, agent-m, 2026-04-22) hay que actualizarlo para que no marque el service como caído al desinstalarlo.
  • Auth: la SPA nueva NO usa middleware.ts de Next ni cookies firmadas de Starlette. Usa el outpost Authentik de Caddy (mismo patrón que cis-infra + cis-docs + idea.indieweb.cl). Esto requiere registrar un provider Authentik forward_auth para admin.innovacionsantiago.cl análogo al infra-innovacionsantiago-forward-authpendiente cuando arranque Fase B, no necesario para Fase A (la SPA en bootstrap puede leer un stub /api/_me o hacer dev sin auth real).
  • Coexistencia: durante Fases A–D, admin.innovacionsantiago.cl sirve el legacy Next.js por default y la SPA nueva sólo si la URL trae ?next=1 o si el browser tiene cookie cis-admin-next=1. El alias admin.circulodesantiago.cl mantiene el redirect 301 (no se ramifica).
  • Datos tributarios reales en cis_admin: este ADR no toca esquema ni datos. La SPA es un nuevo cliente del backend existente.
  • Puertos: el reservado :8211 en CLAUDE.md ("nuevo backend post-cutover") queda obsoleto — el frontend nuevo no es un service, es estático. Se libera. Backend sigue en :8244.
  • Tests: vitest + msw para frontend; los tests pytest del backend FastAPI son inmutables.
  • Rollback: si la SPA no funciona, el flag se desactiva (cookie clear) y el legacy sigue siendo el default. Fase E es la única irreversible (decomm del Next.js); todo lo anterior es dual-run.
  • cis-monitoreo (29 servicios): durante Fases A–D no cambia (cis-admin-frontend sigue corriendo). En Fase E, ajustar la lista de servicios monitoreados.
  • AGENT-CONTEXT.md de cis-admin: actualizar el header "3 stacks coexisten" → "2 stacks coexisten (FastAPI + Next legacy + SPA Vite nueva en migración)" cuando arranque Fase B.

Relaciones: - Resuelve el item #1 del parking lot (refactor Django in-place vs rewrite). Respuesta: rewrite, en dos pasos — backend ya migró a FastAPI (decisión 2026-04-08), frontend ahora migra a Vite+React (este ADR). Django queda en modo cascarón hasta Fase E. - Es independiente del rewrite Claudia v1→v2 (ADR pendiente, sin número asignado): los timers Claudia están en el backend, no en el frontend. - Refuerza ADR-014 (consolidación user canon Google-primary): la SPA leerá authentik_sub, email, picture desde /api/_me igual que cis-infra. No re-implementa OIDC. - Alinea con el patrón establecido por cis-infra (panel infra, 2026-04-14) + cis-docs (legal, 2026-04-17) + idea.indieweb.cl (waitlist, 2026-04-17): paneles internos = Vite+React+TS + shell federado + Caddy auth-forward outpost.

Implementación: Fase A aplicada en sesión 2026-04-27. Ver canal cis-note y commits en /srv/projects/cis/cis-admin/. Las fases B–E quedan en el backlog.