Spaces:
Sleeping
Sleeping
| /** | |
| * autoFix.ts — Motore di auto-fix diagnostico | |
| * | |
| * Mappa ogni tipo di errore (DiagType) a una lista di azioni correttive | |
| * che possono essere applicate con un solo tap. | |
| * | |
| * Ogni azione: | |
| * - Ha un label, descrizione e indicatore di pericolosità | |
| * - Esegue la correzione in modo safe (try/catch, non blocca la UI) | |
| * - Ritorna un FixResult con esito e dettaglio leggibile | |
| * - Viene tracciata nello storico (in-memoria + sessionStorage) | |
| * | |
| * Fix disponibili: | |
| * clearAgentStorage — rimuove chiavi agente da localStorage | |
| * compactDatabase — pruna messaggi/eventi/telemetry vecchi | |
| * pruneAggressivo — libera spazio aggressivamente (quota critica) | |
| * resetServiceWorker — ri-registra il service worker | |
| * testNetwork — verifica connettività al sito live | |
| * clearDiagLog — svuota il log diagnostico | |
| * reloadApp — ricarica la pagina | |
| * resetProviderState — resetta lo stato di exhausted dei provider | |
| * clearTasksStuck — cancella task bloccati da > 10 minuti | |
| * clearCorruptedLS — svuota completamente localStorage (emergenza) | |
| */ | |
| import type { DiagType } from "./autoDiagnostics"; | |
| import { makeTimedSignal } from "./agentLoop/networkConstants"; // iOS-safe timeout | |
| // ─── Tipi (estratti in autoFixTypes.ts — S550 — re-esportati per backward compat) ────────── | |
| import type { FixResult, FixHistoryEntry, FixAction, HealthCheck } from "./autoFixTypes"; | |
| export type { FixResult, FixHistoryEntry, FixAction, HealthCheck } from "./autoFixTypes"; | |
| // ─── Storico fix ────────────────────────────────────────────────────────────── | |
| const HISTORY_KEY = "agente:fix-history"; | |
| const _history: FixHistoryEntry[] = []; | |
| function _loadHistory(): void { | |
| try { | |
| const raw = sessionStorage.getItem(HISTORY_KEY); | |
| if (raw) _history.push(...(JSON.parse(raw) as FixHistoryEntry[])); | |
| } catch { /* */ } | |
| } | |
| function _saveHistory(): void { | |
| try { | |
| sessionStorage.setItem(HISTORY_KEY, JSON.stringify(_history.slice(-50))); | |
| } catch { /* */ } | |
| } | |
| _loadHistory(); | |
| function _record(fixId: string, fixLabel: string, result: FixResult): void { | |
| _history.push({ at: Date.now(), fixId, fixLabel, result }); | |
| if (_history.length > 100) _history.splice(0, _history.length - 100); | |
| _saveHistory(); | |
| } | |
| export function getFixHistory(): FixHistoryEntry[] { return [..._history].reverse(); } | |
| export function clearFixHistory(): void { _history.length = 0; _saveHistory(); } | |
| // ─── Implementazioni dei fix ────────────────────────────────────────────────── | |
| async function clearAgentStorage(): Promise<FixResult> { | |
| try { | |
| const keys = Object.keys(localStorage).filter(k => | |
| k.startsWith("agente") || k.startsWith("groq-") || k.startsWith("together-") || | |
| k.startsWith("openrouter-") || k.startsWith("hf-") || k.startsWith("gemini-") | |
| ); | |
| const tokenKeys = keys.filter(k => | |
| k.includes("token") || k.includes("key") || k.includes("api") | |
| ); | |
| const safeKeys = keys.filter(k => !tokenKeys.includes(k)); | |
| safeKeys.forEach(k => localStorage.removeItem(k)); | |
| return { | |
| ok: true, | |
| message: `Rimosso ${safeKeys.length} chiavi localStorage (token preservati)`, | |
| detail: safeKeys.slice(0, 5).join(", ") + (safeKeys.length > 5 ? "..." : ""), | |
| }; | |
| } catch(e) { | |
| return { ok: false, message: "Errore pulizia localStorage", detail: String(e) }; | |
| } | |
| } | |
| async function compactDatabase(): Promise<FixResult> { | |
| try { | |
| const { messagesDb, tasksDb, telemetryDb } = await import("./vfsDb"); | |
| const [msgs, tasks, tel] = await Promise.all([ | |
| messagesDb.pruneOlderThan(7), | |
| tasksDb.pruneOlderThan(3), | |
| telemetryDb.pruneOlderThan(7), | |
| ]); | |
| return { | |
| ok: true, | |
| message: `Compattato: ${msgs + tasks + tel} record eliminati`, | |
| detail: `messaggi >7gg: ${msgs} | task >3gg: ${tasks} | telemetry >7gg: ${tel}`, | |
| }; | |
| } catch(e) { | |
| return { ok: false, message: "Errore compattazione DB", detail: String(e) }; | |
| } | |
| } | |
| async function pruneAggressivo(): Promise<FixResult> { | |
| try { | |
| const { messagesDb, tasksDb, telemetryDb, vfsDb } = await import("./vfsDb"); | |
| const [msgs, tasks, tel] = await Promise.all([ | |
| messagesDb.pruneOlderThan(1), | |
| tasksDb.pruneOlderThan(1), | |
| telemetryDb.pruneOlderThan(1), | |
| ]); | |
| await vfsDb.diagnosticsLog.clear(); | |
| await vfsDb.runtimeEvents.where("at").below(Date.now() - 86_400_000).delete(); | |
| return { | |
| ok: true, | |
| message: `Liberato spazio: ${msgs + tasks + tel} record eliminati (aggressivo)`, | |
| detail: `msgs: ${msgs} | tasks: ${tasks} | tel: ${tel} | diag: svuotato`, | |
| }; | |
| } catch(e) { | |
| return { ok: false, message: "Errore pruning aggressivo", detail: String(e) }; | |
| } | |
| } | |
| async function resetServiceWorker(): Promise<FixResult> { | |
| try { | |
| if (!("serviceWorker" in navigator)) { | |
| return { ok: false, message: "Service Worker non supportato su questo browser" }; | |
| } | |
| const regs = await navigator.serviceWorker.getRegistrations(); | |
| for (const r of regs) await r.unregister(); | |
| setTimeout(() => window.location.reload(), 1500); | |
| return { | |
| ok: true, | |
| message: `${regs.length} Service Worker rimossi — ricarico in 1.5s`, | |
| detail: regs.map(r => r.scope).join(", "), | |
| }; | |
| } catch(e) { | |
| return { ok: false, message: "Errore reset SW", detail: String(e) }; | |
| } | |
| } | |
| async function testNetwork(): Promise<FixResult> { | |
| const url = "https://ai-agente-ai.vercel.app/"; | |
| try { | |
| const start = Date.now(); | |
| const r = await fetch(url, { method: "HEAD", cache: "no-store", signal: makeTimedSignal(8000) }); | |
| const ms = Date.now() - start; | |
| return { | |
| ok: r.ok, | |
| message: r.ok ? `Sito raggiungibile in ${ms}ms` : `HTTP ${r.status} — sito con problemi`, | |
| detail: `${url} → ${r.status} ${r.statusText}`, | |
| }; | |
| } catch(e) { | |
| const msg = e instanceof Error ? e.message : String(e); | |
| return { ok: false, message: "Sito non raggiungibile", detail: msg }; | |
| } | |
| } | |
| async function clearDiagLog(): Promise<FixResult> { | |
| try { | |
| const { autoDiag } = await import("./autoDiagnostics"); | |
| autoDiag.clear(); | |
| return { ok: true, message: "Log diagnostico svuotato" }; | |
| } catch(e) { | |
| return { ok: false, message: "Errore pulizia diag log", detail: String(e) }; | |
| } | |
| } | |
| async function reloadApp(): Promise<FixResult> { | |
| window.location.reload(); | |
| return { ok: true, message: "Ricaricando la pagina..." }; | |
| } | |
| async function resetProviderState(): Promise<FixResult> { | |
| try { | |
| const agente = Object.keys(localStorage).filter(k => | |
| k.includes("exhausted") || k.includes("fallback") || k.includes("provider-state") | |
| ); | |
| agente.forEach(k => localStorage.removeItem(k)); | |
| return { | |
| ok: true, | |
| message: `Stato provider resettato (${agente.length} chiavi rimossi)`, | |
| detail: agente.join(", ") || "nessuna chiave provider-state trovata", | |
| }; | |
| } catch(e) { | |
| return { ok: false, message: "Errore reset provider", detail: String(e) }; | |
| } | |
| } | |
| async function clearTasksStuck(): Promise<FixResult> { | |
| try { | |
| const { vfsDb } = await import("./vfsDb"); | |
| const tenMinsAgo = Date.now() - 10 * 60 * 1000; | |
| const stuckKeys = await vfsDb.tasks | |
| .where("status").equals("running") | |
| .filter(t => t.startedAt < tenMinsAgo) | |
| .primaryKeys(); | |
| await vfsDb.tasks.bulkDelete(stuckKeys as string[]); | |
| return { | |
| ok: true, | |
| message: `${stuckKeys.length} task bloccati rimossi`, | |
| }; | |
| } catch(e) { | |
| return { ok: false, message: "Errore rimozione task bloccati", detail: String(e) }; | |
| } | |
| } | |
| async function verifyVercelBuild(): Promise<FixResult> { | |
| const url = "https://ai-agente-ai.vercel.app/"; | |
| try { | |
| const r = await fetch(url, { cache: "no-store", signal: makeTimedSignal(9000) }); | |
| const html = await r.text(); | |
| const hasBundle = /assets\/(index|main)[^"]*\.js/.test(html); | |
| const scriptCount = (html.match(/<script[^>]+src/g) ?? []).length; | |
| const ok = r.ok && hasBundle; | |
| return { | |
| ok, | |
| message: ok | |
| ? `Build ok — ${scriptCount} script caricati` | |
| : `Build incompleto — bundle JS non trovato nella pagina`, | |
| detail: `HTTP ${r.status} | bundle: ${hasBundle} | script tags: ${scriptCount}`, | |
| }; | |
| } catch(e) { | |
| return { ok: false, message: "Impossibile verificare il build Vercel", detail: e instanceof Error ? e.message : String(e) }; | |
| } | |
| } | |
| async function clearModuleCache(): Promise<FixResult> { | |
| try { | |
| if (!("caches" in window)) { | |
| return { ok: false, message: "Cache API non disponibile su questo browser" }; | |
| } | |
| const keys = await caches.keys(); | |
| const toClear = keys.filter(k => /vite|module|workbox|precache/i.test(k)); | |
| for (const k of toClear) await caches.delete(k); | |
| return { | |
| ok: true, | |
| message: `Cache moduli rimossa (${toClear.length} cache svuotate)`, | |
| detail: toClear.join(", ") || "nessuna cache Vite/Workbox trovata", | |
| }; | |
| } catch(e) { | |
| return { ok: false, message: "Errore pulizia cache moduli", detail: e instanceof Error ? e.message : String(e) }; | |
| } | |
| } | |
| async function clearCorruptedLS(): Promise<FixResult> { | |
| try { | |
| const total = localStorage.length; | |
| const saved: Record<string, string> = {}; | |
| const preserve = ["groq-token", "together-token", "openrouter-token", "hf-token", "gemini-token", "openai-token"]; | |
| preserve.forEach(k => { | |
| const v = localStorage.getItem(k); | |
| if (v) saved[k] = v; | |
| }); | |
| localStorage.clear(); | |
| Object.entries(saved).forEach(([k, v]) => localStorage.setItem(k, v)); | |
| return { | |
| ok: true, | |
| message: `localStorage svuotato completamente (${total} chiavi, token preservati)`, | |
| detail: `Token preservati: ${Object.keys(saved).join(", ") || "nessuno"}`, | |
| }; | |
| } catch(e) { | |
| return { ok: false, message: "Errore pulizia localStorage", detail: String(e) }; | |
| } | |
| } | |
| // ─── Registro azioni ────────────────────────────────────────────────────────── | |
| export const FIX_ACTIONS: FixAction[] = [ | |
| { | |
| id: "test-network", | |
| label: "Verifica connessione", | |
| description: "Testa la raggiungibilità del sito live su Vercel", | |
| applyTo: ["network-error", "promise-rejection", "js-error"], | |
| patterns: [/fetch|network|failed to fetch|connection|cors/i], | |
| dangerous: false, | |
| run: testNetwork, | |
| }, | |
| { | |
| id: "compact-db", | |
| label: "Compatta database", | |
| description: "Elimina messaggi, task e telemetry più vecchi di 7 giorni", | |
| applyTo: ["console-error", "js-error"], | |
| patterns: [/quota|storage|indexeddb|dexie/i], | |
| dangerous: false, | |
| run: compactDatabase, | |
| }, | |
| { | |
| id: "prune-aggressivo", | |
| label: "Libera spazio (aggressivo)", | |
| description: "Elimina tutto il possibile: dati >1 giorno, diag log, eventi runtime", | |
| applyTo: ["console-error", "js-error"], | |
| patterns: [/quota|storage|full|exceeded/i], | |
| dangerous: true, | |
| run: pruneAggressivo, | |
| }, | |
| { | |
| id: "clear-agent-storage", | |
| label: "Svuota cache locale", | |
| description: "Rimuove chiavi agente da localStorage (token preservati)", | |
| applyTo: ["react-crash", "js-error", "console-error"], | |
| dangerous: false, | |
| run: clearAgentStorage, | |
| }, | |
| { | |
| id: "reset-provider", | |
| label: "Reset provider", | |
| description: "Pulisce lo stato di fallback/exhausted dei provider AI", | |
| applyTo: ["promise-rejection", "js-error"], | |
| patterns: [/provider|groq|together|openrouter|exhausted|fallback/i], | |
| dangerous: false, | |
| run: resetProviderState, | |
| }, | |
| { | |
| id: "clear-stuck-tasks", | |
| label: "Rimuovi task bloccati", | |
| description: "Cancella i task in stato 'running' da più di 10 minuti", | |
| applyTo: ["promise-rejection", "js-error"], | |
| patterns: [/task|timeout|abort|stuck/i], | |
| dangerous: false, | |
| run: clearTasksStuck, | |
| }, | |
| { | |
| id: "reset-service-worker", | |
| label: "Reset Service Worker", | |
| description: "Disinstalla i SW registrati e ricarica la pagina", | |
| applyTo: ["js-error", "network-error"], | |
| patterns: [/service.worker|sw\.js|cache|install/i], | |
| dangerous: true, | |
| run: resetServiceWorker, | |
| }, | |
| { | |
| id: "clear-diag-log", | |
| label: "Svuota log diagnostico", | |
| description: "Rimuove tutte le voci dal log diagnostico", | |
| applyTo: "all", | |
| dangerous: false, | |
| run: clearDiagLog, | |
| }, | |
| { | |
| id: "clear-ls-full", | |
| label: "Reset localStorage completo", | |
| description: "EMERGENZA: svuota tutto localStorage (token preservati)", | |
| applyTo: ["react-crash", "js-error"], | |
| patterns: [/localstorage|storage|corrupt|invalid/i], | |
| dangerous: true, | |
| run: clearCorruptedLS, | |
| }, | |
| { | |
| id: "verify-vercel-build", | |
| label: "Verifica build Vercel", | |
| description: "Controlla che il bundle JS del sito live sia stato compilato correttamente", | |
| applyTo: ["console-error", "js-error", "promise-rejection"], | |
| patterns: [/TS\d{4}|is not assignable|does not exist on type|cannot find|unexpected token|SyntaxError|chunk|module/i], | |
| dangerous: false, | |
| run: verifyVercelBuild, | |
| }, | |
| { | |
| id: "clear-module-cache", | |
| label: "Svuota cache moduli JS", | |
| description: "Rimuove cache Vite/Workbox del browser — risolve errori di caricamento moduli", | |
| applyTo: ["js-error", "console-error", "network-error"], | |
| patterns: [/TS\d{4}|module|import|chunk|loading|failed to fetch|unexpected token|cache/i], | |
| dangerous: false, | |
| run: clearModuleCache, | |
| }, | |
| { | |
| id: "reload-app", | |
| label: "Ricarica app", | |
| description: "Ricarica la pagina (equivale a F5)", | |
| applyTo: "all", | |
| dangerous: false, | |
| run: reloadApp, | |
| }, | |
| ]; | |
| // ─── Util: trova fix applicabili a un errore ────────────────────────────────── | |
| export function getApplicableFixes(type: DiagType, message: string): FixAction[] { | |
| return FIX_ACTIONS.filter(f => { | |
| const typeMatch = f.applyTo === "all" || f.applyTo.includes(type); | |
| if (!typeMatch) return false; | |
| if (f.patterns && f.patterns.length > 0) { | |
| return f.patterns.some(p => p.test(message)); | |
| } | |
| return true; | |
| }); | |
| } | |
| // ─── Esegui fix e registra ──────────────────────────────────────────────────── | |
| export async function runFix(action: FixAction): Promise<FixResult> { | |
| let result: FixResult; | |
| try { | |
| result = await action.run(); | |
| } catch(e) { | |
| result = { | |
| ok: false, | |
| message: "Errore imprevisto durante il fix", | |
| detail: e instanceof Error ? e.message : String(e), | |
| }; | |
| } | |
| _record(action.id, action.label, result); | |
| return result; | |
| } | |
| // ─── Health check completo del sistema ──────────────────────────────────────── | |
| // HealthCheck estratto in autoFixTypes.ts — S550 | |
| export async function runHealthChecks(): Promise<HealthCheck[]> { | |
| const results: HealthCheck[] = []; | |
| // 1. IndexedDB | |
| try { | |
| const { vfsDb } = await import("./vfsDb"); | |
| const testKey = `__healthcheck__${Date.now()}`; | |
| await vfsDb.agentContext.put({ key: testKey, value: "ok", updatedAt: Date.now() }); | |
| const r = await vfsDb.agentContext.get(testKey); | |
| await vfsDb.agentContext.delete(testKey); | |
| results.push({ id: "indexeddb", label: "IndexedDB (Dexie)", ok: r?.value === "ok", value: r?.value === "ok" ? "lettura/scrittura ok" : "errore round-trip" }); | |
| } catch(e) { | |
| results.push({ id: "indexeddb", label: "IndexedDB (Dexie)", ok: false, value: String(e).slice(0, 60), fixId: "compact-db" }); | |
| } | |
| // 2. localStorage | |
| try { | |
| const testKey = `__lscheck__${Date.now()}`; | |
| localStorage.setItem(testKey, "1"); | |
| const v = localStorage.getItem(testKey); | |
| localStorage.removeItem(testKey); | |
| const lsKeys = localStorage.length; | |
| const lsSize = Object.keys(localStorage).reduce((n, k) => n + k.length + (localStorage.getItem(k)?.length ?? 0), 0); | |
| const lsKb = Math.round(lsSize / 1024); | |
| const ok = v === "1" && lsKb < 3000; | |
| results.push({ | |
| id: "localstorage", label: "localStorage", ok: ok ? true : "warn", | |
| value: `${lsKb} KB (${lsKeys} chiavi)${lsKb >= 3000 ? " ⚠ vicino al limite" : ""}`, | |
| fixId: lsKb >= 3000 ? "clear-agent-storage" : undefined, | |
| }); | |
| } catch { | |
| results.push({ id: "localstorage", label: "localStorage", ok: false, value: "non disponibile o corrotto", fixId: "clear-ls-full" }); | |
| } | |
| // 3. Storage quota | |
| try { | |
| if (navigator.storage?.estimate) { | |
| const { usage = 0, quota = 0 } = await navigator.storage.estimate(); | |
| const usedMB = Math.round(usage / 1024 / 1024); | |
| const quotaMB = Math.round(quota / 1024 / 1024); | |
| const pct = quota > 0 ? Math.round((usage / quota) * 100) : 0; | |
| results.push({ | |
| id: "quota", label: "Storage quota", ok: pct < 80 ? true : pct < 95 ? "warn" : false, | |
| value: `${usedMB} / ${quotaMB} MB (${pct}%)`, | |
| fixId: pct >= 80 ? (pct >= 95 ? "prune-aggressivo" : "compact-db") : undefined, | |
| }); | |
| } else { | |
| results.push({ id: "quota", label: "Storage quota", ok: "warn", value: "stima non disponibile (Safari < 15.4)" }); | |
| } | |
| } catch(e) { | |
| results.push({ id: "quota", label: "Storage quota", ok: "warn", value: String(e).slice(0, 50) }); | |
| } | |
| // 4. Service Worker | |
| try { | |
| if ("serviceWorker" in navigator) { | |
| const regs = await navigator.serviceWorker.getRegistrations(); | |
| const active = regs.filter(r => r.active).length; | |
| results.push({ | |
| id: "sw", label: "Service Worker", ok: active > 0 ? true : "warn", | |
| value: active > 0 ? `${active} attivo/i` : "nessuno attivo", | |
| fixId: active === 0 ? "reset-service-worker" : undefined, | |
| }); | |
| } else { | |
| results.push({ id: "sw", label: "Service Worker", ok: "warn", value: "non supportato" }); | |
| } | |
| } catch(e) { | |
| results.push({ id: "sw", label: "Service Worker", ok: "warn", value: String(e).slice(0, 50) }); | |
| } | |
| // 5. Connettività sito live | |
| try { | |
| const start = Date.now(); | |
| const r = await fetch("https://ai-agente-ai.vercel.app/", { | |
| method: "HEAD", cache: "no-store", signal: makeTimedSignal(6000), | |
| }); | |
| const ms = Date.now() - start; | |
| results.push({ | |
| id: "vercel", label: "Sito live (Vercel)", ok: r.ok ? true : false, | |
| value: r.ok ? `HTTP ${r.status} in ${ms}ms` : `HTTP ${r.status} — verifica deploy`, | |
| }); | |
| } catch(e) { | |
| const msg = e instanceof Error ? e.message : String(e); | |
| results.push({ id: "vercel", label: "Sito live (Vercel)", ok: false, value: `Non raggiungibile: ${msg.slice(0, 60)}`, fixId: "test-network" }); | |
| } | |
| // 6. Token provider configurati | |
| try { | |
| const { getAllConfiguredTokens } = await import("./apiKeys"); | |
| const tokens = getAllConfiguredTokens(); | |
| const configured = Object.entries(tokens).filter(([, ok]) => ok); | |
| results.push({ | |
| id: "providers", label: "Provider AI configurati", ok: configured.length > 0 ? true : false, | |
| value: configured.length > 0 | |
| ? configured.map(([n]) => n).join(", ") | |
| : "nessun token configurato — vai in Impostazioni", | |
| }); | |
| } catch { | |
| results.push({ id: "providers", label: "Provider AI", ok: "warn", value: "impossibile verificare" }); | |
| } | |
| // 7. Vercel build (verifica che il bundle JS sia presente nell'HTML) | |
| try { | |
| const r = await fetch("https://ai-agente-ai.vercel.app/", { | |
| method: "GET", cache: "no-store", signal: makeTimedSignal(8000), | |
| }); | |
| const html = await r.text(); | |
| const hasBundle = /assets\/(index|main)[^"]*\.js/.test(html); | |
| const scriptCount = (html.match(/<script[^>]+src/g) ?? []).length; | |
| results.push({ | |
| id: "build", label: "Build Vercel (bundle JS)", | |
| ok: r.ok && hasBundle ? true : false, | |
| value: r.ok && hasBundle | |
| ? `ok — ${scriptCount} script` | |
| : `HTTP ${r.status} — bundle non trovato (build fallito?)`, | |
| fixId: !hasBundle ? "verify-vercel-build" : undefined, | |
| }); | |
| } catch(e) { | |
| const msg = e instanceof Error ? e.message.slice(0, 50) : String(e).slice(0, 50); | |
| results.push({ id: "build", label: "Build Vercel", ok: false, value: `Non verificabile: ${msg}`, fixId: "verify-vercel-build" }); | |
| } | |
| // 8. JS heap (Chrome only) | |
| try { | |
| const mem = (performance as unknown as Record<string, unknown>).memory as Record<string, number> | undefined; | |
| if (mem?.usedJSHeapSize && mem.jsHeapSizeLimit) { | |
| const used = Math.round(mem.usedJSHeapSize / 1024 / 1024); | |
| const limit = Math.round(mem.jsHeapSizeLimit / 1024 / 1024); | |
| const pct = Math.round((mem.usedJSHeapSize / mem.jsHeapSizeLimit) * 100); | |
| results.push({ | |
| id: "heap", label: "JS Heap (Chrome)", ok: pct < 70 ? true : pct < 90 ? "warn" : false, | |
| value: `${used} / ${limit} MB (${pct}%)`, | |
| fixId: pct >= 70 ? "compact-db" : undefined, | |
| }); | |
| } | |
| } catch { /* heap non disponibile — non aggiungere */ } | |
| // 9. Backend exec-engine (HF Space) — latenza + sessioni attive | |
| try { | |
| const _backendUrl = ( | |
| typeof import.meta !== "undefined" | |
| ? (import.meta.env?.VITE_EXEC_BACKEND_URL ?? "") | |
| : "" | |
| ).replace(/\/$/, ""); | |
| if (_backendUrl) { | |
| const _t0 = Date.now(); | |
| const _r = await fetch(`${_backendUrl}/health`, { | |
| cache: "no-store", signal: makeTimedSignal(8_000), | |
| }); | |
| const _ms = Date.now() - _t0; | |
| if (_r.ok) { | |
| const _d = await _r.json() as { status?: string; active_sessions?: number; cpu_pct?: number; mem_pct?: number; disk_free_mb?: number }; | |
| const _warn = (_d.cpu_pct ?? 0) > 85 || (_d.mem_pct ?? 0) > 85 || (_d.disk_free_mb ?? 9999) < 200; | |
| const _val = [ | |
| `${_ms}ms`, | |
| _d.active_sessions != null ? `${_d.active_sessions} sessioni` : "", | |
| _d.cpu_pct != null ? `CPU ${_d.cpu_pct}%` : "", | |
| _d.mem_pct != null ? `RAM ${_d.mem_pct}%` : "", | |
| _d.disk_free_mb != null ? `disk ${_d.disk_free_mb}MB` : "", | |
| ].filter(Boolean).join(" | "); | |
| results.push({ | |
| id: "backend", | |
| label: "Backend (exec-engine)", | |
| ok: _warn ? "warn" : true, | |
| value: _val || `HTTP ${_r.status}`, | |
| fixId: _warn ? "restart-backend-process" : undefined, | |
| }); | |
| } else { | |
| results.push({ id: "backend", label: "Backend (exec-engine)", ok: false, value: `HTTP ${_r.status}`, fixId: "restart-backend-process" }); | |
| } | |
| } | |
| } catch(_e) { | |
| const _msg = _e instanceof Error ? _e.message.slice(0, 60) : String(_e).slice(0, 60); | |
| results.push({ id: "backend", label: "Backend (exec-engine)", ok: false, value: `non raggiungibile: ${_msg}`, fixId: "restart-backend-process" }); | |
| } | |
| return results; | |
| } | |