# Integración CloudRestoreAS ↔ PANEL_BASES_ANEXO24 ## Variables de entorno y configuración | Panel (`.env`) | CloudRestoreAS (pestaña Config → PANEL) | Debe coincidir | |----------------|-------------------------------------------|----------------| | `CLOUDRESTORE_API_TOKEN` | `panel.api_token` | **Sí** — mismo valor en todas las instalaciones | | `ENCRYPTION_KEY` | — | Solo panel (cifra credenciales de `restore_targets`) | | URL del panel (ej. `https://panel:3000`) | `panel.api_url` | **Sí** — base URL sin barra final | | — | `panel.instance_key` | Nombre de **este** servidor en el panel (`restore_targets.name`) | Generar token: ```bash node -e "console.log(require('crypto').randomBytes(32).toString('hex'))" ``` Generar clave de cifrado (panel): ```bash node -e "console.log(require('crypto').randomBytes(32).toString('base64'))" ``` `BACKUP_PATH` del panel es independiente: lista respaldos en el dashboard. Cada CloudRestoreAS reporta su carpeta local vía `instance-config` (clave = nombre del servidor). --- ## Enrutamiento automático (todos los CRA) No hay modos que configurar. **Todo CRA con panel** hace lo mismo: 1. Llega un ZIP a la carpeta de entrada. 2. `GET resolve-route?filename=X&instance=` — el panel identifica el nodo y el servidor asignado. 3. Si `action=restore_local` → extract + RESTORE aquí (credenciales SQL de este servidor en el panel). 4. Si `action=forward` → SFTP del ZIP al `input_folder` del destino (credenciales SSH del destino). 5. Mover ZIP a `processed`; reportar `completed` o `forwarded`. ```mermaid flowchart TB ZIP[ZIP en carpeta local] CRA[Any CRA con instance_key] Panel["resolve-route"] SFTP[SFTP al destino] SQL[RESTORE local] ZIP --> CRA --> Panel Panel -->|restore_local| SQL Panel -->|forward| SFTP ``` Aplica igual en Alfa, Omega o un hub donde llegan todos los ZIP: | Situación | Qué hace el CRA | |-----------|-----------------| | Nodo asignado a **esta** instancia | RESTORE local | | Nodo asignado a **otro** servidor | SFTP ZIP al destino | La decisión la toma el **panel** (nodo + asignación en Gestión BD), no el operador. ### Qué configura cada instalación | Campo | Para qué | |-------|----------| | URL + token del panel | Conectar al panel | | **Instancia** | Identidad de este servidor (`restore_targets.name`) | | Carpeta entrada | Dónde vigila ZIPs esta máquina | El selector de instancia se llena desde `GET /api/restore/target-catalog`. No hay límite de servidores. ### Hub donde llegan todos los ZIP Ejemplo: máquina **Alfa** recibe todos los archivos y también tiene bases propias: 1. Panel: servidor Alfa registrado; bases de Alfa/Omega/Gamma asignadas en Gestión BD. 2. CRA en Alfa: instancia **Alfa**, misma URL/token que el resto. 3. ZIP de base Alfa → RESTORE local. 4. ZIP de base Omega → SFTP a carpeta de Omega (card **Reportada**). 5. CRA Omega detecta el ZIP y restaura localmente. No hay paso extra ni modo especial. ### Sin panel (legacy local) Si `panel.api_url` está vacío, CloudRestoreAS usa nodos SQLite locales y SQL de la pestaña Config (sin reparto automático). ### Config legacy `panel.mode` Valores antiguos (`orchestrator`, `hub_restore`, `colocated`) se **ignoran**. Se registra un aviso en log y se usa siempre `resolve-route`. El modo hub que subía `.bak` por SFTP ya no aplica. --- ## Contrato API (`/api/restore/*`) Autenticación: `Authorization: Bearer `. ### GET `/api/restore/target-catalog` **Cliente:** `panel_client.list_restore_target_names()` | Respuesta 200 | Descripción | |---------------|-------------| | `targets[]` | `{ "id", "name" }` — sin credenciales | ### GET `/api/restore/resolve-route?filename=&instance=` **Cliente:** `panel_client.resolve_route()` | Query | Obligatorio | Descripción | |-------|-------------|-------------| | `filename` | Sí | Nombre del ZIP (panel resuelve nodo desde el stem) | | `instance` | Sí | Instancia de este CRA (`restore_targets.name`) | | Respuesta 200 | Descripción | |---------------|-------------| | `action` | `restore_local` o `forward` | | `db_name` | Base resuelta | | `node_key` | Nodo en panel | | `target` | Servidor destino (SQL/SSH + `input_folder`) | | Código | Significado | |--------|-------------| | 404 | Sin nodo/base o sin servidor asignado | | 503 | Destino sin `input_folder` reportado | ### GET `/api/restore/target-for?database=&instance=` Usado por utilidades legacy; el flujo principal de jobs usa `resolve-route`. ### POST `/api/restore/job-result` `status`: `completed` | `failed` | `forwarded`. ### POST `/api/restore/instance-config` Reporte de carpeta de entrada e identidad del agente (`instance_key` = nombre del servidor). Es *best-effort*: un fallo aquí nunca bloquea una restauración. ```jsonc { "input_folder": "D:\\Backups\\Entrada", "processed_folder": "D:\\Backups\\Procesados", // opcional; el panel la deriva si falta "host_name": "WIN-RESTORE-01", "app_version": "1.1.0", // versión instalada → cloudrestore_status.app_version "platform": "windows", // "windows" | "linux" "arch": "x86_64", // "x86_64" | "arm64" "instance_key": "Alfa" // = restore_targets.name } ``` `platform` y `arch` le dicen al panel **qué artefacto le corresponde a este servidor** al instalar o actualizar: `a24c.cras_releases` se llavea por `version + platform + arch`. Un agente viejo que no las mande sigue funcionando; el panel cae al texto libre de `restore_targets.os` para la primera instalación. Respuesta: `200 { "ok": true, "trace_id": "…" }`. ### POST `/api/restore/agent-sync` Dispara la sincronización del catálogo de versiones contra Gitea. Lo usa `publish-release.sh --notify-panel` para que una versión recién publicada aparezca de inmediato, sin esperar a que un admin abra `/versiones-cras`. Body vacío (`{}`). Respuesta: `200 { "ok": true, "discovered": N, "versions": N }`. --- ## Distribución de versiones (Gitea → PANEL → servidor) Los binarios se publican en el registro de paquetes genéricos de Gitea; el panel los descubre leyendo su API, los cachea verificando el `sha256` que Gitea calcula, e instala por SSH/SFTP en el servidor destino. ``` build local → Gitea (generic packages) → PANEL (caché + instalador SSH) → servidor ``` - **Publicar:** ver [BUILD.md](BUILD.md) §5 (`publish-release.sh`). - **Instalar/actualizar:** panel → **Versiones CRAS** (`/versiones-cras`) → *Sincronizar con Gitea* → *Activar* → *Instalar* en el servidor. - El panel siembra `config/.env` con `api_url`, `api_token` e `instance_key` durante la instalación, así que el servidor queda operativo sin configuración manual. - El servidor destino **no descarga nada de internet**: el binario es autocontenido y los bytes llegan del panel por SFTP. Tablas involucradas: `a24c.cras_releases` (catálogo de versiones publicadas) y `a24c.cras_install_runs` (bitácora de instalaciones con progreso paso a paso). --- ## Agregar servidores adicionales 1. Panel → **Servidores de Restauración → + Nuevo servidor** (incluye credenciales SSH) 2. **Gestión de Bases de Datos** → dropdown servidor por base 3. Panel → **Versiones CRAS** → *Instalar* en ese servidor (siembra el `.env` solo) El camino manual sigue disponible: instalar el CRA a mano → Config → instancia → Guardar (card **Reportada**). --- ## Despliegue inicial ### Orden de arranque (migraciones automáticas) 1. **a24c-postgres** — volumen Postgres. 2. **a24c-backend** — aplica `alembic upgrade head` al iniciar (incluye tablas CRA: `restore_targets`, `restore_job_logs`, `cloudrestore_status`, `restore_target_id`). 3. **Panel** (`docker compose up`) — espera el esquema CRA en Postgres antes de abrir el puerto 3000. No hace falta ejecutar `database/migrations/*.sql` a mano: la fuente de verdad es Alembic en **a24c**. ### Panel ```bash cd ~/dev/PANEL_BASES_ANEXO24 cp .env.example .env docker compose up -d ``` Servidores de restauración + asignación de bases en Gestión BD. ### Cada Windows con CloudRestoreAS | Config | Valor | |--------|--------| | URL PANEL | misma en todos | | API Token | mismo en todos | | **Instancia** | nombre de **este** servidor en panel | | Carpeta Entrada | local de esta máquina | ### Verificaciones | Paso | Qué comprobar | |------|----------------| | Cards en panel | **Reportada** con carpeta | | ZIP propio | RESTORE local | | ZIP ajeno en esta carpeta | SFTP al destino; bitácora `forwarded` | | Destino recibe ZIP | RESTORE local allí | ### Troubleshooting - **Sin reportar**: CRA destino no guardó config o no alcanza el panel. - **503 en forward**: destino sin `input_folder` reportado. - **Job diferido**: panel caído, sin `instance_key`, o base sin asignación. - **401**: token distinto entre panel y CRA. --- ## Prueba local Un CRA con `instance_key` igual al nombre en panel. El panel muestra una card por servidor; las no reportadas aparecen como **Sin reportar**.