3 Projekt Landkarte
Ralf Warmuth edited this page 2026-01-09 22:32:38 +01:00
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.

Projekt-Landkarte (Code-getrieben)

Diese Seite ist eine kurze mentale Landkarte des Projekts, basierend auf dem aktuellen Quellcode.

Was ist das System?

  • Frontend: statische HTML-Seiten, generiert aus content/ (Markdown/HTML) via Node-Build.
  • Backend: PHP + SQLite für Anmeldung + Admin.
  • Deployment-Artifact: public/ (wird gebaut und deployed).

Build & Laufzeit-Pfade (wichtig für das Verständnis)

Der Build (scripts/build.mjs) erzeugt/füllt public/:

  • Content-Seiten: content/*.md|*.htmlpublic/<slug>/index.html
  • Assets: static/assets/*public/assets/* (CSS/JS/Images)
  • PHP Public Endpoints: static/<dir>/*.phppublic/<dir>/*.php
    • z.B. static/anmeldung/index.phppublic/anmeldung/index.php
    • z.B. static/registration/index.phppublic/registration/index.php
    • z.B. static/api/event-date.phppublic/api/event-date.php
  • Backend-Code: zgb-backend/*public/backend/*
  • Datenverzeichnis: zgb-data/ (falls vorhanden) → public/data/
    • falls keine Quelle vorhanden: public/data/ wird angelegt (DB entsteht beim ersten Zugriff)
    • Build stellt außerdem eine sperrende public/data/.htaccess sicher (Schutz vor HTTP-Zugriff).

Merksatz: Alles, was der Webserver serviert, kommt aus public/.

Haupt-User-Flow: Anmeldung

1) Formular anzeigen: /anmeldung/

Datei: public/anmeldung/index.php (aus static/anmeldung/index.php)

  • Lädt Config aus SQLite (über ConfigRepository).
  • Prüft Anmeldezeitraum (startDate/endDate); außerhalb wird das Formular nicht angezeigt (ggf. Countdown bis Start).
  • Ermittelt eine freie Nummer im Bereich (startNumber bis startNumber+maxNumber-1) nur zur Anzeige.
  • Setzt serverseitig einen Form-Timer in der Session (form_start_time) für Timeout beim POST.
  • CSRF: erzeugt Token mit kurzer TTL (CSRFService::getTokenWithTTL(180)).

2) Formular abschicken: /registration/ (POST)

Datei: public/registration/index.php (aus static/registration/index.php)

  • Validiert Eingaben + prüft:
    • CSRF (TTL 180s)
    • Form-Timeout (serverseitig, 180s seit Formularanzeige)
    • Duplikat (gleiche Kerndaten) → Redirect ?duplicate=1
  • Speichert Anmeldung in SQLite mit atomarer Nummernvergabe:
    • Nummer wird erst beim POST vergeben
    • innerhalb einer SQLite Write-Transaktion (BEGIN IMMEDIATE) → parallel sicher
    • bei voller Kapazität → Redirect ?full=1
  • Danach Mail:
    • Standard: Mail-Queue (Jobs in DB) wenn mailQueueEnabled=1
    • Optional: Direktversand (wenn Queue deaktiviert)
  • Erfolgs-Redirect (PRG): /anmeldung/?success=1

“Event-Datum” im Header (Countdown)

  • JS: public/assets/script.js versucht Event-Datum zu finden:
    • auf PHP-Seiten über Meta-Tag meta[name="event-date"]
    • auf statischen HTML-Seiten via API:
      • GET /api/event-date.php liefert {"eventDate": "YYYY-MM-DD"}.
  • API-Endpoint: public/api/event-date.php
    • Browser-Cache: 30s
    • Server-Cache: file-basiert 60s (public/data/.event-date-cache.json)

Admin-Flow

Login: /backend/web/admin_login.php

  • Session-basiert (Flag is_admin)
  • Passwort-Hash liegt in Config (adminPasswordHash)
  • Rate-Limit (session-basiert): 5 Fehlversuche → 10 Minuten Block
  • “Passwort vergessen?”:
    • erzeugt neues Passwort (random)
    • speichert neuen Hash in Config
    • sendet Mail an operatorEmail
    • zusätzliches Cooldown file-basiert (z.B. 10 Minuten)

Admin-Konfiguration: /backend/web/admin.php

Tabs:

  • Konfiguration: startDate, endDate, startNumber, maxNumber, operatorEmail, eventDate
  • Admin & Export: CSV-Download, Logout, Passwort ändern, Link zum Löschen
  • Mail-Queue: Queue aktiv, max mails/h, Test-Redirect, Status + “failed retry”

Live-Status:

  • admin_queue_status.php liefert JSON (pending/sending/failed + registrationsTotal)
  • admin.php pollt das JSON alle 10s (Fetch, no-store).

CSV-Export:

  • POST /backend/web/export_registrations.php (mit CSRF) → CSV-Download aller Registrierungen

Löschen:

  • GET/POST /backend/web/clear_registrations.php (CSRF-geschützt, Bestätigung)
  • UI deaktiviert Löschlink in admin.php während Anmeldezeitraum (zusätzlicher Schutz ist sinnvoll, Server-seitig kann man das bei Bedarf ebenfalls erzwingen).

Mail-Queue (asynchroner Versand)

Datenmodell

  • Tabelle mail_queue (SQLite) mit Status pending|sending|failed
  • Idempotenz: Unique-Index (registration_number, mail_type) verhindert doppelte Jobs pro Anmeldung+Mailtyp.
  • Jobs werden nach Erfolg gelöscht (“Option A”).

Worker (Cron)

  • Entry: public/backend/cron/send_mail_queue.php (CLI-only)
  • Worker: MailQueueWorker::run()
    • Token-Bucket Rate-Limit (Config-Key mailMaxPerHour, Default 40, Range 160)
    • Claiming/Locking via DB (locked_until)
    • Backoff bei Fehlern, nach vielen Versuchen “parkt” er Jobs bis Admin-Retry.

Test-Modus:

  • Key mailRedirectToSchumbi=1 leitet User-Mails (nicht Orga) auf @schumbi.de um,
    • aber nur außerhalb des echten Anmeldezeitraums (serverseitig erzwungen).

Config-Keys (SQLite config Key/Value)

Kern:

  • startDate (YYYY-MM-DD)
  • endDate (YYYY-MM-DD)
  • startNumber (int als string)
  • maxNumber (int als string)
  • operatorEmail
  • eventDate (YYYY-MM-DD)
  • adminPasswordHash

Mail-Queue:

  • mailQueueEnabled (1/0)
  • mailMaxPerHour (1..60)
  • mailRedirectToSchumbi (1/0)
  • intern für Token-Bucket:
    • mailTokens
    • mailLastRefill

Sicherheit (Kurzliste)

  • Session-Härtung: SessionService setzt Cookie-Flags (HttpOnly, SameSite=Lax, Secure bei HTTPS).
  • CSRF: CSRFService (Standard-Token + TTL-Token für Registrierung).
  • Admin Rate-Limit: session-basiert in AuthService.
  • Reset-Cooldown: file-basiert in RateLimitService.
  • DB-Schutz: public/data/.htaccess wird beim Build erzeugt/validiert.

“Wo schaue ich zuerst hin?” (Debug-Map)

  • Build/Output: scripts/build.mjs
  • Anmelde-UI: static/anmeldung/index.php
  • POST-Verarbeitung: static/registration/index.php
  • DB/Repositories: zgb-backend/Repository/* + zgb-backend/Repository/Schema/*
  • Admin: zgb-backend/web/admin*.php
  • Mail-Queue: zgb-backend/Service/MailQueueWorker.php + zgb-backend/Repository/MailQueueRepository.php
  • Frontend JS: static/assets/script.js (Header-Countdown, Menu, Formular-Countdown)