""" rndrSBC - Production Web Management Dashboard & Secure API Hardened server with first-run authentication, session validation, path traversal protection, safe command execution, quiet hours scheduling, and multi-playlist management. """ import os import sys import json import time import io import secrets import logging import subprocess import threading from http.server import ThreadingHTTPServer, BaseHTTPRequestHandler import urllib.parse from PIL import Image # Secure password hashing from Werkzeug from werkzeug.security import generate_password_hash, check_password_hash from core.paths import CONFIG_PATH, resolve # Onboarding: QR claim-token flow + AP-mode provisioning from server.onboarding import ( claim_url_for_token, validate_claim_token, consume_claim_token, issue_claim_token, invalidate_unclaimed_tokens, onboarding_state, ap_manager as onboarding_ap_manager, ) logger = logging.getLogger("rndrSBC.server") # In-memory active session tokens: {session_token: {"created_at": float, "user": "admin"}} ACTIVE_SESSIONS: dict[str, dict] = {} # Sessions are also persisted to CONFIG_PATH so a service restart does not # log every client out (otherwise stats / OTA / photos / dev-studio all 401 # until a manual re-login). _SESSION_LOCK = threading.Lock() SESSION_TTL_SECS = 86400 * 7 # 7 days def _load_sessions(force: bool = False) -> None: """Populate ACTIVE_SESSIONS from the persisted copy in CONFIG_PATH.""" if not force and ACTIVE_SESSIONS: return try: if os.path.exists(CONFIG_PATH): with open(CONFIG_PATH, "r") as f: cfg = json.load(f) saved = cfg.get("admin_sessions") or {} now = time.time() for tok, meta in saved.items(): if now - float(meta.get("created_at", 0)) < SESSION_TTL_SECS: ACTIVE_SESSIONS[tok] = meta except Exception: logger.debug("Could not load persisted admin sessions", exc_info=True) def _save_sessions() -> None: """Persist ACTIVE_SESSIONS into CONFIG_PATH (best-effort, atomic).""" try: with _SESSION_LOCK: cfg = {} if os.path.exists(CONFIG_PATH): with open(CONFIG_PATH, "r") as f: cfg = json.load(f) cfg["admin_sessions"] = { tok: m for tok, m in ACTIVE_SESSIONS.items() } tmp = CONFIG_PATH + ".tmp" with open(tmp, "w") as f: json.dump(cfg, f, indent=2) os.replace(tmp, CONFIG_PATH) except Exception: logger.debug("Could not persist admin sessions", exc_info=True) DASHBOARD_HTML = """ rndrSBC Management Portal
rS

Display Hardware Settings

Configure physical SPI screen drivers and resolution targets

0.1–1.0; higher = more intense color on Inky panels

Quiet Hours & Timezone

Suspend physical e-Paper refreshes during sleeping hours

Enable Night Quiet Mode
Puts display to sleep and prevents night flashing
NEXT pin 5
PREV pin 6
TOGGLE QUIET pin 12

Wire buttons to GND; cycle widgets, go back, or pause/restart refreshes. Disabled automatically on simulator/laptop (no GPIO).

🔄 Display Transitions

Auto: B/W e-paper, LCD & OLED partial-refresh automatically; 7-color/BWR panels always full-refresh. Full: forces full frame every refresh.

🔒 Admin Security & Password

Update your dashboard administrator password

📡 Device Health
Load monitoring…
⚡ System & Power
💾 Backup & Updates
Checking…
🖼️ Photo Library