From 5f9c7cf00006465fcb9496fb5603b878bff060c4 Mon Sep 17 00:00:00 2001 From: Ernesto Herrera Date: Mon, 20 Jul 2026 07:34:30 -0600 Subject: [PATCH] =?UTF-8?q?docs(deploy):=20runbook=20de=20despliegue=20a?= =?UTF-8?q?=20producci=C3=B3n=20(auth=20SIWEB=20+=20RBAC)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pasos para que un operador con acceso a prod despliegue con seguridad: env, build del frontend en CI, up del stack, seed + sync de permisos, smoke test y rollback. Incluye feature-flag SESSION_STORE_ENABLED como mitigación rápida. Co-Authored-By: Claude Opus 4.8 (1M context) --- deploy/RUNBOOK-produccion.md | 162 +++++++++++++++++++++++++++++++++++ 1 file changed, 162 insertions(+) create mode 100644 deploy/RUNBOOK-produccion.md diff --git a/deploy/RUNBOOK-produccion.md b/deploy/RUNBOOK-produccion.md new file mode 100644 index 0000000..301ee89 --- /dev/null +++ b/deploy/RUNBOOK-produccion.md @@ -0,0 +1,162 @@ +# Runbook — Despliegue a PRODUCCIÓN · CRM Agente de Carga + +> **Quién ejecuta:** un operador con acceso al servidor de producción y a la base de +> datos de prod. **Este runbook no lo ejecuta ningún agente automático.** +> **Prerrequisito de proceso:** el PR de `feature/crm-workspace-altas-org-usuario` +> debe estar **revisado y mergeado** a la rama que corresponda antes de desplegar. +> +> **Antes de empezar: respaldo.** Toma un backup de la base `crm_core` de prod +> (`pg_dump`) y del `.env` actual. Sin respaldo verificado, no continúes. + +Este cambio es grande (auth/RBAC): login 100% vía Workspace/Hub, **sesión local +(patrón SIWEB)**, gestión de compañías/usuarios/roles y provisión vía Hub. Léelo +completo antes de tocar prod. + +--- + +## 0. Alcance del cambio + +- **Auth:** el login deja de hablar directo con Keycloak; todo pasa por el Hub + (App Launcher → `/auth/sso?relay=` → `POST {HUB_URL}/api/v1/auth/sso-exchange`). +- **Sesión local:** cookie de sesión propia (HS256) + token KC guardado en **valkey** + por `crm_sid`. Requiere valkey arriba. Se controla con `SESSION_STORE_ENABLED`. +- **RBAC/Compañías:** compañías en `a76.company` por tenant; roles por carril + (Ventas, Operaciones, Facturación, Consulta); permisos por módulo. + +**Migraciones de esquema:** este set **no** agrega tablas nuevas (usa `core.*`, +`a76.company` y valkey). Aun así, **verifica migraciones pendientes** en prod antes +de desplegar (paso 5). No corras migraciones a ciegas. + +--- + +## 1. Prerrequisitos de infraestructura + +- [ ] DNS de prod (p. ej. `crm.aduanasoft.com`) apuntando al server de prod. +- [ ] Docker + Docker Compose en el server. +- [ ] Servicios del stack: `backend`, `frontend`, `postgres`, `valkey`, `minio`, + `celery_worker`, `celery_beat`. +- [ ] **valkey** operativo (lo usan la sesión local y `hub_token`). +- [ ] nginx + TLS (certbot) para servir app + API en el **mismo origen**. +- [ ] Acceso al **Hub de producción** (no el de testing) y al client/realm correctos. + +--- + +## 2. Variables de entorno (`.env` en el server de prod) + +Basado en `deploy/env.testing.example`, pero con **hosts, dominio y secretos de +producción**. Nunca subas el `.env` con secretos al repo. + +```env +# --- Seguridad --- +ENVIRONMENT=production +DEV_LOCAL_AUTH=false +SECRET_KEY= + +# --- Sesión local (patrón SIWEB) --- +SESSION_STORE_ENABLED=true +SESSION_IDLE_MINUTES=30 +SESSION_MAX_HOURS=10 +VALKEY_URL=redis://valkey:6379/0 + +# --- Workspace / Hub de PRODUCCIÓN (mismo Hub que genera el relay) --- +WORKSPACE_URL=https:// +HUB_URL=https:// +INTERNAL_HUB_URL=https:// +VITE_HUB_URL=https:// + +# --- Keycloak (single-realm / single-client) --- +KEYCLOAK_URL=https:///kcauth +VITE_KEYCLOAK_URL=https:///kcauth +KEYCLOAK_REALM=master +KEYCLOAK_CLIENT_ID=aduanasoft +KEYCLOAK_CLIENT_SECRET= + +# --- Dominio del CRM (mismo origen app + API vía nginx) --- +ORIGIN=https:// +APP_PUBLIC_URL=https:// +VITE_API_URL=https:///api/ +INTERNAL_API_URL=http://backend:8000/api/ +CORS_ORIGINS=https:// + +# --- PostgreSQL --- +CORE_DB_HOST=postgres +CORE_DB_PORT=5432 +CORE_DB_NAME=crm_core +CORE_DB_USER= +POSTGRES_APP_PASSWORD= + +# --- MinIO / S3 --- +S3_ENDPOINT_URL=http://minio:9000 +S3_ACCESS_KEY= +S3_SECRET_KEY= +S3_BUCKET=crm +S3_REGION=us-east-1 +S3_USE_SSL=false +``` + +> **Importante:** `ENVIRONMENT=production` hace que el bootstrap de permisos solo dé +> `super_admin` al **primer** usuario (no a todos). Es el comportamiento deseado en prod. + +--- + +## 3. Build del frontend — **en CI, no en el server** + +La VM de prod no debe compilar el frontend (el build satura RAM). Compila la imagen +en **Jenkins/CI** y publícala al registry, o compílala en una máquina de build: + +```bash +docker compose -f docker-compose.yml -f docker-compose.prod.yml build frontend +# push al registry interno si aplica +``` + +El backend usa la imagen/código directamente (uvicorn sin --reload). + +--- + +## 4. Despliegue + +```bash +# En el server de prod, en el directorio del proyecto: +docker compose -f docker-compose.yml -f docker-compose.prod.yml pull # si usas registry +docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d +docker compose ps # verifica que todos queden healthy +``` + +--- + +## 5. Post-despliegue (datos) — **operador humano, con respaldo hecho** + +1. **Verificar migraciones pendientes** (si el proyecto usa Alembic u otro): + revisar y aplicar **solo** las que correspondan, con backup previo. +2. **Seed base** (compañía por tenant + carriles), equivalente a `seed_crm.py` + (`ensure_company` + roles Ventas/Operaciones/Facturación/Consulta). +3. **Sync de permisos** (registra el catálogo por módulo): + +```bash +docker compose exec backend python -c "from api.v1.modules.core.permissions.service import PermissionService; from core.database import CoreSessionLocal; PermissionService(CoreSessionLocal()).sync_permissions()" +``` + +--- + +## 6. Verificación / smoke test + +```bash +curl -fsS https:///api/health && echo OK +``` + +- [ ] Login vía **App Launcher del Workspace** entra sin bucle. +- [ ] El dashboard carga compañías del tenant (switcher con el tenant correcto). +- [ ] **Usuarios**: lista carga tras refrescar; alta por invitación crea enlace. +- [ ] **Roles y permisos**: catálogo por módulo carga; marcar un permiso lo guarda + (toast) y persiste al refrescar. +- [ ] Licencia válida (sin bucle 401 / "sesión expirada"). + +--- + +## 7. Rollback + +1. `docker compose ... up -d` con la **imagen/tag anterior** del frontend/backend. +2. Restaurar `.env` anterior si se cambió. +3. Restaurar el backup de `crm_core` **solo** si hubo cambios de datos irreversibles. +4. `SESSION_STORE_ENABLED=false` revierte al comportamiento previo de sesión sin + redeploy de código (feature-flag), como mitigación rápida.