Files
Wabenhain/k8s/01-configmap.yaml
T
Nick SzaunigandClaude Opus 5 6639da130e fix: Google-Rezensionen auf "Places API (New)" umgestellt
Symptom: dauerhaft REQUEST_DENIED mit "The provided API key is invalid",
obwohl der Schluessel keinerlei Einschraenkung traegt.

Die Meldung fuehrt in die Irre. Der Code rief den alten Endpunkt
maps.googleapis.com/maps/api/place/details/json auf, und Google laesst
die dazugehoerige alte "Places API" in seit Maerz 2025 angelegten
Cloud-Projekten gar nicht mehr aktivieren. Der Fehler betrifft also nicht
den Schluessel, sondern die API — wer der Meldung folgt, sucht
zwangslaeufig an der falschen Stelle. Das README hatte diesen Fall als
"Legacy-Hinweis" bereits vorhergesagt.

Jetzt places.googleapis.com/v1 mit X-Goog-Api-Key im Header (statt des
Schluessels in der Adresszeile, wo er in fremden Zugriffsprotokollen
landet) und einer engen X-Goog-FieldMask — die API rechnet nach
angeforderten Feldern ab.

Die Fehlermeldung im Log nennt jetzt die drei Dinge, an denen es
tatsaechlich liegen kann: API nicht aktiviert, keine Abrechnung am
Projekt, oder ein OAuth-Client statt eines API-Schluessels hinterlegt.

Zwei Verhaltensaenderungen, beide unvermeidbar:
  - Die neue API kennt kein reviews_sort=newest; sie liefert bis zu fuenf
    Rezensionen in Googles eigener Reihenfolge.
  - Rezensionen ohne Namen werden uebersprungen (zuvor nur die ohne
    Text): ohne Namen fehlt die von den Nutzungsbedingungen verlangte
    Zuordnung zur Quelle.

README und .env.example nennen jetzt alle vier noetigen Schritte und
weisen ausdruecklich darauf hin, dass eine Referrer-Beschraenkung den
Abruf blockiert — er laeuft serverseitig.

Dazu, mit Blick auf mehrere Instanzen: DATABASE_POOL_SIZE ist jetzt
einstellbar (Vorgabe 20) und in der ConfigMap dokumentiert. Der Wert gilt
je Instanz und multipliziert sich mit "replicas" — drei Instanzen belegen
60 der voreingestellt 100 PostgreSQL-Verbindungen.

tsc: 0 Fehler. vitest: 336/336 gruen. eslint: 0 Warnungen.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
2026-09-19 06:16:19 +02:00

131 lines
6.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.
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"
# Port des SMTP-Anbieters. 587 ist STARTTLS (der Normalfall), 465 waere
# implizites TLS.
# 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"
SMTP_PORT: "587"
# 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"
# 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"
# 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"
# ---------------------------------------------------------------------
# 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: ""