252 lines
8.9 KiB
Markdown
252 lines
8.9 KiB
Markdown
# 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=<esta instancia>` — 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 <CLOUDRESTORE_API_TOKEN>`.
|
|
|
|
### 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=<zip>&instance=<nombre>`
|
|
|
|
**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=<db_name>&instance=<nombre>`
|
|
|
|
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**.
|