feat: Agregar controles de Iniciar/Detener/Reiniciar en panel web

Cambios en el frontend (index.html):
- Cambiar header "Actions" a "Acciones"
- Agregar botones de control según estado de la app:
  * Si está Running: botones Detener (rojo) y Reiniciar (amarillo)
  * Si está Stopped: botón Iniciar (verde)
  * Siempre: botón Ver logs (azul)
- Agregar función controlApp() para llamar a la API
- Diálogo de confirmación antes de ejecutar acciones
- Recarga automática de la tabla después de ejecutar acción

Cambios en el backend (lifecycle.rs):
- Corregir formato de service_name en start_app()
- Corregir formato de service_name en stop_app()
- Corregir formato de service_name en restart_app()
- Ahora usa: siax-app-{app_name}.service en lugar de {app_name}.service

Los botones ahora funcionan correctamente con los servicios systemd
This commit is contained in:
2026-01-18 03:55:07 -05:00
parent 87ce154789
commit 868f3a2d30
2 changed files with 71 additions and 7 deletions

View File

@@ -26,7 +26,7 @@ impl LifecycleManager {
logger.info("Lifecycle", &format!("Iniciando aplicación: {}", app_name)); logger.info("Lifecycle", &format!("Iniciando aplicación: {}", app_name));
let service_name = format!("{}.service", app_name); let service_name = format!("siax-app-{}.service", app_name);
SystemCtl::start(&service_name)?; SystemCtl::start(&service_name)?;
// Actualizar rate limiter // Actualizar rate limiter
@@ -45,7 +45,7 @@ impl LifecycleManager {
logger.info("Lifecycle", &format!("Deteniendo aplicación: {}", app_name)); logger.info("Lifecycle", &format!("Deteniendo aplicación: {}", app_name));
let service_name = format!("{}.service", app_name); let service_name = format!("siax-app-{}.service", app_name);
SystemCtl::stop(&service_name)?; SystemCtl::stop(&service_name)?;
// Actualizar rate limiter // Actualizar rate limiter
@@ -64,7 +64,7 @@ impl LifecycleManager {
logger.info("Lifecycle", &format!("Reiniciando aplicación: {}", app_name)); logger.info("Lifecycle", &format!("Reiniciando aplicación: {}", app_name));
let service_name = format!("{}.service", app_name); let service_name = format!("siax-app-{}.service", app_name);
SystemCtl::restart(&service_name)?; SystemCtl::restart(&service_name)?;
// Actualizar rate limiter // Actualizar rate limiter

View File

@@ -364,7 +364,7 @@
<th class="px-6 py-4">Mem %</th> <th class="px-6 py-4">Mem %</th>
<th class="px-6 py-4">Tiempo Activo</th> <th class="px-6 py-4">Tiempo Activo</th>
<th class="px-6 py-4 text-right"> <th class="px-6 py-4 text-right">
Actions Acciones
</th> </th>
</tr> </tr>
</thead> </thead>
@@ -557,9 +557,35 @@
<td class="px-6 py-4 text-sm">-</td> <td class="px-6 py-4 text-sm">-</td>
<td class="px-6 py-4 text-sm text-slate-500">-</td> <td class="px-6 py-4 text-sm text-slate-500">-</td>
<td class="px-6 py-4 text-right"> <td class="px-6 py-4 text-right">
<button class="text-slate-400 hover:text-white transition-colors" onclick="window.location.href='/logs'"> <div class="flex items-center justify-end gap-2">
<span class="material-symbols-outlined">visibility</span> ${
</button> app.status === "Running"
? `
<button class="text-red-400 hover:text-red-300 transition-colors p-1.5 rounded hover:bg-red-900/20"
onclick="controlApp('${app.name}', 'stop')"
title="Detener">
<span class="material-symbols-outlined text-[20px]">stop</span>
</button>
<button class="text-yellow-400 hover:text-yellow-300 transition-colors p-1.5 rounded hover:bg-yellow-900/20"
onclick="controlApp('${app.name}', 'restart')"
title="Reiniciar">
<span class="material-symbols-outlined text-[20px]">refresh</span>
</button>
`
: `
<button class="text-green-400 hover:text-green-300 transition-colors p-1.5 rounded hover:bg-green-900/20"
onclick="controlApp('${app.name}', 'start')"
title="Iniciar">
<span class="material-symbols-outlined text-[20px]">play_arrow</span>
</button>
`
}
<button class="text-blue-400 hover:text-blue-300 transition-colors p-1.5 rounded hover:bg-blue-900/20"
onclick="window.location.href='/logs'"
title="Ver logs">
<span class="material-symbols-outlined text-[20px]">visibility</span>
</button>
</div>
</td> </td>
</tr> </tr>
`; `;
@@ -579,6 +605,44 @@
`; `;
} }
async function controlApp(appName, action) {
const actionNames = {
start: "Iniciar",
stop: "Detener",
restart: "Reiniciar",
};
const confirmed = confirm(
`¿Estás seguro de ${actionNames[action]} la aplicación "${appName}"?`,
);
if (!confirmed) return;
try {
const response = await fetch(
`/api/apps/${appName}/${action}`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
},
);
const result = await response.json();
if (result.success) {
alert(`${result.data.message}`);
// Recargar la lista de apps
loadApps();
} else {
alert(`❌ Error: ${result.error}`);
}
} catch (error) {
console.error("Error:", error);
alert("❌ Error al ejecutar la acción");
}
}
function toggleMenu() { function toggleMenu() {
const menu = document.getElementById("mobile-menu"); const menu = document.getElementById("mobile-menu");
menu.classList.toggle("hidden"); menu.classList.toggle("hidden");