all
This commit is contained in:
+11
-15
@@ -1,4 +1,4 @@
|
|||||||
"""Routes du dashboard admin — authentification requise."""
|
"""Routes du dashboard admin -- authentification requise."""
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
@@ -25,13 +25,13 @@ def _require_auth(request: Request):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
# ── Auth ──────────────────────────────────────────────────────────────────────
|
# -- Auth ----------------------------------------------------------------------
|
||||||
|
|
||||||
@router.get("/admin/login", response_class=HTMLResponse)
|
@router.get("/admin/login", response_class=HTMLResponse)
|
||||||
async def login_page(request: Request):
|
async def login_page(request: Request):
|
||||||
if _is_auth(request):
|
if _is_auth(request):
|
||||||
return RedirectResponse(url="/admin", status_code=302)
|
return RedirectResponse(url="/admin", status_code=302)
|
||||||
return _templates.TemplateResponse("admin/login.html", {"request": request, "error": None})
|
return _templates.TemplateResponse(request, "admin/login.html", {"error": None})
|
||||||
|
|
||||||
|
|
||||||
@router.post("/admin/login")
|
@router.post("/admin/login")
|
||||||
@@ -41,8 +41,9 @@ async def login(request: Request, password: str = Form(...)):
|
|||||||
request.session["authenticated"] = True
|
request.session["authenticated"] = True
|
||||||
return RedirectResponse(url="/admin", status_code=302)
|
return RedirectResponse(url="/admin", status_code=302)
|
||||||
return _templates.TemplateResponse(
|
return _templates.TemplateResponse(
|
||||||
|
request,
|
||||||
"admin/login.html",
|
"admin/login.html",
|
||||||
{"request": request, "error": "Mot de passe incorrect"},
|
{"error": "Mot de passe incorrect"},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -52,7 +53,7 @@ async def logout(request: Request):
|
|||||||
return RedirectResponse(url="/admin/login", status_code=302)
|
return RedirectResponse(url="/admin/login", status_code=302)
|
||||||
|
|
||||||
|
|
||||||
# ── Pages admin ───────────────────────────────────────────────────────────────
|
# -- Pages admin ---------------------------------------------------------------
|
||||||
|
|
||||||
@router.get("/admin", response_class=HTMLResponse)
|
@router.get("/admin", response_class=HTMLResponse)
|
||||||
async def admin_dashboard(request: Request):
|
async def admin_dashboard(request: Request):
|
||||||
@@ -68,8 +69,7 @@ async def admin_dashboard(request: Request):
|
|||||||
stats = sys_svc.get_stats()
|
stats = sys_svc.get_stats()
|
||||||
services = sys_svc.get_services_status()
|
services = sys_svc.get_services_status()
|
||||||
|
|
||||||
return _templates.TemplateResponse("admin/dashboard.html", {
|
return _templates.TemplateResponse(request, "admin/dashboard.html", {
|
||||||
"request": request,
|
|
||||||
"config": cfg,
|
"config": cfg,
|
||||||
"stats": stats,
|
"stats": stats,
|
||||||
"services": services,
|
"services": services,
|
||||||
@@ -85,8 +85,7 @@ async def admin_gallery(request: Request):
|
|||||||
if redirect:
|
if redirect:
|
||||||
return redirect
|
return redirect
|
||||||
cfg = request.app.state.config
|
cfg = request.app.state.config
|
||||||
return _templates.TemplateResponse("admin/gallery.html", {
|
return _templates.TemplateResponse(request, "admin/gallery.html", {
|
||||||
"request": request,
|
|
||||||
"config": cfg,
|
"config": cfg,
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -102,8 +101,7 @@ async def admin_actions(request: Request):
|
|||||||
pb_config = await pb.read_pb_config()
|
pb_config = await pb.read_pb_config()
|
||||||
actions = pb_config.get("actions", {}).get("image", [])
|
actions = pb_config.get("actions", {}).get("image", [])
|
||||||
|
|
||||||
return _templates.TemplateResponse("admin/actions.html", {
|
return _templates.TemplateResponse(request, "admin/actions.html", {
|
||||||
"request": request,
|
|
||||||
"config": cfg,
|
"config": cfg,
|
||||||
"pb_actions": actions,
|
"pb_actions": actions,
|
||||||
"button_mapping": cfg.button_actions,
|
"button_mapping": cfg.button_actions,
|
||||||
@@ -116,8 +114,7 @@ async def admin_settings(request: Request):
|
|||||||
if redirect:
|
if redirect:
|
||||||
return redirect
|
return redirect
|
||||||
cfg = request.app.state.config
|
cfg = request.app.state.config
|
||||||
return _templates.TemplateResponse("admin/settings.html", {
|
return _templates.TemplateResponse(request, "admin/settings.html", {
|
||||||
"request": request,
|
|
||||||
"config": cfg,
|
"config": cfg,
|
||||||
"button": cfg.button,
|
"button": cfg.button,
|
||||||
})
|
})
|
||||||
@@ -135,8 +132,7 @@ async def admin_print(request: Request):
|
|||||||
queue = await printer_svc.get_queue()
|
queue = await printer_svc.get_queue()
|
||||||
printers = await printer_svc.get_printers_status()
|
printers = await printer_svc.get_printers_status()
|
||||||
|
|
||||||
return _templates.TemplateResponse("admin/print.html", {
|
return _templates.TemplateResponse(request, "admin/print.html", {
|
||||||
"request": request,
|
|
||||||
"config": cfg,
|
"config": cfg,
|
||||||
"queue": queue,
|
"queue": queue,
|
||||||
"printers": printers,
|
"printers": printers,
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ async def gallery_page(request: Request):
|
|||||||
cfg = request.app.state.config
|
cfg = request.app.state.config
|
||||||
if not cfg.gallery.public_enabled:
|
if not cfg.gallery.public_enabled:
|
||||||
return HTMLResponse("<h1>Galerie desactivee</h1>", status_code=403)
|
return HTMLResponse("<h1>Galerie desactivee</h1>", status_code=403)
|
||||||
return _templates.TemplateResponse("public/gallery.html", {"request": request, "config": cfg})
|
return _templates.TemplateResponse(request, "public/gallery.html", {"config": cfg})
|
||||||
|
|
||||||
|
|
||||||
@router.get("/api/gallery/photos")
|
@router.get("/api/gallery/photos")
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ app:
|
|||||||
photobooth:
|
photobooth:
|
||||||
base_url: "http://localhost:8000"
|
base_url: "http://localhost:8000"
|
||||||
data_dir: "/home/pi/photobooth-data"
|
data_dir: "/home/pi/photobooth-data"
|
||||||
config_file: "/home/pi/.config/photobooth-app/config.json"
|
config_file: "/home/pi/photobooth-data/config/config.json"
|
||||||
media_dir: "/home/pi/photobooth-data/media/processed_full"
|
media_dir: "/home/pi/photobooth-data/media/processed_full"
|
||||||
userdata_dir: "/home/pi/photobooth-data/userdata"
|
userdata_dir: "/home/pi/photobooth-data/userdata"
|
||||||
show_delete_button: false
|
show_delete_button: false
|
||||||
|
|||||||
@@ -194,9 +194,7 @@ libcamera-hello --list-cameras
|
|||||||
photobooth-app est l'application kiosque qui gère la caméra, le décompte et les frames.
|
photobooth-app est l'application kiosque qui gère la caméra, le décompte et les frames.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Créer le dossier de données
|
# photobooth-app crée lui-même ~/photobooth-data/ et ses sous-dossiers au premier lancement
|
||||||
mkdir -p /home/pi/photobooth-data/{media/processed_full,userdata,script}
|
|
||||||
|
|
||||||
# Installer dans un venv avec accès aux paquets système (nécessaire pour picamera2)
|
# Installer dans un venv avec accès aux paquets système (nécessaire pour picamera2)
|
||||||
sudo apt install -y python3-venv
|
sudo apt install -y python3-venv
|
||||||
python3 -m venv /home/pi/photobooth-venv --system-site-packages
|
python3 -m venv /home/pi/photobooth-venv --system-site-packages
|
||||||
@@ -217,7 +215,7 @@ After=network.target
|
|||||||
[Service]
|
[Service]
|
||||||
Type=simple
|
Type=simple
|
||||||
User=pi
|
User=pi
|
||||||
WorkingDirectory=/home/pi
|
WorkingDirectory=/home/pi/photobooth-data/
|
||||||
ExecStart=/home/pi/photobooth-venv/bin/photobooth
|
ExecStart=/home/pi/photobooth-venv/bin/photobooth
|
||||||
Restart=always
|
Restart=always
|
||||||
RestartSec=5
|
RestartSec=5
|
||||||
@@ -227,6 +225,9 @@ Environment=HOME=/home/pi
|
|||||||
WantedBy=multi-user.target
|
WantedBy=multi-user.target
|
||||||
```
|
```
|
||||||
|
|
||||||
|
> **WorkingDirectory=/home/pi/photobooth-data/** — c'est le répertoire de travail de photobooth-app.
|
||||||
|
> Tous ses sous-dossiers (`config/`, `media/`, `userdata/`, `database/`...) y sont créés au premier lancement.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo systemctl daemon-reload
|
sudo systemctl daemon-reload
|
||||||
sudo systemctl enable photobooth-app
|
sudo systemctl enable photobooth-app
|
||||||
@@ -269,7 +270,7 @@ Copier la config depuis le dépôt après l'avoir cloné (étape 7) :
|
|||||||
```bash
|
```bash
|
||||||
# Après avoir cloné le dépôt JH Photomaton :
|
# Après avoir cloné le dépôt JH Photomaton :
|
||||||
cp /home/pi/jh-photomaton/photobooth-app/config/plugin_commander_jh.json \
|
cp /home/pi/jh-photomaton/photobooth-app/config/plugin_commander_jh.json \
|
||||||
~/.config/photobooth-app/plugin_commander.json
|
/home/pi/photobooth-data/config/plugin_commander.json
|
||||||
|
|
||||||
sudo systemctl restart photobooth-app
|
sudo systemctl restart photobooth-app
|
||||||
```
|
```
|
||||||
@@ -334,7 +335,7 @@ app:
|
|||||||
photobooth:
|
photobooth:
|
||||||
base_url: "http://localhost:8000"
|
base_url: "http://localhost:8000"
|
||||||
data_dir: "/home/pi/photobooth-data"
|
data_dir: "/home/pi/photobooth-data"
|
||||||
media_dir: "/home/pi/photobooth-data/media/processed_full"
|
media_dir: "/home/pi/photobooth-data/media/processed_full" # chemin réel créé par photobooth-app
|
||||||
|
|
||||||
event:
|
event:
|
||||||
name: "Evenement" # sera modifié depuis l'admin
|
name: "Evenement" # sera modifié depuis l'admin
|
||||||
@@ -407,10 +408,14 @@ Répéter pour `Selphy_Noire_WiFi`.
|
|||||||
|
|
||||||
### Script d'impression
|
### Script d'impression
|
||||||
|
|
||||||
Vérifier que le script est exécutable et pointe vers les bons noms d'imprimantes :
|
> ⚠️ `script_print.sh` est un script **custom** — il n'existe pas par défaut.
|
||||||
|
> Il doit être copié depuis l'ancienne carte SD (voir Étape 12).
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
ls -la /home/pi/photobooth-data/script/script_print.sh
|
# Créer le dossier s'il n'existe pas
|
||||||
|
mkdir -p /home/pi/photobooth-data/script/
|
||||||
|
|
||||||
|
# Après copie depuis l'ancienne carte, rendre exécutable
|
||||||
chmod +x /home/pi/photobooth-data/script/script_print.sh
|
chmod +x /home/pi/photobooth-data/script/script_print.sh
|
||||||
|
|
||||||
# Test manuel
|
# Test manuel
|
||||||
@@ -439,7 +444,7 @@ Ajouter dans `/etc/dhcpcd.conf` :
|
|||||||
|
|
||||||
```ini
|
```ini
|
||||||
interface wlan0
|
interface wlan0
|
||||||
static ip_address=192.168.4.1/24
|
static ip_address=192.168.230.1/24
|
||||||
nohook wpa_supplicant
|
nohook wpa_supplicant
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -478,27 +483,27 @@ interface=wlan0
|
|||||||
bind-interfaces
|
bind-interfaces
|
||||||
|
|
||||||
# DHCP : distribue des IPs aux clients WiFi
|
# DHCP : distribue des IPs aux clients WiFi
|
||||||
dhcp-range=192.168.4.2,192.168.4.100,255.255.255.0,24h
|
dhcp-range=192.168.230.2,192.168.230.100,255.255.255.0,24h
|
||||||
# Annonce le Pi comme serveur DNS aux clients
|
# Annonce le Pi comme serveur DNS aux clients
|
||||||
dhcp-option=6,192.168.4.1
|
dhcp-option=6,192.168.230.1
|
||||||
|
|
||||||
# ── Portail captif ──────────────────────────────────────────────
|
# ── Portail captif ──────────────────────────────────────────────
|
||||||
# Tout le trafic DNS → Pi (192.168.4.1)
|
# Tout le trafic DNS → Pi (192.168.230.1)
|
||||||
# google.com, instagram.com, lessapinsduweb.com... tout arrive ici.
|
# google.com, instagram.com, lessapinsduweb.com... tout arrive ici.
|
||||||
# Zoraxy fait le tri par HTTP Host header :
|
# Zoraxy fait le tri par HTTP Host header :
|
||||||
# - *.lessapinsduweb.com → apps configurées dans Zoraxy
|
# - *.lessapinsduweb.com → apps configurées dans Zoraxy
|
||||||
# - tout le reste → redirect vers photomaton.lessapinsduweb.com
|
# - tout le reste → redirect vers photomaton.lessapinsduweb.com
|
||||||
address=/#/192.168.4.1
|
address=/#/192.168.230.1
|
||||||
|
|
||||||
# Sondes de détection portail captif — iOS et Android ouvrent
|
# Sondes de détection portail captif — iOS et Android ouvrent
|
||||||
# automatiquement le navigateur sur la galerie
|
# automatiquement le navigateur sur la galerie
|
||||||
address=/captive.apple.com/192.168.4.1
|
address=/captive.apple.com/192.168.230.1
|
||||||
address=/connectivitycheck.gstatic.com/192.168.4.1
|
address=/connectivitycheck.gstatic.com/192.168.230.1
|
||||||
address=/detectportal.firefox.com/192.168.4.1
|
address=/detectportal.firefox.com/192.168.230.1
|
||||||
address=/www.msftconnecttest.com/192.168.4.1
|
address=/www.msftconnecttest.com/192.168.230.1
|
||||||
```
|
```
|
||||||
|
|
||||||
> **Note :** `address=/#/192.168.4.1` s'applique uniquement aux clients WiFi
|
> **Note :** `address=/#/192.168.230.1` s'applique uniquement aux clients WiFi
|
||||||
> (DHCP sur wlan0). Le Pi lui-même utilise le DNS de son routeur via eth0.
|
> (DHCP sur wlan0). Le Pi lui-même utilise le DNS de son routeur via eth0.
|
||||||
|
|
||||||
### Activer les services
|
### Activer les services
|
||||||
@@ -558,7 +563,7 @@ sudo systemctl start zoraxy
|
|||||||
| **`*` (catch-all)** | **Redirect → `http://photomaton.lessapinsduweb.com`** | **Portail captif** |
|
| **`*` (catch-all)** | **Redirect → `http://photomaton.lessapinsduweb.com`** | **Portail captif** |
|
||||||
|
|
||||||
La règle catch-all est la clé : quand un client WiFi tape `google.com`, DNS
|
La règle catch-all est la clé : quand un client WiFi tape `google.com`, DNS
|
||||||
résout vers `192.168.4.1`, Zoraxy reçoit la requête, ne reconnaît pas l'hôte,
|
résout vers `192.168.230.1`, Zoraxy reçoit la requête, ne reconnaît pas l'hôte,
|
||||||
et redirige vers la galerie photo.
|
et redirige vers la galerie photo.
|
||||||
|
|
||||||
> **Limite HTTPS :** si un client tape `https://google.com`, le navigateur voit
|
> **Limite HTTPS :** si un client tape `https://google.com`, le navigateur voit
|
||||||
@@ -585,8 +590,9 @@ rsync -av /mnt/old-sd/home/pi/photobooth-data/script/ \
|
|||||||
pi@<IP_DU_PI>:/home/pi/photobooth-data/script/
|
pi@<IP_DU_PI>:/home/pi/photobooth-data/script/
|
||||||
|
|
||||||
# Config photobooth-app (actions, frames configurées)
|
# Config photobooth-app (actions, frames configurées)
|
||||||
rsync -av /mnt/old-sd/home/pi/.config/photobooth-app/ \
|
# photobooth-app stocke tout dans ~/photobooth-data/ (WorkingDirectory du service)
|
||||||
pi@<IP_DU_PI>:/home/pi/.config/photobooth-app/
|
rsync -av /mnt/old-sd/home/pi/photobooth-data/ \
|
||||||
|
pi@<IP_DU_PI>:/home/pi/photobooth-data/
|
||||||
# ⚠️ Après cette copie, vérifier que plugin_commander.json pointe bien vers :8090 (pas :1880)
|
# ⚠️ Après cette copie, vérifier que plugin_commander.json pointe bien vers :8090 (pas :1880)
|
||||||
|
|
||||||
# Config JH Photomaton (settings.yaml + base SQLite)
|
# Config JH Photomaton (settings.yaml + base SQLite)
|
||||||
@@ -633,8 +639,8 @@ Interface admin : `http://<IP>:8090/admin`
|
|||||||
| JH Photomaton (admin) | 8090 | `http://admin.lessapinsduweb.com` ou `http://<IP>:8090/admin` |
|
| JH Photomaton (admin) | 8090 | `http://admin.lessapinsduweb.com` ou `http://<IP>:8090/admin` |
|
||||||
| photobooth-app (kiosque) | 8000 | `http://photomaton.lessapinsduweb.com` |
|
| photobooth-app (kiosque) | 8000 | `http://photomaton.lessapinsduweb.com` |
|
||||||
| CUPS (imprimantes) | 631 | `http://<IP>:631` (réseau local uniquement) |
|
| CUPS (imprimantes) | 631 | `http://<IP>:631` (réseau local uniquement) |
|
||||||
| Zoraxy (proxy admin) | 8888 | `http://192.168.4.1:8888` |
|
| Zoraxy (proxy admin) | 8888 | `http://192.168.230.1:8888` |
|
||||||
| WiFi hotspot | — | SSID `Photomaton-LSDW` — IP Pi : `192.168.4.1` |
|
| WiFi hotspot | — | SSID `Photomaton-LSDW` — IP Pi : `192.168.230.1` |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -62,7 +62,15 @@ echo ""
|
|||||||
echo "▶ Création de l'environnement virtuel Python..."
|
echo "▶ Création de l'environnement virtuel Python..."
|
||||||
python3 -m venv "$VENV_DIR" --system-site-packages
|
python3 -m venv "$VENV_DIR" --system-site-packages
|
||||||
|
|
||||||
|
# Le venv est créé par root (sudo) — le remettre à pi pour que pip fonctionne sans sudo
|
||||||
|
chown -R pi:pi "$VENV_DIR"
|
||||||
|
|
||||||
"$VENV_DIR/bin/pip" install --upgrade pip -q
|
"$VENV_DIR/bin/pip" install --upgrade pip -q
|
||||||
|
|
||||||
|
# Force jinja2 dans le venv pour ecraser la version systeme Debian
|
||||||
|
# (la version systeme sur Trixie a un bug LRUCache incompatible avec Starlette)
|
||||||
|
"$VENV_DIR/bin/pip" install --upgrade --ignore-installed jinja2 -q
|
||||||
|
|
||||||
"$VENV_DIR/bin/pip" install -r "$INSTALL_DIR/requirements.txt" -q
|
"$VENV_DIR/bin/pip" install -r "$INSTALL_DIR/requirements.txt" -q
|
||||||
|
|
||||||
# rpi_ws281x (nécessite les headers, installé depuis PyPI)
|
# rpi_ws281x (nécessite les headers, installé depuis PyPI)
|
||||||
|
|||||||
Reference in New Issue
Block a user