CreaRack-SL

Feature s115: Búsquedas guardadas persistidas en BD

Resumen Ejecutivo

Sesión: 115 (2026-06-08)
Ámbito: Auto-Provision · Producto
Estado: Merged (commit@1cd2537)
Autor: Edu (con Claude Code Opus)
Cierra: Parte 4 de la iniciativa Auto-Provision (no desperdiciar búsquedas)

Lo que cambia

Las búsquedas de escaneo que guardabas en Auto-Provision se guardaban en el navegador, así que al limpiar la caché o recargar la página a veces desaparecían y el botón “Load Previous Results” decía que no había ninguna (reporte de Edu).

Ahora se guardan en la base de datos, de forma que no se pierden, y además las ve todo tu equipo (cualquier compañero de la misma organización). Se conservan las 20 más recientes por org.

El aspecto y el funcionamiento del listado son los mismos; solo cambia dónde se guardan — de localStorage a BD.


Antecedente: Parte 4a (s114)

La sesión 114 (2026-06-08) añadió el botón “Refresh” por dispositivo en el Step 3, que permite reutilizar las credenciales para recomprobar un dispositivo sin volver al Step 1. La s115 cierra la Parte 4 añadiendo persistencia de búsquedas.


Cambios Técnicos

Modelo Nuevo: network.ScanSession

  • Tabla: scan_sessions (BD)
  • Campos principales:
    • organization (FK): organización propietaria (no hay aislamiento por usuario — es org-wide).
    • created_by (FK, nullable): quién guardó la búsqueda (auditoría).
    • name (CharField): etiqueta legible del escaneo (p.ej. “10.0.0.0/24”, “Prod-Network”).
    • profiles (M2M a DeviceProfile): lista de dispositivos capturados.
    • created_at (DateTimeField): timestamp de creación.
  • Índice: (organization, -created_at) para búsquedas rápidas.
  • Límite automático: solo 20 más recientes por org (poda al crear).
  • Integridad: si se borra un perfil, desaparece solo de la sesión; no elimina la búsqueda.
  • Migración: network/migrations/0048_scansession.py (2026-06-08 13:54 UTC).

API REST: /auto-provision/scans

Router: network/api/scan_sessions.py

Operaciones:

MétodoURLPermisosDescripción
GET/auto-provision/scansnetwork.viewLista búsquedas de la org (máx 20, recientes primero)
POST/auto-provision/scansnetwork.editCrea una búsqueda. Poda automáticamente si ≥20.
DELETE/auto-provision/scans/{id}network.editBorra una búsqueda. RLS: solo si pertenece a la org.

Esquemas Ninja:

  • ScanSessionOut: respuesta de lectura (con device_count calculado en vivo).
  • ScanSessionCreate: payload esperado en POST.

RLS: Aislamiento completo por organización. Un usuario solo ve/modifica búsquedas de su org.

Frontend: static/js/network/auto_provision/sessions.js

Cambios principales:

AntesDespués
localStorage["auto_provision_scans"]GET /api/network/auto-provision/scans
_getSavedScans() → JSON en localStorageloadSavedProfiles() → API call
_saveScanSession() → localStorage.setItem_saveScanSession() → POST /auto-provision/scans
_deleteSavedScan(index) → localStorage.splice_removeScanByIndex(index) → DELETE /auto-provision/scans/{id}

Caché local:

  • _savedScansCache: almacena el resultado de GET en memoria, usado para operaciones “Load by row” y “Delete by row” que usan índice.

Escaneo interrumpido (transitorio):

  • Sigue en localStorage["auto_provision_active_scan"] (por-pestaña, no sincronizado org-wide).
  • Se limpia cuando se promociona a búsqueda completada o se recarga la pestaña.

Auditoría

  • Evento: log_action(request, "NETWORK", "auto_provision.scan_saved", name)
  • Disparado: Cada vez que se crea una nueva búsqueda.
  • Campo registrado: nombre de la búsqueda.

Comportamiento Específico

Compartición a Nivel Organización

  • Una búsqueda guardada es visible para todos los miembros de la org que tengan permiso network.view.
  • No hay filtro por created_by: un usuario puede ver y cargar búsquedas de otros colegas.
  • created_by se guarda solo para auditoría.

