Fase 5 (migracion de datos): - api/scripts/migrate-sqlite-to-postgres.ts: ETL unico SQLite -> Postgres. Pre-flight (CURP/RFC duplicados case-insensitive, FKs huerfanas, tenant_id invalido, fechas mal formateadas) -> carga por base/esquema con credenciales _owner (setval de secuencias, OVERRIDING SYSTEM VALUE, ON CONFLICT DO NOTHING idempotente) -> verificacion de conteos y sumas de dinero con tolerancia. Probado end-to-end contra un dataset SQLite legacy sintetico y un Postgres limpio (solo schema+catalogos, sin contexto dev): 100% de las filas migradas, sumas de dinero exactas, snapshot de uploaded_by/created_by resuelto correctamente. - db/RUNBOOK-corte.md: checklist go/no-go para el corte por ambiente. - Fix de bug real encontrado al probar: sin --context-filter explicito, Liquibase corre TAMBIEN los changesets context=dev (comportamiento por defecto, no "modo seguro") -- documentado en db/README.md con el ejemplo correcto (--context-filter='!dev' para staging/produccion). Fase 5b: patron de compensacion para createTenant ya resuelto en el rewrite de saas.ts (fase 3/4). Fase 6 (backups): db/backups/ con plantillas pgBackRest por base, politica de retencion, nota de persistencia minima de Redis (RDB, sin retencion de negocio), checklist de simulacro de restauracion mensual, y la validacion pendiente de si Coolify permite WAL archiving antes de comprometerse a PITR real. Fase 7 (limpieza y CI): - Dockerfile.api simplificado (sin JRE/Liquibase/FFI). Dockerfile.migrate y Dockerfile.provision nuevos, de un solo uso, para el paso explicito de deploy (nunca sirven trafico). - docker-compose.yml: postgres+redis+provision+migrate para dev local end-to-end; api ya no arranca hasta que migrate termina bien. - docs/coolify.md y .env.example actualizados al modelo de 2 bases + Redis + Contabo. - api/schema.sql eliminado (desincronizado, competia con Liquibase como fuente de verdad). - .github/workflows/ci.yml: Postgres+Redis de servicio, aprovisiona roles/ACLs, dry-run de Liquibase (updateSQL) antes de aplicar, verify-isolation.sh, deno check + deno test, build de los dos frontends. Co-authored-by: alberto.martinez <alberto.martinez@mrdev.mx> |
||
|---|---|---|
| .. | ||
| backups | ||
| core | ||
| iam | ||
| platform | ||
| provision | ||
| bootstrap-tools.sh | ||
| README.md | ||
| RUNBOOK-corte.md | ||
| update.sh | ||
Base de datos (Postgres + Liquibase)
PANELS usa Postgres organizado como monolito modular: dos bases de datos físicamente separadas en el mismo servidor/instancia por ambiente.
| Base de datos | Esquema(s) | Changelog | Qué vive ahí |
|---|---|---|---|
panels_platform |
public |
db/platform/ |
Control plane SaaS: tenants, platform_users (identidad de PANELS como operador), smtp_settings. Solo lo tocan las rutas /v1/saas/*. |
panels_product |
iam |
db/iam/ |
Identidad/roles/permisos de cada tenant (ej. usuarios de ARCTEC). Aislado de core -- sin JOIN/FK cruzado. |
panels_product |
core |
db/core/ |
Negocio: empresas, personal, obras, expedientes, presupuesto, nómina, gafetes. |
No hay FK real entre panels_platform y panels_product (bases distintas),
ni entre los esquemas iam y core (aislamiento a propósito). tenant_id
es una referencia lógica validada en la capa de aplicación. Ver el plan de
migración para el detalle de la arquitectura y las reglas del monolito
modular.
Aprovisionamiento (una vez por ambiente, antes de Liquibase)
Ver db/provision/README.md: crea los 6 roles de
Postgres (panels_{platform,iam,core}_{owner,app}), las 2 bases de datos,
los esquemas iam/core, la extensión citext, y los usuarios ACL de
Redis. Incluye dev-local.sh para reproducir todo esto en una máquina de
desarrollo sin depender de Coolify, y verify-isolation.sh para confirmar
que el aislamiento realmente se cumple.
Aplicar migraciones
Requiere Java 17+ y las credenciales del rol _owner de cada módulo (nunca
_app, que es solo runtime):
# Local, después de correr db/provision/dev-local.sh:
set -a && source .env.dev-local && set +a
./db/update.sh all --context-filter=dev # dev: incluye datos de demo
./db/update.sh all --context-filter='!dev' # staging/producción: solo esquema + catálogos
# Un solo módulo
./db/update.sh core --context-filter='!dev'
./db/update.sh iam --context-filter='!dev'
./db/update.sh platform --context-filter='!dev'
Importante: Liquibase, si NO se le pasa --context-filter, corre TODOS
los changesets sin importar su context -- incluidos los de context="dev".
No pasar el filtro en staging/producción NO es "modo seguro por defecto",
es lo contrario: cargaría el tenant/empresas/proyecto de demostración. Por
eso --context-filter es obligatorio siempre, nunca opcional.
npm run db:migrate corre ./db/update.sh all. Las migraciones ya NO
corren automáticamente al arrancar la API -- son un paso explícito de
deploy (a diferencia del runLiquibase() que existía con SQLite).
Contexts de Liquibase:
- Sin
context: changesets de esquema y catálogos requeridos (risk_levels,document_types, etc.) -- corren en todos los ambientes. context="dev": datos de demostración (tenant/empresas/proyecto ARCTEC) -- nunca en staging/producción. Siempre pasar--context-filterexplícito; no confiar en el default de Liquibase.
Nuevos cambios de schema
Un changeset nuevo en db/{platform,iam,core}/changesets/, referenciado en
el changelog-master.xml correspondiente. Reglas:
- Nunca editar un changeset ya aplicado en un ambiente compartido -- crear uno nuevo.
- Usar
dbms:postgresqlen vez decontextpara lógica específica de motor. - No añadir migraciones directamente en
api/*.ts. - Las herramientas (Liquibase + driver JDBC de Postgres) viven en
db/tools/(gitignored);bootstrap-tools.shlas descarga.
Bootstrap del primer usuario administrador
El password_hash (PBKDF2) no lo puede generar un changeset SQL. El primer
platform_admin y el primer tenant_admin de un tenant nuevo se crean con
scripts/bootstrap-admin.ts (ver Fase 4 del plan de migración), no con
lógica de seed en el arranque de la API ni con datos hardcodeados en
Liquibase.