Commit graph

28 commits

Author SHA1 Message Date
e28527ed91 E-Mails und Druckvorlage im neuen Design
E-Mail:
- Nachrichten werden in ein markentreues HTML-Gerüst gelegt (Tabellen +
  Inline-Styles, damit Outlook & Co. es zuverlässig darstellen); die
  Kopfleiste nutzt den eingestellten Markenverlauf
- neuer Platzhalter {VOUCHER_CARD} rendert den Code als hervorgehobene
  Karte; bestehende Vorlagen mit {VOUCHER_CODE} funktionieren unverändert
  weiter, sie werden lediglich eingerahmt
- Zeilenumbrüche der Vorlage werden vor dem Einsetzen der Platzhalter
  umgewandelt, Markup aus dem WYSIWYG-Editor bleibt unangetastet

Druck:
- neue Standardvorlage als Ticket mit QR-Code, Code, Gültigkeit und
  Anleitung; zentral in Ui::defaultPrintTemplate()
- neuer Platzhalter {QR_CODE}; der QR-Code wird lokal im Browser erzeugt
  (qrcodejs aus assets/vendor), auch bei Bulk-Erstellung für jede Karte
- die Druckvorlage ist am Bildschirm ausgeblendet und erscheint nur im
  Ausdruck

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-23 06:42:45 +00:00
7f1d93debd Barrierefreiheit und mobile Darstellung
- Kontrast: gedämpfter Text war mit 3,1:1 unter WCAG AA, jetzt 4,9:1
  (hell) bzw. 6,4:1 (dunkel)
- Sprungmarke „Zum Inhalt springen" in Admin-Shell und Voucher-Seite
- aria-label für alle reinen Icon-Schaltflächen (Theme, Menü, Abmelden,
  Zeilenaktionen), aria-current auf dem aktiven Navigationspunkt,
  role="group" für den Sprachumschalter
- 194 dekorative Icons mit aria-hidden versehen, damit Screenreader sie
  nicht vorlesen
- Toast-Container als aria-live-Bereich ausgezeichnet
- prefers-reduced-motion schaltet Animationen und Übergänge ab

Tabellen (Benutzer, Vouchers, Profile, Audit-Log, Dashboard, API-Keys)
werden unter 720 px zu Karten: die Spaltenüberschrift steht per
data-label vor dem Wert, statt horizontal zu scrollen.

Datums- und Zeitformat in der Voucher-Liste folgen jetzt der gewählten
Sprache statt fest de-DE.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-23 06:38:08 +00:00
30d0ce3a23 Branding: Farben systemweit einstellbar + Bild-Upload statt nur URLs
Design-Tab (Administration → Einstellungen → Design):
- Akzentfarbe für Hell- und Dark-Mode, Markenverlauf und Eckenradius
- abgeleitete Töne (Hover, weiche Flächen, Fokusring) werden per
  color-mix aus der Grundfarbe berechnet – eine Farbe genügt
- Live-Vorschau mit Button, Badge, Chip, Logo-Kachel und Link
- Ausgabe als schlanker :root-Override über Ui::brandingStyle(), greift
  auf allen Seiten inklusive Login und Installer

Uploads (includes/Upload.php):
- Logo, Favicon, Login-Logo und Login-Hintergrund lassen sich jetzt
  hochladen; das URL-Feld bleibt als Alternative bestehen
- Whitelist nach Endung, 3-MB-Grenze, getimagesize-Prüfung für Raster,
  SVGs werden von Skripten, Event-Handlern und externen Verweisen befreit
- Zufällige Dateinamen in uploads/, dort sperrt eine .htaccess die
  Ausführung von PHP; beim Ersetzen wird die alte Datei gelöscht

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-23 06:27:30 +00:00
6da46f040a Alle Frontend-Assets lokal ausliefern statt über CDNs
Inter, Font Awesome, Chart.js, qrcodejs und TinyMCE liegen jetzt unter
assets/vendor/ und werden vom eigenen Server ausgeliefert.

