feat: Mejorar estructura de monitored_apps.json con metadata completa
- Añadir campos al modelo MonitoredApp: * service_name: Nombre del servicio systemd * path: WorkingDirectory de la aplicación * entry_point: Archivo de entrada (server.js, app.js, etc.) * node_bin: Ruta completa al binario de node/python * mode: Modo de ejecución (production, development, test) * service_file_path: Ruta al archivo .service de systemd * registered_at: Timestamp de registro (ISO 8601) - Actualizar discovery.rs para extraer toda la información: * Parsear ExecStart para obtener node_bin y entry_point * Extraer NODE_ENV para determinar el modo * Guardar ruta completa al archivo .service * Usar add_app_full() con información completa - Integrar ConfigManager en AppManager: * Guardar automáticamente en monitored_apps.json al registrar apps * Eliminar del JSON al desregistrar apps * Extraer metadata desde ServiceConfig (puerto, entry_point, mode, etc.) - Mantener retrocompatibilidad con JSON antiguo mediante campos deprecated - Todos los nuevos campos usan #[serde(default)] para evitar errores de deserialización
This commit is contained in:
563
web/health.html
Normal file
563
web/health.html
Normal file
@@ -0,0 +1,563 @@
|
||||
<!doctype html>
|
||||
<html class="dark" lang="es" dir="ltr">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta content="width=device-width, initial-scale=1.0" name="viewport" />
|
||||
<title>System Health - SIAX Monitor</title>
|
||||
<link rel="icon" type="image/svg+xml" href="/static/icon/favicon.svg" />
|
||||
<script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
<script>
|
||||
tailwind.config = {
|
||||
darkMode: "class",
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
primary: "#137fec",
|
||||
"background-light": "#f6f7f8",
|
||||
"background-dark": "#101922",
|
||||
},
|
||||
fontFamily: {
|
||||
display: ["Inter", "sans-serif"],
|
||||
mono: ["JetBrains Mono", "monospace"],
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style>
|
||||
body {
|
||||
font-family: "Inter", sans-serif;
|
||||
}
|
||||
.material-symbols-outlined {
|
||||
font-variation-settings:
|
||||
"FILL" 0,
|
||||
"wght" 400,
|
||||
"GRAD" 0,
|
||||
"opsz" 24;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-background-dark text-white min-h-screen">
|
||||
<!-- Header -->
|
||||
<header class="border-b border-[#283039] bg-[#0a0f16]">
|
||||
<div class="container mx-auto px-4 py-4">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center gap-4">
|
||||
<div
|
||||
class="rounded-full size-9 border-2 border-slate-700 overflow-hidden"
|
||||
>
|
||||
<img
|
||||
src="/static/icon/logo.png"
|
||||
alt="Logo"
|
||||
class="w-full h-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<h1 class="text-xl font-bold">SIAX Monitor</h1>
|
||||
<p class="text-xs text-slate-400">System Health</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Desktop Navigation -->
|
||||
<nav class="hidden md:flex items-center gap-2">
|
||||
<a
|
||||
href="/scan"
|
||||
class="px-4 py-2 rounded-lg text-slate-300 hover:bg-[#161f2a] transition-colors flex items-center gap-2"
|
||||
>
|
||||
<span class="material-symbols-outlined text-lg"
|
||||
>search</span
|
||||
>
|
||||
<span>Escanear</span>
|
||||
</a>
|
||||
<a
|
||||
href="/logs"
|
||||
class="px-4 py-2 rounded-lg text-slate-300 hover:bg-[#161f2a] transition-colors flex items-center gap-2"
|
||||
>
|
||||
<span class="material-symbols-outlined text-lg"
|
||||
>article</span
|
||||
>
|
||||
<span>Logs</span>
|
||||
</a>
|
||||
<a
|
||||
href="/register"
|
||||
class="px-4 py-2 rounded-lg text-slate-300 hover:bg-[#161f2a] transition-colors flex items-center gap-2"
|
||||
>
|
||||
<span class="material-symbols-outlined text-lg"
|
||||
>app_registration</span
|
||||
>
|
||||
<span>Registrar</span>
|
||||
</a>
|
||||
<a
|
||||
href="/"
|
||||
class="px-4 py-2 rounded-lg text-slate-300 hover:bg-[#161f2a] transition-colors flex items-center gap-2"
|
||||
>
|
||||
<span class="material-symbols-outlined text-lg"
|
||||
>dashboard</span
|
||||
>
|
||||
<span>Dashboard</span>
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
<!-- Mobile Menu Button -->
|
||||
<button
|
||||
onclick="toggleMenu()"
|
||||
class="md:hidden px-3 py-2 rounded-lg text-slate-300 hover:bg-[#161f2a] transition-colors"
|
||||
>
|
||||
<span class="material-symbols-outlined text-2xl"
|
||||
>menu</span
|
||||
>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Mobile Menu Dropdown -->
|
||||
<div
|
||||
id="mobile-menu"
|
||||
class="hidden md:hidden mt-4 pb-4 space-y-2"
|
||||
>
|
||||
<a
|
||||
href="/health"
|
||||
class="block px-4 py-3 rounded-lg bg-[#161f2a] text-primary transition-colors flex items-center gap-3"
|
||||
>
|
||||
<span class="material-symbols-outlined"
|
||||
>monitor_heart</span
|
||||
>
|
||||
<span>Health</span>
|
||||
</a>
|
||||
<a
|
||||
href="/scan"
|
||||
class="block px-4 py-3 rounded-lg text-slate-300 hover:bg-[#161f2a] transition-colors flex items-center gap-3"
|
||||
>
|
||||
<span class="material-symbols-outlined">search</span>
|
||||
<span>Escanear</span>
|
||||
</a>
|
||||
<a
|
||||
href="/logs"
|
||||
class="block px-4 py-3 rounded-lg text-slate-300 hover:bg-[#161f2a] transition-colors flex items-center gap-3"
|
||||
>
|
||||
<span class="material-symbols-outlined">article</span>
|
||||
<span>Logs</span>
|
||||
</a>
|
||||
<a
|
||||
href="/"
|
||||
class="block px-4 py-3 rounded-lg text-slate-300 hover:bg-[#161f2a] transition-colors flex items-center gap-3"
|
||||
>
|
||||
<span class="material-symbols-outlined">dashboard</span>
|
||||
<span>Dashboard</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- Main Content -->
|
||||
<main class="container mx-auto px-4 py-8">
|
||||
<!-- Page Header -->
|
||||
<div class="mb-8">
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<h2 class="text-3xl font-black mb-2">System Health</h2>
|
||||
<p class="text-slate-400">
|
||||
Diagnóstico y estado del sistema de monitoreo
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
onclick="refreshHealth()"
|
||||
class="px-4 py-2 rounded-lg bg-primary hover:brightness-110 transition-all flex items-center gap-2"
|
||||
>
|
||||
<span class="material-symbols-outlined text-sm"
|
||||
>refresh</span
|
||||
>
|
||||
<span>Actualizar</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Loading State -->
|
||||
<div id="loading-state" class="text-center py-12">
|
||||
<div
|
||||
class="inline-block animate-spin rounded-full h-12 w-12 border-4 border-primary border-t-transparent"
|
||||
></div>
|
||||
<p class="mt-4 text-slate-400">
|
||||
Cargando estado del sistema...
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Health Cards Container -->
|
||||
<div id="health-content" class="hidden">
|
||||
<!-- Status Overview -->
|
||||
<div class="grid grid-cols-1 md:grid-cols-4 gap-4 mb-8">
|
||||
<!-- Overall Status -->
|
||||
<div
|
||||
class="rounded-2xl border border-[#283039] bg-[#161f2a] p-6"
|
||||
>
|
||||
<div class="flex items-center justify-between mb-2">
|
||||
<span class="text-slate-400">Estado General</span>
|
||||
<span
|
||||
class="material-symbols-outlined text-green-400"
|
||||
id="status-icon"
|
||||
>check_circle</span
|
||||
>
|
||||
</div>
|
||||
<div class="text-2xl font-bold" id="overall-status">
|
||||
OK
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Config Status -->
|
||||
<div
|
||||
class="rounded-2xl border border-[#283039] bg-[#161f2a] p-6"
|
||||
>
|
||||
<div class="flex items-center justify-between mb-2">
|
||||
<span class="text-slate-400">Configuración</span>
|
||||
<span
|
||||
class="material-symbols-outlined text-blue-400"
|
||||
>settings</span
|
||||
>
|
||||
</div>
|
||||
<div class="text-2xl font-bold" id="config-status">
|
||||
Cargada
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Apps Count -->
|
||||
<div
|
||||
class="rounded-2xl border border-[#283039] bg-[#161f2a] p-6"
|
||||
>
|
||||
<div class="flex items-center justify-between mb-2">
|
||||
<span class="text-slate-400">Apps Registradas</span>
|
||||
<span
|
||||
class="material-symbols-outlined text-purple-400"
|
||||
>apps</span
|
||||
>
|
||||
</div>
|
||||
<div class="text-2xl font-bold" id="apps-count">0</div>
|
||||
</div>
|
||||
|
||||
<!-- Version -->
|
||||
<div
|
||||
class="rounded-2xl border border-[#283039] bg-[#161f2a] p-6"
|
||||
>
|
||||
<div class="flex items-center justify-between mb-2">
|
||||
<span class="text-slate-400">Versión</span>
|
||||
<span
|
||||
class="material-symbols-outlined text-yellow-400"
|
||||
>info</span
|
||||
>
|
||||
</div>
|
||||
<div class="text-2xl font-bold font-mono" id="version">
|
||||
-
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Detailed Information -->
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||||
<!-- Configuration Details -->
|
||||
<div
|
||||
class="rounded-2xl border border-[#283039] bg-[#161f2a] p-6"
|
||||
>
|
||||
<div class="flex items-center gap-3 mb-6">
|
||||
<div
|
||||
class="w-10 h-10 rounded-xl bg-blue-500/20 flex items-center justify-center"
|
||||
>
|
||||
<span
|
||||
class="material-symbols-outlined text-blue-400"
|
||||
>folder</span
|
||||
>
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="text-lg font-bold">Configuración</h3>
|
||||
<p class="text-sm text-slate-400">
|
||||
Detalles del archivo de configuración
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="space-y-4">
|
||||
<div
|
||||
class="flex items-start justify-between py-3 border-b border-[#283039]"
|
||||
>
|
||||
<div>
|
||||
<p class="text-sm text-slate-400">
|
||||
Ruta del archivo
|
||||
</p>
|
||||
<p
|
||||
class="font-mono text-sm text-primary"
|
||||
id="config-path"
|
||||
>
|
||||
-
|
||||
</p>
|
||||
</div>
|
||||
<span
|
||||
class="material-symbols-outlined text-green-400"
|
||||
id="config-loaded-icon"
|
||||
>check_circle</span
|
||||
>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="flex items-start justify-between py-3 border-b border-[#283039]"
|
||||
>
|
||||
<div>
|
||||
<p class="text-sm text-slate-400">Estado</p>
|
||||
<p
|
||||
class="font-semibold"
|
||||
id="config-loaded-text"
|
||||
>
|
||||
Archivo cargado correctamente
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-start justify-between py-3">
|
||||
<div>
|
||||
<p class="text-sm text-slate-400">
|
||||
Aplicaciones en config
|
||||
</p>
|
||||
<p
|
||||
class="text-2xl font-bold"
|
||||
id="config-apps-count"
|
||||
>
|
||||
0
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Systemd Services -->
|
||||
<div
|
||||
class="rounded-2xl border border-[#283039] bg-[#161f2a] p-6"
|
||||
>
|
||||
<div class="flex items-center gap-3 mb-6">
|
||||
<div
|
||||
class="w-10 h-10 rounded-xl bg-green-500/20 flex items-center justify-center"
|
||||
>
|
||||
<span
|
||||
class="material-symbols-outlined text-green-400"
|
||||
>settings_system_daydream</span
|
||||
>
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="text-lg font-bold">
|
||||
Servicios Systemd
|
||||
</h3>
|
||||
<p class="text-sm text-slate-400">
|
||||
Servicios creados por SIAX Monitor
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
id="systemd-services-list"
|
||||
class="space-y-2 max-h-80 overflow-y-auto"
|
||||
>
|
||||
<!-- Services will be injected here -->
|
||||
</div>
|
||||
|
||||
<div
|
||||
id="no-services"
|
||||
class="hidden text-center py-8 text-slate-400"
|
||||
>
|
||||
<span
|
||||
class="material-symbols-outlined text-4xl mb-2 opacity-50"
|
||||
>info</span
|
||||
>
|
||||
<p>No hay servicios systemd registrados</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- System Commands -->
|
||||
<div
|
||||
class="mt-6 rounded-2xl border border-[#283039] bg-[#161f2a] p-6"
|
||||
>
|
||||
<div class="flex items-center gap-3 mb-6">
|
||||
<div
|
||||
class="w-10 h-10 rounded-xl bg-purple-500/20 flex items-center justify-center"
|
||||
>
|
||||
<span
|
||||
class="material-symbols-outlined text-purple-400"
|
||||
>terminal</span
|
||||
>
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="text-lg font-bold">Comandos Útiles</h3>
|
||||
<p class="text-sm text-slate-400">
|
||||
Comandos para gestionar servicios systemd
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div class="rounded-lg bg-[#0a0f16] p-4">
|
||||
<p class="text-xs text-slate-400 mb-2">
|
||||
Listar servicios SIAX
|
||||
</p>
|
||||
<code class="text-sm text-primary font-mono"
|
||||
>systemctl list-units 'siax-app-*'</code
|
||||
>
|
||||
</div>
|
||||
|
||||
<div class="rounded-lg bg-[#0a0f16] p-4">
|
||||
<p class="text-xs text-slate-400 mb-2">
|
||||
Ver estado de un servicio
|
||||
</p>
|
||||
<code class="text-sm text-primary font-mono"
|
||||
>systemctl status siax-app-nombre.service</code
|
||||
>
|
||||
</div>
|
||||
|
||||
<div class="rounded-lg bg-[#0a0f16] p-4">
|
||||
<p class="text-xs text-slate-400 mb-2">
|
||||
Ver logs de un servicio
|
||||
</p>
|
||||
<code class="text-sm text-primary font-mono"
|
||||
>journalctl -u siax-app-nombre -f</code
|
||||
>
|
||||
</div>
|
||||
|
||||
<div class="rounded-lg bg-[#0a0f16] p-4">
|
||||
<p class="text-xs text-slate-400 mb-2">
|
||||
Recargar daemon de systemd
|
||||
</p>
|
||||
<code class="text-sm text-primary font-mono"
|
||||
>systemctl daemon-reload</code
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<script>
|
||||
async function loadHealth() {
|
||||
const loading = document.getElementById("loading-state");
|
||||
const content = document.getElementById("health-content");
|
||||
|
||||
try {
|
||||
loading.classList.remove("hidden");
|
||||
content.classList.add("hidden");
|
||||
|
||||
const response = await fetch("/api/health");
|
||||
if (!response.ok) throw new Error("Failed to fetch health");
|
||||
|
||||
const result = await response.json();
|
||||
const data = result.data;
|
||||
|
||||
loading.classList.add("hidden");
|
||||
content.classList.remove("hidden");
|
||||
|
||||
// Update status cards
|
||||
document.getElementById("overall-status").textContent =
|
||||
data.status.toUpperCase();
|
||||
document.getElementById("config-status").textContent =
|
||||
data.config_loaded ? "Cargada" : "No encontrada";
|
||||
document.getElementById("apps-count").textContent =
|
||||
data.apps_count;
|
||||
document.getElementById("version").textContent =
|
||||
"v" + data.version;
|
||||
|
||||
// Update config details
|
||||
document.getElementById("config-path").textContent =
|
||||
data.config_path;
|
||||
document.getElementById("config-apps-count").textContent =
|
||||
data.apps_count;
|
||||
|
||||
const configIcon =
|
||||
document.getElementById("config-loaded-icon");
|
||||
const configText =
|
||||
document.getElementById("config-loaded-text");
|
||||
|
||||
if (data.config_loaded) {
|
||||
configIcon.textContent = "check_circle";
|
||||
configIcon.className =
|
||||
"material-symbols-outlined text-green-400";
|
||||
configText.textContent =
|
||||
"Archivo cargado correctamente";
|
||||
} else {
|
||||
configIcon.textContent = "error";
|
||||
configIcon.className =
|
||||
"material-symbols-outlined text-yellow-400";
|
||||
configText.textContent =
|
||||
"Archivo no encontrado (se creará automáticamente)";
|
||||
}
|
||||
|
||||
// Update systemd services list
|
||||
const servicesList = document.getElementById(
|
||||
"systemd-services-list",
|
||||
);
|
||||
const noServices = document.getElementById("no-services");
|
||||
|
||||
if (
|
||||
data.systemd_services &&
|
||||
data.systemd_services.length > 0
|
||||
) {
|
||||
servicesList.innerHTML = data.systemd_services
|
||||
.map(
|
||||
(service) => `
|
||||
<div class="flex items-center justify-between p-3 rounded-lg bg-[#0a0f16] hover:bg-[#0d1218] transition-colors">
|
||||
<div class="flex items-center gap-3">
|
||||
<span class="material-symbols-outlined text-green-400 text-sm">check_circle</span>
|
||||
<span class="font-mono text-sm">${service}</span>
|
||||
</div>
|
||||
<button onclick="copyToClipboard('${service}')" class="text-slate-400 hover:text-primary transition-colors">
|
||||
<span class="material-symbols-outlined text-sm">content_copy</span>
|
||||
</button>
|
||||
</div>
|
||||
`,
|
||||
)
|
||||
.join("");
|
||||
noServices.classList.add("hidden");
|
||||
} else {
|
||||
servicesList.innerHTML = "";
|
||||
noServices.classList.remove("hidden");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading health:", error);
|
||||
loading.classList.add("hidden");
|
||||
content.innerHTML = `
|
||||
<div class="text-center py-12 text-red-400">
|
||||
<span class="material-symbols-outlined text-6xl mb-4">error</span>
|
||||
<p class="text-xl font-bold mb-2">Error al cargar el estado del sistema</p>
|
||||
<p class="text-slate-400">${error.message}</p>
|
||||
<button onclick="loadHealth()" class="mt-4 px-6 py-2 bg-primary rounded-lg hover:brightness-110">
|
||||
Reintentar
|
||||
</button>
|
||||
</div>
|
||||
`;
|
||||
content.classList.remove("hidden");
|
||||
}
|
||||
}
|
||||
|
||||
function refreshHealth() {
|
||||
loadHealth();
|
||||
}
|
||||
|
||||
function copyToClipboard(text) {
|
||||
navigator.clipboard.writeText(text).then(() => {
|
||||
// Simple feedback - you could add a toast notification here
|
||||
console.log("Copied:", text);
|
||||
});
|
||||
}
|
||||
|
||||
function toggleMenu() {
|
||||
const menu = document.getElementById("mobile-menu");
|
||||
menu.classList.toggle("hidden");
|
||||
}
|
||||
|
||||
// Load on page load
|
||||
document.addEventListener("DOMContentLoaded", loadHealth);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user