Next reicht Route-Handlern einen Proxy um NextRequest. undici 7 (Node 24)
prueft private Felder (#state), die ein Proxy nicht besitzt; new Request(request)
warf "Cannot read private member #state" und jede /api/auth-Anfrage endete
lokal mit 500. Produktion (Node 22, undici 6) war nicht betroffen.
withClientIp baut die Anfrage jetzt aus URL, Methode, Headern und Body neu.
Regressionstest mit einem Proxy wie in Next.
- CF-Connecting-IP gilt nur noch als oeffentliche Unicast-Adresse
(nicht 0/8, 10/8, 100.64/10, 127/8, 169.254/16, 172.16/12,
192.168/16, 240/4, Broadcast, ::, ::1, fc00::/7, fe80::/10,
::ffff:privat); sonst gilt die Gegenstelle.
- X-Real-IP ohne X-Forwarded-For nur noch ausserhalb von Produktion;
in Produktion ergibt das "unknown".
- Neuer Helfer headersWithClientIp, von withClientIp und der
Admin-Session-Pruefung genutzt: ein vom Client gesendeter
x-artista-client-ip-Wert wird nie geglaubt.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Bisher setzte jeder SMTP-5xx die Mail sofort auf FAILED, auch Anmelde-
und Richtlinienfehler (EAUTH, EMESSAGE, 552). Ein kaputtes SMTP-Secret
haette alle Buchungs- und Zahlungsmails verworfen, ohne Rueckweg
(dedupeKey verhindert erneutes Einreihen).
Endgueltig ist jetzt nur EENVELOPE mit 550, 551, 553, 556 oder 5xx mit
Status 5.1.x. Alles andere bleibt PENDING und wird bis
MAX_EMAIL_ATTEMPTS wiederholt.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
consumeRateLimit bekommt IPv6-Adressen als /64-Praefix (rateLimitSubject):
ein Anschluss hat meist ein ganzes /64 und koennte sonst durch Adresswechsel
jedes Limit umgehen. better-auth bekommt weiter die einzelne Adresse und
normalisiert sie selbst auf /64 (ipv6Subnet, Standard 64).
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Traefik vertraut seit 2026-10-12 den weitergeleiteten Headern aus
Cloudflare-Netzen. Wer eine eigene Cloudflare-Zone auf den Server zeigen
laesst, konnte damit X-Real-IP und den ersten X-Forwarded-For-Eintrag
faelschen und die Rate-Limits (Buchung, Kontakt, Login, 2FA) umgehen.
- clientIp: Gegenstelle ist der letzte X-Forwarded-For-Eintrag (von Traefik
angehaengt); CF-Connecting-IP nur, wenn sie eine Cloudflare-Edge ist.
- better-auth liest nur noch x-artista-client-ip, den der Auth-Endpunkt
per withClientIp setzt (ein mitgeschickter Wert wird ersetzt).
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Eine Mail an eine Adresse ohne Mailserver (SMTP 556, nullMX) wurde bis zu
8-mal erneut versendet und liess jeden Worker-Lauf mit Exit-Code 1 enden;
das SMTP-Fehlerlog enthielt dabei die Empfaengeradresse.
- SMTP-Antworten 5xx markieren die Mail sofort als FAILED, 4xx und
Verbindungsfehler bleiben wiederholbar.
- Adressen werden aus Log und lastError entfernt.
- Worker-CronJob: backoffLimit 0 (kein zweiter Pod je Fehlschlag) und
ttlSecondsAfterFinished 1 Tag, damit Fehler-Pods nicht stehen bleiben.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Gleiche Prüfung wie bei der Buchung: mindestens 6 Zeichen, nur Ziffern, Leerzeichen und + ( ) / -.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Keine Vorauswahl mehr. Unter der gewählten Option stehen Preisaufstellung und Raten mit Fälligkeit; Weiter erst nach Auswahl.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Vor dem 01.11.2026 gilt dann der Early-Bird-Preis. Fehlt eine Freigabe, bleibt die Vorschau.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Neuer Abschnitt nach den Fragen, im Menü als Erstgespräch. Anfrage geht als E-Mail an die Veranstalter-Adresse (Admin) bzw. ADMIN_NOTIFY_EMAIL, sofort zugestellt, sonst über den Worker wiederholt. Gleicher Schutz wie die Buchung: nur JSON von der eigenen Seite, Rate Limit (5/h je IP), Prüfung auf dem Server, Honeypot gegen Bots, doppeltes Absenden ergibt eine Mail. Keine automatische Mail an die angegebene Adresse (sonst Missbrauch für Spam an Dritte).
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
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]>
- 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]>
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]>