feat: Bienenpatenschaft mit Urkunde, Abo, Medaillen und 3D-Konto
Setzt Teilprojekte 1-5 der Spec 2026-10-16 um: - Urkunden-Engine (lib/certificate): ein Layout fuer PDF, Vorschau und 3D-Textur, Vorlage classic@1, eingebettete OFL-Schriften, Testmatrix - Einmal-Patenschaft ueber Traegerprodukte (Kategorie PATENSCHAFT), Paket wird im Checkout in Patenschaft (Regelsatz) und Honig aufgeteilt, Kontopflicht, rein digitale Bestellungen (FulfillmentMethod DIGITAL) - Ausstellung nach Zahlung idempotent, Wiederholung im App-Prozess (instrumentation + background-jobs), Ablage in CERTIFICATE_DIR - Verkaufsseite /patenschaft mit Konfigurator und Live-Vorschau, Warenkorb mit Schluessel je Position - Kundenkonto: 3D-Ansicht von Urkunde und Medaille, Downloads, ZIP, Uebertragung per befristetem Einmal-Link, Treuestufen bis VIP - Admin: Angebote, Patenschaften, Druck, Korrektur mit Revisionen, Abos, Erntefenster, Deckung, SEPA-Frist, Treuestufen - Abo ueber Stripe Billing: Lieferung erst nach bezahlten Monaten, SEPA-Neukunden-Frist und Rueckbuchungsschutz, Honigreservierung, Kuendigungsbutton, Monatsrechnungen, Stripe-Einrichtungsskript Startangebote werden inaktiv angelegt; Livebetrieb erst nach Freigabe. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
1 parent
dcb578f062
commit
f227aeb8f9
269 files changed
+28054
-141
No files matched your search
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* Ruecksprungziel nach der Anmeldung (/konto?next=/patenschaft).
|
||||
*
|
||||
* Nur Pfade dieser Seite: "//host" und "/\host" werten Browser als Adresse
|
||||
* auf einem fremden Host — ein ungeprueftes next waere eine offene
|
||||
* Weiterleitung, mit der ein Link auf wabenhain.com nach dem Login auf eine
|
||||
* nachgebaute Seite fuehrt (dieselbe Regel wie redirectWithinSite in
|
||||
* lib/redirect.ts). Steuerzeichen und Leerraum fallen ebenfalls durch, weil
|
||||
* Browser sie beim Aufloesen teils entfernen und so aus "/ /host" wieder
|
||||
* "//host" machen.
|
||||
*/
|
||||
const MAX_NEXT_LENGTH = 200;
|
||||
|
||||
export function safeNextPath(value: unknown): string | null {
|
||||
if (typeof value !== "string") return null;
|
||||
if (value.length === 0 || value.length > MAX_NEXT_LENGTH) return null;
|
||||
if (!value.startsWith("/") || value.startsWith("//") || value.startsWith("/\\")) return null;
|
||||
if (/[\s\\\p{Cc}]/u.test(value)) return null;
|
||||
return value;
|
||||
}
|
||||
|
||||
/** Login-Seite mit erhaltenem Ruecksprungziel, z.B. fuer Fehlermeldungen. */
|
||||
export function loginPathWithNext(next: string | null): string {
|
||||
return next ? `/konto?next=${encodeURIComponent(next)}` : "/konto";
|
||||
}
|
||||
Reference in new issue
Block a user