Límite de 20 por Organización

  1. Al crear una nueva búsqueda, se cuenta cuántas hay.
  2. Si ya hay 20, se podan las más antiguas (hasta quedarse con 20 recientes).
  3. Responsable: lógica en el handler POST (no es automático a nivel DBMS).

Integridad de Perfiles

  • Si se borra un DeviceProfile, desaparece solo de la sesión M2M, no borra la sesión.
  • El device_count se calcula en vivo desde profiles.all(), sin IDs fantasma.

Validación de Perfiles en Cross-Tenant

  • Al crear una búsqueda, si incluyes un ID de perfil que pertenece a otra org, se descarta silenciosamente sin error.
  • Solo los perfiles de tu org son aceptados.

Tests

Archivo: tests/api/test_network_scan_sessions.py (10 tests)

Cobertura:

✅ Permisos RLS:

  • Viewer puede listar, no crear/borrar.
  • Operator puede crear/borrar.

✅ Creación:

  • Persiste y vincula perfiles correctamente.
  • Descarta perfiles de otra org sin error.
  • Poda automáticamente a MAX_SESSIONS (20).

✅ Listado:

  • Filtra por org (no mezcla orgs).
  • Ordena descendente por created_at (recientes primero).

✅ Borrado:

  • Borra propio exitosamente.
  • Retorna 404 si intenta borrar de otra org.

Archivos Modificados

ArchivoCambioDescripción
network/models.pyAddedNuevo modelo ScanSession (36 líneas)
network/migrations/0048_scansession.pyAddedMigración Django (53 líneas)
network/api/scan_sessions.pyAddedRouter y handlers (103 líneas)
network/api/__init__.pyModifiedImporta nuevo router
static/js/network/auto_provision/sessions.jsModifiedMigra localStorage → API (~100 líneas)
CHANGELOG.mdModifiedEntrada s115
RELEASE_NOTES.mdModifiedNota para usuarios (s115)
context/agents/dev-auto-provision.mdModifiedSección “Persistencia de sesiones” actualizada
tests/api/test_network_scan_sessions.pyAdded10 tests unitarios (103 líneas)

Total LOC: ~250+ (código + tests + migraciones)


Impacto en UX

  • Modal “Saved Scans”: Idéntico en apariencia. Los datos vienen de BD en lugar de localStorage.
  • Step 2 (“Load Previous Results”): Funciona igual, carga búsquedas de la org.
  • Velocidad: Leve latencia de red (GET al entrar en Step 2), pero es previsible.
  • Compartición: Nuevo comportamiento: un usuario ve búsquedas de otros colegas → mejora la colaboración.

Backward Compatibility

  • ✅ Las búsquedas viejas en localStorage no se migran automáticamente. Se pierden en el primer uso.
  • ✅ Es aceptable: la caché de navegador suele limpiarse regularmente, así que de todas formas se hubieran perdido.
  • ✅ Los usuarios simplemente volverán a guardar búsquedas (una sola vez) en la BD.

Relación con Otras Features

  • s114 (2026-06-08): Botón “Refresh” por dispositivo. Juntas, cierran la iniciativa “no desperdiciar búsquedas”.
  • Auto-Provision Feature (general): Mejora la experiencia del asistente de descubrimiento automático.

Próximos Pasos (No Incluidos en s115)

  • Sincronización real-time de búsquedas guardadas entre pestañas del mismo usuario (WebSocket o Server-Sent Events).
  • Historial de escaneos exitosos (Step 4 adicional con resumen de hallazgos).
  • Export/import de búsquedas.

Historial de Cambios

  • 2026-06-08 14:08 UTC (commit@1cd2537): Merge de edu/autoprovision-scans-db. Cierra Parte 4 de Auto-Provision.

Véase también

  • [[entity—network—model—scan-session]]
  • [[entity—network—endpoint—auto-provision-scans]]
  • [[concept—saas—multi-tenancy]]
  • [[concept—auto-provision—ciclo-escaneo]]
  • [[feature—auto-provision—sesion-114-refresh-dispositivo]]