panels-origin/db/README.md
Cursor Agent b87b0205a8
db: migrar infraestructura de BD a Postgres (fase 0-1)
- Fase 0: scripts de aprovisionamiento (db/provision/) para roles, dos
  bases de datos separadas (panels_platform / panels_product con
  esquemas iam+core) y ACLs de Redis por modulo, con verificacion
  automatizada de aislamiento (verify-isolation.sh) y setup local
  reproducible (dev-local.sh).
- Fase 1: changelogs de Liquibase reescritos para Postgres
  (db/platform, db/iam, db/core reemplazan db/app + los changesets
  SQLite de platform). Baseline como estado final (no replay literal),
  tipos traducidos (IDENTITY, TIMESTAMPTZ/DATE, NUMERIC, BOOLEAN,
  CITEXT), contexts dev vs. schema/catalogos, RLS por tenant_id como
  defensa en profundidad, uploaded_by/created_by como snapshot
  desnormalizado (sin FK hacia iam).
- Migraciones ya no corren en el arranque de la API: paso explicito de
  deploy via db/update.sh con credenciales _owner.

Verificado end-to-end contra Postgres 16 + Redis local.

Co-authored-by: alberto.martinez <alberto.martinez@mrdev.mx>
2026-09-02 20:00:49 +00:00

3.4 KiB

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                        # staging/producción: solo esquema + catálogos

# Un solo módulo
./db/update.sh core
./db/update.sh iam
./db/update.sh platform

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-filter explí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:postgresql en vez de context para 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.sh las 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.