# 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 ```bash 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?".