v2.4: Medienbibliothek+WebP, Varianten-Matrix, Litestream-Backups, intelligentere Analytics
P1 Medien: eigener Admin-Bereich /admin/medien (Grid, Mehrfach-Upload, Drag&Drop, Alt-Text, URL kopieren, Loeschen). Upload konvertiert JPG/PNG via sharp zu WebP (Qualitaet 82, max 2000px), Original wird verworfen; WebP/SVG/GIF/AVIF unveraendert; Konvertierungsfehler -> Original behalten statt 500. media um alt/width/height erweitert. Wiederverwendbarer Medien-Picker (public/media-picker.js) ersetzt den URL-Prompt im Block-Editor, Produkt-Editor (Karte/Galerie/Varianten-Bild), Slides und Popups. JSON-Quelle /api/admin/media (session-gesichert). P2 Varianten: products.options_json + Tabelle product_variants. Produkt-Editor mit Options-Definition + Matrix-Generator (Preis-Override/Bestand/SKU/Bild/aktiv je Variante). PDP-Selektoren -> Variante; Cart/Checkout tragen sku+Options, Order-Item bekommt sku/variant, Variantenpreis serverseitig verifiziert. Produkte ohne Optionen unveraendert. P3 Litestream: Binary im Dockerfile, docker-entrypoint.sh (Restore+replicate nur bei LITESTREAM_REPLICA_URL, sonst reiner Node-Start), litestream.yml, Backup-Status unter Einstellungen, README + .env.example. P4 Analytics: Bestseller, Top-Suchbegriffe, Umsatz/Quelle, Umsatz-Zeitreihe, AOV, Wiederkaufrate, Lager-Warnungen. Neue Dep sharp. +19 Unit-Tests (49 gesamt gruen), Build + Smoke (P1-P4) gruen.
This commit is contained in:
@@ -23,6 +23,10 @@ Die mitgelieferte Demo-Instanz heißt **„Brittas Nähkiste"** (Kurzwaren/Nähb
|
||||
- **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 1–5, 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.
|
||||
- **Medienbibliothek + WebP (v2.4):** eigener Admin-Bereich **Medien** (`/admin/medien`) mit Grid (Thumbnail, Dateiname, Größe/Maße, Alt-Text, „URL kopieren", Löschen) und **Mehrfach-Upload** (Drag&Drop). Beim Upload werden **JPG/JPEG/PNG automatisch zu WebP konvertiert** (sharp, Qualität ~82, max-Breite ~2000px); das Original wird verworfen, nur die `.webp` bleibt. WebP/SVG/GIF/AVIF werden unverändert durchgereicht; bei Konvertierungsfehler bleibt das Original erhalten (kein Absturz). Ein **wiederverwendbarer Medien-Picker** (`public/media-picker.js`) ersetzt überall den alten URL-Prompt: Block-Editor (Hero/Bild/Galerie-Mehrfach), Produkt-Editor (Karten-Bild, Galerie, Varianten-Bild), Slides & Popups. `media` um `alt`/`width`/`height` erweitert; JSON-Quelle `/api/admin/media` (session-gesichert).
|
||||
- **Varianten-Matrix (v2.4):** Produkte definieren Optionen (`options` = `[{name, values[]}]`, z. B. Größe × Farbe). Der Produkt-Editor erzeugt daraus die **Varianten-Matrix** (`product_variants`: `sku`, `options_json`, `price_cents`-Override, `stock`, `image`, `active`). Storefront-PDP zeigt Options-Selektoren → wählt Variante → Preis/Bestand/Bild aktualisieren, „nicht lieferbar" bei inaktiv/ausverkauft. Warenkorb & Checkout tragen `sku` + Options; das Order-Item bekommt `sku`/`variant`, der Variantenpreis wird **serverseitig** verifiziert. Produkte ohne Optionen verhalten sich wie bisher (einfache „Größen"-Liste bleibt).
|
||||
- **Intelligentere Analytics (v2.4):** Conversion je Produkt (Ansichten→Käufe), **Bestseller** (Menge/Umsatz), **Umsatz pro Quelle/UTM**, **Top-Suchbegriffe** (`search`-Events von `/suche`, inkl. Null-Treffer-Markierung), **Umsatz-Zeitreihe** (Chart.js, 30 Tage), **AOV**, **Wiederkaufrate** und **Lager-Warnungen** (knappe Produkte & Varianten) — alles aus SQLite aggregiert, kein externer Dienst.
|
||||
- **Litestream-Backups (v2.4):** optionales Streaming-Backup der SQLite-DB nach S3/Backblaze B2. Ist `LITESTREAM_REPLICA_URL` gesetzt, stellt der Container beim Start die DB bei Bedarf wieder her (`litestream restore`) und repliziert dann live (`litestream replicate -exec`); ohne Replica startet die App normal ohne Backup. Status sichtbar unter **Admin → Einstellungen → Backup**. Siehe Abschnitt „Backups".
|
||||
- **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).
|
||||
@@ -110,9 +114,31 @@ docker run -p 4321:4321 -v hdc-data:/data \
|
||||
|
||||
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 `/`.
|
||||
|
||||
Das Image enthält zusätzlich das **Litestream**-Binary und startet über `docker-entrypoint.sh`: ohne Backup-ENV ein reiner `node ./dist/server/entry.mjs`, mit `LITESTREAM_REPLICA_URL` ein `litestream replicate -exec` (nach optionalem Restore).
|
||||
|
||||
## Backups (Litestream → Backblaze B2 / S3)
|
||||
|
||||
Optionales kontinuierliches Streaming-Backup der SQLite-DB. Ohne Konfiguration läuft die App unverändert ohne Backup.
|
||||
|
||||
1. **B2-Bucket anlegen** (Backblaze) und einen Application-Key mit Schreibrechten erzeugen. B2 ist S3-kompatibel.
|
||||
2. **ENV setzen** (Coolify → App → Environment):
|
||||
```
|
||||
LITESTREAM_REPLICA_URL=s3://<bucket>/<pfad>
|
||||
LITESTREAM_ACCESS_KEY_ID=<keyID>
|
||||
LITESTREAM_SECRET_ACCESS_KEY=<applicationKey>
|
||||
LITESTREAM_ENDPOINT=s3.eu-central-003.backblazeb2.com # B2-S3-Endpoint; für AWS S3 leer lassen
|
||||
```
|
||||
(Für AWS S3 genügen `LITESTREAM_REPLICA_URL` + die beiden Keys; `LITESTREAM_ENDPOINT` bleibt leer.)
|
||||
3. **Restore** (z. B. neue Instanz / Disaster-Recovery) — die DB wird beim Start automatisch wiederhergestellt, falls lokal keine existiert. Manuell:
|
||||
```
|
||||
litestream restore -config /app/litestream.yml -if-replica-exists /data/hdc.db
|
||||
```
|
||||
|
||||
Der Status (konfiguriert? Ziel?) ist unter **Admin → Einstellungen → Backup (Litestream)** sichtbar. Hinweis: Die Live-Demo nutzt `DB_PATH=/data/hdc2.db`.
|
||||
|
||||
## 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.
|
||||
`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` (inkl. `alt`/`width`/`height`), `product_variants` (Größe×Farbe-Matrix), `users`, `audit` — alles seed-bar und im Admin pflegbar.
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user