# 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.