Doku: CLAUDE.md (Arbeitsvereinbarung) + HANDOVER.md (Betriebs-/Go-Live-Runbook)
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
# 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?".
|
||||
Reference in New Issue
Block a user