Warum:
- Datenschutz: bisher ging bei jedem Seitenaufruf die IP der Nutzer an
  Google Fonts, cdnjs, jsDelivr und Tiny Cloud
- Funktion: UniFi-Installationen stehen oft in abgeschotteten Netzen –
  dort fehlten bisher Schrift, Icons, Diagramme und Editor

Neu: includes/Ui.php
- Ui::head()/Ui::script() binden die Assets ein und hängen einen
  Versionsstempel an (?v=filemtime), damit Browser nach einem Update
  nicht das alte CSS aus dem Cache nehmen
- Ui::themeScript() setzt das Theme aus der gespeicherten Auswahl oder
  – wenn keine vorliegt – aus prefers-color-scheme; global.js folgt
  Systemwechseln live, solange nichts manuell gewählt wurde
- <meta name="color-scheme"> ergänzt, damit Formularelemente passen

Der TinyMCE-API-Key entfällt: der Editor läuft immer lokal (GPL-Variante),
inklusive deutscher Oberfläche, wenn die App auf Deutsch steht.

Neu: tools/demo/build.py – baut aus dem Projekt eine Demo-Instanz ohne
Datenbank (Stubs für Database/Auth, feste Beispieldaten). Damit lassen
sich Screenshots reproduzierbar erzeugen und alle Seiten einmal rendern
(Smoke-Test), ohne eine MySQL-Instanz aufzusetzen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-23 06:23:44 +00:00
498c7e28e0 Redesign: gemeinsames Design-System für Frontend und Backend
Some checks failed
CI / PHP Lint (push) Waiting to run
CI / PHP Lint-1 (push) Waiting to run
CI / Unit Tests & Static Analysis (push) Waiting to run
CI / PHP Lint (pull_request) Has been cancelled
CI / PHP Lint-1 (pull_request) Has been cancelled
CI / Unit Tests & Static Analysis (pull_request) Has been cancelled
Frontend, Login/Installer/Updater und der komplette Admin-Bereich nutzen
jetzt ein einziges Stylesheet (assets/global.css) statt pro Seite
dupliziertem Inline-CSS.

Design-System
- Tokens für Flächen, Text, Linien, Marke, Status, Radien, Schatten und
  Layout-Maße; Dark Mode ausschließlich über Tokens (keine !important-
  Overrides mehr)
- Komponenten: Buttons, Formularfelder, Cards, Tabellen, Badges, Alerts,
  Tabs, Pagination, Modals, Toasts, Statistik-Kacheln, Empty States
- Schrift Inter mit System-Fallback

Oberfläche
- Admin-Shell neu: durchgehende Sidebar mit Marke, gruppierter Navigation
  und Benutzerbereich; schlanke Topbar mit Breadcrumb
- Dashboard: ruhige KPI-Kacheln mit Icon-Chips, Charts an Theme-Farben
  gekoppelt
- Öffentliche Voucher-Seite: App-Topbar, klare Formularstruktur und
  Ticket-Darstellung des erstellten Codes inkl. QR-Code
- Login/Passwort/2FA: zweispaltiges Auth-Layout bzw. Fokus-Karten
- Updater und Wartungsmodus im gleichen Look (Wartungsseite bleibt
  bewusst eigenständig ohne externe Abhängigkeiten)
- Emoji-Icons in der UI durch Font-Awesome-Icons ersetzt

Nebenbei behoben
- Falscher SRI-Hash blockierte qrcode.min.js – QR-Codes wurden auf der
  Voucher-Seite und bei der 2FA-Einrichtung nie gerendert
- assets/global.css wurde in mehreren Admin-Seiten über einen falschen
  Pfad eingebunden ($adminBase = '' statt '../')
- TinyMCE lädt ohne API-Key jetzt die GPL-Variante von cdnjs – kein
  "valid API key required"-Banner mehr im Einstellungs-Editor
- Tabellen in Cards scrollen horizontal statt zu überlaufen

