Files
hd-commerce/README.md
T
till 30c41c355e v2.3: Feature-Module live — Suche, Merkliste, Kundenkonten+Adressbuch, Bewertungen, Abandoned-Cart
- feature_search: Storefront-Header-Suche + /suche (SSR, SQLite LIKE, case-insensitiv; Name/Kurz/Desc/Material/Kategorie), Treffer als Karten, Leer-Zustand
- feature_wishlist: Herz-Button auf Karten/PDP (localStorage, public/wishlist.js) + /merkliste
- feature_accounts: getrennte Kunden-Session (Cookie hdc_customer, scrypt), /konto/registrieren|anmelden|abmelden, /konto (Bestellhistorie+Adressbuch), Tabelle customer_addresses, Checkout-Vorbefuellung + orders.customer_id-Zuordnung; Gast-Checkout bleibt
- feature_reviews: Tabelle reviews (1-5, Moderation), /api/review (approved=0), PDP-Anzeige Durchschnitt+Reviews + aggregateRating-JSON-LD, Admin /bewertungen (Freigeben/Verbergen/Loeschen) + Nav-Zaehler
- feature_abandoned_cart: Tabelle abandoned_carts, /api/cart-capture beim Checkout-Start, /api/cron/abandoned (CRON_TOKEN) sendet Erinnerungsmail (Mailer/Log) + reminded=1, recovered=1 bei Bestellung; Status in Einstellungen
- Gating: Flag aus => Storefront-Elemente weg, Routen 302/404, Admin-Nav-Punkt entfaellt; KEIN 'in Vorbereitung' mehr
- API/MCP: reviews CRUD + abandoned_carts (read) in admin-api + ai-admin.txt + MCP-Tools; Manifest v2.3
- README + .env.example (CRON_TOKEN, ABANDONED_AFTER_MINUTES); 16 neue Unit-Tests (Suche/Review-Avg/Kunden/Abandoned)
2026-06-18 07:27:34 +00:00

