docs: Schritt 2 ist gemessen -- das Abbild ist gebaut und gelaufen

Der Bau stand ungeprueft im Repo, und alles daran war Behauptung: ob
`npm ci` die fertige Binaerdatei zieht, ob das Volume greift, ob der
Healthcheck etwas taugt. Jetzt nachgesehen, auf einer Maschine mit Docker
Desktop 29.6.2:

- better-sqlite3 kommt als Binaerdatei, nicht aus node-gyp (datiert Mai 2025,
  kein Makefile daneben). Node 22, ABI 127 -- die Festnagelung traegt.
- Derselbe Durchstich wie in Schritt 1, diesmal gegen den Container: 24
  Pruefungen, keine durchgefallen. Am Grundgeruest war nichts zu aendern.
- Healthcheck meldet healthy; das Volume traegt ueber einen Containerwechsel;
  Compose bricht ohne PUBLIC_URL/ADMIN_PASSWORD ab, wie das :? verspricht.

Neu aufgefallen, weil der Container es sichtbar macht: im Volume liegen VIER
Dinge, nicht zwei. SQLite laeuft im WAL-Modus, und das WAL hielt nach wenigen
Uploads 152 KB, die in media.db noch nicht standen -- eine Sicherung nur ueber
media.db waere unvollstaendig. Steht jetzt bei den offenen Entscheidungen.

