Files
Wabenhain/k8s/01-configmap.yaml
T
Nick SzaunigandClaude Opus 5.5 f227aeb8f9 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]>
2026-10-16 14:07:30 +02:00

163 lines
8.3 KiB
YAML

# Nicht-geheime Konfiguration. Geheimnisse (Passwoerter, API-Schluessel)
# gehoeren in 02-secret.yaml, NIE hierher: eine ConfigMap ist unverschluesselt
# und in der Cluster-Oberflaeche im Klartext lesbar.
#
# ANPASSEN vor dem ersten Ausrollen: NEXT_PUBLIC_SITE_URL und SMTP_FROM. Alle
# uebrigen Werte sind sinnvolle Vorgaben und koennen so bleiben; die leeren
# ("") gehoeren zu optionalen Funktionen, die dann einfach ruhen.
#
# Zusammen mit 02-secret.yaml deckt diese Datei JEDE Umgebungsvariable ab, die
# die Anwendung liest. Die Vollstaendigkeit wird automatisch geprueft, siehe
# src/lib/k8s-manifest-env.test.ts — kommt im Code eine neue Variable dazu,
# faellt das beim naechsten "pnpm test" auf, nicht erst im Betrieb.
apiVersion: v1
kind: ConfigMap
metadata:
name: wabenhain-config
namespace: wabenhain
data:
# ---------------------------------------------------------------------
# ANPASSEN
# ---------------------------------------------------------------------
# Oeffentliche Adresse des Shops. Einzige Quelle fuer Sitemap,
# Canonical-Tags, JSON-LD und JEDEN Link in JEDER E-Mail (src/lib/url.ts).
# Muss zum "host" in 06-ingress.yaml passen — mit https, ohne
# abschliessenden Schraegstrich.
NEXT_PUBLIC_SITE_URL: "https://wabenhain.com"
# Absenderadresse aller Transaktions-E-Mails. Sollte auf einer eigenen
# Domain liegen, sonst landen die Mails regelmaessig im Spam-Ordner.
# Muss die Adresse des Postfachs aus SMTP_USER (Secret) sein — der Versand
# laeuft ueber Microsoft 365 mit OAuth 2.0, siehe README "E-Mail-Versand".
SMTP_FROM: "Wabenhain <[email protected]>"
# ---------------------------------------------------------------------
# VORGABEN — koennen so bleiben
# ---------------------------------------------------------------------
NODE_ENV: "production"
# "postgresql" oder "mysql" — muss zu dem Datenbank-Manifest passen, das du
# tatsaechlich ausrollst (03-postgres.yaml bzw. 03-mysql.yaml), UND zum
# Schema in DATABASE_URL im Secret. Ein einzelnes Image traegt beide
# Prisma-Client-Varianten, siehe src/lib/prisma.ts.
DATABASE_PROVIDER: "postgresql"
# Gleichzeitig offene Datenbankverbindungen JE INSTANZ. Leer = 20.
#
# Beim Hochskalieren mitdenken: der Wert multipliziert sich mit "replicas"
# in 05-app-deployment.yaml. Drei Instanzen mit je 20 belegen 60 der
# voreingestellt 100 Verbindungen von PostgreSQL — daneben brauchen der
# Aufraeum-CronJob, die Sicherung und gelegentlich eine Konsole noch
# welche. Entweder diesen Wert senken oder max_connections anheben.
DATABASE_POOL_SIZE: "20"
# Verzeichnis fuer hochgeladene Bilder und Videos. MUSS exakt dem mountPath
# des "uploads"-Volumes in 05-app-deployment.yaml entsprechen — die App
# weiss nicht, was dort gemountet ist, sie schreibt nur in diesen Pfad.
# Fehlt der Wert, schreibt die App nach "./uploads" relativ zum
# Arbeitsverzeichnis: das trifft im Image zufaellig denselben Ort, ist aber
# nirgends zugesichert. Deshalb hier ausdruecklich setzen.
UPLOAD_DIR: "/app/uploads"
# Ablage der Rechnungs-PDFs, nach Jahr und Kunde sortiert:
# /app/invoices/2026/mustermann-erika/RE-2026-A3F9C1B2.pdf
# Muss zum mountPath des "invoices"-Volumes in 05-app-deployment.yaml
# passen. BEWUSST ein anderes Verzeichnis als UPLOAD_DIR: auf die Uploads
# zeigt eine oeffentliche Route, auf dieses Verzeichnis keine. Siehe
# src/lib/invoice-storage.ts und 04-uploads-pvc.yaml.
INVOICE_DIR: "/app/invoices"
# Urkunden-PDFs der Bienenpatenschaft (src/lib/sponsorship/storage.ts):
# /app/invoices/urkunden/2026/WH-P-2026-00042/r1.pdf
# Unterordner des Rechnungs-Volumes: ebenso privat (keine oeffentliche Route
# zeigt dorthin), ebenso taeglich gesichert (07-backup-cronjob.yaml sichert
# das ganze Volume), und kein zweites ReadWriteOnce-Volume am App-Pod. Die
# Pfade der Rechnungen beginnen mit dem Jahr, "urkunden" kollidiert nicht.
CERTIFICATE_DIR: "/app/invoices/urkunden"
# Hintergrundaufgaben im App-Prozess (src/lib/background-jobs.ts): Urkunden
# nachholen, Abo-Lieferungen anlegen, Verzug nach Rueckbuchung beenden.
# Laufen im App-Pod, weil nur er das Volume mit CERTIFICATE_DIR einhaengt.
# "off" nur fuer zusaetzliche Instanzen, die keine Aufgaben uebernehmen sollen.
BACKGROUND_JOBS: "on"
# Stripe-Kundenportal fuer "Zahlungsart aendern" im Abo (bpc_..., kein
# Geheimnis). Legt scripts/stripe-setup.ts --apply an und gibt die ID aus.
# Leer lassen ist der Normalfall: dann sucht die App die Konfiguration ueber
# ihre Metadaten (wabenhain=abo-zahlungsart). Gibt es keine, bietet das Konto
# "Zahlungsart aendern" nicht an — die Standardkonfiguration von Stripe wird
# bewusst nie benutzt, weil sie Kuendigung oder Tarifwechsel erlauben koennte.
STRIPE_PORTAL_CONFIGURATION_ID: ""
# Umsatzsteuersatz in GANZEN Prozent, der auf der Rechnung ausgewiesen wird.
# 7 = ermaessigter Satz (Honig als Lebensmittel, Anlage 2 zum UStG)
# 19 = voller Satz
# 0 = Kleinunternehmerregelung; die Rechnung traegt dann statt der
# Steuerzeilen den Pflichthinweis nach § 19 UStG
#
# Die Shop-Preise sind BRUTTOPREISE (§ 1 PAngV). Die Steuer wird daraus
# herausgerechnet, nicht aufgeschlagen — der Endpreis bleibt also genau der,
# den die Kundschaft im Warenkorb gesehen hat.
#
# Der Satz wird bei jeder Rechnung FESTGESCHRIEBEN. Eine Aenderung hier gilt
# nur fuer kuenftige Rechnungen; bereits ausgestellte behalten ihren Satz.
VAT_RATE_PERCENT: "7"
# Satz je Kategorie. Der Steuersatz haengt an der Art der Ware: Honig ist
# ein Lebensmittel und ermaessigt besteuert, eine Bienenwachskerze nicht.
# Fehlt hier ein Wert, gilt VAT_RATE_PERCENT von oben.
#
# PROPOLIS bitte pruefen: als Lebensmittel waeren 7% richtig, als
# Nahrungsergaenzungs- oder Kosmetikprodukt 19%. Voreingestellt ist der
# volle Satz, weil er im Zweifel der sicherere ist.
VAT_RATE_PERCENT_HONIG: "7"
VAT_RATE_PERCENT_KERZEN: "19"
VAT_RATE_PERCENT_PROPOLIS: "19"
# Weitere Bienenprodukte (Kategorie SONSTIGES, z. B. Salben, Wachstuecher).
VAT_RATE_PERCENT_SONSTIGES: "19"
# Bienenpatenschaft (Urkunde, Namensgebung, Abo-Monatsbeitrag ohne
# Honiganteil): Leistung zum Regelsatz. Vor der Veroeffentlichung mit der
# Steuerberatung bestaetigen (Spec, "Geschaeftliche Konfiguration").
VAT_RATE_PERCENT_PATENSCHAFT: "19"
# Threads fuer das Umrechnen hochgeladener Videos (Admin -> Inhalte ->
# Hero-Video). NICHT ohne Not erhoehen: ffmpeg teilt sich das Speicherlimit
# dieses Pods mit dem Node-Prozess, und jeder zusaetzliche Thread haelt
# eigene Bildpuffer — bei 4K-Material rund 12 MB je Bild. Zu viele Threads
# bringen den Pod beim Video-Upload um. Siehe src/lib/video-transcode.ts.
VIDEO_TRANSCODE_THREADS: "2"
# "sandbox", bis die DHL-Versandmarken getestet sind, danach "production".
# Wirkt nur, wenn die DHL-Zugangsdaten im Secret gesetzt sind.
DHL_ENVIRONMENT: "sandbox"
# Oeffentlicher Stripe-Schluessel (pk_live_...) fuer das eingebettete
# Zahlungsformular. Nicht geheim — er steht ohnehin im HTML der Kasse.
# Muss zum STRIPE_SECRET_KEY im Secret passen (beide live). Leer = der
# Checkout nimmt keine Bestellungen an.
STRIPE_PUBLISHABLE_KEY: ""
# ---------------------------------------------------------------------
# OPTIONAL — leer lassen, solange die Funktion nicht genutzt wird
# ---------------------------------------------------------------------
# Oeffentlicher Teil von Cloudflare Turnstile (CAPTCHA). Der geheime
# Gegenpart TURNSTILE_SECRET_KEY steht im Secret — beide setzen oder beide
# leer lassen. Leer = kein CAPTCHA-Widget, die Formulare bleiben nutzbar.
NEXT_PUBLIC_TURNSTILE_SITE_KEY: ""
# Google-Eintrag, aus dem die echten Rezensionen auf der Startseite kommen.
# Die Place-ID ist oeffentlich (sie steckt in jeder Google-Maps-Adresse des
# Eintrags) und darf deshalb hier stehen; der abrechenbare
# GOOGLE_PLACES_API_KEY liegt im Secret. Leer = statische Beispiel-Stimmen.
GOOGLE_PLACE_ID: ""
# Adresse der ApiCore-API fuer den Stammdaten-Sync (Ernten, Produkte,
# Chargen — siehe src/lib/apicore-client.ts), mit dem globalen Praefix
# "api", z.B. "https://apicore.example.org/api". Nur die Adresse; die
# Zugangsdaten des Sync-Kontos liegen im Secret. Leer = der Sync-Knopf im
# Admin meldet einen Konfigurationsfehler.
APICORE_API_URL: ""