Unifi-Voucher-Tool/tools/screenshots.py
Friederich Loheide 36e06ac817 Sicherheits-Header, Werkzeuge und Dokumentation
Sicherheit:
- .htaccess im Projektstamm mit X-Content-Type-Options, X-Frame-Options,
  Referrer-Policy, Permissions-Policy und einer Content-Security-Policy;
  da alle Assets lokal liegen, erlaubt sie nur noch die eigene Herkunft
  (Ausnahme: hCaptcha, falls aktiviert)
- includes/, tools/, tests/, updater/storage und uploads/ schützen sich
  über eigene .htaccess-Dateien – auch bei Installation im Unterordner
- Docker: AllowOverride All, damit diese Regeln überhaupt greifen, und
  ein Volume für uploads/, damit Logos ein Image-Update überstehen

Werkzeuge:
- tools/screenshots.py erzeugt alle Bilder in docs/screenshots aus der
  Demo-Instanz; tools/README.md beschreibt beides
- Einstellungs-Tabs sind per ?tab=… direkt verlinkbar (serverseitig, also
  auch ohne JavaScript)

Dokumentation: Readme um Markenfarben, Bild-Upload, lokale Assets,
Sicherheits-Header (inkl. Nginx-Entsprechung) und einen Abschnitt
"Entwicklung" ergänzt; Screenshots neu erzeugt, Version 2.6.0.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-23 06:49:14 +00:00

78 lines
3.3 KiB
Python
Executable file
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
"""Erzeugt die Screenshots in docs/screenshots aus der laufenden Demo-Instanz.
Voraussetzung:
python3 tools/demo/build.py /tmp/uvt-demo
php -S 127.0.0.1:8123 -t /tmp/uvt-demo &
Aufruf:
python3 tools/screenshots.py [basis-url]
"""
import os
import shutil
import subprocess
import sys
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
OUT = os.path.join(ROOT, 'docs', 'screenshots')
SCALE = 2
# (Datei, Pfad, Breite, Hoehe) Breite/Hoehe in CSS-Pixeln, Ausgabe in 2x
SHOTS = [
('login.png', 'login.php?anon=1', 1200, 850),
('login-branding.png', 'login.php?anon=1&brand=custom', 1200, 850),
('voucher-form.png', 'index.php', 1200, 880),
('voucher-result.png', 'index.php?demo=result', 1200, 900),
('bulk-vouchers.png', 'index.php?demo=bulk', 1200, 800),
('admin-dashboard.png', 'admin/index.php', 1200, 1400),
('admin-dashboard-dark.png', 'admin/index.php?theme=dark', 1200, 1400),
('vouchers.png', 'admin/vouchers.php', 1200, 1120),
('settings.png', 'admin/settings.php', 1200, 950),
('settings-login.png', 'admin/settings.php?tab=login', 1200, 1000),
('settings-branding.png', 'admin/settings.php?tab=branding', 1200, 900),
('api-keys.png', 'admin/api_keys.php?demo=new', 1200, 830),
('integrations.png', 'admin/integrations.php', 1200, 900),
('two-factor.png', 'admin/security.php', 1200, 900),
('updater.png', 'admin/update.php', 1200, 780),
('updater-available.png', 'admin/update.php?demo=available', 1200, 780),
('mobile-vouchers.png', 'admin/users.php', 430, 860),
('maintenance.png', 'updater/templates/maintenance.html', 1200, 700),
]
CANDIDATES = [
os.environ.get('CHROME_BIN', ''),
'/usr/bin/chromium', '/usr/bin/chromium-browser', '/usr/bin/google-chrome',
'/opt/pw-browsers/chromium', shutil.which('chromium') or '',
]
def find_browser() -> str:
for path in CANDIDATES:
if path and os.path.isfile(path) and os.access(path, os.X_OK):
return path
raise SystemExit('Kein Chromium gefunden Pfad ueber CHROME_BIN setzen.')
def main() -> int:
base = (sys.argv[1] if len(sys.argv) > 1 else 'http://127.0.0.1:8123').rstrip('/')
browser = find_browser()
os.makedirs(OUT, exist_ok=True)
for name, path, width, height in SHOTS:
target = os.path.join(OUT, name)
subprocess.run([
browser, '--headless', '--no-sandbox', '--disable-gpu', '--hide-scrollbars',
'--force-color-profile=srgb', '--disable-lcd-text', '--font-render-hinting=none',
'--virtual-time-budget=4000', '--force-prefers-reduced-motion',
'--force-device-scale-factor=%d' % SCALE,
'--window-size=%d,%d' % (width, height),
'--screenshot=%s' % target,
'%s/%s' % (base, path),
], check=False, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
print('%-28s %s' % (name, 'ok' if os.path.exists(target) else 'FEHLER'))
return 0
if __name__ == '__main__':
raise SystemExit(main())