Files
hd-commerce/CLAUDE.md
T

7.1 KiB

CLAUDE.md — Arbeitsvereinbarung für hd-commerce

Diese Datei ist die verbindliche Kurzanleitung für jede KI (Claude/Cowork/Codex) und jeden Menschen, die an hd-commerce arbeiten. Vor Code-Änderungen lesen. Sie ergänzt README.md (Feature-Referenz) und HANDOVER.md (Betrieb/Go-Live).


1. Was das hier ist

hd-commerce ist ein brand-neutrales, wiederverwendbares E-Commerce-Backend (Produkt, kein Einzelprojekt). Eine Codebasis → beliebig viele Shops, unterschieden nur durch settings + ENV. Die mitgelieferte Demo heißt „Brittas Nähkiste" und ist nur Beispiel — niemals Demo-spezifische Hacks fest in den Code schreiben.

Leitsatz: Alles, was sich pro Shop unterscheidet, gehört in settings/ENV/DB — nicht in den Quellcode.

2. Stack (nicht ohne Grund wechseln)

  • Astro 5 SSR (output: 'server', @astrojs/node standalone, security.checkOrigin: false).
  • better-sqlite3 (synchron, WAL) — die einzige Datenschicht. Kein ORM, bewusst.
  • sharp (WebP), nodemailer (SMTP), stripe (Stripe-SDK; Mollie über fetch/REST).
  • Self-hosted Fonts Fraunces + Public Sans (@fontsource-variable). Kein Google-CDN. Chart.js via cdnjs.
  • Node 22. Docker node:22-slim. Port 4321. Persistentes Volume auf /data.

EU/DSGVO-first (HD-Linie): keine US-SaaS-Abhängigkeit ohne Grund. Zahlung Default Mollie, Mail Listmonk/SMTP, Backup Litestream → Backblaze B2 (EU). Analytics ist first-party (eigene events-Tabelle), kein externer Tracker.

3. Goldene Regeln (die teuren Fehler)

  1. Preise immer in Cent (Integer). Keine Floats für Geld. Helper: taxFromGross(gross, rate), shippingFor(country, subtotal), discountAmount(...).
  2. Block-Shape ist FLACH: ein Block ist { type, headline, html, image, ... }NICHT unter data: {} verschachtelt. Das API-Manifest liest sich missverständlich; maßgeblich sind src/components/BlockRenderer.astro + der Editor. Beim Erzeugen/Ändern von pages.blocks immer die flache Form verwenden.
  3. Datenzugriff nur über src/lib/store-sqlite.js (bzw. store.js-Fassade). Kein SQL verstreut in Seiten. Das ist die Postgres-Exit-Versicherung (Portal-Empfehlung): SQLite-Spezifika hinter der Fassade halten.
  4. Migrationen idempotent: neue Spalten via ensureColumn(...), neue Tabellen via CREATE TABLE IF NOT EXISTS, System-Seiten via ensureSystemPages(). Alles läuft bei jedem Boot — muss mehrfach gefahrlos sein. Kein Migrations-Framework.
  5. Geld/Rabatt/Versand serverseitig re-validieren (in /api/checkout), nie dem Client-Cart-Preis trauen. Gleiches für Varianten-Preise.
  6. Sanitizen: jeglicher roher HTML-Output (richtext/html-Blöcke, Seiten-body) läuft durch sanitizeHtml() aus src/lib/sanitize.js. Niemals ungefiltertes set:html.
  7. Secrets (Passwörter, Tokens, API-Keys) gehören in ENV, nie ins Repo, nie in Memory/Doku. .env ist gitignored; .env.example zeigt nur Platzhalter.
  8. Rollen serverseitig gaten (owner/redaktion/versand) — nicht nur die Nav ausblenden. Auth in src/lib/auth.js; Kunden-Session getrennt in customer-auth.js.

4. Projektkarte (wo liegt was)

