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/):
- Django legacy (
cds_admin.settings,manage.py, ~10 años de UI productiva — remanente del cds-admin original migrado en 2026-04-09). - FastAPI en :8244 — backend nuevo con 30+ routers, modelos SQLAlchemy async, integración Authentik OIDC, Claudia v1 embebida (services/claudia_*.py + 3 timers systemd).
- 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 buildtoca 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 destyle.innovacionsantiago.cl/v6/(cis.min.css+shell.min.jscon<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.tsactual lee cookiessession+autheddesde el browser para hacer un fast-path. Esa lógica ya la cubre la combinación Caddyauth-forward-only+ Authentik outpost que usa cis-infra (gate al borde, headersX-Authentik-*, SPA solo lee/api/_me). El stack legacy mantiene cookies firmadas de Starlette por compat con Django (cookiesessionhttponly de Starlette + cookie redundanteauthedpost-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:
- 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 buildlento. Beneficio: cero riesgo de migración, los devs siguen escribiendo en el mismo framework. - 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.
- Rewrite a Vite+React+TS preservando FastAPI ← elegida. 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. - 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/healthdel backend. Build genera/srv/projects/cis/cis-admin/dist/index.htmllisto 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_snapshotsagregado 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=1deja de ser opt-in, el legacy queda detrás de?legacy=1). Después: detenercis-admin-frontend.service, archivarcds_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 paratsc -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.tsde 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 paraadmin.innovacionsantiago.clanálogo alinfra-innovacionsantiago-forward-auth— pendiente cuando arranque Fase B, no necesario para Fase A (la SPA en bootstrap puede leer un stub/api/_meo hacer dev sin auth real). - Coexistencia: durante Fases A–D,
admin.innovacionsantiago.clsirve el legacy Next.js por default y la SPA nueva sólo si la URL trae?next=1o si el browser tiene cookiecis-admin-next=1. El aliasadmin.circulodesantiago.clmantiene 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.mdde 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.