Screenshots in docs/screenshots neu erstellt, README aktualisiert (neuer
Abschnitt "Design-System", Version 2.5.0).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-22 20:33:52 +00:00
Claude
1a51721477
DB-gestützte Sessions (opt-in) + 'überall abmelden'
- includes/DbSessionHandler.php (SessionHandlerInterface, fehlertolerant)
- Auth: opt-in-Registrierung (Setting session_driver=db), activeSessionCount(),
  logoutOtherSessions()
- Migration 0004 (sessions.user_id NULL) + database.sql
- Settings-Toggle in integrations.php; 'überall abmelden' in security.php
2026-06-06 05:49:53 +00:00
Claude
976b357952
SMS-Versand (Twilio): includes/Sms.php + Option im Voucher-Formular + Settings 2026-06-06 05:44:46 +00:00
Claude
46c03d53b8
Batch-CSV-Import: admin/import.php (Name,MaxGeräte,Minuten; max 200 Zeilen) 2026-06-06 05:43:09 +00:00
Claude
ce75215498
CAPTCHA für öffentlichen Modus (Rechenaufgabe oder hCaptcha)
- includes/Captcha.php (math self-contained / hcaptcha via siteverify)
- index.php: Verifikation bei anonymer Erstellung + Anzeige in beiden Formularen
- Settings in admin/integrations.php (Modus, Site-Key, Secret)
2026-06-06 05:42:06 +00:00
Claude
546accc185
Konsolidierung: Security-Härtung + .gitignore
- TOTP-Secret wird verschlüsselt-at-rest gespeichert (Crypto, Klartext-Fallback)
- forgot_password.php: Throttle (3/15min je Session) gegen Reset-Spam
- .gitignore für vendor/, composer.lock, phpunit-Cache, Updater-Laufzeitdaten
- Verifiziert: PHPUnit 15/15 grün, PHPStan ohne Fehler
2026-06-06 05:38:40 +00:00
Claude
997cda01a8
E-Mail-Retry, Voucher-Resend, Tageslimit, Docker-Politur
- Mailer::send mit Retry (2 Versuche); SMTP-Test & Templates waren bereits da
- admin/vouchers.php: Code per E-Mail (erneut) versenden (ajax_resend)
- Tageslimit Voucher pro Nicht-Admin-Benutzer (Setting + Durchsetzung in index)
- Docker: HEALTHCHECK (health.php) + curl; GHCR-Publish-Workflow
- Setting user_daily_voucher_limit + enforce in integrations.php
2026-06-05 20:56:26 +00:00
Claude
2e0f36d6c1
Reporting: admin/reports.php mit Charts, CSV-Export (täglich/Site/Nutzer) & Druck/PDF 2026-06-05 20:50:18 +00:00
Claude
0b1d4ec3b8
Webhook-Events & Health-Endpoint
- Notifier: controllerUnreachable, loginNewIp, updateAvailable
- Login von neuer IP -> Webhook (Abgleich gegen audit_log)
- cron_sync meldet nicht erreichbare Controller; cron_cleanup prüft täglich
  optional auf Updates und meldet Verfügbarkeit
- health.php: Status-Endpoint (DB; optional Controller per ?deep=1&token=)
2026-06-05 20:49:06 +00:00
Claude
c7f9b7d39c
API-Reife: Scopes (read/write), Rate-Limit pro Key, OpenAPI-Spec
- ApiKey::hasScope + checkRateLimit (Fixed-Window/min via api_key_hits)
- bootstrap erzwingt Rate-Limit (429) und api_require_scope() in Endpunkten
- admin/api_keys.php: Scope-Auswahl + Limit beim Erstellen, Anzeige in Tabelle
- api/openapi.php: OpenAPI-3.0-Spec (Import in Postman/Swagger)
- Schema 0003 (api_keys.scope, api_keys.rate_limit, Tabelle api_key_hits)
2026-06-05 20:47:26 +00:00
Claude
ba121d60e6
2FA-Reife: Recovery-Codes, Enforce-Policy für Admins, Admin-Reset
- Recovery-/Backup-Codes (8x, einmalig nutzbar) bei Aktivierung + Regenerieren;
  Login akzeptiert TOTP ODER Recovery-Code
