#!/usr/bin/env python3 """Baut eine Demo-Instanz des Voucher-Tools ohne Datenbank. Die Demo dient zwei Zwecken: * Screenshots fuer die Dokumentation reproduzierbar erzeugen * Smoke-Test: rendert jede Seite einmal ohne echte Datenbank Ablauf: Projekt in ein Zielverzeichnis kopieren, das Overlay darueberlegen (Stubs fuer Database/Auth) und ein paar klar benannte Demo-Zustaende in die Kopie patchen. Das Original bleibt unveraendert. python3 tools/demo/build.py /tmp/uvt-demo php -S 127.0.0.1:8123 -t /tmp/uvt-demo """ import os import shutil import sys ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) OVERLAY = os.path.join(ROOT, 'tools', 'demo', 'overlay') # Nur auf oberster Ebene ueberspringen – assets/vendor gehoert dazu! SKIP_TOP = {'.git', '.github', 'tools', 'docs', 'tests', 'vendor', 'node_modules'} def copy_project(target: str) -> None: if os.path.exists(target): shutil.rmtree(target) def ignore(directory, names): if os.path.abspath(directory) == ROOT: return [n for n in names if n in SKIP_TOP] return [] shutil.copytree(ROOT, target, ignore=ignore) for name in os.listdir(OVERLAY): src = os.path.join(OVERLAY, name) dst = os.path.join(target, name) if os.path.isdir(src): shutil.copytree(src, dst, dirs_exist_ok=True) else: shutil.copy2(src, dst) def patch(path: str, anchor: str, replacement: str) -> None: """Ersetzt einen Anker in der Demo-Kopie. Fehlt der Anker, bricht der Build ab – so faellt auf, wenn sich die Vorlage geaendert hat.""" with open(path, encoding='utf-8') as fh: content = fh.read() if anchor not in content: raise SystemExit('Anker nicht gefunden in %s:\n%s' % (path, anchor[:80])) with open(path, 'w', encoding='utf-8') as fh: fh.write(content.replace(anchor, replacement, 1)) def apply_demo_states(target: str) -> None: # Voucher-Ergebnis und Bulk-Ansicht ohne Controller-Zugriff zeigen anchor = "$currentUser = $auth->isLoggedIn() ? $auth->getCurrentUser() : null;" patch(os.path.join(target, 'index.php'), anchor, anchor + """ // --- Demo-Instanz: Zustaende ohne UniFi-Controller darstellen --- if (($_GET['demo'] ?? '') === 'result') { $voucherCreated = true; $voucherCode = '4829-17364'; $voucherData = ['code' => '4829-17364', 'site_name' => 'Hauptstandort Nord', 'max_uses' => 2, 'expire_min' => 480, 'expiry_date' => '23.09.2026', 'expiry_time' => '08:00']; } if (($_GET['demo'] ?? '') === 'bulk') { $bulkCreated = true; $bulkVouchers = []; foreach (['4829-17364', '5517-90422', '7731-64508', '2094-38177', '6640-52913'] as $code) { $bulkVouchers[] = ['code' => $code, 'site_name' => 'Hauptstandort Nord', 'max_uses' => 2, 'expire_min' => 480, 'expiry_date' => '23.09.2026', 'expiry_time' => '08:00']; } } """) # Live-Voucher-Liste automatisch laden (sonst wartet sie auf Site-Auswahl) patch(os.path.join(target, 'admin', 'vouchers.php'), "\n", """ """) # Frisch erzeugten API-Schluessel zeigen patch(os.path.join(target, 'admin', 'api_keys.php'), '$keys = $db->fetchAll(', "if (($_GET['demo'] ?? '') === 'new') { $newKey = 'uvt_3f9a2c7d41e8b60592af18cc4d7e0b3a95f2617c'; }\n$keys = $db->fetchAll(") # Kiosk: ausgegebenen Code zeigen, ohne echten Controller (?demo=code) patch(os.path.join(target, 'kiosk.php'), "$headline = trim((string)($kiosk['headline'] ?? ''))", '''if (($_GET['demo'] ?? '') === 'code' && $kiosk) { $voucher = ['code' => '4829-17364', 'site_name' => $kiosk['site_name'], 'max_uses' => 2, 'expire_min' => 480, 'expiry_date' => '24.09.2026', 'expiry_time' => '08:00']; } $headline = trim((string)($kiosk['headline'] ?? ''))''') # Kiosk-Verwaltung: Formular fuer den Screenshot geoeffnet zeigen patch(os.path.join(target, 'admin', 'kiosks.php'), '