Commit Graph
20 Commits
Author SHA1 Message Date
Nick SzaunigandClaude Opus 5.5 5a88f3dcd6 fix: Client-IP haerten (CF-Connecting-IP, X-Real-IP, Admin-Session)
Docker-Images bauen und veroeffentlichen / build-and-push (push) Successful in 8m8s
- 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]>
2026-10-12 11:26:56 +02:00
Nick SzaunigandClaude Opus 5.5 34ccbc3b9a fix: SMTP-Fehler nur bei Empfaengerablehnung endgueltig
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]>
2026-10-12 11:20:34 +02:00
Nick SzaunigandClaude Opus 5.5 e6fa383934 fix(security): eigene Rate-Limits zaehlen IPv6 je /64-Netz
Docker-Images bauen und veroeffentlichen / build-and-push (push) Successful in 7m24s
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]>
2026-10-12 10:06:28 +02:00
Nick SzaunigandClaude Opus 5.5 f75a5f7769 fix(security): Client-IP nicht aus X-Real-IP, better-auth nur mit Server-Wert
Docker-Images bauen und veroeffentlichen / build-and-push (push) Canceled after 0s
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]>
2026-10-12 09:49:58 +02:00
Nick SzaunigandClaude Opus 5.5 c3fdfbdc77 fix(worker): endgueltig abgelehnte Mails nicht wiederholen, Adressen nicht loggen
Docker-Images bauen und veroeffentlichen / build-and-push (push) Canceled after 0s
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]>
2026-10-12 09:24:28 +02:00
Nick SzaunigandClaude Opus 5.5 a47e91e9d5 feat: Telefonnummer im Kontaktformular ist Pflicht
Docker-Images bauen und veroeffentlichen / build-and-push (push) Successful in 2m50s
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]>
2026-10-07 00:56:30 +02:00
Nick SzaunigandClaude Opus 5.5 5c2878875b feat: Buchung sofort möglich, sobald alle Freigaben erteilt sind
Docker-Images bauen und veroeffentlichen / build-and-push (push) Successful in 2m54s
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]>
2026-10-06 23:07:39 +02:00
Nick SzaunigandClaude Opus 5.5 7745d5862d feat: Kontaktformular für ein Erstgespräch
Docker-Images bauen und veroeffentlichen / build-and-push (push) Successful in 4m31s
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]>
2026-10-06 11:37:27 +02:00
Nick SzaunigandClaude Opus 5.5 1ca09cbaef feat: Logo deutlich größer und auf jedem Untergrund sichtbar
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]>
2026-10-06 07:15:09 +02:00
Nick SzaunigandClaude Opus 5.5 33f5096bf1 feat: Video unter dem Headerbild aus dem Medienverzeichnis (intro.mp4)
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]>
2026-10-06 07:09:46 +02:00
Nick SzaunigandClaude Opus 5.5 a28d18c3e6 fix: Worker übersteht Stripe-Fehler einzelner Buchungen, Rechnungsentwürfe beim Beenden löschen
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]>
2026-10-06 06:34:20 +02:00
Nick SzaunigandClaude Opus 5.5 1ce7947d93 fix: Stripe-Ratenplan bei Prüfung anhalten, doppelte Pläne verhindern
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]>
2026-10-06 06:28:41 +02:00
Nick SzaunigandClaude Opus 5.5 c1ad9ad2ca feat: Folgeraten über Stripe Billing einziehen
Statt eigener Off-Session-Einzüge durch den Worker legt die Anwendung nach
bezahlter erster Rate einen Stripe Subscription Schedule an. Stripe übernimmt
Einzug, Wiederholungen (Smart Retries), 3-D-Secure-Links und Mahnungen.

- Gateway: Zeitplan anlegen/beenden, Rechnungen lesen; chargeOffSession entfällt
- Webhook: invoice.paid, invoice.payment_failed, invoice.payment_action_required
- Worker: fehlende Zeitpläne anlegen, überfällige Raten abgleichen,
  Zeitpläne stornierter Buchungen beenden
- Storno beendet den Zeitplan und storniert offene Rechnungen
- "Rate bezahlen" führt auf Stripes Rechnungsseite
- Migration: Booking.stripeScheduleId, Installment.stripeInvoiceId

Zeitplan-Form mit Stripe-Testuhr gegen die Test-API geprüft.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
2026-10-05 23:52:43 +02:00
Nick SzaunigandClaude Opus 5.5 bcf0f5c937 fix: Medien unabhaengig von Gross- und Kleinschreibung finden
Docker-Images bauen und veroeffentlichen / build-and-push (push) Successful in 2m53s
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]>
2026-10-02 06:37:00 +02:00
Nick SzaunigandClaude Opus 5.5 7203cd1e6e feat: Onepage nach Filmcamp-Gliederung umgebaut
- 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]>
2026-10-01 23:53:18 +02:00
Nick SzaunigandClaude Opus 5.5 804bb832ea feat: Team, Villa-Bilder und bestaetigte Camp-Eckdaten
Docker-Images bauen und veroeffentlichen / build-and-push (push) Successful in 2m49s
- 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]>
2026-10-01 07:39:27 +02:00
Nick SzaunigandClaude Opus 5.5 f74a08fa68 feat: Google-Rezensionen als eigener Abschnitt auf der Onepage
- 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]>
2026-09-28 17:46:19 +02:00
Nick SzaunigandClaude Opus 5.5 b4eafbe433 feat: Google-Rezensionen ueber Places API (New) abrufen
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]>
2026-09-28 17:30:30 +02:00
Nick SzaunigandClaude Opus 5.5 771eeee887 fix: Admin-Routen nicht vorab rendern (Docker-Build ohne Secrets)
Docker-Images bauen und veroeffentlichen / build-and-push (push) Successful in 4m25s
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]>
2026-09-27 12:42:25 +02:00
Nick Szaunig 59bb5a38a3 first commit
Docker-Images bauen und veroeffentlichen / build-and-push (push) Failing after 1m32s
2026-09-27 06:06:56 +02:00