Initial release 0.1.0

This commit is contained in:
hnzio committed 2026-10-21 14:50:31 +02:00
commit 9f97d8407a
52 files changed
+6774

No files matched your search

+38
View File
@@ -0,0 +1,38 @@
# Abnahme
## Etappe 1
| Kriterium | Nachweis | Stand |
| ------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- | ---------------------------- |
| Frischer Checkout startet ohne Cloudron | `npm ci` erfolgreich; README-Setup und lokaler Produktionsstart ausgeführt | erfüllt am 21.10.2026 |
| Typprüfung und Build erfolgreich | `npm run check`, `npm run build` | erfüllt am 21.10.2026 |
| Theme bleibt nach Neuladen erhalten | Playwright: System auf Hell wechseln, Reload, DOM und Schalterzustand prüfen | erfüllt am 21.10.2026 |
| Testdaten überleben Neustart | Gebauten Server zweimal gegen `.data/sendewerk.sqlite` gestartet; Fundusdaten und `/healthz` jeweils vorhanden | erfüllt am 21.10.2026 |
| Migration wiederholt ausführbar | lokal mehrfach ausgeführt; Verhaltenstest führt Migration zweimal in temporärer DB aus | erfüllt am 21.10.2026 |
| Seed reproduzierbar | lokal und im Verhaltenstest zweimal ausgeführt; danach 4 Pieces, 1 Sendung und 3 Verwendungen | erfüllt am 21.10.2026 |
| Tests verwenden getrennte Daten | Vitest und Playwright erzeugen je eigene Datenbank im Betriebssystem-Tempdir | erfüllt am 21.10.2026 |
| Desktop und Smartphone ohne horizontales Scrollen | 4 Playwright-Abläufe; 1440x1000, 1280x900 und 390x844; Screenshots visuell kontrolliert | erfüllt am 21.10.2026 |
| Cloudron-Pfade und Healthcheck vorbereitet | Manifestpfad `/app/data/sendewerk.sqlite`, `/healthz`, `start.sh`; JSON-Kernfelder und Bash-Syntax geprüft | vorbereitet; Container offen |
| Echte Cloudron-Prüfung | Installation, Backup/Restore, Proxy und read-only Laufzeit | offen, kein Serverzugang |
## Ausgeführte Prüfungen
- `npm ci`: erfolgreich, 190 Pakete geprüft, 0 gemeldete Schwachstellen
- `npm run setup`, anschließend erneute Migration und erneuter Seed: erfolgreich
- `npm test`: 2 Verhaltenstests erfolgreich
- `npm run lint`: erfolgreich
- `npm run check`: 0 Fehler und 0 Warnungen
- `npm run build`: erfolgreich mit `@sveltejs/adapter-node`
- `npm run test:e2e`: 4 von 4 Browsertests erfolgreich
- Zwei getrennte `npm start`-Läufe: Healthcheck und persistente Daten erfolgreich
- `bash -n start.sh` und programmatische Manifest-Kernfeldprüfung: erfolgreich
## Nicht erfolgreich oder nicht ausführbar
- `docker build -t sendewerk:local .`: nicht ausgeführt, weil der installierte
Docker-Client keinen laufenden Daemon unter `/var/run/docker.sock` erreichen
konnte.
- Echte Cloudron-Installation: mangels Serverzugang nicht ausgeführt.
- Ein zusätzlicher Python-Playwright-Lauf: nicht gestartet, weil das optionale
Python-Modul `playwright` nicht installiert war. Die projektgebundene
TypeScript-Playwright-Suite lief stattdessen vollständig erfolgreich.
+84
View File
@@ -0,0 +1,84 @@
# Architektur
## Systemgrenze
Sendewerk ist ein einzelner SvelteKit-Prozess. Browserseiten, serverseitige
Actions, spätere REST-Endpunkte und Hintergrundaufgaben verwenden dieselbe
Geschäftslogik. SQLite und ein lokales Upload-Verzeichnis sind die einzigen
persistenten Laufzeitabhängigkeiten. Externe Mail-, RSS- oder KI-Dienste sind
nicht Voraussetzung für Kernfunktionen.
```text
Browser -> SvelteKit-Routen -> Anwendungsdienste -> SQLite
| -> Upload-Verzeichnis
+-> später: Mail/RSS über Adapter
```
Servercode lebt unter `src/lib/server` und darf nicht in Browser-Bundles
gelangen. Routen liefern nur die für ihre Ansicht erlaubten Daten. Fachlogik
wird nicht an Svelte-Komponenten gebunden, damit Browseraktionen und die
spätere API dieselben Regeln verwenden.
## Laufzeit und Speicher
- Lokal: `.data/sendewerk.sqlite` und `.data/uploads`, jeweils konfigurierbar.
- Tests: pro Lauf ein temporäres Verzeichnis unter dem Betriebssystem-Tempdir.
- Cloudron: `/app/data/sendewerk.sqlite` und `/app/data/uploads`.
- Migrationen liegen nummeriert unter `migrations/`, laufen in Transaktionen
und werden in `schema_migrations` protokolliert.
- Der Seed nutzt stabile IDs und Transaktionen. Wiederholung liefert denselben
fachlichen Ausgangsbestand, ohne Migrationstabellen anzutasten.
- Zeitpunkte werden als ISO-8601-UTC-Text gespeichert. Dauern sind nichtnegative
ganze Sekunden. Zeitzonendarstellung ist `Europe/Berlin`.
## Geplantes Gesamtdatenmodell
Etappe 1 materialisiert nur den für echte Vorschau- und Seed-Daten benötigten
Kern. Folgende Grenzen sind für spätere Migrationen bereits festgelegt.
| Bereich | Geplante Entitäten und Beziehungen |
| ----------- | ---------------------------------------------------------------------------------------------------------------------------- |
| Identität | `users`, Rollen, Sessions, Einladungen, Reset-Tokens, Sperren und technische Zugänge; Tokens nur gehasht |
| Taxonomie | getrennte `section_categories`, `formats`, `topics`; Pieces und Vorschläge über Zuordnungstabellen mehrfach verknüpft |
| Inhalt | `pieces` mit Status und Dauer; Quellen, Medien, private Recherche, Moderation, Präsentation und Vorlagen als getrennte Daten |
| Sendungsbau | `shows`, geordnete `show_sections`, kopierte `piece_usages`, Reservebank und Sendungsvorlagen; Original-ID bleibt Referenz |
| Live | expliziter Live-Stand, aktuelles Element, Status je Verwendung, tatsächliche Zeiten und getrennte Präsentationsausgabe |
| Historie | unveränderliche Abschluss-Snapshots und Verwendungshistorie; Übersprungenes zählt nicht als gesendet |
| Freigaben | widerrufbare, gehashte Lesetokens mit Sendung, Umfang, Passwortoption und Ablaufzeit |
| RSS | Feedquellen, Abrufläufe und externe Einträge mit stabiler Herkunfts-ID sowie Status neu/übernommen/verworfen |
| API/Audit | technische Berechtigungen, Idempotenzschlüssel und Audit-Ereignisse mit Akteur, Aktion und Ziel |
| Betrieb | Anwendungseinstellungen und spätere Job-Zustände, keine Geheimnisse im Klartext ohne begründete Schutzstrategie |
### Historien- und Kopierregel
Beim Einplanen entsteht eine bearbeitbare `piece_usage`-Kopie der
sendungsrelevanten Texte und Dauer. Das Original bleibt verknüpft, wird aber
nicht live gespiegelt. Beim Abschluss wird der damalige Sendungsinhalt stabil
eingefroren. Eine historische Sendung wird dupliziert, nie weiterbearbeitet.
### Dauerregel
Eine Abschnittsdauer ist entweder die Summe ihrer eingeplanten Verwendungen
oder eine explizite Abschnittsdauer. Beides wird nie addiert. Reserveelemente
zählen erst nach dem Einplanen. Geplante und tatsächliche Dauer bleiben
getrennt.
## Oberflächenstruktur
Die Hauptnavigation umfasst Eingang, Fundus, Sendungen, Archiv und Verwaltung.
Auf breiten Ansichten stehen Fundus und Ablauf nebeneinander. Auf schmalen
Ansichten werden sie über eine gut erreichbare Umschaltung einzeln gezeigt.
Der Live-Modus ist eine eigene, fokussierte Ansicht; die spätere
Präsentationsausgabe erhält eine separate, strikt reduzierte Datenansicht.
Etappe 1 zeigt Fundus, Sendungsbau und Live-Modus als sichtbare
`Gestaltungsvorschau`. Die Daten kommen bereits aus SQLite, mutierende
Fachabläufe werden jedoch nicht vorgetäuscht.
## Sicherheitsgrenzen ab den Folgeetappen
Authentifizierung und Autorisierung werden serverseitig an jeder Route und
Action geprüft. Markdown und fremdes HTML werden vor Ausgabe bereinigt.
Uploadzugriff folgt denselben Rechten. Netzwerkabrufe erhalten Schutz gegen
interne Ziele, Größen-/Zeitlimits und kontrollierte Redirects. Diese Regeln
sind geplant, aber nicht Bestandteil der Etappe-1-Vorschauen.
+83
View File
@@ -0,0 +1,83 @@
# Entscheidungen
Stand der Quellenprüfung: 21. Oktober 2026. Verwendet wurden offizielle
Dokumentationen und die npm-Registry für konkrete Paket-Metadaten.
## D-001: SvelteKit 3 und Node-Unterstützung
Die Anwendung verwendet SvelteKit 3, Svelte 5, TypeScript und Vite 8. Das
Projekt wurde gemäß aktueller Empfehlung mit `npx sv create` angelegt. Die
Kit-Konfiguration liegt bei SvelteKit 3 in `vite.config.ts`, nicht in einer
`svelte.config.js`.
Die lokale Mindestversion ist Node 22.17. Das Cloudron-Image nutzt Node 24 LTS.
Der vorhandene lokale Lauf wurde mit Node 22.23.1 geplant.
Quellen:
- <https://svelte.dev/docs/kit/creating-a-project>
- <https://svelte.dev/docs/kit/migrating-to-sveltekit-3>
- <https://svelte.dev/docs/cli/sv-create>
- npm-Metadaten für `@sveltejs/kit` 3.1.0 am 21.10.2026
## D-002: Offizieller Node-Adapter
`@sveltejs/adapter-node` erzeugt den Produktionsserver in `build/`. Gebaut wird
mit `npm run build`, gestartet mit `node build`. `HOST` und `PORT` konfigurieren
den Server. Der Adapter behandelt SIGTERM/SIGINT und kontrolliertes Herunter-
fahren; ein eigener Server ist für Etappe 1 nicht erforderlich.
Quelle: <https://svelte.dev/docs/kit/adapter-node>
## D-003: SQLite mit better-sqlite3
`better-sqlite3` 13.x wird als gekapselter synchroner Treiber verwendet. Das
Paket ist MIT-lizenziert und verlangt Node 22 oder neuer. Gegenüber
`node:sqlite` wird damit in Etappe 1 keine API verwendet, die in der offiziellen
Node-24-Dokumentation noch als Release Candidate gekennzeichnet ist. Die
Datenbankgrenze bleibt in `src/lib/server` gekapselt.
Migrationen sind eigene nummerierte SQL-Dateien und laufen transaktional. WAL
wird in Etappe 1 nicht aktiviert; für die kleine Redaktion ist das
Rollback-Journal zunächst ausreichend und vereinfacht die frühe
Betriebsprüfung.
Quellen:
- <https://nodejs.org/docs/latest-v24.x/api/sqlite.html>
- <https://www.sqlite.org/lang_transaction.html>
- npm-Metadaten für `better-sqlite3` 13.0.3 am 21.10.2026
## D-004: Cloudron-Paket
Das Paket nutzt `manifestVersion: 2`, internen HTTP-Port 3000 und `/healthz`.
`localstorage.sqlite.paths` nennt `/app/data/sendewerk.sqlite`, damit Cloudron
eine konsistente Sicherung der aktiven SQLite-Datei erstellen kann. Das
Startskript stellt Verzeichnisse und Besitzrechte her, migriert als Benutzer
`cloudron` und ersetzt sich zuletzt per `exec` durch den Node-Prozess.
Das Image ist auf die am 21.10.2026 offiziell dokumentierte datierte Node-24-
Basis samt Digest gepinnt. Außer `/app/data`, `/run` und `/tmp` wird das
Laufzeitdateisystem als read-only behandelt.
Quellen:
- <https://docs.cloudron.io/packaging/>
- <https://docs.cloudron.io/packaging/manifest/>
- <https://docs.cloudron.io/packaging/addons/#localstorage>
- <https://docs.cloudron.io/packaging/cheat-sheet/>
## D-005: Theme ohne serverseitiges Nutzerkonto
Etappe 1 speichert `light`, `dark` oder `system` in `localStorage`. Ein kurzes
Inline-Skript in `app.html` setzt das resultierende Theme vor dem ersten Paint
auf dem Wurzelelement. Dies vermeidet einen falschen Theme-Blitz und benötigt
vor Etappe 2 weder Cookie noch Konto. Die Einstellung bleibt bei Neuladen im
gleichen Browser erhalten.
## D-006: Etappe-1-Vorschauen statt Attrappen
Vorschauseiten lesen echte persistente SQLite-Seed-Daten, bieten aber keine
funktionslosen Speichern- oder Live-Schaltflächen. Ein sichtbarer Hinweis
grenzt Gestaltungsvorschauen von den späteren Fachfunktionen ab. Damit wird
keine noch nicht implementierte Funktion als fertig dargestellt.
+43
View File
@@ -0,0 +1,43 @@
# Projektstatus
## Aktueller Umfang
Etappe 1 ist abgeschlossen. Architektur, Gesamtdatenmodell, Entscheidungen und
lokaler Betriebsweg sind dokumentiert. Etappe 2 wurde nicht begonnen.
## Umgesetzter Stand Etappe 1
- SvelteKit-/TypeScript-Monolith mit Node-Adapter
- konfigurierbare lokale SQLite- und Uploadpfade
- wiederholbare Migrationen und realistischer Seed
- persistentes Light-/Dark-/System-Theme
- responsive Navigation und gekennzeichnete Vorschauen für Fundus,
Sendungsbau und Live-Modus
- Healthcheck, Produktionsstart und frühes Cloudron-Paket
- getrennte Unit-/Integrations- und Browser-Testdaten
Die Vorschauen lesen echte Daten aus SQLite. Suche und mobile Pane-Umschaltung
funktionieren lokal; redaktionelle Schreiboperationen bleiben bewusst den
späteren Etappen vorbehalten.
## Nicht Bestandteil dieser Etappe
Anmeldung, Rollen, echte Inhaltsbearbeitung, Autospeichern, Sendungsplanung,
Live-Steuerung, Präsentationsfenster, Archivabschluss, Freigaben, RSS und API.
Die Vorschauen dürfen nicht als Implementierung dieser Funktionen verstanden
werden.
## Bekannte offene Betriebsprüfung
Eine echte Cloudron-Installation samt Backup/Restore, Reverse-Proxy-Verhalten
und read-only Laufzeit konnte ohne bereitgestellten Cloudron-Server nicht
durchgeführt werden. Auch der lokale Container-Smoke-Test blieb offen, weil
auf der Prüfmaschine kein Docker-Daemon lief. Manifestkernfelder und
Startskript-Syntax wurden lokal geprüft.
## Verifizierte Umgebung
- Node.js 22.23.1, npm 10.9.8
- SvelteKit 3.1.0, Svelte 5.57.2, Vite 8.3.5
- SQLite-Treiber `better-sqlite3` 13.0.3
- Playwright 1.64.1 mit Chromium 156.0.8078.4 (Fallback-Build für Ubuntu 24.04)