Ursachen der langen Ladezeiten: Reel-Video 1,6 GB in 4K mit Index am Dateiende, Headerbild 15 MB, Vorschaubild 1,3 MB im Original. Neues Skript erzeugt Web-Fassungen (<name>.web.mp4, 1080p, faststart; verkleinerte Bilder), die Seite bevorzugt sie automatisch. Mediendateien mit ?v= werden ein Jahr (immutable) gecacht, optimierte Bilder 30 Tage.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Das hochgeladene logo.png ist die helle Fassung und war auf der hellen Kopfzeile und im Fuß bis auf das goldene A unsichtbar. Die Seite misst jetzt die Helligkeit (sharp) und ordnet die Datei selbst zu; fehlt eine passende Fassung, wird per CSS abgedunkelt bzw. aufgehellt. Über dem Foto bis 176 px hoch mit weichem Schatten, nach dem Scrollen 80 px. Raster-Logos laufen über die Bildoptimierung (Original 5378 px, 313 KB).
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Startet nur nach Klick; mit Vorschaubild (intro.jpg) lädt vorab nichts vom Video. Vorschaubilder von Intro und Reels laufen über die Bildoptimierung statt im Original, Videos bevorzugen eine Web-Fassung <name>.web.mp4.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Positionen kommen aus getBoundingClientRect statt offsetLeft (bezog sich auf <body> und verfehlte den Einrastpunkt: Weiter sprang zwei Tage, Zurück hing am Ende). Weiter ist am Ende deaktiviert, Pfeile stehen über den Karten, ab md passen die Karten vollständig in den Inhaltsbereich.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Befunde aus dem Sicherheits-Review:
- Anhalten, Beenden und Abgleich der Ratenpläne fangen Fehler je Buchung ab.
Ein Stripe-Fehler bei einer Buchung bricht nicht mehr den ganzen
Worker-Lauf ab (E-Mails, Reservierungen liefen sonst nicht weiter).
Teilfehler setzen den Exit-Code 1, damit der CronJob-Lauf sichtbar
fehlschlägt.
- Beim Beenden eines Plans werden neben offenen Rechnungen auch Entwürfe
gelöscht. Laut Stripe-Testuhr bleibt ein Entwurf nach dem Beenden zwar
liegen und wird nicht eingezogen; das Löschen schließt das aus.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Befunde aus dem Sicherheits-Review des vorherigen Commits:
- Gerät eine Buchung in "Prüfung nötig" (Rückbuchung, Doppelzahlung ...),
wird der Stripe-Plan beendet und offene Rechnungen storniert. Nach
Bestätigung legt der Worker einen neuen Plan für die offenen Raten an.
Vorher zog Stripe auch bei Buchungen in Prüfung weiter ein.
- Vor dem Anlegen wird bei Stripe nach einem laufenden Plan der Buchung
gesucht; der Idempotenzschlüssel enthält Generation und Start. Verhindert
einen zweiten Plan nach verlorener ID und die Rückgabe des alten,
beendeten Plans nach einer Prüfung.
- Start bei bereits verstrichener Fälligkeit deterministisch am nächsten
9-Uhr-Termin, letzte Rate bleibt am Stichtag.
- Rückbuchungs-Event meldet die betroffene Buchung zurück.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Im Container (Linux) wurde z. B. reels/Reel-1.JPG nicht als Vorschaubild
erkannt, weil nur exakt reel-1.jpg gesucht wurde. Die Suche vergleicht
Dateinamen jetzt ohne Ruecksicht auf die Schreibweise und liefert den
echten Dateinamen aus. Gilt fuer Logo, Headerbild, Portraets und Videos.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
- Logo ueber dem Headerbild bis 112 px hoch, nach dem Scrollen 64 px,
im Fussbereich 80 px; Kopfzeile waechst mit
- Videos aus dem Medienverzeichnis direkt mit Steuerleiste, quer oder
hoch, erstes Bild als Vorschau; ein einzelnes Video breit und mittig
- Abschnitt heisst "Stimmen": Models erzaehlen, wie sie ARTISTA erleben
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
- Alle sieben Tage aus dem Wochenplan-PDF mit Uhrzeit, Coach und
Foto-Kennzeichnung im Karussell
- Migration: Camp-Termin 03.05. bis 09.05.2027 (ersetzt 08. bis 15.05.)
- Leistungen laut PDF: 20 Workshops und Shootings, sechs Naechte,
Bustransfer ab und bis Florenz, Fruehstueck, Snacks und Abendessen
- FAQ Anreise mit Transferzeiten
- Hinweise "Programmentwurf" und "Laut Beschreibung der Unterkunft" entfernt
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
- Figtree als einzige Schrift, Logo mittig in fester Kopfzeile
- Headerbild ueber volle Breite und Hoehe, ohne Preis, mit Terminleiste
- "Was dich erwartet" stellt die Villa mit animierter Bilderfolge vor,
darunter Tagesablauf als Karussell (Programm folgt als PDF)
- Preis: regulaerer Preis durchgestrichen, kein Verkaufsstartdatum,
Ueberschrift "Jetzt Platz sichern mit Early Bird"
- Team als "Mit dabei", danach drei Reels im Hochformat
- Logo, Headerbild, Portraets und Reels ueber feste Dateinamen im
Medienverzeichnis (docs/MEDIEN.md), ohne Neustart austauschbar
- Widerrufsbelehrung im Admin pflegbar, Seite /widerruf, Live-Verkauf
ohne Freigabe gesperrt
- Anreise: Abholung am Bahnhof Firenze Santa Maria Novella
- Booking-Link entfernt, Gedankenstriche aus Kundentexten entfernt
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
- Team-Abschnitt mit neun Personen, Eric Heitz als Head of Modelcamp,
Portraets im Rundbogen mit Initialen-Platzhaltern bis Fotos vorliegen
- Unterkunftsbilder der Villa La Chiesina lokal in public/images/unterkunft
(vom Auftraggeber freigegeben), Hero-Leitbild, Ausstattung, Booking-Link
- Migration: Termin 08.05.-15.05.2027, Ort Arcidosso (Toskana), eigenes
Bett im geteilten Zimmer, 13 Plaetze; Admin-Werte bleiben erhalten
- Reveal: kein Hydration-Fehler mehr bei reduzierter Bewegung
- Doku zu offenen Punkten aktualisiert
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
cert-manager legte die Challenge in einem separaten Ingress ohne TLS an.
Die HTTP->HTTPS-Umleitung schickte die Pruefung auf HTTPS, wo nur die App
antwortete ("expected <token> but got <!doctype html>"), das Zertifikat
wurde nie ausgestellt und Traefik zeigte sein Default-Zertifikat.
Mit http01-edit-in-place landet der Challenge-Pfad im App-Ingress und
gilt damit auch unter HTTPS.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
- Hero: Schlagzeile zweizeilig direkt unter dem Masthead, Buchung im
ersten Bildschirm; Termin, Ort und Preis als Cover-Line-Leiste
- Leistungen: Inklusivleistungen in Bodoni ohne Linie unter jeder Zeile,
Beispielablauf ruhiger, Hinweis unter statt neben der Ueberschrift
- Unterkunft ohne Split-Header, Ueber ARTISTA kompakter
- FAQ-Symbol gezeichnet statt Textzeichen, groesserer Abschnittsabstand
- Header auf Mobil ohne doppeltes "Modelcamp 2027"
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Beige Verlaeufe mit Bogen- und Silhouettenformen wirkten wie generische
Fuellbilder. Jetzt: ruhiger Palettenton, feine Papierstruktur,
Passepartout und ein angeschnittener Bodoni-Buchstabe aus "ARTISTA"
(Cover: Jahreszahl), skaliert ueber Container-Einheiten. Eindeutig kein
Foto, in Produktion ohne Label. Galerie etwas flacher.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
- ReviewsSection (Server Component) mit Durchschnitt, zugaenglichen
Sternen, Initialen statt Profilbildern, relativer Zeit, Links zur
Rezension und zum Maps-Eintrag sowie Hinweis "Rezensionen von Google"
- lange Texte optisch gekuerzt, "Weiterlesen" mit aria-expanded
- ohne Daten ausgeblendet; in Entwicklung/Staging ein Einrichtungshinweis
- Laden parallel zu loadPublicPage, Fehler blenden nur den Abschnitt aus
- Links aus der API-Antwort nur zu HTTPS-Google-Hosts (safeGoogleUrl)
- Linkstile und Browser-Oberflaechen (Scrollbar, Caret, Unterstreichung)
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Muster wie Wabenhain (Feldmaske, 1 h Cache, 5 s Timeout, Schluessel im
Header). Ohne Konfiguration oder bei Fehler null, keine Ersatz-Stimmen.
Keine Profilbilder (kein IP-Transfer der Besucherinnen an Google),
Quellenlinks nur als https. Neue Variablen GOOGLE_PLACES_API_KEY und
GOOGLE_PLACE_ID in .env.example und Secret-Vorlage.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Traefik laeuft im Cluster in kube-system ohne Host-Netzwerk (geprueft).
Die App ist damit nur noch fuer Traefik-Pods aus kube-system erreichbar
(Namespace + Label), was die Umgehung der Login-/2FA-Rate-Limits per
gefaelschtem X-Real-Ip aus anderen Pods verhindert.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
- Dateinamen nach Wabenhain-Schema (01-configmap, 02-secret,
03-storage, 04-postgres, 05-app-deployment, 06-ingress,
07-worker-cronjob, 08-networkpolicy); Secret-Vorlage als
02-secret.yaml.example, echte 02-secret.yaml gitignored.
- Namespace: Pod Security "baseline" erzwungen, "restricted" als warn/audit.
"restricted" kann den HTTP-01-Solver-Pod von cert-manager abweisen und
damit die Zertifikatsausstellung blockieren.
- NetworkPolicy nur noch fuer PostgreSQL; keine Policy vor der App (wie
Wabenhain/ApiCore), da sie bei Traefik im Host-Netzwerk die Seite sperrt.
- README: kompletter Neuaufbau inkl. Freigabe der Retain-PVs nach
"kubectl delete namespace", Zertifikats-Diagnose.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Nur Label-Abgleich ueber alle Namespaces liess jeden Pod mit Label
app.kubernetes.io/name=traefik zu. Jetzt muss zusaetzlich der Namespace
traefik oder kube-system sein (Namespace-Namen sind nicht faelschbar).
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Voraussetzungen pruefen (Traefik, cert-manager, csi-driver-smb),
Einzel-Applies in Reihenfolge mit kubectl wait auf Postgres,
Fehlersuche fuer Migration, App und Zertifikat.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
- Ingress: HTTP->HTTPS-Middleware redirect-to-https wieder am Ingress
(wie Wabenhain), Service-Port als Nummer, cert-manager letsencrypt-prod.
- NetworkPolicy: Traefik ueber das Pod-Label (traefik/rke2-traefik) in
jedem Namespace erkannt statt fester Namespace "traefik" - verhindert,
dass die Seite unerreichbar wird, wenn Traefik z. B. in kube-system laeuft.
- README: Voraussetzungen pruefen und Deployment Schritt fuer Schritt
(kubectl wait auf Postgres) wie bei Wabenhain.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Der Migrations-initContainer scheiterte: "pnpm install --ignore-scripts"
ueberspringt das postinstall von @prisma/engines, das die Schema-Engine
fuer "prisma migrate deploy" herunterlaedt. Der Builder holt das jetzt mit
"pnpm rebuild @prisma/engines prisma" nach. Zusaetzlich openssl in der
Basisstufe, weil die Engine unter Alpine libssl braucht.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Befunde des Commit-Security-Reviews: automountServiceAccountToken: false
fuer Postgres, App und Worker; Speicherlimits fuer Migrations-initContainer
und Worker; Laufzeitgrenze (activeDeadlineSeconds) fuer den Worker;
Groessenlimits fuer die emptyDir-Volumes.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Auf Wunsch des Betreibers auf das Noetige reduziert: StatefulSet
PostgreSQL, Services, Ingress, App-Deployment, zwei SMB-PersistentVolumes
(Postgres-Daten, Medien). Beibehalten nach Absprache: Worker-CronJob
(Folgeraten, Reservierungsfreigabe, Mail-Wiederholung), ConfigMap und die
NetworkPolicies (Sicherheitsbefund Rate-Limit-Umgehung).
- Migration als initContainer im App-Deployment statt eigenem Job
- Entfernt: Backup-CronJob/-Volume (NAS sichert die Freigabe),
Migrations-CronJob, PodDisruptionBudget, HTTP-Redirect-Ingress und
Middleware, Namespace-Objekt (einmalig von Hand anzulegen)
- PostgreSQL auf SMB als bewusste, dokumentierte Ausnahme mit
hard, cache=strict, SMB 3.1.1 + seal, uid 70 / 0700
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Befunde des Commit-Security-Reviews behoben:
- Klartextuebertragung: vers=3.1.1 mit "seal" (SMB3-Verschluesselung)
fuer Medien- und Backup-Volume.
- Rueckschritt Zugriffskontrolle: Backup-PV/PVC wieder ReadWriteOnce statt
ReadWriteMany (nur der Backup-Job haengt es ein).
- Zu weite Rechte: Medien-Volume nur lesbar (dir_mode 0550, file_mode
0440); geschrieben wird ausschliesslich ueber das NAS.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Statische PVs ueber csi-driver-smb (smb.csi.k8s.io), fest an die PVCs
artista-modelcamp-media (App liest, uid 1001) und
artista-modelcamp-db-backup (pg_dump-Job, uid 70, 0700/0600) gebunden,
reclaimPolicy Retain. Zugang ueber Secret artista-modelcamp-smb.
Das PostgreSQL-Datenverzeichnis bleibt bewusst auf Blockspeicher
(SMB erfuellt dessen Sperr- und fsync-Anforderungen nicht, Briefing §13);
die Datenbank landet ueber die taeglichen Dumps trotzdem auf dem NAS.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Alle Ressourcen tragen den Praefix artista-modelcamp- im Namespace
artista; Images <owner>/artista-modelcamp und
<owner>/artista-modelcamp-tools. DATABASE_URL-Host, Backup-Host,
Traefik-Middleware-Referenz und Betriebsdoku entsprechend angepasst.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Alle Ressourcen tragen den Praefix artista-filmcamp- (Deployment,
Service, StatefulSet, CronJobs, PVCs, ConfigMap, Secret, Ingress,
NetworkPolicies, Middleware) und liegen im Namespace artista. Die Images
heissen wieder <owner>/artista-filmcamp und <owner>/artista-filmcamp-tools,
passend zum Repo. DATABASE_URL-Host, Backup-Host und Betriebsdoku
entsprechend angepasst.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Die Pipeline leitete den Image-Namen vom Repo "Artista-Filmcamp" ab. Das
Produkt ist das ARTISTA MODELCAMP 2027: Images heissen jetzt fest
<owner>/artista-modelcamp und <owner>/artista-modelcamp-tools, passend zu
Overlay und Betriebsdoku.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
next build versuchte /admin statisch zu rendern. currentAdmin() rief dabei
getAuth() vor headers() auf und scheiterte ohne BETTER_AUTH_SECRET im
Build-Container. Jetzt werden zuerst die Request-Header gelesen, wodurch
alle Admin-Routen dynamisch sind und Auth erst zur Laufzeit geladen wird.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>