Volver a la wiki

Restauración aditiva con confirmación (v1.66.2 · Fix integridad datos)

Resumen

La restauración de backups en CreaRack es aditiva por diseño: no reemplaza datos existentes, sino que los suma a los ya presentes en la organización. El 16-07-2026, esta característica causó un incidente: una restauración duplicó 100 racks de una organización real sin aviso alguno, porque la UI mentía (“overwrite” + “safety backup” inexistente).

v1.66.2 (06-08-2026) añade:

  1. Validación de 409 con confirmación dual: si la org ya tiene datos vivos, el servidor rechaza el restore sin confirm_add=true.
  2. UI honesta: el diálogo ahora dice “AÑADE” (no “reemplaza”), muestra los números reales de racks/planos existentes, y pide segundo sí explícito.
  3. Preservación de metadatos: los campos mode y sort_order de blueprints sobreviven al round-trip backup→restore (antes renacían con valores por defecto).
  4. Limpieza PROD: los 100 racks + 4 planos duplicados fueron movidos a la Papelera (reversibles).

Arquitectura de la Fix

Endpoint: POST /api/racks/restore/full

Cambio de firma:

def restore_full(request, file: UploadedFile = File(...), confirm_add: bool = Form(False)):

Validación previa (antes de encolar):

existing_racks = Rack.objects.filter(organization=org, deleted_at__isnull=True).count()
existing_bps = Blueprint.objects.filter(organization=org, deleted_at__isnull=True).count()

if (existing_racks or existing_bps) and not confirm_add:
    return JsonResponse({
        "error": "confirmation_required",
        "message": "This organization already has N racks and M maps...",
        "existing_racks": existing_racks,
        "existing_blueprints": existing_bps,
    }, status=409)

Comportamiento:

Serialización de Blueprints (Backup)

En racks/api/export/backup.py, función _collect_backup_data():

blueprint_data = {
    # ... campos previos ...
    "mode": bp.mode,          # NEW: type='map'|'row'|'room'
    "sort_order": bp.sort_order,  # NEW: orden manual
    # ... resto ...
}

Desserialización de Blueprints (Restore)

En racks/api/export/restore.py, función apply_full_restore_from_zip():

blueprint = Blueprint.objects.create(
    organization=org,
    name=bp["name"],
    mode=bp.get("mode", "map"),            # Default "map" para compat ZIPs antiguos
    sort_order=bp.get("sort_order"),       # None si no está (compat)
    # ... resto de campos ...
)

Esto asegura que:

Lógica Frontend (static/js/base.js)

// Paso 1: primer confirm con texto honesto
if (!confirm(t('FULL SYSTEM RESTORE\n\nThis ADDS the backup contents...'))) {
    return;
}

// Paso 2: POST sin confirm_add
try {
    started = await ApiService.postForm('/api/racks/restore/full', formData);
} catch (err) {
    if (err?.status === 409 && err?.error === 'confirmation_required') {
        // Paso 3: 409 → segundo confirm con números reales
        if (!confirm(`${err.message}\n\n${t('Are you absolutely sure...')}`)) {
            return;
        }
        // Paso 4: reintento con confirm_add=true
        formData.append('confirm_add', 'true');
        started = await ApiService.postForm('/api/racks/restore/full', formData);
    } else {
        throw err;
    }
}

Flujo usuario:

  1. Selecciona ZIP → primer diálogo (texto honesto).
  2. Si org tiene datos → error 409 mostrando los números.
  3. Usuario ve “N racks y M planos existentes” → segundo sí explícito.
  4. Sistema encola la restauración.

Mensajes Localizados

Se añadieron dos msgids nuevos a locale/es/LC_MESSAGES/djangojs.po (compilado en Docker):

msgid ""
"FULL SYSTEM RESTORE\n"
"\n"
"This ADDS the backup contents alongside your current data. Nothing is "
"deleted, and items present in both will appear DUPLICATED.\n"
"\n"
"Proceed?"
msgstr ""
"RESTAURACIÓN COMPLETA DEL SISTEMA\n"
"\n"
"Esto AÑADE el contenido del backup junto a tus datos actuales. No se borra "
"nada, y los elementos presentes en ambos aparecerán DUPLICADOS.\n"
"\n"
"¿Continuar?"

msgid "Are you absolutely sure you want to add it on top?"
msgstr "¿Seguro del todo que quieres añadirlo encima?"

Cobertura de Tests

Se añadieron 4 casos clave en tests/api/test_backup_restore_async.py:

1. test_restore_over_populated_org_requires_confirmation

2. test_restore_enqueues_and_applies_with_confirmation

3. test_restore_into_empty_org_needs_no_confirmation

4. test_blueprint_mode_and_sort_order_survive_roundtrip


Cambios en Versionado


Impacto de Negocio

Antes (hasta 16-07-2026)

Después (v1.66.2)


Metadatos de Campos (Round-trip)

CampoTipoSerializaciónDesserializaciónDefault (compat)
Blueprint.modestr enum✅ Backup incluye✅ Restore aplica“map”
Blueprint.sort_orderint✅ Backup incluye✅ Restore aplicaNone

Véase también

Subir