Files
sendewerk/README.md
T
2026-10-21 23:52:05 +02:00

179 lines
8.9 KiB
Markdown

# Sendewerk
Sendewerk ist die redaktionelle Webanwendung für den Weg vom vorbereiteten
Inhalt bis zur moderierten und archivierten Sendung. Der aktuelle Stand umfasst
Etappe 1 bis 4: technisches Grundgerüst, Anmeldung und Rollen, einen
persistenten Fundus sowie den vollständigen Sendungsbaukasten. Live-Steuerung
und Präsentationsausgabe folgen in späteren Etappen.
## Voraussetzungen
- Linux
- Node.js 22.17 oder neuer (Node 24 LTS ist für das Cloudron-Image vorgesehen)
- npm 10 oder neuer
Cloudron, Docker, eine Domain und externe Dienste sind für den lokalen Betrieb
nicht erforderlich.
## Frischer lokaler Start
```sh
npm ci
cp .env.example .env
SETUP_TOKEN="$(openssl rand -hex 32)"
sed -i "s/^SENDEWERK_SETUP_TOKEN=.*/SENDEWERK_SETUP_TOKEN=$SETUP_TOKEN/" .env
printf 'Betreiber-Setup-Token: %s\n' "$SETUP_TOKEN"
npm run setup
npm run dev
```
Danach ist die Anwendung unter <http://localhost:5173> erreichbar. `setup`
erstellt die konfigurierten Verzeichnisse, führt ausstehende Migrationen aus
und lädt den reproduzierbaren Beispieldatensatz. Der Seed darf beliebig oft
ausgeführt werden und ersetzt nur die von ihm verwalteten Beispieldaten.
Beim ersten Aufruf verlangt `/setup` den ausgegebenen Wert aus
`SENDEWERK_SETUP_TOKEN` und legt genau einen Barkeeper an. Der Befehl schreibt
den Wert in die nicht versionierte `.env`, damit auch spätere lokale Starts
dieselbe Konfiguration verwenden. Nach erfolgreicher Einrichtung ist `/setup`
dauerhaft geschlossen und der Token kann aus `.env` entfernt werden. Der Seed
enthält bewusst kein Konto und kein Standardpasswort.
## Konfiguration
| Variable | Vorgabe | Bedeutung |
| --------------------------- | ------------------------ | ---------------------------------------------------- |
| `SENDEWERK_DB_PATH` | `.data/sendewerk.sqlite` | Pfad zur persistenten SQLite-Datei |
| `SENDEWERK_UPLOAD_DIR` | `.data/uploads` | Persistentes Upload-Verzeichnis |
| `SENDEWERK_MAIL_OUTBOX_DIR` | `.data/mail-outbox` | Lokale, nicht öffentlich ausgelieferte Testmail |
| `BODY_SIZE_LIMIT` | `6M` | Requestlimit für private Bild-Uploads bis 5 MB |
| `SENDEWERK_SETUP_TOKEN` | keine | einmaliges Betreibergeheimnis, mindestens 32 Zeichen |
| `SENDEWERK_APP_ORIGIN` | `http://localhost:5173` | feste Origin für Links und Cookie-Sicherheit |
| `SENDEWERK_MAIL_TRANSPORT` | `local` | `local` oder ausdrücklich `smtp` |
| `SENDEWERK_MAIL_FROM` | lokaler Absender | Absender; bei SMTP verpflichtend |
| `SENDEWERK_SMTP_HOST` | keine | SMTP-Server, nur im SMTP-Modus |
| `SENDEWERK_SMTP_PORT` | `587` | SMTP-Port |
| `SENDEWERK_SMTP_SECURE` | `false` | `true` für implizites TLS, üblicherweise Port 465 |
| `SENDEWERK_SMTP_USER` | keine | optionaler SMTP-Benutzer |
| `SENDEWERK_SMTP_PASSWORD` | keine | optionales SMTP-Passwort |
| `HOST` | adapterabhängig | Bind-Adresse des Produktionsservers |
| `PORT` | `3000` | Port des Produktionsservers |
Relative Sendewerk-Pfade werden vom Projektverzeichnis aus aufgelöst. In
Cloudron liegen Datenbank und Uploads unter `/app/data`.
Der Vorgabemodus `local` baut keine Netzwerkverbindung auf. Einladungen und
Passwort-Resets landen als JSON-Dateien in `.data/mail-outbox`; die enthaltenen
Links sind Geheimnisse und dürfen nicht veröffentlicht werden. Echter Versand
findet nur statt, wenn `SENDEWERK_MAIL_TRANSPORT=smtp` und die SMTP-Werte
explizit gesetzt sind.
## Inhalte und private Bilder
Neue Gedanken oder Links lassen sich im Eingang schnell erfassen. Im Fundus
stehen leere Pieces sowie Vorlagen für Gespräch, Reaction/Fundstück und
Interaktion bereit. Der Editor speichert Änderungen nach kurzer Pause
automatisch; ein sichtbarer Status meldet Erfolg, Fehler oder eine parallele
Änderung. In diesem Fall muss die Seite neu geladen werden, bevor erneut
gespeichert wird.
Markdown ist in Einleitung, Recherche, Moderation, Überleitung und Präsentation
möglich. Die Ausgabe wird serverseitig auf eine begrenzte Auswahl sicherer
Elemente und Links reduziert. Bilder dürfen höchstens 5 MB groß sein und müssen
als JPEG, PNG, WebP oder GIF erkannt werden. Sie liegen im konfigurierten
Uploadverzeichnis und sind nur über eine angemeldete Route erreichbar.
## Sendungen planen
Unter **Sendungen** lässt sich ein neuer Stream leer, mit dem Standardablauf
oder als Spieleabend anlegen. Abschnitte sind frei benennbar und können eine
explizite Dauer erhalten; andernfalls ergibt sich ihre Dauer aus den
eingeplanten Pieces. Die Reservebank zählt nie zur Planzeit.
Der sendefertige Fundus steht neben dem Ablauf. Pieces können mehrfach
eingeplant und je Verwendung mit eigenem Titel, eigener Dauer und eigenen
Redaktionstexten bearbeitet werden, ohne das Original zu verändern. Abschnitte
und Verwendungen lassen sich per Drag-and-drop sowie gleichwertig mit sichtbaren
Schaltflächen und Auswahlfeldern verschieben. Twitch-Titel und Ankündigung sind
kopierbar. Gleichzeitige Änderungen werden über die Revision der gesamten
Sendung erkannt und nicht still überschrieben.
## Befehle
| Befehl | Zweck |
| -------------------- | ---------------------------------------------------------- |
| `npm run setup` | Verzeichnisse, Migrationen und Seed für lokale Entwicklung |
| `npm run db:migrate` | Ausstehende Migrationen atomar ausführen |
| `npm run db:seed` | Reproduzierbare Beispieldaten laden |
| `npm run dev` | Migrationen ausführen und Entwicklungsserver starten |
| `npm run check` | Svelte- und TypeScript-Prüfung |
| `npm run lint` | Formatierung und ESLint prüfen |
| `npm test` | Verhaltenstests mit temporärer, getrennter Datenbank |
| `npm run test:e2e` | Browserprüfungen gegen eine getrennte E2E-Datenbank |
| `npm run build` | Produktionsartefakt in `build/` erstellen |
| `npm start` | gebautes Artefakt mit `node build` starten |
## Lokaler Produktionslauf
```sh
npm ci
npm run setup
export SENDEWERK_APP_ORIGIN=http://localhost:3000
npm run build
HOST=127.0.0.1 PORT=3000 npm start
```
Der Healthcheck liegt unter <http://localhost:3000/healthz> und prüft auch den
Datenbankzugriff.
Für lokales HTTP wird das Sitzungscookie gezielt ohne `Secure` gesetzt, bleibt
aber `HttpOnly` und `SameSite=Lax`. Bei einer `https:`-Origin ist `Secure`
automatisch aktiv. Die Build-Origin muss beim lokalen Produktionsbuild mit der
später verwendeten Browser-Origin übereinstimmen; SvelteKits Origin-Prüfung
bleibt aktiv.
Vor der ersten Browserprüfung wird der Testbrowser einmalig installiert:
```sh
npx playwright install chromium
npm run test:e2e
```
## Container und Cloudron
Das frühe Cloudron-Paket besteht aus `Dockerfile`, `CloudronManifest.json` und
`start.sh`. Ein lokaler Container-Smoke-Test kann nach dem Build so erfolgen:
```sh
docker build -t sendewerk:local .
docker run --rm -p 3000:3000 \
-e SENDEWERK_APP_ORIGIN=http://localhost:3000 \
-e SENDEWERK_SETUP_TOKEN="$(openssl rand -hex 32)" \
-v sendewerk-data:/app/data sendewerk:local
```
Auf Cloudron muss das Betreiber-Setup-Token vor dem ersten Browseraufruf als
Umgebungsvariable gesetzt werden. `CLOUDRON_APP_ORIGIN` liefert dort die feste
HTTPS-Origin. SMTP-Zugangsdaten werden ebenfalls ausschließlich als
Laufzeitvariablen gesetzt, nicht in Manifest oder Image geschrieben.
Das ersetzt keinen Test auf einem echten Cloudron. Insbesondere Addon-Backup,
Restore, Proxy-Origin und das read-only Laufzeitdateisystem können erst dort
abschließend geprüft werden.
## Dokumentation
- `docs/ARCHITECTURE.md`: Systemgrenzen und geplanter Gesamtdatenbestand
- `docs/DECISIONS.md`: technische Entscheidungen und offizielle Quellen
- `docs/ACCEPTANCE.md`: Abnahmekriterien und konkrete Nachweise
- `docs/STATUS.md`: aktueller Projektstand und bekannte Grenzen
Der Anwendungscode steht unter der MIT-Lizenz. Rechte an später eingestellten
Medien und redaktionellen Inhalten werden dadurch nicht verändert.
Die direkten Laufzeit- und Werkzeugabhängigkeiten verwenden MIT- oder
Apache-2.0-Lizenzen: Svelte/SvelteKit, Vite, Vitest, ESLint und
`better-sqlite3`, `@node-rs/argon2`, Marked, sanitize-html und file-type sind
MIT-lizenziert; Nodemailer verwendet MIT-0, TypeScript und Playwright stehen
unter Apache-2.0. Die Lockdatei fixiert den tatsächlich installierten Baum.