diff --git a/src/lib/server/controldesk-pg.ts b/src/lib/server/controldesk-pg.ts index bd11f69..3d6c258 100644 --- a/src/lib/server/controldesk-pg.ts +++ b/src/lib/server/controldesk-pg.ts @@ -342,18 +342,17 @@ export function matchNodeRowFromBackupStem(stem: string, nodes: any[]): any | nu /** Datos de contacto para alertas (equivalente a la consulta previa sobre Usuarios/BasesDeDatos). */ export async function lookupAlertClientData(nodoName: string): Promise { + // El "Cliente" de la alerta es el nombre de la tabla de bases de datos + // (database_nodes.legal_name), NO el full_name del usuario del portal. Se resuelve el nodo por + // database_name o node_subnode_key; así también funciona para bases sin usuario asociado. const sql = ` SELECT - pu.is_authority_client AS "ClienteAutoridad", - pu.full_name AS "Nombre", - pu.username AS "Usuario", + dn.legal_name AS "Nombre", dn.notification_email AS "CorreoNotificacion", dn.node_subnode_key AS "NodoSubNodo" - FROM ${qUsers()} pu - LEFT JOIN ${qNodes()} dn ON pu.database_node_id = dn.id - WHERE pu.username = $1 - OR dn.database_name = $1 - OR dn.node_subnode_key = $1 + FROM ${qNodes()} dn + WHERE LOWER(TRIM(dn.database_name)) = LOWER(TRIM($1::text)) + OR LOWER(TRIM(dn.node_subnode_key)) = LOWER(TRIM($1::text)) LIMIT 1 `; const r = await pgPool.query(sql, [nodoName]); diff --git a/src/lib/server/mssql-nodes.ts b/src/lib/server/mssql-nodes.ts index d979461..6a58560 100644 --- a/src/lib/server/mssql-nodes.ts +++ b/src/lib/server/mssql-nodes.ts @@ -463,7 +463,9 @@ export async function loadSqlDashboardFromNodes(nodes: CatalogNodeRow[]): Promis queryDatabaseAlertRow(pool, dbn), queryRestoreHistoryForDatabase(pool, dbn) ]); - if (!row) return null; + // Conexión al servidor OK pero la base no existe en él: se marca como alerta + // "no encontrada" (sin métricas ni días sin sincronizar), no se descarta el nodo. + if (!row) return { node, dbn, row: null, notFound: true, alertRow: null, hist: [] }; return { node, dbn, row, alertRow, hist }; } catch (e) { console.error( @@ -480,6 +482,17 @@ export async function loadSqlDashboardFromNodes(nodes: CatalogNodeRow[]): Promis if (!res) continue; const { node, dbn, row, alertRow, hist } = res; + // Base no encontrada en el servidor: solo alerta (sin fila de métricas ni tamaño). + // last_restore_date en null => la UI muestra los días sin sincronizar como N/D. + if ((res as any).notFound) { + alertsData.push({ + visible_name: dbn, + last_restore_date: null, + not_found: true + }); + continue; + } + const visible = String(row.visible_name ?? dbn); const keyLower = visible.toLowerCase(); diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index 0912166..0c0be20 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -736,9 +736,11 @@ const now = new Date(); const csvRows = rows.map((alert: any) => { - const fecha = alert.last_restore_date - ? new Date(alert.last_restore_date).toLocaleString() - : 'Nunca'; + const fecha = alert.not_found + ? 'No encontrada' + : alert.last_restore_date + ? new Date(alert.last_restore_date).toLocaleString() + : 'Nunca'; const dias = alert.last_restore_date ? alert.daysWithout : ''; const cliente = alert.clientData?.Nombre ?? 'N/D'; const correo = alert.clientData?.CorreoNotificacion ?? 'N/D'; @@ -1958,9 +1960,16 @@ {alert.visible_name} - {alert.last_restore_date - ? new Date(alert.last_restore_date).toLocaleString() - : 'Nunca'} + {#if alert.not_found} + + error_outline + No encontrada + + {:else} + {alert.last_restore_date + ? new Date(alert.last_restore_date).toLocaleString() + : 'Nunca'} + {/if} {alert.clientData?.Nombre ?? 'N/D'} {alert.clientData?.CorreoNotificacion ?? 'N/D'} diff --git a/src/routes/api/dashboard.json/+server.ts b/src/routes/api/dashboard.json/+server.ts index f90aa6d..61b4fa8 100644 --- a/src/routes/api/dashboard.json/+server.ts +++ b/src/routes/api/dashboard.json/+server.ts @@ -1,5 +1,5 @@ import { json } from '@sveltejs/kit'; -import { listDatabaseNodesForMssql } from '$lib/server/controldesk-pg'; +import { listDatabaseNodesForMssql, lookupAlertClientData } from '$lib/server/controldesk-pg'; import { loadSqlDashboardFromNodes, type CatalogNodeRow } from '$lib/server/mssql-nodes'; export const GET = async () => { @@ -7,7 +7,23 @@ export const GET = async () => { const nodes = (await listDatabaseNodesForMssql()) as CatalogNodeRow[]; const bundle = await loadSqlDashboardFromNodes(nodes); const { databaseRows, summaryMain, alertsData } = bundle; - return json({ databaseRows, summaryMain, alertsData }); + + // Enriquecer las alertas con datos de cliente/correo (mismo criterio que la carga inicial + // en +page.server.ts). Sin esto, el auto-refresh reemplazaba las alertas por versiones sin + // clientData y la tabla mostraba Cliente y Correo como "N/D" tras el primer refresco. + const enrichedAlerts = await Promise.all( + alertsData.map(async (alert) => { + let clientData: any = null; + try { + clientData = await lookupAlertClientData(String(alert.visible_name)); + } catch { + /* ignore */ + } + return { ...alert, clientData }; + }) + ); + + return json({ databaseRows, summaryMain, alertsData: enrichedAlerts }); } catch (e: any) { console.error('Error refreshing dashboard data:', e); return json({ error: 'Error refreshing dashboard data' }, { status: 500 });