feat: bouton restart photobooth-app après modification des actions
This commit is contained in:
@@ -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")
|
||||||
|
|||||||
@@ -121,8 +121,13 @@
|
|||||||
<!-- 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>
|
||||||
|
<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>
|
<button class="btn btn-primary" onclick="newAction()">+ Nouvelle action</button>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- ── Actions ──────────────────────────────────────────────────── -->
|
<!-- ── Actions ──────────────────────────────────────────────────── -->
|
||||||
<div class="section">
|
<div class="section">
|
||||||
@@ -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,'"').replace(/'/g,''');
|
.replace(/"/g,'"').replace(/'/g,''');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── 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>
|
||||||
|
|||||||
Reference in New Issue
Block a user