Admin
Betriebshandbuch
Interne Referenz für Preismodell, Permissions, Slide-QA und Deploy-Pipeline. Kein Kundendokument — nur für den Admin-Bereich.
Pakete & Preise
Modell B — Access-Pass
User kaufen keinen Einzelkurs, sondern einen zeitlich unbegrenzten Zugangs-Pass (Paket). Das Paket enthält eine definierte Liste von Schulungen plus optionale Permission-Bits. Preisänderungen wirken immer nur für Neukäufe — bestehende Paketzuweisungen bleiben unverändert (Grandfathering).
Preisfelder
Streichpreis (compare_at_price)
Optionaler Vergleichspreis, der durchgestrichen neben dem echten Preis angezeigt wird. Nur sichtbar wenn compare_at_price > price. Gilt ausschließlich für Neukäufe — bestehende Paketzuweisungen werden nicht berührt (Grandfathering).
Streichpreis (compare_at_price)
Optionaler Vergleichspreis, der durchgestrichen neben dem echten Preis angezeigt wird. Nur sichtbar wenn compare_at_price > price. Gilt ausschließlich für Neukäufe — bestehende Paketzuweisungen werden nicht berührt (Grandfathering).
- price_eur
- Tatsächlicher Verkaufspreis. Pflichtfeld wenn das Paket verkauft werden soll.
- compare_at_price
- Streichpreis. Nur angezeigt wenn größer als price_eur.
- badge_label
- Text auf dem Highlight-Badge, z.B. „Beliebt" oder „Angebot".
- is_highlighted
- Hebt das Paket auf der Landingpage hervor (Badge + prominente Platzierung).
Sonderflags
All-Access-Flag
Ein Paket mit access_all = 1 gewährt automatisch Zugriff auf ALLE neutralen Schulungen — auch solche, die nach dem Kauf neu hinzukommen. PST-Varianten sind immer ausgenommen. Nützlich für unbegrenzte Mitgliedschafts-Pakete.
All-Access-Flag
Ein Paket mit access_all = 1 gewährt automatisch Zugriff auf ALLE neutralen Schulungen — auch solche, die nach dem Kauf neu hinzukommen. PST-Varianten sind immer ausgenommen. Nützlich für unbegrenzte Mitgliedschafts-Pakete.
- access_all
- Gewährt Zugriff auf alle neutralen Schulungen, auch zukünftige. PST-Varianten immer ausgenommen. Nur für Premium/Unbegrenzt-Pakete setzen.
- is_free / Köder
- Schulung ohne Paket für alle eingeloggten User frei. Kein Package-Eintrag nötig.
Gratis-Köder (is_free)
Schulungen mit is_free = 1 sind für jeden eingeloggten User ohne Paket oder Zahlung sofort zugänglich. Kein Package-Eintrag nötig. Taktik: niedrige Einstiegshürde, um Leute auf die Plattform zu bringen. Vorsicht: Änderungen wirken sofort für alle bestehenden Accounts.
Permission-System
Auflösungs-Priorität
Die erste Regel, die greift, gewinnt. Weiter unten liegende Regeln werden nicht mehr geprüft.
- 1. user_overrides Direkt-Permission auf User ↔ Schulung. Absoluter Vorrang, auch wenn das Paket nichts enthält.
- 2. courses.is_free Schulung ist für alle eingeloggten User frei — kein Paket nötig.
- 3. access_all-Paket User hat ein Paket mit access_all = 1 → alle neutralen Schulungen freigegeben.
- 4. package_courses Schulung ist im Paket des Users enthalten (und Paket ist dem User zugewiesen).
Permission-Bits
View / Handout / PDF
Drei unabhängige Permission-Bits pro Schulung innerhalb eines Pakets:
• View — Schulung anschauen (Slides + Player)
• Handout — HTML-Skript herunterladen
• PDF — ZIP-Archiv mit Vorlagen-Dateien
View ist Voraussetzung für Handout und PDF. Alle drei können auf Paket-Ebene oder als Direkt-Permission gesetzt werden.
View / Handout / PDF
Drei unabhängige Permission-Bits pro Schulung innerhalb eines Pakets: • View — Schulung anschauen (Slides + Player) • Handout — HTML-Skript herunterladen • PDF — ZIP-Archiv mit Vorlagen-Dateien View ist Voraussetzung für Handout und PDF. Alle drei können auf Paket-Ebene oder als Direkt-Permission gesetzt werden.
Schulung anschauen — Slides + Player sichtbar. Voraussetzung für alle anderen Bits.
HTML-Skript-Download. Erfordert view = 1.
ZIP-Archiv mit Vorlagen. Erfordert view = 1. Im UI oft als "ZIP" beschriftet.
Bits lassen sich sowohl auf Paket-Ebene (gilt für alle User des Pakets) als auch per Direkt-Permission (gilt für einen einzelnen User) setzen.
Slide-Q&A
Approval-Status
Approval: pending_review
Slide-Feedback von regulären Usern landet zunächst in pending_review und wird niemandem außer dem Admin angezeigt. Erst nach manueller Freigabe (approve) wird es für andere User sichtbar. Admin-eigene Einträge bekommen automatisch den Status "auto" und brauchen keine Freigabe.
Standard-Workflow
- 1 User markiert Feedback auf einer Folie → landet als pending_review in der DB.
- 2 Admin öffnet Slide-Q&A, sieht alle pending_review-Einträge.
- 3 Admin ergänzt optional eine Notiz und exportiert als Markdown.
- 4 Markdown wird in Claude Code eingefügt → alle betroffenen Folien werden angepasst.
- 5 Nach dem Update: Bulk-Resolve-Button nutzen oder einzeln auf resolved setzen.
- 6 Resolved-Einträge verschwinden aus der Standard-Ansicht (Filter „Alle" zeigt sie noch).
Token-Action: resolve-ids
Maschinenlesbare Endpoint-Action, die Claude Code nach einem Bulk-Fix aufrufen kann, um eine Liste von Feedback-IDs auf resolved zu setzen — ohne manuellen Admin-Klick.
POST /api/admin/slide-qa.php?action=resolve-ids
Headers: X-CSRF-Token: <token>
Body: { "ids": [42, 43, 44], "token": "$APP_SECRET" }
APP_SECRET liegt im Secret-Store unter pst/erfolg-durch-ki/app-secret — niemals in den Code.
Deploy-Pipeline & Fallen
Pipeline-Übersicht
Bekannte Fallen
Nach einem Deploy mit neuen DB-Migrationen muss manuell aufgerufen werden:
/api/admin/migrate.php?token=$APP_SECRET
Erst aufrufen wenn die CI-Queue komplett leer ist —
sonst läuft die Migration gegen den alten Stand.
Der Migrator stoppt bei der ersten fehlerhaften Migration und rollt nicht zurück.
CSS, JavaScript, Bilder, Filme und Untertitel dürfen relativ eingebunden werden.
scripts/build_course_manifest.py
führt sie getrennt von den sichtbaren Materialien unter assets.
Der Sync ruft den Generator automatisch auf. Fehlt eine Datei dort, blockiert serve.php sie bewusst.
MP4- und WebM-Filme werden mit HTTP-Range-Unterstützung ausgegeben; Spulen und das Fortsetzen eines Abrufs funktionieren dadurch auch im Browser.
Inter-Schriften liegen global unter /fonts/.
Zwei schnell aufeinanderfolgende Pushes queuen sich. Der zweite startet erst nach Abschluss des ersten. Plan: warte mit Migrationen, bis du weißt dass kein weiterer Push in den nächsten 25 Minuten kommt.
Migration — Kurzanleitung
- 1 git push → warten bis CI-Queue leer (GitHub Actions → alle grünen Häkchen).
- 2 Migration-Endpoint aufrufen: /api/admin/migrate.php?token=$APP_SECRET
- 3 Response prüfen: {"status":"ok","applied":N} oder {"error":"...","failed_at":"..."}
- 4 Bei Fehler: Logs im Backend prüfen, Migration manuell fixen, erneut pushen + warten + aufrufen.