Files
Webklar-Kundenbereich/docs/superpowers/specs/2026-09-30-preview-runner-design.md
T
2026-09-30 21:53:41 +00:00

136 lines
9.8 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.
# Preview 2.0 – Preview-Runner, Modi, Variablen – Design
Stand: 2026-09-30 · Status: vom Nutzer im Chat freigegeben (Abschnitte 1–3), Spec zur Durchsicht
Teilprojekt **2** von 3 (1 Projektarten – live seit 2026-09-30; 3 Website-Analyse – eigene Spec).
Regel des Nutzers: bauen → live → Nutzer testet live → erst dann das nächste Teilprojekt.
Recherche: `/root/reports/analyse-funktion-2026-09-30/projekttypen-preview.md`.
## Ziel
Previews starten automatisch, auch für vibe-gecodete Projekte, die man mit `npm run dev` startet.
Builds laufen **nicht mehr im Portal-Container** (der Appwrite-, Gitea-, SMTP- und Stripe-Zugangsdaten hat),
sondern in Wegwerf-Containern ohne Zugangsdaten. Projekte können Preview-Variablen bekommen, ohne dass
eine `.env` im Repo liegt.
## Entscheidungen (vom Nutzer)
| # | Entscheidung |
|---|---|
| V1 | Modus **pro Projekt**: `auto` (Vorauswahl) · `build` · `dev` · `off`. `auto` = erst statisch/Build, Dev-Server nur als Ausweg. |
| V2 | Neuer Dienst **Preview-Runner** als eigener Container (Variante A). Nur er darf Docker-Container starten. |
| V3 | **Preview-Variablen pro Projekt** im WOMS, verschlüsselt gespeichert, nur an Build/Dev dieses Projekts. |
| V4 | Build-Fehler ⇒ **letzte funktionierende Version bleibt online** (keine Platzhalterseite mehr). |
| V5 | Dev-Server starten beim ersten Aufruf, stoppen nach **20 min** ohne Aufruf, **max. 3** gleichzeitig. |
| V6 | Kundenportal unverändert (außer der Seite „Preview startet …“ bei schlafendem Dev-Server). |
## 1. Aufbau
```
Gitea-Push ──▶ Portal (webhook) ──Auftrag──▶ Preview-Runner ──▶ Docker
│ │ ├─ Build-Container (Wegwerf)
│ │ └─ Dev-Container (bei Bedarf)
│ ▼
│ /var/www/previews/<sub> (fertige Dateien)
▼
Traefik: <sub>.project.webklar.com ──preview-chain──▶ preview-nginx (static/build)
└────▶ Runner-Proxy (dev)
```
### 1.1 Portal (bleibt Chef)
- Neue Attribute an `websiteProjects` (Schema-Skript `scripts/preview-runner-setup.mjs`, idempotent,
Attributliste mit `limit 200` lesen):
`previewMode` (string 10: auto|build|dev|off; leer = auto), `previewEnv` (string 20000, verschlüsselt),
`previewState` (string 20: online|building|failed|sleeping|starting|off), `previewDetected` (string 40,
z. B. `build:vite:npm`, `static`, `dev:npm`), `previewBuiltSha` (string 40), `previewBuiltAt` (ISO),
`previewError` (string 500).
- Vorauswahl des Modus bei neuer Zuordnung aus dem Projektarten-Katalog (`projectKinds.js`):
Art website/webapp ⇒ `auto`, alle anderen ⇒ `off`. Bestehende Projekte ohne Wert gelten als `auto`.
- `server/services/previewDeploy.js` bekommt einen Schalter `config.previewRunner.enabled`
(`PREVIEW_RUNNER=on|off`, Vorgabe off). `off` = bisheriger Weg unverändert. `on` = Aufträge an den Runner.
- `syncPreviewTls()` schreibt für Projekte im Modus dev (oder `auto` mit Ergebnis dev) den Router auf den
Service `preview-runner@docker` statt `preview-nginx@docker`; Middleware `preview-chain` bleibt gleich
(öffentliche Subdomains wie heute ohne Middleware).
- Verschlüsselung `server/services/previewEnv.js`: AES-256-GCM, Schlüssel `PREVIEW_ENV_KEY` (32 Byte base64)
aus `/home/webklar/apps/.env`; Format `v1:<iv>:<tag>:<ciphertext>` (base64). Namen `^[A-Z_][A-Z0-9_]*$`,
max. 30 Variablen, Wert max. 4096 Byte. Ohne Schlüssel ⇒ Variablen-Funktion aus, Fehlermeldung im WOMS.
- Protokolle: `upload-data/preview-logs/<projektId>/<zeitstempel>.log`, letzte 300 Zeilen je Lauf,
die letzten 5 Läufe je Projekt, ältere löschen. Variablenwerte (≥ 4 Zeichen) werden vor dem Speichern
durch `•••` ersetzt.
- Admin-API (requireAppwriteStaff):
`GET /api/admin/website-projects/:id/preview` (Modus, Status, erkannt, Namen der Variablen, Protokoll-Liste),
`PATCH …/preview` `{ mode }`, `PUT …/preview/env` `{ set: {NAME: wert}, remove: [NAME] }`,
`POST …/preview/rebuild`, `POST …/preview/dev/start|stop`, `GET …/preview/logs/:runId`.
Änderung von Variablen oder Modus ⇒ automatisch Neubau bzw. Dev-Neustart.
### 1.2 Preview-Runner (neuer Container `webklar-preview-runner`)
- Code im Portal-Repo unter `runner/` (eigenes `package.json`, Node 20, keine Abhängigkeit zum Portal-Code).
Compose-Dienst in `/home/webklar/apps/docker-compose.yml`, Image `node:20-alpine` + Docker-CLI,
Mount `build/runner` (read-only), `/var/run/docker.sock`, `/var/www/previews`, Cache-Volume.
`deploy.sh` startet den Runner neu, wenn sich `runner/` geändert hat.
- Netze: `webklar-network` (für Portal und Traefik) und `preview-sandbox` (Dev-Container). **Kein**
veröffentlichter Port.
- Umgebung: nur `PREVIEW_RUNNER_TOKEN`, `GITEA_DEPLOY_TOKEN` (read:repository), Gitea-URL, Limits.
**Keine** Appwrite-/SMTP-/Stripe-Zugangsdaten.
- HTTP-API (Bearer `PREVIEW_RUNNER_TOKEN`, Port 8080 nur intern):
`POST /jobs/build` `{ projectId, repoFullName, branch, sha, subdomain, mode, env, index }` ⇒ `{ runId }`;
`GET /jobs/:runId` ⇒ `{ state, detected, sha, log, error }`;
`POST /dev/:subdomain/start|stop`; `GET /status` (laufende Dev-Server, Warteschlange, freier Speicher).
Nur diese festen Aufträge – keine freien Docker-Befehle.
- **Erkennung (`auto`)**: `index.html` im Wurzelverzeichnis (oder `index` aus `.webklar-preview.json`) und
kein `build`-Skript ⇒ `static`; sonst `build`-Skript vorhanden ⇒ Build; Build fehlgeschlagen oder kein
Build-Skript, aber `dev`-Skript ⇒ `dev`; sonst `static` mit Hinweis. Paketmanager aus Lock-Datei:
`pnpm-lock.yaml` ⇒ pnpm, `yarn.lock` ⇒ yarn, `bun.lockb`/`bun.lock` ⇒ bun, sonst npm (`npm ci`, ohne Lock
`npm install`). Ausgabeordner: `dist`, `build`, `out`, `web-build` (wie heute) bzw. aus `.webklar-preview.json`
(`output`).
- **Build-Container**: `node:20` (bun: `oven/bun`), `--memory 1g --cpus 1 --pids-limit 512 --cap-drop ALL
--security-opt no-new-privileges --user 1000:1000 --network preview-sandbox`, Zeitlimit 15 min, genau **einer**
gleichzeitig (Warteschlange). Quelle per `git clone --depth 1` mit Deploy-Token in ein Arbeitsverzeichnis;
Cache `node_modules` je Repo (Volume, max. 5 GB gesamt, Einträge > 14 Tage löschen). Erfolg ⇒ Ausgabe atomar
nach `/var/www/previews/<sub>` (in `<sub>.new` kopieren, dann umbenennen). Fehler ⇒ vorhandener Ordner bleibt.
- **Dev-Container**: gleiches Sicherheitsprofil, `--memory 512m`, Name `preview-dev-<sub>`, nur im Netz
`preview-sandbox`. Start `npm run dev -- --host 0.0.0.0 --port 5173` (bzw. passender Paketmanager; Port aus
`.webklar-preview.json` `devPort` überschreibbar); Vite bekommt `__VITE_ADDITIONAL_SERVER_ALLOWED_HOSTS=.project.webklar.com`.
Bereit, sobald der Port antwortet (max. 180 s). Leerlauf 20 min ⇒ stoppen (Container entfernen, Cache bleibt).
Max. 3; beim 4. weicht der am längsten ungenutzte. Neuer Push ⇒ Code aktualisieren und neu starten.
- **Runner-Proxy** (Port 8081, Traefik-Service `preview-runner`): leitet `Host: <sub>.project.webklar.com`
inkl. WebSocket (Hot-Reload) an `preview-dev-<sub>:<port>` weiter; läuft keiner ⇒ Start anstoßen und Seite
„Preview startet …“ (deutsch, Neuladen alle 3 s, nach 180 s Fehlermeldung „Preview konnte nicht starten“).
- **Schutzschalter**: freier Speicher (`/proc/meminfo` MemAvailable des Hosts) < 1,5 GB ⇒ keine neuen Builds
oder Dev-Starts; Antwort `503 { error: 'Server ausgelastet' }`, Build bleibt in der Warteschlange.
## 2. WOMS
Block **„Preview“** auf der Projektkarte im Ticket (`TicketProjects`/`AssignedProjectCard`) und im
„Bearbeiten“-Dialog unter Projekte:
1. **Modus**: Automatisch · Build · Dev-Server · Aus; darunter „Automatisch → Build (Vite, npm)“ o. ä. aus
`previewDetected`.
2. **Status**: 🟢 „Online seit TT.MM. HH:MM (Build <sha7>)“ · 🟡 „Baut gerade …“ / „Dev-Server schläft,
startet beim nächsten Aufruf“ · 🔴 „Build fehlgeschlagen – letzte funktionierende Version ist online“.
Knöpfe: Neu bauen · Protokoll ansehen · bei Dev: Jetzt starten / Stoppen.
3. **Protokoll**: letzte 300 Zeilen, aufklappbar, letzte 5 Läufe wählbar.
4. **Preview-Variablen**: Tabelle Name/Wert, Werte nach dem Speichern nur `•••`; Hinweis „Alles mit `VITE_` /
`NEXT_PUBLIC_` ist im Browser sichtbar“.
5. **Services-Seite**: Preview-Punkt (grün/gelb/rot/grau) je Projektzeile.
## 3. Sicherheit, Speicher, Tests, Livegang
Sicherheit und Grenzen wie in Abschnitt 1.2. Zusätzlich: Runner-Eingaben streng prüfen (Subdomain
`^[a-z0-9-]{1,63}$`, Repo `^[\w.-]+/[\w.-]+$`, sha `^[0-9a-f]{7,40}$`), Pfade nur unterhalb der festen Wurzeln.
**Tests**: Runner-Unit-Tests mit Schein-Docker (Auftragsprüfung, Warteschlange, Erkennung, Leerlauf-Stopp,
Verdrängung, Schutzschalter, Proxy inkl. Startseite); Portal (Verschlüsselung, Maskierung, Modus-Vorgaben,
TLS-Router für dev, Schalter off = altes Verhalten); WOMS (Preview-Block-Logik, Playwright gegen Fixtures);
echter Durchlauf auf dem Server mit drei Beispiel-Repos (HTML, Vite-Build, nur `dev`).
**Livegang** (jeder Schritt mit OK des Nutzers):
L1 Netz `preview-sandbox`, Schlüssel `PREVIEW_RUNNER_TOKEN` + `PREVIEW_ENV_KEY` in `.env`, Runner-Dienst in
Compose, Schema-Skript · L2 Portal pushen mit `PREVIEW_RUNNER=off` · L3 Schalter `on`, 12 Previews einzeln neu
bauen und vorher/nachher vergleichen · L4 WOMS pushen · L5 Live-Test des Nutzers mit Dev-Modus.
Rückweg jederzeit: `PREVIEW_RUNNER=off` + Portal neu starten.
## Nicht Teil dieser Spec
Datenbanken/Backends als Beiwagen-Container, Next.js-SSR mit eigenem Start-Modus (`start`), gemeinsame
Standard-Variablen (Variante C), Previews für Handy-Apps (Expo-Web), Website-Analyse (Teilprojekt 3),
„Art ändern“ aus Teilprojekt 1.