feat: bouton restart photobooth-app après modification des actions
🚀 Deploy — JH Photomaton / 🔍 Vérification (push) Has been cancelled
🚀 Deploy — JH Photomaton / 🍓 Deploy sur le Pi (push) Has been cancelled

This commit is contained in:
2026-07-18 10:33:57 +02:00
parent 2b99ef6469
commit 3522d2d996
2 changed files with 55 additions and 4 deletions
+20 -1
View File
@@ -1,5 +1,6 @@
"""API de surveillance des ressources système.""" """API de surveillance des ressources système."""
import asyncio
import logging import logging
from typing import Optional from typing import Optional
from fastapi import APIRouter, Request, Body from fastapi import APIRouter, Request, Body
@@ -9,6 +10,16 @@ from pydantic import BaseModel
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
router = APIRouter() router = APIRouter()
# Services autorisés pour le restart individuel (jh-photomaton a son propre endpoint)
_ALLOWED_SERVICES = {
"photobooth-app",
"photobooth-kiosk",
"zoraxy",
"hostapd",
"dnsmasq",
"cups",
}
@router.get("/system/stats") @router.get("/system/stats")
async def system_stats(request: Request): async def system_stats(request: Request):
@@ -90,7 +101,15 @@ async def _restart_photobooth():
stderr=asyncio.subprocess.DEVNULL, stderr=asyncio.subprocess.DEVNULL,
) )
await proc.wait() await proc.wait()
logger.info("photobooth-app redémarré après changement CSS") logger.info("photobooth-app redémarré")
@router.post("/system/restart-photobooth")
async def restart_photobooth(request: Request):
"""Redémarre photobooth-app.service pour recharger la config (actions, etc.)."""
import asyncio
asyncio.create_task(_restart_photobooth())
return {"ok": True, "message": "Redémarrage de photobooth-app en cours…"}
@router.post("/system/screen/refresh") @router.post("/system/screen/refresh")
+35 -3
View File
@@ -121,7 +121,12 @@
<!-- Header --> <!-- Header -->
<div class="flex items-center justify-between mb-2"> <div class="flex items-center justify-between mb-2">
<h1 class="page-title" style="margin:0">Gestion des actions</h1> <h1 class="page-title" style="margin:0">Gestion des actions</h1>
<button class="btn btn-primary" onclick="newAction()">+ Nouvelle action</button> <div class="flex gap-1">
<button class="btn btn-ghost btn-sm" id="restart-btn" onclick="restartPhotobooth()" style="display:none">
🔄 Redémarrer photobooth-app
</button>
<button class="btn btn-primary" onclick="newAction()">+ Nouvelle action</button>
</div>
</div> </div>
<!-- ── Actions ──────────────────────────────────────────────────── --> <!-- ── Actions ──────────────────────────────────────────────────── -->
@@ -385,6 +390,7 @@ async function saveAction(idx) {
try { try {
await api('PUT', `/api/actions/photobooth/${realIdx}`, updates); await api('PUT', `/api/actions/photobooth/${realIdx}`, updates);
showToast('✅ Action sauvegardée', 'success'); showToast('✅ Action sauvegardée', 'success');
showRestartBtn();
await loadAll(); await loadAll();
} catch(e) { } catch(e) {
showToast(`❌ Erreur: ${e.message}`, 'error'); showToast(`❌ Erreur: ${e.message}`, 'error');
@@ -397,6 +403,7 @@ async function cloneAction(idx) {
try { try {
await api('POST', `/api/actions/photobooth/${realIdx}/clone`); await api('POST', `/api/actions/photobooth/${realIdx}/clone`);
showToast('⧉ Action dupliquée', 'success'); showToast('⧉ Action dupliquée', 'success');
showRestartBtn();
await loadAll(); await loadAll();
} catch(e) { showToast(`${e.message}`, 'error'); } } catch(e) { showToast(`${e.message}`, 'error'); }
} }
@@ -408,6 +415,7 @@ async function deleteAction(idx) {
try { try {
await api('DELETE', `/api/actions/photobooth/${realIdx}`); await api('DELETE', `/api/actions/photobooth/${realIdx}`);
showToast(`🗑 « ${name} » supprimée`, 'info'); showToast(`🗑 « ${name} » supprimée`, 'info');
showRestartBtn();
await loadAll(); await loadAll();
} catch(e) { showToast(`${e.message}`, 'error'); } } catch(e) { showToast(`${e.message}`, 'error'); }
} }
@@ -418,6 +426,7 @@ async function newAction() {
try { try {
await api('POST', '/api/actions/photobooth', { name: name.trim() || 'Nouvelle action' }); await api('POST', '/api/actions/photobooth', { name: name.trim() || 'Nouvelle action' });
showToast('✅ Action créée', 'success'); showToast('✅ Action créée', 'success');
showRestartBtn();
await loadAll(); await loadAll();
} catch(e) { showToast(`${e.message}`, 'error'); } } catch(e) { showToast(`${e.message}`, 'error'); }
} }
@@ -481,7 +490,8 @@ async function saveOrder() {
const order = actions.map(a => a._index); const order = actions.map(a => a._index);
try { try {
await api('PUT', '/api/actions/photobooth/reorder', order); await api('PUT', '/api/actions/photobooth/reorder', order);
showToast('✅ Ordre sauvegardé — redémarrez photobooth-app pour appliquer', 'success', 7000); showToast('✅ Ordre sauvegardé', 'success');
showRestartBtn();
await loadAll(); await loadAll();
} catch(e) { showToast(`${e.message}`, 'error'); } } catch(e) { showToast(`${e.message}`, 'error'); }
} }
@@ -581,7 +591,8 @@ async function restorePreset(name, label) {
if (!confirm(`Restaurer le preset « ${label} » ?\nLes actions actuelles seront remplacées.`)) return; if (!confirm(`Restaurer le preset « ${label} » ?\nLes actions actuelles seront remplacées.`)) return;
try { try {
const r = await api('POST', `/api/actions/presets/${encodeURIComponent(name)}/restore`); const r = await api('POST', `/api/actions/presets/${encodeURIComponent(name)}/restore`);
showToast(`✅ Preset « ${label} » restauré (${r.action_count} actions) — redémarrez photobooth-app`, 'success', 8000); showToast(`✅ Preset « ${label} » restauré (${r.action_count} actions)`, 'success');
showRestartBtn();
await loadAll(); await loadAll();
} catch(e) { showToast(`${e.message}`, 'error'); } } catch(e) { showToast(`${e.message}`, 'error'); }
} }
@@ -603,6 +614,27 @@ function esc(s) {
.replace(/"/g,'&quot;').replace(/'/g,'&#39;'); .replace(/"/g,'&quot;').replace(/'/g,'&#39;');
} }
// ── Restart photobooth-app ────────────────────────────────────────────────────
function showRestartBtn() {
document.getElementById('restart-btn').style.display = '';
}
async function restartPhotobooth() {
const btn = document.getElementById('restart-btn');
btn.disabled = true;
btn.textContent = '⏳ Redémarrage…';
try {
await api('POST', '/api/system/restart-photobooth');
showToast('🔄 photobooth-app redémarre — patientez ~10s', 'info', 8000);
btn.style.display = 'none';
} catch(e) {
showToast('❌ Erreur redémarrage: ' + e.message, 'error');
} finally {
btn.disabled = false;
btn.textContent = '🔄 Redémarrer photobooth-app';
}
}
// ── Init ────────────────────────────────────────────────────────────────────── // ── Init ──────────────────────────────────────────────────────────────────────
loadAll(); loadAll();
</script> </script>