src/lib/
  store-sqlite.js   Datenschicht: Schema, Seed, alle CRUD-/Rechen-Helper (Single Source of Truth)
  store.js          dünne Fassade über store-sqlite (Import-Stabilität)
  auth.js           Admin-Session (HMAC-Cookie, scrypt, Rollen, Rate-Limit, resolveSecret/cookieSecure)
  customer-auth.js  Kunden-Session (Cookie hdc_customer), getrennt vom Admin
  payments.js       Provider-Abstraktion mollie|stripe|demo (+ publicBase aus X-Forwarded-*/PUBLIC_BASE_URL)
  mailer.js         listmonk|smtp|Log-Fallback (Tabelle email_log)
  sanitize.js       sanitizeHtml() — Pflicht für jeden rohen HTML-Output
  blocks.js         Block-Typ-Definitionen/Defaults
  seed.js           Demo-Seed-Daten
src/components/BlockRenderer.astro   rendert pages.blocks (flache Felder!)
src/middleware.js                    ADMIN_PATH-Rewrite + Auth-Gate
src/pages/admin/**                   Admin-UI (Astro-Seiten, rollen-gegatet)
src/pages/api/**                     Endpunkte (checkout, discount, review, upload, cron/abandoned, payments/webhook, admin/*)
src/pages/{index,shop,produkt/[slug],warenkorb,checkout,suche,merkliste,konto/*,seite/[slug]}.astro   Storefront
mcp/                                 eigenständiger MCP-Server (stdio) über die Admin-API
test/unit.mjs                        Unit-Tests (Geld/Versand/Rabatt/Sanitizer)
scripts/sync-css.mjs                 prebuild: CSS-Sync
Dockerfile, docker-entrypoint.sh, litestream.yml   Container + optionales Litestream-Backup

5. Entwickeln, testen, bauen

npm install
npm run dev          # http://localhost:4321  (Storefront /, Admin /admin)
npm test             # Unit-Tests — MUSS grün sein vor jedem Commit (nutzt temporäre DB)
npm run build && node ./dist/server/entry.mjs   # Produktiv-Lauf lokal prüfen
  • Vor jedem Commit npm test. Neue Geld-/Rabatt-/Versand-/Sanitizer-Logik bekommt einen Test dazu.
  • Erst-Login lokal mit ADMIN_EMAIL/ADMIN_PASS aus .env.
  • ADMIN_PATH testen, wenn an Auth/Middleware angefasst wird (Default admin; abweichend ⇒ /admin muss 404 sein).

6. Deploy (Coolify) — und die Stolpersteine

Deploy via Coolify (Docker-Build aus dem Repo). Nach jedem Deploy gilt:

  1. App control restart ausführen. Rolling-Updates lassen zeitweise Altcontainer stehen → sonst gemischte 401/404/502.
  2. Verifizieren mit cache-bustendem curl (nicht WebFetch — cacht): Storefront 200, Login 200, ein API-Call.
  3. Coolify-Gotchas:
    • application create_public verwirft den Git-Host → danach application update mit voller https://till:TOKEN@…-URL.
    • Änderung von ports_exposes regeneriert die Traefik-Labels nicht → bei Port-Wechsel custom_labels (base64) manuell setzen.
    • Domain-Wechsel http→https regeneriert die Labels (anders als ports_exposes) → danach restart gegen Altcontainer.
  4. Migrationen brauchen keinen Sonderschritt — sie laufen idempotent beim Boot (siehe Regel 4).
  5. Sandbox-Hinweis (Cowork): der FUSE-Mount erlaubt kein rm/Symlinks → in /tmp klonen/bauen, nicht im Mount. Push braucht Tills Gitea-Token (anonymer Clone ist read-only).

7. Versionierung

package.json#version + sprechende Commit-Message pro Feature-Stufe (v2.4: …). Aktueller Stand: 2.4 (Medien/WebP, Varianten-Matrix, Litestream, intelligentere Analytics). Bei Schema-/ENV-Änderung: README-Tabelle, .env.example und HANDOVER.md mitziehen.

8. „Selbst bauen vs. kaufen" — Leitplanke

Eigenbau ist gerechtfertigt, weil hd-commerce ein mehrfach ausrollbares HD-Produkt ist (Wartung amortisiert über die Care-Abo-Basis). Für einen einzelnen 08/15-Shop bleibt Shopware/Medusa/Snipcart pragmatischer. Halte den Code deshalb generisch und wartungsarm — jede Demo-Sonderlocke und jede SQLite-Spezifik außerhalb der Store-Fassade erhöht genau die laufende Wartung, die das Produkt rechtfertigen muss. Belegte Einordnung: Portal-Research „SQLite-Eigenbau-Commerce — realistisch oder Overkill?".