- Setting enforce_2fa_admins: Admins ohne 2FA werden zur Einrichtung geleitet
- Admin kann 2FA eines Nutzers zurücksetzen (admin/users.php)
- Schema 0003 (users.totp_backup_codes); security.php zeigt Codes & Restanzahl
2026-06-05 20:45:28 +00:00
Claude
9463486225
Erweiterte Features (2/2): Cleanup/DSGVO, Webhooks, REST-API, Backup, CI, Docker
- Auto-Cleanup/DSGVO: cron_cleanup.php (token-geschützt) löscht abgelaufene
  Voucher, Audit-Log, Login-Versuche und Reset-Tokens nach konfigurierbaren
  Fristen
- Webhooks: includes/Notifier.php (Slack/Teams/generisch), Auslösung bei
  Voucher-Erstellung (Web + API)
- REST-API: includes/ApiKey.php (SHA-256, Präfix-Lookup), api/bootstrap.php,
  api/vouchers.php (GET/POST), api/sites.php; admin/api_keys.php zur Verwaltung
- Config-Backup/Restore: admin/backup.php (JSON Export/Import, Cron-Token
  geschützt)
- admin/integrations.php: Trusted-Proxy, Webhook, Cleanup-Fristen
- CI: .github/workflows/ci.yml (php -l auf 7.4 & 8.2, lang-Validierung)
- Docker: Dockerfile, docker-compose.yml (MariaDB), entrypoint (config aus ENV),
  .dockerignore
- Nav + i18n (DE/EN) für alle neuen Admin-Seiten
2026-06-05 19:45:28 +00:00
Claude
eec28f77b8
Erweiterte Features (1/2): Trusted-Proxy-IP, Bandbreitenlimits, 2FA
- Schema: updater/migrations/0002 + database.sql (users.totp_*, voucher_templates
  qos_*, neue Tabelle api_keys)
- Trusted-Proxy-IP: Auth::clientIp() wertet X-Forwarded-For nur hinter
  konfiguriertem trusted_proxy aus (korrektes Rate-Limit/Audit hinter Proxy)
- Bandbreiten-/Datenlimits: UniFiController::createVoucher akzeptiert QoS
  (down/up kbit/s, Datenkontingent MB); Voucher-Profile speichern Limits,
  Voucher-Formular reicht sie via Template-Quick-Select durch
- 2FA (TOTP, RFC 6238): includes/Totp.php (gegen RFC-Testvektoren verifiziert),
  zweistufiger Login, admin/security.php zum Aktivieren/Deaktivieren mit QR,
  Nav-Link + i18n
2026-06-05 19:39:28 +00:00
Claude
526c43e8ee
Merge: UI/UX-Feature-Branch integrieren + Security-Patches re-applien
Integriert die Feature-Branch (Dark Mode, i18n DE/EN, mobile Admin-Layout,
shared admin_nav, Toasts, Voucher-Templates, Bulk-Erstellung, konfigurierbare
Defaults, Password-Reset, Audit-Log-UI + Audit-Logging) mit der bestehenden
Security-/Updater-Arbeit.