122 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# hd-commerce
**hd-commerce** ist ein eigenständiges, brand-neutrales E-Commerce-Backend von Heidrich Digital: eine wiederverwendbare Astro-SSR-Anwendung mit Commerce-Engine (SQLite), Session-gesichertem Admin, Visual-Block-Builder, KI-/MCP-Editierbarkeit, JSON-API und einem schlanken, neutralen Demo-Storefront.
Die mitgelieferte Demo-Instanz heißt **„Brittas Nähkiste"** (Kurzwaren/Nähbedarf) und dient nur als Beispiel. Name, Akzentfarbe, Texte und Logo-Wortmarke sind über die Einstellungen frei anpassbar — derselbe Code läuft für beliebige Shops.
## Features
- **Storefront** (hell, editorial, neutral): Startseite mit Announcement-Bar, Slider, Kategorien, Featured-Produkten und Newsletter; Shop-Katalog; Produktdetailseiten; Warenkorb (localStorage); Checkout; Inhaltsseiten aus der DB — wahlweise klassisch oder über den **Block-Builder** gestaltet.
- **Admin** (Premium, „Warmth & Approachability"): Session-Login statt Browser-Basic-Auth, Rollen (Owner/Redaktion/Versand), Command-Palette (⌘K), Toasts, aufgewertetes Dashboard mit KPI-Trends, Sparkline, Aktivitäts-Feed und Schnellaktionen.
- **Visual-Block-Builder**: Vollbild-Editor mit Block-Liste (Drag/▲▼/duplizieren/löschen), Live-Vorschau (Desktop/Mobil) und Block-Einstellungen. Block-Typen: Hero, Rich-Text, Bild, Galerie, Slider, Feature-Grid, Produkt-Grid, CTA-Banner, Abstand, Roh-HTML.
- **KI-Editierbarkeit**: token-gesicherte Admin-JSON-API (`/api/admin/*`) plus maschinenlesbares Manifest (`/api/admin`, `/ai-admin.txt`) und ein **MCP-Server** (`mcp/`).
- **Gutschein-/Rabatt-Engine** (v2.1): Codes vom Typ `percent` / `fixed` / `freeshipping` mit Zeitplan, Mindestbestellwert, Gesamt- und Pro-Kunde-Limit, „geheim" (nicht öffentlich listbar) und „automatisch" (greift ohne Code, wenn Bedingungen erfüllt). Admin-Bereich **Rabatte** (Owner/Redaktion) mit Status-Badges (Aktiv/Geplant/Abgelaufen/Aufgebraucht/Inaktiv); Storefront-Einlösung im Checkout über `/api/discount`; serverseitige Re-Validierung in `/api/checkout`; Stripe-Coupon-Anbindung. Popups können einen Code anzeigen (+ Kopieren-Button) — auch für gezielt verteilte geheime Codes; Popup-Stile `modal` / `slidein` / `bar`.
- **Verkaufsfertig-Fundament (v2.2):**
- **Payment-Abstraktion** (`src/lib/payments.js`): einheitliche Schnittstelle für **Mollie** (Default, REST), **Stripe** und **Demo**. Provider via Setting `payment_provider` / ENV `PAYMENT_PROVIDER`, sonst Auto-Wahl nach vorhandenen Keys. Mollie-Webhook unter `/api/payments/webhook`; ungültiger Key ⇒ sauberer Demo-Fallback statt Fehler.
- **DACH-Recht**: MwSt-Ausweis pro Produkt (`mwst` 0/7/19), **Grundpreis** (PAngV) über `base_amount`/`base_unit`/`base_price_per`; Warenkorb/Checkout zeigen Zwischensumme, enthaltene MwSt (nach Satz gruppiert) und **Versand vor dem Bezahlen**.
- **Versandzonen** (Tabelle `shipping_zones`, Admin „Versand"): länderbasierte Preise mit Gratis-ab-Schwelle; Helper `shippingFor(country, subtotal)`; Checkout berechnet Versand serverseitig neu.
- **Bestell-/Versandmails** (`src/lib/mailer.js`): Provider **Listmonk** (Transactional-API) / **SMTP** (nodemailer) / **Log-Fallback** (Tabelle `email_log`, Admin „E-Mail-Log"). Gebrandete Bestellbestätigung bei bezahlter Bestellung.
- **Feature-Flags**: Module pro Shop abschaltbar (`feature_newsletter`, `feature_accounts`, `feature_reviews`, `feature_wishlist`, `feature_abandoned_cart`, `feature_search`) über Admin → Einstellungen → Module; Helper `feature(key)`.
- **Feature-Module (v2.3):** über die jeweiligen Flags ge-gatet — ist ein Flag aus, verschwinden Storefront-Elemente, Routen liefern 302/404 und der Admin-Nav-Punkt entfällt.
- **Volltextsuche** (`feature_search`): Suchfeld im Storefront-Header → Ergebnisseite `/suche?q=` (SSR, SQLite-`LIKE`, case-insensitiv über Name/Kurzname/Beschreibung/Material/Kategorie), Treffer als Produktkarten, „keine Treffer"-Zustand.
- **Merkliste** (`feature_wishlist`): Herz-Button auf Produktkarten & Detailseite, Speicherung clientseitig (localStorage, `public/wishlist.js`), Seite `/merkliste`.
- **Kundenkonten + Adressbuch** (`feature_accounts`): eigene Kunden-Session (Cookie `hdc_customer`, getrennt vom Admin; scrypt-Hash). `/konto/registrieren`, `/konto/anmelden`, `/konto` (Bestellhistorie + Adressbuch), `/konto/abmelden`. Tabelle `customer_addresses`; Checkout füllt die Adresse vor und ordnet die Bestellung dem Konto zu (`orders.customer_id`). Gast-Checkout bleibt möglich.
- **Bewertungen** (`feature_reviews`): Tabelle `reviews` (Sterne 15, Moderation `approved`). Formular auf der Produktseite (`/api/review`, speichert `approved=0`), Anzeige von Durchschnitt + freigegebenen Reviews, optionale `aggregateRating` im Produkt-JSON-LD. Admin-Bereich **Bewertungen** (Owner/Redaktion): Freigeben/Verbergen/Löschen, Zähler offener Reviews in der Nav.
- **Warenkorb-Erinnerung** (`feature_abandoned_cart`): beim Checkout-Start wird der Warenkorb serverseitig in `abandoned_carts` gesichert (`/api/cart-capture`). Versand-Trigger: **`POST /api/cron/abandoned`** (Header `Authorization: Bearer <CRON_TOKEN>` oder `?token=`), schickt für fällige, nicht erinnerte Karten eine gebrandete Erinnerungsmail (Mailer/Log-Fallback) und setzt `reminded=1`. Erfolgreiche Bestellung der Adresse setzt `recovered=1`. Status/Zähler unter Einstellungen. Als **Coolify-Scheduled-Task** z. B. alle 30 Min `curl -fsS -X POST -H "Authorization: Bearer $CRON_TOKEN" https://shop.example.com/api/cron/abandoned` aufrufen.
- **Editierbare, gebrandete 404** (v2.1): `src/pages/404.astro` rendert die System-Seite mit Slug `404` über den Block-Builder. Wird per `ensureSystemPages()` bei jedem Boot idempotent angelegt und ist im Admin unter **Inhalte** editierbar.
- **Engine**: synchron via `better-sqlite3` (WAL), automatisches Seeding beim ersten Start.
- **First-Party-Analytics**: eigene `events`-Tabelle, kein externer Dienst (Session = täglich rollender Hash).
- **Branding konfigurierbar**: Shop-Name, Akzentfarbe, Währung u. a. in einer `settings`-Tabelle.
- **Self-hosted Fonts** (Fraunces + Public Sans), kein Google-CDN. Chart.js via cdnjs.
## Authentifizierung & Rollen
- **Session-Login** per HTML-Formular (signiertes HMAC-Cookie, „Angemeldet bleiben" = 30 Tage). Passwörter werden mit `node:crypto.scryptSync` + zufälligem Salt gehasht.
- **Initial-Owner** wird beim ersten Boot aus `ADMIN_EMAIL` / `ADMIN_PASS` angelegt; weitere Nutzer im Admin unter **Nutzer & Zugänge** (Owner-only).
- **Rollen**: `owner` (alles), `redaktion` (Produkte/Inhalte/Marketing/Analytics), `versand` (nur Bestellungen). Navigation und Seiten werden serverseitig nach Rolle gegated.
- **Konfigurierbarer Admin-Pfad** über `ADMIN_PATH` (Default `admin`, z. B. `intern` → Admin unter `/intern`). Direkter Zugriff auf `/admin` wird bei abweichendem Pfad mit 404 blockiert.
- **Audit-Log** (Tabelle `audit`) protokolliert Create/Update/Delete; Ansicht unter **Aktivität (Audit)** (Owner). Login-Rate-Limit: nach 5 Fehlversuchen 60 s Sperre pro IP.
## Umgebungsvariablen (ENV)
| Variable | Beschreibung | Default |
|---|---|---|
| `DB_PATH` | Pfad zur SQLite-Datenbank (wird angelegt) | `./data/hdc.db` |
| `ADMIN_EMAIL` | Initial-Owner-E-Mail (erster Boot) | `admin@example.com` |
| `ADMIN_PASS` | Initial-Owner-Passwort (erster Boot) | `admin` |
| `ADMIN_PATH` | Pfad-Segment des Admin-Bereichs | `admin` |
| `SESSION_SECRET` | HMAC-Geheimnis für Session-Cookies | interner Fallback (in Prod setzen!) |
| `HDC_API_TOKEN` | Bearer-Token für `/api/admin/*`. Leer ⇒ API gesperrt | |
| `STRIPE_PUBLIC_KEY` | Stripe Publishable Key (optional) | |
| `STRIPE_SECRET_KEY` | Stripe Secret Key. Ohne echten Key läuft der Demo-Checkout. | |
| `PAYMENT_PROVIDER` | Zahlungsanbieter erzwingen: `mollie` / `stripe` / `demo`. Leer ⇒ Auto-Wahl | |
| `MOLLIE_API_KEY` | Mollie API-Key (`test_…`/`live_…`). Ohne gültigen Key Demo-Fallback | |
| `MAIL_PROVIDER` | `listmonk` / `smtp` / leer (⇒ Log-Fallback in `email_log`) | |
| `MAIL_FROM` | Absenderadresse ausgehender Mails | |
| `LISTMONK_URL` / `LISTMONK_USER` / `LISTMONK_PASS` / `LISTMONK_TX_TEMPLATE_ID` | Listmonk Transactional-API | |
| `SMTP_HOST` / `SMTP_PORT` / `SMTP_USER` / `SMTP_PASS` / `SMTP_SECURE` | SMTP-Versand (nodemailer) | |
| `CRON_TOKEN` | Bearer-Token für `/api/cron/abandoned` (Warenkorb-Erinnerung). Leer ⇒ Endpoint gesperrt | |
| `ABANDONED_AFTER_MINUTES` | Alter (Minuten), ab dem eine offene Warenkorb-Karte erinnert wird | `30` |
Siehe `.env.example`.
## Lokal starten
```bash
npm install
npm run dev # http://localhost:4321
# oder produktiv:
npm run build
node ./dist/server/entry.mjs
```
Storefront: `/` · Admin: `/admin` (bzw. `/${ADMIN_PATH}`). Erst-Login mit `ADMIN_EMAIL` / `ADMIN_PASS`.
## Block-Builder
Jede Seite (`pages`) hat ein Feld `blocks` (JSON-Array). Der Vollbild-Editor liegt unter `/${ADMIN_PATH}/inhalte/editor/<id>` (Button „Editor" in der Seitenliste). Gespeicherte Blöcke werden vom **Storefront-Block-Renderer** (`src/components/BlockRenderer.astro`) auf `/seite/<slug>` ausgegeben.
## KI-Editierbarkeit (API)
Token-gesicherte JSON-API unter `/api/admin/*` (Header `Authorization: Bearer <HDC_API_TOKEN>`):
- `GET /api/admin` — maschinenlesbares Manifest (Ressourcen, Felder, Block-Typen, Endpunkte).
- `GET /ai-admin.txt` — dieselbe Beschreibung als Klartext für LLMs.
- `GET /api/admin/{resource}` · `GET /api/admin/{resource}/{id}` — lesen.
- `POST /api/admin/{resource}` — Upsert (mit `id` oder `slug` ⇒ Update, sonst Create).
- `DELETE /api/admin/{resource}/{id}` — löschen.
- `POST /api/admin/pages/{id}/blocks` — Block-Array einer Seite setzen.
Schreibbar: `products`, `pages`, `slides`, `popups`, `settings`. Nur lesbar: `orders`, `customers`. Preise in Cent.
```bash
curl -H "Authorization: Bearer $HDC_API_TOKEN" https://shop.example.com/api/admin/products
curl -H "Authorization: Bearer $HDC_API_TOKEN" -X POST https://shop.example.com/api/admin/products \
-H 'Content-Type: application/json' -d '{"name":"Neues Produkt","priceCents":1990,"category":"Test"}'
```
## MCP-Server
Unter `mcp/` liegt ein eigenständiger **Model-Context-Protocol-Server** (stdio), mit dem ein LLM/Agent den Shop über die Admin-API bearbeitet. Tools u. a.: `list_products`, `upsert_product`, `get_page`, `update_page_blocks`, `list_orders`, `update_settings`, `create_page`. Installation, ENV (`HDC_BASE_URL`, `HDC_API_TOKEN`) und Registrierung in Claude/Cowork: siehe [`mcp/README.md`](mcp/README.md).
## Docker / Coolify
```bash
docker build -t hd-commerce .
docker run -p 4321:4321 -v hdc-data:/data \
-e ADMIN_EMAIL=admin@example.com -e ADMIN_PASS=geheim \
-e SESSION_SECRET=langes-geheimnis -e HDC_API_TOKEN=token hd-commerce
```
Das `Dockerfile` (node:22-slim) baut `better-sqlite3` nativ, legt `/data` an und setzt `DB_PATH=/data/hdc.db`. Auf Coolify ein persistentes Volume auf `/data` mounten. HEALTHCHECK prüft `/`.
## Datenmodell
`settings` (inkl. Feature-Flags & `payment_provider`), `products` (inkl. `mwst` / `base_amount` / `base_unit` / `base_price_per`), `orders` (inkl. `discount_code`/`discount_cents`, `tax_cents`/`shipping_cents`/`country`, `payment_provider`/`payment_id`), `customers`, `slides`, `pages` (inkl. `blocks`; System-Seite `404`), `popups` (inkl. `style` / `discount_id`), `discounts`, `discount_redemptions`, `shipping_zones`, `email_log`, `subscribers`, `events`, `media`, `users`, `audit` — alles seed-bar und im Admin pflegbar.
---
> **Hinweis:** „Brittas Nähkiste" ist nur die mitgelieferte Demo-Instanz. Brand (Name, Farben, Texte) ist über **Admin → Einstellungen** anpassbar.
Lizenz: MIT (siehe `LICENSE`).