2393396948
- actions_api.py: POST /create, POST /{idx}/clone, DELETE /{idx},
PUT /reorder, PUT /{idx} étendu (ui_trigger.*),
GET/POST/POST-restore/DELETE /presets
- admin_api.py: route /admin/actions simplifiée (plus de pré-chargement Jinja2)
- actions.html: page full JS dynamique
· Drag & drop pour réordonner (barre de sauvegarde explicite)
· Clone, suppression, création par bouton
· Edit form complet: général, cadre, fond, traitement, UI bouton
· Sélecteurs cadres/fonds avec dropdown + saisie libre
· Mapping bouton dynamique
· Presets: save/restore/delete avec nom + description
148 lines
4.4 KiB
Python
148 lines
4.4 KiB
Python
"""Routes du dashboard admin -- authentification requise."""
|
|
|
|
import logging
|
|
from datetime import datetime
|
|
from pathlib import Path
|
|
|
|
from fastapi import APIRouter, Request, Form
|
|
from fastapi.responses import HTMLResponse, RedirectResponse
|
|
from fastapi.templating import Jinja2Templates
|
|
|
|
logger = logging.getLogger(__name__)
|
|
router = APIRouter()
|
|
|
|
_templates = Jinja2Templates(
|
|
directory=Path(__file__).parent.parent.parent / "frontend" / "templates"
|
|
)
|
|
|
|
|
|
def _is_auth(request: Request) -> bool:
|
|
return request.session.get("authenticated") is True
|
|
|
|
|
|
def _require_auth(request: Request):
|
|
if not _is_auth(request):
|
|
return RedirectResponse(url="/admin/login", status_code=302)
|
|
return None
|
|
|
|
|
|
# -- Auth ----------------------------------------------------------------------
|
|
|
|
@router.get("/admin/login", response_class=HTMLResponse)
|
|
async def login_page(request: Request):
|
|
if _is_auth(request):
|
|
return RedirectResponse(url="/admin", status_code=302)
|
|
return _templates.TemplateResponse(request, "admin/login.html", {"error": None})
|
|
|
|
|
|
@router.post("/admin/login")
|
|
async def login(request: Request, password: str = Form(...)):
|
|
cfg = request.app.state.config
|
|
if password == cfg.app.admin_password:
|
|
request.session["authenticated"] = True
|
|
return RedirectResponse(url="/admin", status_code=302)
|
|
return _templates.TemplateResponse(
|
|
request,
|
|
"admin/login.html",
|
|
{"error": "Mot de passe incorrect"},
|
|
)
|
|
|
|
|
|
@router.get("/admin/logout")
|
|
async def logout(request: Request):
|
|
request.session.clear()
|
|
return RedirectResponse(url="/admin/login", status_code=302)
|
|
|
|
|
|
# -- Pages admin ---------------------------------------------------------------
|
|
|
|
@router.get("/admin", response_class=HTMLResponse)
|
|
async def admin_dashboard(request: Request):
|
|
redirect = _require_auth(request)
|
|
if redirect:
|
|
return redirect
|
|
|
|
sys_svc = request.app.state.system_service
|
|
cfg = request.app.state.config
|
|
led = request.app.state.led_service
|
|
btn = request.app.state.button_service
|
|
|
|
stats = sys_svc.get_stats()
|
|
services = sys_svc.get_services_status()
|
|
|
|
return _templates.TemplateResponse(request, "admin/dashboard.html", {
|
|
"config": cfg,
|
|
"stats": stats,
|
|
"services": services,
|
|
"led_effect": led.current_effect,
|
|
"relay_state": btn.relay_state if btn else True,
|
|
"print_mode": cfg.print.mode,
|
|
})
|
|
|
|
|
|
@router.get("/admin/gallery", response_class=HTMLResponse)
|
|
async def admin_gallery(request: Request):
|
|
redirect = _require_auth(request)
|
|
if redirect:
|
|
return redirect
|
|
cfg = request.app.state.config
|
|
return _templates.TemplateResponse(request, "admin/gallery.html", {
|
|
"config": cfg,
|
|
})
|
|
|
|
|
|
@router.get("/admin/actions", response_class=HTMLResponse)
|
|
async def admin_actions(request: Request):
|
|
redirect = _require_auth(request)
|
|
if redirect:
|
|
return redirect
|
|
cfg = request.app.state.config
|
|
return _templates.TemplateResponse(request, "admin/actions.html", {"config": cfg})
|
|
|
|
|
|
@router.get("/admin/settings", response_class=HTMLResponse)
|
|
async def admin_settings(request: Request):
|
|
redirect = _require_auth(request)
|
|
if redirect:
|
|
return redirect
|
|
cfg = request.app.state.config
|
|
return _templates.TemplateResponse(request, "admin/settings.html", {
|
|
"config": cfg,
|
|
"button": cfg.button,
|
|
})
|
|
|
|
|
|
@router.get("/admin/events", response_class=HTMLResponse)
|
|
async def admin_events(request: Request):
|
|
redirect = _require_auth(request)
|
|
if redirect:
|
|
return redirect
|
|
cfg = request.app.state.config
|
|
return _templates.TemplateResponse(request, "admin/events.html", {"config": cfg})
|
|
|
|
|
|
@router.get("/admin/print", response_class=HTMLResponse)
|
|
async def admin_print(request: Request):
|
|
redirect = _require_auth(request)
|
|
if redirect:
|
|
return redirect
|
|
|
|
printer_svc = request.app.state.printer_service
|
|
cfg = request.app.state.config
|
|
|
|
queue = await printer_svc.get_queue()
|
|
printers = await printer_svc.get_printers_status()
|
|
|
|
# Formate les dates pour le template Jinja2
|
|
for q in queue:
|
|
ts = q.get("requested_at")
|
|
q["requested_at_str"] = (
|
|
datetime.fromtimestamp(ts).strftime("%d/%m %H:%M") if ts else "—"
|
|
)
|
|
|
|
return _templates.TemplateResponse(request, "admin/print.html", {
|
|
"config": cfg,
|
|
"queue": queue,
|
|
"printers": printers,
|
|
})
|