Konfliktauflösung (6 Dateien: index.php + admin/*): Feature-Version als Basis,
darauf die Security-Patches re-appliziert:
- display_errors=0 + log_errors in allen neuen/gemergten Entry-Points
- Crypto::encrypt/decrypt an allen Site-Passwort-Pfaden (sites/index/vouchers/
  dashboard-Sync, inkl. doCreateVoucher + Bulk)
- CSRF-Prüfung für ALLE Voucher-Erstellungen (auch anonym/öffentlich), Token
  unbedingt im Formular; Session-Throttle gegen Spam
- Updater-Maintenance-Hook am Anfang von index.php wiederhergestellt

Auto-Merge verifiziert: Auth.php enthält Session-Timeout UND writeAuditLog;
login.php behält display_errors=0 + OAuth-state. Updater-Link in shared
admin_nav.php (i18n-Key nav_update DE/EN). Alle PHP-Dateien linten sauber.
2026-06-05 19:10:18 +00:00
Claude
3483da274f
Security: OAuth-state, Verschlüsselung, Session-Timeout & weitere Härtung
- m365_callback.php: OAuth-state-Validierung gegen Login-CSRF
- includes/Crypto.php: Verschlüsselung-at-rest für UniFi-Passwörter
  (AES-256-GCM/libsodium) mit Klartext-Fallback für Bestandsinstallationen
- install.php: APP_KEY-Generierung + Reinstall nur mit Admin-Session
- Auth.php: absolutes Session-Timeout (SESSION_LIFETIME) durchsetzen
- index.php: CSRF + Throttle auch für anonyme öffentliche Voucher-Erstellung
- UniFiController.php: createVoucher liefert nicht mehr den falschen Code
  bei parallelen Erstellungen (note-Match statt blindes reset())
- display_errors in allen Entry-Points deaktiviert, log_errors aktiviert
- test.php & m365_debug.php hinter requireAdmin() (Info-Leak)
- m365_debug.php: abgeschnittene/kaputte Datei vervollständigt
2026-06-05 18:45:47 +00:00
Claude
a1021a0f84
feat: comprehensive UI/UX and feature improvements
- Dark mode: CSS custom properties (global.css) + toggle button, persisted in localStorage
- i18n: German/English language switcher (lang/de.php, lang/en.php, includes/I18n.php)
- Mobile-responsive admin layout: hamburger menu, sidebar overlay (global.js + global.css)
- Shared admin navigation include (includes/admin_nav.php) used across all admin pages
- Toast notifications system globally available via global.js
- Voucher templates/profiles: CRUD UI at admin/templates.php with voucher_templates DB table
- Bulk voucher creation: create 1-20 vouchers at once with multi-print layout on index.php
- Configurable voucher defaults: expire time, device limit, max limit in admin settings
- Template quick-select on voucher form: auto-fills max_uses and expire_minutes
- Password reset flow: forgot_password.php + reset_password.php with token-based reset
- Audit log UI: admin/audit_log.php with filter, pagination, audit_log DB table
- Audit logging on login, user create/edit/delete, site create/edit/delete
- Admin pages updated: index, vouchers, users, sites all use admin_nav.php + dark mode + i18n
- Voucher admin: live search input added alongside existing status filter + pagination
- Users admin: password-reset-link button per user row (when SMTP enabled)
- Login page: i18n, dark mode, language switcher, forgot password link

https://claude.ai/code/session_01YN6Bcm1VSi8mpDeyKpyrdJ
2026-05-08 17:59:17 +00:00
Claude
e0a999b27b
Login only once per UniFiController instance to avoid rate limiting
createVoucher() calls apiRequest() twice (cmd/hotspot + stat/voucher),
which previously triggered two separate login requests in rapid succession.
The UniFi controller was rate-limiting the second attempt with 403.

Fix: $loggedIn flag ensures login() is a no-op after the first successful
authentication, reusing the existing session cookie for all API calls.

https://claude.ai/code/session_01UsuvFAmmeagtQa14QA4iaq
2026-04-22 05:52:45 +00:00
Claude
897392041a
Fix session cookie not persisting due to Partitioned attribute
The TOKEN cookie set by UniFi OS includes the 'Partitioned' attribute
(CHIPS), which some libcurl versions do not write to the Netscape cookie
jar file. This caused every API request to go out unauthenticated,
resulting in 401/403 errors even after a successful login.

Fix: extract the TOKEN value directly from the Set-Cookie response
header in login() and pass it via CURLOPT_COOKIE in apiRequest(),
bypassing the broken file-based cookie jar. Cookie file remains as
fallback for environments where extraction fails.

Also update test.php section 8 to validate this fix and show the
extracted cookie value.

https://claude.ai/code/session_01UsuvFAmmeagtQa14QA4iaq
2026-04-22 05:41:42 +00:00
Claude
dbce0299d6
Add response body detail to login error messages for debugging
https://claude.ai/code/session_01UsuvFAmmeagtQa14QA4iaq
2026-04-22 05:31:04 +00:00
Claude
78a2b41329
Fix HTTP 403 on login: add Origin and Referer headers
UniFi OS enforces CSRF protection on the login endpoint itself and
returns 403 if Origin/Referer headers are absent, regardless of whether
the credentials are correct. Required headers:
  Origin: https://controller:port
  Referer: https://controller:port/login

Documented in Art-of-WiFi UniFi-API-client reference implementation.

https://claude.ai/code/session_01UsuvFAmmeagtQa14QA4iaq
2026-04-22 05:28:17 +00:00
Claude
2874ca0839
Fix UniFi OS API login validation and HTTP method handling
- Login: replace fragile unique_id/email check with proper HTTP 200 trust +
  explicit meta.rc=error detection (matches Art-of-WiFi reference impl)
- Login: add TOKEN cookie fallback for CSRF token extraction in case
  the X-CSRF-Token response header is absent (some firmware versions)
- getVouchers(): pass explicit 'GET' method instead of relying on cURL default
- apiRequest(): use CURLOPT_HTTPGET for GET requests; send CURLOPT_POSTFIELDS
  as empty object for POST with no data; CSRF header only on non-GET requests
- Remove array_filter() header construction, replace with clean conditional append

https://claude.ai/code/session_01UsuvFAmmeagtQa14QA4iaq
2026-04-21 18:29:50 +00:00
Claude
73967caefa
Fix sites freeze bug, add features and shorten README
Bug fixes:
- UniFiController: add CURLOPT_TIMEOUT (10s) and CURLOPT_CONNECTTIMEOUT (5s)
  to login() and apiRequest() — prevents page freeze when controller unreachable
- UniFiController: fix login response validation for UniFi OS API which returns
  a user object instead of meta.rc=ok
- admin/sites.php: add JS loading state on form submit to give visual feedback
- login.php: handle new 'rate_limited' return value from Auth::login()

New features:
- Database: in-memory settings cache eliminates redundant DB queries per request
- Auth: login rate limiting (10 attempts per 10 min per IP/email) via login_attempts table
- admin/vouchers.php: CSV export with UTF-8 BOM for Excel compatibility
- admin/vouchers.php: client-side pagination (50 per page)
- index.php: QR code display after voucher creation (qrcodejs CDN)
- Mailer: sendTestEmail() method
- admin/settings.php: SMTP test button with AJAX handler
- database.sql: add login_attempts and audit_log tables

Readme: condensed from ~420 to ~220 lines, removed duplicated sections,
M365 Azure Portal walkthrough, contribution guidelines, update/migration section

https://claude.ai/code/session_01UsuvFAmmeagtQa14QA4iaq
2026-04-21 16:08:08 +00:00
Claude
3fd9b2190a
Migrate to UniFi OS API (port 11443)
- Update login endpoint: /api/login → /api/auth/login
- Add X-CSRF-Token extraction via CURLOPT_HEADERFUNCTION in login()
- Inject X-CSRF-Token header into all POST requests in apiRequest()
- Prefix all API paths with /proxy/network (createVoucher, getVouchers, deleteVoucher)
- Update admin/sites.php placeholder and help text to reflect port 11443
- Update Readme.md: fix GitHub clone URL, update port references, rewrite
  API documentation section for UniFi OS, add UniFi OS troubleshooting entry,
  bump version to 2.1.0

https://claude.ai/code/session_01UsuvFAmmeagtQa14QA4iaq
2026-04-21 15:54:56 +00:00
friloo
dbdc237fa1
Initial Upload 2026-04-21 17:45:58 +02:00