""" 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

System & Update

Telemetry, backups, updates, power.

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