feat: Sicherung, Anmeldebremse, Vorschaubilder -- und Schritt 5
SICHERUNG (tools/sichern.ps1, taeglich 04:30 als geplante Aufgabe). Die Datenbank wird nicht kopiert, sondern ueber SQLites eigene Sicherungsschnittstelle herausgeholt (server/src/backup.ts): im WAL-Modus liegt das Zuletzte noch nicht in media.db, und selbst alle drei Dateien zu kopieren ist nicht sicher, wenn waehrenddessen geschrieben wird. Die Bilder kommen aus einem NUR LESEND eingehaengten Volume dazu. Und sie prueft sich selbst: auspacken, Datenbank oeffnen, Medien/Token/ Benutzer zaehlen, mit dem laufenden Dienst vergleichen, sonst Fehler. Einmal wirklich zurueckgespielt -- leeres Volume, zweiter Dienst, Anmeldung mit dem echten Passwort, Bild abgerufen, Byte fuer Byte identisch. Eine Sicherung, die nie zurueckgespielt wurde, ist eine Hoffnung. ANMELDEBREMSE. Das Formular steht oeffentlich, und scrypt macht einen Versuch teuer -- aber teuer ist nicht selten. Fuenf freie Versuche je Adresse, dann Sperre ab 30 s mit Verdopplung bis 15 min, 429 samt Retry-After und einem Text, der sagt wie lange. Erfolg setzt zurueck. Nur im Speicher: wer sich aussperrt, startet den Container neu. Durchgemessen bis zur Erholung. VORSCHAUBILDER. sharp erzeugt beim Upload eine 320er WebP-Fassung unter /data/thumbs, ausgeliefert unter /t/<pfad>. Gemessen: 131502 -> 13078 Bytes, Faktor 10; eine Galerieseite faellt von 7,5 MB auf 766 KB. KEINE Spalte in der Datenbank -- ob es eine Vorschau gibt, sagt das Dateisystem, und die Galerie faellt bei 404 aufs Vollbild zurueck. Eine zweite Wahrheit, die auseinander- laufen kann, gibt es damit gar nicht erst. Ein Knopf zieht Fehlendes nach und nennt Zahlen statt "fertig". SCHRITT 5. Die Fivemanage-Dateien liegen in legacy/ mit Erklaerung; unsere docker-compose.media.yml heisst jetzt docker-compose.yml. Drei Volumes und drei Abbilder geloescht, rund 670 MB -- nachgezaehlt war vorher, dass kein Bild darin lag. Nebenbefund: `npm i sharp` scheitert auf diesem Rechner nicht an sharp, sondern an better-sqlite3 -- der Host laeuft auf Node 24 (ABI 137), node-gyp uebernimmt und findet keine Bauwerkzeuge. Genau die Falle aus dem Dockerfile. Eingetragen wurde mit --package-lock-only, gebaut wird im Abbild auf Node 22. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+84
-7
@@ -201,7 +201,7 @@ Grundgerüst war nichts zu ändern; der Bau hat keinen einzigen Befund erzeugt.
|
||||
|
||||
```bash
|
||||
export PUBLIC_URL=http://localhost:9102 ADMIN_PASSWORD=…
|
||||
docker compose -f docker-compose.media.yml up -d --build
|
||||
docker compose up -d --build
|
||||
```
|
||||
|
||||
**Was jetzt gemessen ist und vorher nur behauptet war:**
|
||||
@@ -407,6 +407,81 @@ location ^~ /f/ {
|
||||
Dass `^~` nötig ist und ein blankes `location /f/` nicht reicht, ist in einem
|
||||
Wegwerf-nginx nachgemessen: ohne `^~` gewinnt die Regex, mit `^~` das Präfix.
|
||||
|
||||
### ✅ Fertig — Sicherung, Anmeldebremse, Vorschaubilder
|
||||
|
||||
**Sicherung.** `tools/sichern.ps1`, täglich um 04:30 als geplante Aufgabe
|
||||
(nur bei angemeldetem Benutzer — Docker Desktop läuft ohnehin nur dann).
|
||||
|
||||
Die Datenbank wird **nicht kopiert**, sondern über SQLites eigene
|
||||
Sicherungsschnittstelle herausgeholt (`server/src/backup.ts`, `db.backup()`).
|
||||
Der Grund steht weiter oben: im WAL-Modus liegt das Zuletzte noch nicht in
|
||||
`media.db`, und selbst alle drei Dateien zu kopieren ist nicht sicher, wenn
|
||||
währenddessen geschrieben wird. Die Bilder kommen aus einem **nur lesend**
|
||||
eingehängten Volume dazu, alles in ein `tar.gz`.
|
||||
|
||||
**Und die Sicherung prüft sich selbst:** sie wird ausgepackt, die Datenbank
|
||||
geöffnet, Medien, Token und Benutzer gezählt und mit dem laufenden Dienst
|
||||
verglichen. Stimmt es nicht, endet das Skript mit Fehler.
|
||||
|
||||
**Einmal wirklich zurückgespielt**, nicht nur behauptet: in ein leeres
|
||||
Wegwerf-Volume ausgepackt, ein zweiter Dienst darauf gestartet, mit dem echten
|
||||
Passwort angemeldet, 21 Dateien vorgefunden, ein Bild abgerufen — Byte für Byte
|
||||
identisch mit dem Betrieb.
|
||||
|
||||
Nicht mitgesichert werden die Vorschaubilder: sie sind abgeleitet und lassen
|
||||
sich mit einem Knopf neu rechnen.
|
||||
|
||||
**Anmeldebremse.** Das Formular steht öffentlich; `scrypt` macht einen Versuch
|
||||
teuer, aber teuer ist nicht selten. Jetzt: fünf freie Versuche je Adresse, dann
|
||||
Sperre ab 30 Sekunden mit Verdopplung bis 15 Minuten, `429` samt `Retry-After`
|
||||
und einem Text, der sagt, wie lange noch. Ein Erfolg setzt den Zähler zurück.
|
||||
Bewusst nur im Speicher — wer sich aussperrt, startet den Container neu.
|
||||
|
||||
Durchgemessen: Versuche 1–5 geben 401, ab dem sechsten 429 mit „noch 30
|
||||
Sekunden warten", nach Ablauf geht das richtige Passwort, und danach gibt ein
|
||||
Fehlversuch wieder 401 statt 429.
|
||||
|
||||
> Die Adresse kommt aus `X-Real-IP`, den NPM mit `$remote_addr`
|
||||
> **überschreibt**. Wer den Container direkt auf seinem Port erreicht, kann sie
|
||||
> sich ausdenken — genau deshalb gehört 9101 hinter die Firewall.
|
||||
|
||||
**Vorschaubilder.** `sharp` erzeugt beim Upload eine 320 Pixel breite
|
||||
WebP-Fassung unter `/data/thumbs`, ausgeliefert unter `/t/<pfad>`. Gemessen an
|
||||
`vehicles/sultan.webp`: **131 502 → 13 078 Bytes, Faktor 10.** Eine
|
||||
Galerieseite mit 60 Kacheln fällt damit von 7,5 MB auf 766 KB.
|
||||
|
||||
Keine Spalte in der Datenbank: ob es eine Vorschau gibt, sagt das Dateisystem,
|
||||
und die Galerie fällt bei 404 auf das Vollbild zurück. Eine zweite Wahrheit,
|
||||
die auseinanderlaufen kann, gibt es damit gar nicht erst. Was vor dieser
|
||||
Funktion hochgeladen wurde, zieht ein Knopf unter *Speicher* nach — er nennt
|
||||
Zahlen, nicht nur „fertig" (21 erzeugt, 0 Fehler beim ersten Lauf).
|
||||
|
||||
Das Abbild wächst dadurch von 373 auf 457 MB. `sharp` bringt fertige
|
||||
Binärdateien für `linux-x64` mit; im Abbild nachgemessen: libvips 8.18.3,
|
||||
ein 800×600-PNG wird zu einem 320er WebP.
|
||||
|
||||
> **Nebenbefund beim Einbauen:** `npm i sharp` scheitert auf diesem Rechner —
|
||||
> aber nicht an sharp, sondern an **better-sqlite3**. Der Host läuft auf Node
|
||||
> 24 (ABI 137), und dafür gibt es keine fertige Binärdatei; `node-gyp`
|
||||
> übernimmt und findet keine Bauwerkzeuge. Genau die Falle, die im Dockerfile
|
||||
> steht. Umgehung: `npm i sharp --package-lock-only` trägt es nur ein, gebaut
|
||||
> wird im Abbild auf Node 22.
|
||||
|
||||
### ✅ Fertig — Schritt 5, der Umzug
|
||||
|
||||
Die Fivemanage-Dateien liegen in `legacy/` mit einer Erklärung daneben; unsere
|
||||
`docker-compose.media.yml` heißt jetzt schlicht **`docker-compose.yml`** (in
|
||||
Portainer also `Compose path: docker-compose.yml`). Gelöscht: drei Volumes und
|
||||
drei Abbilder, rund **670 MB**. Nachgezählt war vorher, dass kein einziges Bild
|
||||
darin lag.
|
||||
|
||||
**Was noch bei dir liegt** — an NPM komme ich ohne Zugang nicht:
|
||||
|
||||
1. Den Schnipsel `location ^~ /f/ { proxy_cache off; … }` einsetzen (siehe
|
||||
oben), sonst bleiben überschriebene Bilder 30 Minuten alt.
|
||||
2. Den Proxy-Host `fivecdn.d4rkst3r.de` löschen — er zeigt auf den toten Port
|
||||
9100 und gibt 502.
|
||||
|
||||
### ⬜ Als Nächstes
|
||||
|
||||
**5 · Umzug.** Root-`docker-compose.yml` wird unsere; die Fivemanage-Dateien
|
||||
@@ -440,12 +515,14 @@ Leergewicht). **Löschen kostet nichts**, es ist nur noch nicht getan.
|
||||
|
||||
## Offene Entscheidungen
|
||||
|
||||
- **Sicherung.** Ein Volume mit Bildern und `media.db`. Restic gegen die
|
||||
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.
|
||||
- ~~**Sicherung.**~~ **Entschieden und gebaut** — `tools/sichern.ps1`, täglich
|
||||
um 04:30, siehe oben. Weder Restic noch `docker cp`, sondern SQLites eigene
|
||||
Sicherungsschnittstelle plus ein `tar.gz` aus einem nur lesend eingehängten
|
||||
Volume. Die Vermutung von damals war richtig: ein blosses Kopieren wäre wegen
|
||||
des WAL still unvollständig gewesen.
|
||||
**Offen bleibt das Ziel:** die Archive liegen auf derselben Platte wie die
|
||||
Daten. Gegen einen Plattenausfall hilft das nicht — dafür müssten sie in die
|
||||
Nextcloud oder auf eine andere Maschine.
|
||||
- **Bilder aus dem Spiel** (Screenshots, Clips von Spielern) — dafür brauchte
|
||||
es Größenbegrenzungen je Token und vermutlich eine Warteschlange. Erst
|
||||
planen, wenn es ansteht.
|
||||
|
||||
Reference in New Issue
Block a user