100 lines
7.1 KiB
Markdown
100 lines
7.1 KiB
Markdown
# 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?".
|