CreaRack-SL

Endpoint GET /api/racks/backup/full/download/{job_id} — Descargar ZIP (v1.56.0)

Signature

@router.get("/backup/full/download/{job_id}")
def backup_full_download(request, job_id: str):
    """GET /api/racks/backup/full/download/{job_id} - Descarga el ZIP de un backup async listo."""

Método: GET
Path: /api/racks/backup/full/download/{job_id}
Permisos: racks.admin
Respuesta: 200 OK (ZIP file, streaming) | 404 (no listo / no existe)

Descripción

Descarga el ZIP del backup completado, aislado por organización (RLS + ORM). El ZIP se auto-limpia tras servirse (POSIX unlink).

Response (200)

Content-Type: application/zip
Content-Disposition: attachment; filename="crearack_backup_ORGNAME_20260716_110418.zip"

[ZIP binary stream, ~50-500 MB típico]
  • Streaming: FileResponse no carga el ZIP en RAM. Evita el guard PayloadBudgetMiddleware (hard_limit 5 MB).

Response (404)

{
  "error": "Backup not ready"  // o "Backup file expired"
}
  • "Backup not ready": Job no encontrado o no está done aún.
  • "Backup file expired": Job está done pero el ZIP ya no existe en disco (se borró después de otra descarga).

Lógica interna

  1. Valida require_perm(request, "racks", "admin").
  2. Obtiene org = get_current_org(request).
  3. Query aislada por org + RLS:
    job = AsyncJob.objects.filter(
        job_id=job_id,
        organization=org,
        kind=AsyncJob.Kind.BACKUP_FULL
    ).first()
  4. Verifica:
    • Job existe y es de esta org.
    • Job está en status "done".
    • Archivo ZIP existe en ASYNC_BACKUP_DIR.
  5. Sirve el ZIP con FileResponse (streaming).
  6. Auto-limpia con os.unlink(zip_path) inmediato (en POSIX, la inode se mantiene viva mientras el filehandle de FileResponse esté abierto).

Seguridad

Multi-tenancy

  • ORM filter: Job debe ser de organization=org (tabla FK).
  • RLS: Via get_current_org(request) que respeta las políticas de RLS en Postgres.
  • Resultado: Un admin de Org A no puede descargar backups de Org B.

Almacenamiento

  • ZIP guardado en MEDIA_ROOT/backups/async/{job_id}.zip.
  • Solo la tarea run_full_backup() puede escribir (no hay endpoint GET de listado).
  • Auto-limpieza al servirse.

Contenido

  • Datos: ~13 modelos, creds SNMP en claro (T1 decision, reversible al restaurar).
  • Uploads: Solo archivos referenciados por esta org (no cross-tenant bleed).

Consideraciones

  • Timeout descarga: 30 minutos típico (depende del size del ZIP y la red).
  • Reintentos: Si la descarga falla a mitad, el cliente debe re-hacer polling y re-intentar (el ZIP no se borra en el primer fallo).
  • Expiración: Sin lógica explícita de TTL (puede permanecer días). Future: agregar cron de limpieza.

Véase también

  • [[entity—racks—endpoint—backup-full-start]]
  • [[entity—racks—service—run-full-backup]]
  • [[entity—core—model—asyncjob]]
  • [[feature—racks—backup-restore-async-v156]]
  • [[concept—saas—multi-tenancy]]