Und ein Zeuge weniger: curl taugt fuer die Secure-Cookie-Falle nicht, er
schickt das Cookie auch ueber http und meldet Erfolg. Was zaehlt, ist der
Browser.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-11 16:47:51 +02:00
co-authored by Claude Opus 5
parent 076d909f62
commit 7cc89512d8
+50 -19
View File
@@ -160,7 +160,7 @@ gibt 409, Überschreiben `replaced: true` · 413 vor dem Einlesen · ETag/304,
HEAD, Bereichsanfragen und 416 · unter dem Dateihost gibt es weder API noch HEAD, Bereichsanfragen und 416 · unter dem Dateihost gibt es weder API noch
`/f/`-Präfix · Medienliste, Suche, Statistik nach Ordnern. `/f/`-Präfix · Medienliste, Suche, Statistik nach Ordnern.
Nicht getestet: Docker, das Dashboard (gibt es noch nicht), und echte Last. Nicht getestet: das Dashboard (gibt es noch nicht) und echte Last.
### ✅ Fertig — ein Name statt zwei ### ✅ Fertig — ein Name statt zwei
@@ -176,27 +176,49 @@ Beide Betriebsarten sind durchgemessen: mit einem Namen kommt
Dashboard-Namen zusätzlich unter `/f/` erreichbar, und unter dem Dateihost gibt Dashboard-Namen zusätzlich unter `/f/` erreichbar, und unter dem Dateihost gibt
es weiterhin keine API. es weiterhin keine API.
### ⬜ Als Nächstes ### ✅ Fertig — Schritt 2, das Abbild ist gebaut und gelaufen
**2 · Dockerfile und Compose — geschrieben, aber NIE GEBAUT.** Gebaut, gestartet, durchgemessenauf einer Maschine mit Docker (Desktop
`server/Dockerfile` und `docker-compose.media.yml` stehen. Auf der Maschine, 29.6.2, WSL2). Derselbe Durchstich wie in Schritt 1, diesmal gegen den
auf der sie entstanden sind, gibt es weder Docker noch WSL — das Abbild ist **Container** statt gegen `tsx`: 24 Prüfungen, keine durchgefallen. Am
also ungebaut und der Container nie gelaufen. Das ist der nächste Handgriff, Grundgerüst war nichts zu ändern; der Bau hat keinen einzigen Befund erzeugt.
und er gehört auf eine Maschine mit Docker.
Was daran trotzdem geprüft ist: **So läuft er:**
- **Node 22 ist festgenagelt, mit Grund.** `better-sqlite3` 11.10.0 liefert ```bash
fertige Binärdateien nur für ABI 108 (Node 20), 115, 127 (Node 22) und 131. export PUBLIC_URL=http://localhost:9102 ADMIN_PASSWORD=
**Node 24 hat ABI 137 und fehlt** — der naheliegende Griff zur neuesten docker compose -f docker-compose.media.yml up -d --build
Version fällt auf `node-gyp` zurück und braucht python3, make und g++ im ```
Abbild. Aus den Veröffentlichungen des Projekts abgelesen, nicht geraten.
- **`node dist/index.js`** — der Startbefehl des Abbilds — läuft und antwortet
auf `/health`. Bis dahin war der Dienst nur je über `tsx` gestartet.
- **Der Healthcheck** gibt Exitcode 0 bei laufendem und 1 bei totem Dienst.
Ungeprüft bleibt alles, wofür es Docker braucht: ob `npm ci` im Abbild die **Was jetzt gemessen ist und vorher nur behauptet war:**
Binärdatei zieht, ob das Volume greift, ob der Bau überhaupt durchläuft.
- **`npm ci --omit=dev` zieht die fertige Binärdatei.** Im Abbild liegt
`better-sqlite3/build/Release/better_sqlite3.node`, datiert Mai 2025 —
heruntergeladen, nicht übersetzt; ein `Makefile` oder `obj.target` von
`node-gyp` steht nirgends daneben. `require('better-sqlite3')` läuft im
Laufzeit-Abbild, Node 22.23.2, ABI 127. **Die Festnagelung trägt.**
- **Der Bau läuft durch**, alle drei Stufen. Das Abbild: 373 MB auf der
Platte, 88,4 MB Inhalt.
- **Der Healthcheck greift.** Docker meldet `healthy`, Exitcode 0, kein
Fehlschlag — ohne `curl` im Abbild, `fetch` aus Node reicht.
- **Das Volume greift.** Container weggeworfen, einen neuen an dasselbe
Volume gehängt: die Datei aus dem ersten kommt weiter unter ihrer URL, und
`admin` wird nicht noch einmal angelegt. Nebenbei bestätigt, was im Compose
steht: ein geändertes `ADMIN_PASSWORD` setzt nichts zurück — die alte
Anmeldung gilt (200), die neue nicht (401).
- **Compose bricht ohne Variablen ab**, wie das `:?` verspricht, und zwar mit
dem Text, der danebensteht — erst `PUBLIC_URL`, dann `ADMIN_PASSWORD`. Mit
beiden fährt der Stack hoch und bindet `0.0.0.0:9102->8080`.
- **`Secure` steht wirklich im Cookie**, wenn `NODE_ENV=production` gilt:
`sid=…; Path=/; HttpOnly; Secure; SameSite=Lax`. Ohne Cookie oder mit einem
erfundenen gibt jede Dashboard-Route 401 — auch `/auth/me`.
**Neu aufgefallen, weil der Container es sichtbar macht:** im Volume liegen
**vier** Dinge, nicht zwei — `files/`, `media.db`, `media.db-shm` und
`media.db-wal`. SQLite läuft im WAL-Modus (`db.ts`), und das WAL war nach
wenigen Uploads 152 KB groß, mit Daten, die in `media.db` noch nicht standen.
Eine Sicherung, die nur `media.db` mitnimmt, ist deshalb unvollständig. Siehe
*Offene Entscheidungen*.
> **Beim Ausprobieren:** `NODE_ENV=production` setzt das Sitzungs-Cookie auf > **Beim Ausprobieren:** `NODE_ENV=production` setzt das Sitzungs-Cookie auf
> `Secure`. Wer den Container ohne HTTPS aufruft, bekommt auf die Anmeldung > `Secure`. Wer den Container ohne HTTPS aufruft, bekommt auf die Anmeldung
@@ -204,11 +226,16 @@ Binärdatei zieht, ob das Volume greift, ob der Bau überhaupt durchläuft.
> still verwirft; die nächste Anfrage ist 401. Im Browser gegen eine > still verwirft; die nächste Anfrage ist 401. Im Browser gegen eine
> LAN-Adresse nachgemessen. Die naheliegende Gegenprobe über `localhost` > LAN-Adresse nachgemessen. Die naheliegende Gegenprobe über `localhost`
> führt in die Irre: das gilt Browsern als sicherer Kontext und funktioniert. > führt in die Irre: das gilt Browsern als sicherer Kontext und funktioniert.
> Und `curl` taugt hier nicht als Zeuge — der schickt ein Secure-Cookie auch
> über `http` und meldet fröhlich Erfolg. Was zählt, ist der Browser.
In Portainer aus diesem Repo deploybar — **`Compose path` nimmt EINE Datei**, In Portainer aus diesem Repo deploybar — **`Compose path` nimmt EINE Datei**,
Ergänzungen werden stillschweigend ignoriert. Das hat uns bei Lite einen Ergänzungen werden stillschweigend ignoriert. Das hat uns bei Lite einen
Nachmittag gekostet. Host-Port ist 9102, solange der Lite-Stack 9100/9101 Nachmittag gekostet. Host-Port ist 9102, solange der Lite-Stack 9100/9101
belegt. belegt. **Auf dem Server selbst ist noch nicht deployt** — gebaut und gelaufen
ist auf der Arbeitsmaschine. Das ist Schritt 5.
### ⬜ Als Nächstes
**3 · `server/upload.lua` im Fotostudio.** `Upload.put(pfad, bytes) → url`. **3 · `server/upload.lua` im Fotostudio.** `Upload.put(pfad, bytes) → url`.
Token in `config.upload.lua`, server-only, gitignored, mit committetem Token in `config.upload.lua`, server-only, gitignored, mit committetem
@@ -247,6 +274,10 @@ Lite-Stack als Vergleichsmaßstab laufen.
- **Sicherung.** Ein Volume mit Bildern und `media.db`. Restic gegen die - **Sicherung.** Ein Volume mit Bildern und `media.db`. Restic gegen die
Nextcloud? Oder reicht ein `docker cp` vor größeren Änderungen? Nextcloud? Oder reicht ein `docker cp` vor größeren Änderungen?
**Was dabei feststeht:** wegen WAL gehören `media.db-wal` und `media.db-shm`
mit ins Kopieren, sonst fehlen die letzten Uploads. Sauberer wäre
`sqlite3 media.db ".backup …"` oder ein `wal_checkpoint(TRUNCATE)` davor —
beides ungeprüft, weil noch nicht entschieden ist, wie gesichert wird.
- **Bilder aus dem Spiel** (Screenshots, Clips von Spielern) — dafür brauchte - **Bilder aus dem Spiel** (Screenshots, Clips von Spielern) — dafür brauchte
es Größenbegrenzungen je Token und vermutlich eine Warteschlange. Erst es Größenbegrenzungen je Token und vermutlich eine Warteschlange. Erst
planen, wenn es ansteht. planen, wenn es ansteht.