ab7605a6d249b25d4aea50d9f0f6f7bff6d99823
28
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
ab7605a6d2 |
Der Waechter kennt jetzt die Verwaltungszugaenge von Spieleservern
Der Betreiber hat es angemahnt, waehrend nebenan am Spiele-Panel gearbeitet
wurde: "vergiss nicht die ports an den waechter weiter zu geben."
Nachgesehen und gemessen -- die offenen Ports des Panels standen bereits in
der Basis (25566, 26014), keine Abweichung. Dabei ist etwas anderes
aufgefallen.
AUF 10011 LAUSCHT ts3server.exe AUF ALLEN ADRESSEN, aus C:\Server\TS3, seit dem
09.07.2026. Und eine handangelegte Regel laesst herein:
Regelname: TS3_10011 (TCP)
Richtung: Eingehend
Profile: Domaene, Privat, Oeffentlich
Remote-IP: Beliebig
Aktion: Zulassen
Das ist kein Spielport. Ueber ServerQuery verwaltet man einen TeamSpeak-Server
komplett -- Rechte, Konten, Kennwoerter. Im Panel nebenan ist derselbe Port
deshalb ausdruecklich an 127.0.0.1 gebunden und bekommt KEINE Firewallregel.
DER WAECHTER SCHWIEG DAZU, weil 10011 beim Uebernehmen des Standes schon
dastand -- also genau der Fall, fuer den die HEIKEL-Liste gedacht ist: "Port
3000 war auch schon immer so."
Dazu drei weitere aus derselben Familie, bevor sie hier auftauchen: Minecraft
RCON (25575), Rust RCON (28016) und die Palworld-REST-API (8212, kann kicken
und bannen). Im Panel stehen alle drei auf `veroeffentlichen: 'lokal'`; wenn
einer von ihnen je auf allen Adressen lauscht, ist etwas an einem Server von
Hand eingerichtet worden.
Gemessen nach der Aenderung (-Still): der Waechter meldet 10011 jetzt neben
RPC, SMB, MySQL, RDP und WinRM. Was er weiterhin NICHT sieht, ist UDP -- und
die meisten Spielports sind UDP. Das steht als naechstes an.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
||
|
|
4af8366517 |
fix: der Merker wird erst gesetzt, wenn die Meldung wirklich rausging
Beim ersten echten Meldetest lief das Skript ohne -Webhook. Es hat den Alarm
angezeigt, NICHTS gesendet -- und den Zustand trotzdem als "gemeldet"
abgehakt. Der naechste Lauf MIT Webhook waere still geblieben.
Ein Merker bedeutet "dieser Zustand wurde berichtet". Wenn nichts berichtet
wurde, darf er nicht gesetzt werden -- sonst verschluckt ausgerechnet der
Fehlerfall die Meldung, fuer die das Ganze da ist.
Jetzt vier Faelle, sauber getrennt:
alles gut -> Merker leeren, keine Nachricht noetig
schon gemeldet -> still bleiben
kein Webhook -> sagen, dass NICHTS gemeldet wurde, Merker nicht setzen
Webhook da -> senden; NUR bei HTTP 200/204 den Merker setzen
Der letzte Punkt ist der zweite Teil derselben Sache: schlaegt das Senden fehl,
bleibt der Merker leer und der naechste Lauf versucht es erneut. Eine Meldung,
die im Netz haengengeblieben ist, ist keine Meldung.
Nachgemessen, beide Richtungen:
Alarm ohne Webhook -> "NICHTS gemeldet", Merker vorhanden: False
derselbe mit Webhook -> "Gemeldet (HTTP 204)", Merker gesetzt: True
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
befa393b84 |
fix: das Alter kommt aus dem NAMEN, nicht aus dem Zeitstempel
Der erste echte Lauf auf dem Heimrechner sagte:
d4rk_media-2026-08-11-2307.tar.gz, 0 Tage alt
Ein Archiv vom 11. August, an einem 13. August, mit "0 Tage alt" -- und
ausserdem war es gar nicht das neueste. Beides derselbe Fehler: ich habe nach
LastWriteTime sortiert und gerechnet, und die setzt der Nextcloud-Client auf
den Zeitpunkt des HERUNTERLADENS.
Damit war der Alarm wertlos. Er haette nie ausgeloest, weil frisch geholte
Archive immer taufrisch aussehen -- also genau in dem Fall geschwiegen, fuer
den er gebaut ist.
Jetzt wird das Datum aus dem Dateinamen gelesen (d4rk_media-2026-08-13-0929,
d4rkbot-2026-08-12). Das ist das Datum der SICHERUNG, und es ueberlebt jedes
Kopieren. Faellt der Name aus dem Muster, bleibt der Zeitstempel als Notnagel.
Nachgemessen mit Dateien, die ALLE den heutigen Zeitstempel tragen:
neuestes erkannt: d4rk_media-2026-08-13-0929 0 Tage
dieses entfernt -> d4rkbot-2026-08-12 1.4 Tage
Ausserdem sagt die Zeile jetzt, wieviel in der QUELLE liegt: "0 neu, 3 in der
Ablage, 5 in der Quelle" beantwortet die Frage, ob der Client ueberhaupt schon
durch ist -- "3 insgesamt" allein tat das nicht.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
9e4132cc7e |
fix: Platzhalter erkennen, statt den Rechner damit lahmzulegen
Der Heimrechner ist beim ersten Lauf haengengeblieben. Ursache:
d4rk_media-2026-08-11-2307.tar.gz 327030051 Archive, ReparsePoint
d4rk_media-2026-08-12-0430.tar.gz 352258706 4199968
4199968 = 0x401620:
0x400000 RECALL_ON_DATA_ACCESS beim Zugriff erst herunterladen
0x001000 OFFLINE Inhalt liegt nicht hier
0x000400 ReparsePoint
0x000200 SPARSE_FILE
0x000020 Archive
Der Nextcloud-Client hatte "virtuelle Dateien" aktiviert: im Ordner steht ein
Verweis mit der richtigen GROESSE, aber ohne Inhalt. robocopy fasst alle an,
und jedes Anfassen loest einen Download aus -- bei fuenf Archiven 1,7 GB auf
einmal, waehrend Windows blockierte.
Das Skript prueft das jetzt VORHER und bricht ab, ohne etwas zu kopieren oder
zu loeschen. Mit dem Hinweis, wo man es abstellt, und woran man es im Explorer
sieht: Wolken-Symbol statt gruenem Haken.
An einer Datei mit gesetztem OFFLINE-Attribut nachgemessen -- erkannt und
abgebrochen; ohne das Attribut laeuft es normal durch.
Die Lehre ist dieselbe wie den ganzen Tag: eine Groessenangabe ist keine Datei.
robocopy hat die 336 MB geglaubt, die dastanden, statt zu pruefen, ob sie da
sind.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
89d0ec2b14 |
fix: das Abholskript sagt jetzt, WORAN es haengt
Gemeldet: "danach nur ==> Nachsehen und dann passiert nichts mehr." Zwischen der Ueberschrift und der ersten Ausgabe liefen drei Schritte, die alle drei minutenlang blockieren koennen -- Test-Path, New-Item und Resolve-Path, wenn ein Pfad auf eine Netzfreigabe oder eine schlafende Platte zeigt. Welcher davon stand, war von aussen nicht zu sehen. Jetzt sagt jeder Schritt VORHER, was er tut. Eine Ausgabe, die erst nach dem Blockieren kommt, hilft genau dann nicht, wenn man sie braucht. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
0f3f63749c |
feat: tools/heim-abholen.ps1 -- die Auswaertskopie, die der Server nicht loeschen kann
Der Weg nach draussen fuehrt jetzt ueber die Nextcloud, die ohnehin jede Nacht
ein geprueftes Archiv bekommt: der Desktop-Client zieht den Ordner auf die
Platte zu Hause. Serverseitig ist dafuer nichts noetig -- kein Konto, kein
Schluessel, kein Skript.
WARUM DAS ALLEIN NICHT REICHT: der Client gleicht in BEIDE Richtungen ab.
Verschwinden die Archive auf dem Server -- Trojaner, Fehlgriff, kaputter
Auftrag --, loescht er sie auch zu Hause. Der synchronisierte Ordner ist eine
Kopie, aber keine Sicherung.
Dieses Skript kopiert daraus in einen zweiten Ordner, den der Client nicht
kennt, und bricht damit die Kette: was einmal dort liegt, bleibt liegen, bis
die Aufbewahrungsfrist es wegraeumt. Der Server kann es nicht mehr erreichen.
robocopy OHNE /MIR, und das ist der ganze Punkt -- /MIR wuerde das Ziel dem
Quellordner angleichen und dort Geloeschtes mitloeschen.
Es weigert sich ausserdem, wenn das Ziel IM synchronisierten Ordner liegt: dann
wuerde der Client die Kopien wieder hochladen und alles waere umsonst.
UND ES MELDET SICH, WENN NICHTS MEHR ANKOMMT. Ein Kopierauftrag, der still
aufhoert, ist genau der Schaden, gegen den er gebaut ist: man glaubt, man haette
eine Sicherung, und merkt es an dem Tag, an dem man sie braucht. Gemeldet wird
nur bei Zustandswechsel -- eine taegliche "alles in Ordnung"-Meldung liest nach
einer Woche niemand mehr, und dann auch die eine nicht, auf die es ankommt.
An Wegwerf-Ordnern durchgespielt, alle Faelle einzeln:
drei Archive holen -> 3 neu
Datei in der QUELLE geloescht -> bleibt im Ziel <- der Punkt
Aufbewahrung auf 2 -> aeltestes entfernt
seit 8 Tagen nichts Neues -> Alarm
zweiter Lauf -> bleibt still
neues Archiv kommt an -> Entwarnung
Ziel im Quellordner -> abgelehnt
Der vierte Test hat beim ersten Anlauf nichts gemessen: ich hatte die KOPIEN
gealtert statt der Quelle, und robocopy hat sie korrekt neu geholt. Ein Test,
der das Falsche misst, meldet "in Ordnung" und wird geglaubt.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
6e654e0ef8 |
fix: die Sicherung laedt wieder hoch -- ueber localhost statt ueber Cloudflare
Der naechtliche Lauf scheiterte mit HTTP 413. Nachgemessen mit drei Groessen,
statt geraten:
1 KB -> 201 | 60 MB -> 201 | 340 MB -> 413, "Server: cloudflare"
cdn.d4rkst3r.de laeuft ueber Cloudflare, und das deckelt Uploads im Gratistarif
bei 100 MB. Das Archiv ist 336 MB. Weder der Proxy (client_max_body_size 0) noch
die Nextcloud (16 GB) waren es.
Bestellt war ein eigener Name ohne Cloudflare. Vorher aber geprueft, ob es den
ueberhaupt braucht -- und er braucht es nicht: die Nextcloud laeuft auf
DERSELBEN Maschine. Ueber http://localhost:11000 verlaesst der Verkehr die
Rueckschleife nicht, und "localhost" steht schon in trusted_domains. Kein DNS,
kein Zertifikat, kein Proxy, und nebenbei fuenfzehnmal schneller:
ueber 192.168.100.1:11000 340 MB in 59 s
ueber localhost:11000 340 MB in 4 s
Kein TLS dabei, und das ist in Ordnung: die Verbindung geht nicht hinaus.
Ganzer Lauf zur Gegenprobe: 4630 Eintraege, 335,9 MB, zweite Platte identisch,
hochgeladen 201, zurueckgeholt und verglichen identisch. Statusseite sagt
wieder "sicherung ok an 3 Orten".
WICHTIG, DAMIT NIEMAND ES FALSCH LIEST -- und das steht auch im Skript: diese
Kopie ist KEINE Auswaertskopie. Die Nextcloud liegt auf derselben Platte wie
die Daten. Sie ist die dritte Kopie, nicht der Weg aus dem Haus. Der fehlt
weiterhin.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
f138df44a7 |
feat: tools/waechter.ps1 -- zaehlt offene Tueren und rechnet Dateien nach
Die Statusseite war waehrend des Einbruchs die ganze Zeit gruen. Sie waere auch
gruen geblieben, wenn der Miner gelaufen waere. Es fehlte kein Bildschirm, auf
dem man es haette sehen koennen -- es fehlte etwas, das VON SICH AUS Bescheid
sagt. Deshalb kein Frontend, sondern zwei Pruefungen mit Meldeweg.
1. Ports, die auf allen Adressen lauschen, gegen einen hinterlegten Stand.
Port 3000 stand elf Tage offen; das haette am ersten Tag gemeldet werden
koennen. Dazu eine feste Liste heikler Ports, die auch dann warnt, wenn
der Zustand "schon immer so" war -- 3000 war auch schon immer so.
2. Pruefsummen ueber sshd_config, administrators_authorized_keys, beide
Compose-Dateien und Giteas app.ini und .gitconfig. Der Angriff WAR eine
Datei, die sich um 21:13:57 aenderte.
Gemeldet wird nur bei Aenderung, mit Merker in den Einstellungen. Ein Waechter,
der taeglich meckert, wird stummgeschaltet und ist dann schlechter als keiner.
VOR DEM EINRICHTEN AN BEIDEN FAELLEN BEWIESEN statt behauptet:
Testport 45999 geoeffnet -> "NEU offen: 45999"
Datei geaendert -> Meldung raus, HTTP 204
zurueckgestellt -> "Wieder in Ordnung (Merker geleert)"
Und er prueft zuerst, ob er ueberhaupt etwas sehen kann: findet er keine
Objekte, meldet er "ungueltig" statt "sauber". Das ist die Lehre aus zwei
Suchen in dieser Nacht, die genau das verwechselt haben.
Die Statusseite zeigt den Stand mit; meldet der Waechter sich zwei Stunden
nicht, ist DAS der Befund -- er laeuft alle 15 Minuten.
Im Bericht ausserdem nachgetragen: die Inventur von aussen (NPM-Verwaltung und
MariaDB standen offen, beide jetzt gesperrt), die abgeschalteten Ueberbleibsel
von Caddy und cloudflared, und zwei Irrtuemer von mir -- "Docker geht an der
Firewall vorbei" und "der MariaDB-root hat kein Passwort". Beide waren aus
Messungen geschlossen, die die Frage nicht beantworten konnten.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
70260060c9 |
fix: ein Tabulator im Hilfetext -- der Pfad liess sich nicht kopieren
Die Zeile am Ende zeigte
Get-Content 'C:\Users\...\SSH-Tunnel unnel.log' -Tail 20
statt \tunnel.log. Aus dem Backslash-t ist beim Erzeugen der Datei ein echter
Tabulator geworden -- und der Pfad, den man kopieren soll, fuehrt damit ins
Leere.
Das ist heute Nacht die dritte Ersetzung, die an einer Escape-Ebene
gescheitert ist (nach dem \n im Compose und dem \n des Angreifers, an dem sein
eigener Schadcode zerbrach). Deshalb steht dort jetzt Join-Path statt eines
zusammengesetzten Textes: wo kein Backslash im Text steht, kann auch keiner
umgedeutet werden.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
a36fcec57e |
fix: kein Fenster OHNE Administratorrechte -- VBS-Starter statt S4U
Der S4U-Weg aus dem letzten Commit war richtig gedacht und praktisch unbrauchbar: das Eintragen einer S4U-Aufgabe braucht erhoehte Rechte, und in einem gewoehnlichen PowerShell-Fenster scheitert es mit "Zugriff verweigert". Ein Werkzeug, das Administratorrechte verlangt, benutzt am Ende niemand -- und genau darum ging es ja: was umstaendlich ist, wird umgangen. Jetzt startet die Aufgabe wscript.exe mit einem winzigen VBS, und dessen Run-Aufruf hat den Fenstermodus 0. Das heisst wirklich unsichtbar, im Unterschied zu "-WindowStyle Hidden", das bei Konsolenprogrammen in einer interaktiven Sitzung nur halb wirkt. Besondere Rechte braucht es nicht, weil nichts Besonderes passiert. Der Anmeldetyp bleibt damit Interactive; das Fenster verhindert der Starter und nicht der Anmeldetyp. Das Protokoll aus dem letzten Commit bleibt und wird jetzt erst recht gebraucht: sichtbar ist nichts mehr, also muss nachlesbar sein, was ssh sagt. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
a494f472d1 |
fix: der Tunnel laeuft jetzt OHNE Fenster -- S4U statt Interactive
Gemeldet: "durch dein ssh script habe ich trotzdem ein powershell fenster
offen, gemerkt habe ich das weil ich es gerade geschlossen habe und die
verbindung weg war."
Genau so ist es. -LogonType Interactive laesst die Aufgabe IN der
Anmeldesitzung laufen; ein Konsolenprogramm bekommt dort ein Fenster, und
"-WindowStyle Hidden" haelt es nur halb zurueck. Wer es zumacht, kappt den
Tunnel, ohne zu wissen was er tut -- und das ist keine Randerscheinung, sondern
passiert genau einmal und dann jedes Mal wieder.
S4U ("Service for User") laeuft AUSSERHALB der Sitzung: es gibt gar keinen
Bildschirm, auf dem etwas erscheinen koennte. Auf lokale Dateien darf es
weiterhin zugreifen, und mehr braucht der Schluessel unter %USERPROFILE% nicht.
Ein Passwort muss dafuer nirgends gespeichert werden.
DAZU GEHOERT EIN PROTOKOLL, sonst tauscht man ein stoerendes Fenster gegen eine
stille Fehlfunktion: ohne Sitzung sieht niemand mehr, was ssh sagt. Ein
abgelehnter Schluessel oder ein besetzter Port waere unsichtbar -- der Tunnel
stuende einfach nicht, und niemand wuesste warum. tunnel.log haelt jeden
Verbindungsversuch fest und wird bei 1 MB verworfen.
Das Skript sagt am Ende auch, wie man ohne Fenster nachsieht:
Get-NetTCPConnection -LocalPort 13389 -State Listen, und die letzten Zeilen des
Protokolls.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
15f4c7bc3d |
fix: der Tunnel scheiterte am Benutzernamen, und die Meldung zeigte woandershin
Der Standard fuer -Benutzer war $env:USERNAME -- der Name am HEIMRECHNER. Auf dem Server heisst das Konto anders (hier "az319" gegen "Darkster"). Das Skript lief sauber durch, trug die Aufgabe ein, und der Tunnel kam trotzdem nicht. SCHLIMMER ALS DER FEHLER WAR DIE DIAGNOSE. Am Ende stand fest verdrahtet "Haeufigste Ursache: der oeffentliche Schluessel ist noch nicht hinterlegt" -- eine GERATENE Ursache, und sie war falsch. Wer dem folgt, prueft den Schluessel, findet ihn in Ordnung und sucht weiter an der falschen Stelle. Jetzt wird gemessen: schlaegt der Tunnel fehl, baut das Skript selbst eine SSH-Verbindung auf und wertet aus, was der Server antwortet -- "Permission denied" (dann Name ODER Schluessel, beides benannt), nicht erreichbar, Weiterleitung verweigert, oder ssh kommt durch und es liegt am Ziel-Port. Und -Benutzer hat keinen Standard mehr. Fehlt er, wird gefragt: "Wie heisst dein Konto AUF DEM SERVER? (nicht der Name hier am PC)". Einmal fragen ist besser als einmal in die Irre fuehren. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
b82d315f8d |
feat: tools/tunnel-einrichten.ps1 -- der Tunnel als Hintergrundaufgabe
LAEUFT AUF DEM HEIMRECHNER, nicht auf dem Server.
Seit dem 13.08.2026 ist RDP am Server gesperrt; hinein geht es nur noch durch
einen SSH-Tunnel mit Schluessel. Von Hand hiesse das: vor jeder Sitzung ein
PowerShell-Fenster oeffnen, einen Befehl tippen, das Fenster offen lassen.
DAS HAELT NIEMAND DURCH, UND WAS NIEMAND DURCHHAELT, WIRD UMGANGEN -- am Ende
steht dann wieder ein offener Port. Das Skript macht den Tunnel deshalb zu
etwas, das man nicht sieht: eine Aufgabe, die beim Anmelden startet, sich bei
Abbruch selbst neu aufbaut und kein Fenster zeigt. Danach verbindet man sich
wie immer, nur auf localhost statt auf die Server-Adresse.
Drei Sachen, die es prueft statt sie anzunehmen:
- Ist ein SSH-Client da? Sonst der Nachinstallationsbefehl statt eines
kryptischen Fehlers.
- Gibt es einen Schluessel? Sonst anlegen UND den oeffentlichen Teil gross
ausgeben, damit klar ist, was zu schicken ist.
- Ist der lokale Port frei? Das ist der Fehler, der uns heute Nacht eine
halbe Stunde gekostet hat: 3389 ist unter Windows selbst belegt, die
Verbindung landet dann auf dem EIGENEN PC, und die Meldung lautet
"Konsolensitzung bereits aktiv" -- man sucht den Fehler ueberall, nur
nicht beim Port. Standard ist deshalb 13389.
Die Schleife um ssh ist Absicht: bei Zwangstrennung, Standby oder kurzem
Aussetzer endet ssh, und ohne Schleife waere der Tunnel weg, ohne dass es
jemand merkt -- bis die naechste Verbindung scheitert.
ExitOnForwardFailure=yes gehoert dazu, sonst laeuft ssh scheinbar, waehrend die
Weiterleitung gar nicht steht.
Gedacht auch fuer Mitbenutzer, die sich mit Rechnern nicht auskennen: einmal
ausfuehren, oeffentlichen Schluessel schicken, fertig. Danach sehen sie vom
Tunnel nie wieder etwas.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
ab93e0ae4e |
feat: die Archive lassen sich aus dem Panel holen
Einstellungen -> "Sicherungen holen": die fertigen Archive mit Datum, Groesse
und einem Knopf. Der erste Schritt, der eine Kopie VON DIESEM RECHNER HERUNTER
bringt -- bisher haette man sich dafuer per Fernwartung auf den Server klicken
muessen, und genau daran hat es gelegen. Nicht Unwille, sondern
Umstaendlichkeit.
Der Ordner haengt NUR LESEND in den Container. Ein Dienst, der seine eigenen
Sicherungen loeschen kann, ist genau das Werkzeug, das man einem uebernommenen
Dienst nicht in die Hand geben will. Nachgemessen im Container:
touch /sicherungen/probe -> Read-only file system
Und es gibt NUR EINE FORM VON NAMEN -- nicht "alles ausser .. und /", sondern
ein Muster, das genau auf die eigenen Archive passt. Wer einen Pfad
hineinschmuggeln will, muesste ihn erst so benennen. Das ist der Unterschied
zwischen dicht gemacht und dicht gebaut:
../../etc/passwd -> 404
..%2f..%2fdata%2fmedia.db -> 400
media.db -> 400
<archiv>.tar.gz.evil -> 400
ohne Anmeldung -> 401
Der ganze Weg einmal wirklich gegangen, nicht nur die Kopfzeilen: 336 MB in
15,9 s durch den Proxy, sha256 auf der Platte gleich sha256 heruntergeladen,
tar tzf zeigt 4639 Eintraege. Gestroemt und nicht in den Speicher gelesen --
sonst haetten Dienst UND Proxy die 336 MB gleichzeitig im Arbeitsspeicher.
Jeder Griff steht im Verwaltungs-Verlauf. Geloggt wird der GRIFF und nicht der
Erfolg: ein abgebrochener Download taucht ebenfalls auf, und das ist gewollt --
wer spaeter fragt, wer sich den Bestand geholt hat, will die Anfrage sehen.
Die fuenf neuen Pruefungen sind in tools/rundgang.py mit drin; der steht jetzt
bei 29 in Ordnung, 0 nicht.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
608b3b3097 |
feat: tools/rundgang.py -- 24 Pruefungen gegen den laufenden Dienst
WOZU, wo doch jede Aenderung einzeln gemessen wird: weil sie einzeln gemessen
wird. An einem Tag mit sechs Neustarts fasst niemand mehr alles an, und die
Sachen, die keiner mehr anfasst, sind genau die, die kaputtgehen -- ein WebP,
das nach einem Umbau nicht mehr ausgehandelt wird, meldet sich nicht.
Kein Ersatz fuer die Tests: er prueft nicht, ob eine Funktion das Richtige
rechnet, sondern ob der Dienst, so wie er gerade laeuft, die Sachen noch tut,
die er koennen muss. Deshalb geht er ueber HTTP und nicht am Code vorbei. Nur
lesend -- kein Upload, kein Loeschen, keine Einstellung wird angefasst.
ZUERST kommt, was OHNE ANMELDUNG DICHT SEIN MUSS (acht Wege, alle 401). Das ist
die Sorte Fehler, die man nicht sieht: eine Route, die versehentlich vor dem
Waechter registriert wird, funktioniert praechtig -- fuer alle. Genau das ist
hier schon zweimal passiert.
Dann Bestand, /f/ mit WebP-Aushandlung an derselben Adresse und Vary: Accept,
beide CSV-Exporte auf nackte LF, Papierkorb-Deckel, Sicherungsziele und die
Statusseite.
ZWEI FEHLSCHLAEGE BEIM BAUEN, beide im Test und nicht im Dienst -- sie stehen
als Kommentar drin, damit der naechste sie nicht wiederholt:
1. Ohne Filter zog er vehicles/asbo.webp und verglich WebP mit WebP. Gleiche
Groesse, Test rot, Dienst richtig.
2. Der Filter hiess dann "q=" statt "query=". Unbekannte Parameter ignoriert
der Dienst und liefert ALLES -- was wie ein kaputter Filter aussieht und
ein kaputter Aufruf ist.
Ein Test, der das Falsche misst, ist schlimmer als keiner: er wird geglaubt.
Gemessen: 24 in Ordnung, 0 nicht.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
74e2128afb |
feat: die Sicherung liegt an drei Orten statt an zweien
Der letzte offene Punkt aus docs/ideen.md. Ein ZWEITES LAUFWERK, einstellbar im
Panel unter Einstellungen -> Sicherung; sichern.ps1 legt das Archiv dort ab,
vergleicht die Pruefsumme und duennt auch dort aus.
WARUM DAS NICHT DASSELBE IST WIE DIE NEXTCLOUD. Die hilft, wenn der ganze
Rechner weg ist -- aber wer 335 MB, spaeter 100 GB, zurueckholen muss, laedt sie
ueber die Leitung. Das zweite Laufwerk ist in Minuten zurueckgespielt. Zwei
Fragen, zwei Antworten, deshalb zwei Ziele.
UND ES WARNT, WENN ES DIESELBE PLATTE IST -- das ist der eigentliche Inhalt.
Ein zweiter Ordner auf C: sieht im Panel genauso gruen aus wie eine echte zweite
Platte und hilft gegen gar nichts. Verglichen wird die PHYSISCHE Platte und
nicht der Laufwerksbuchstabe: zwei Partitionen derselben NVMe sterben zusammen.
Nachgemessen in beide Richtungen:
C:\backup gegen D:\backup -> Nr. 0 gegen Nr. 1 -> "andere Platte"
C:\backup gegen C:\Users -> Nr. 0 gegen Nr. 0 -> Warnung
Auf diesem Rechner sind das zwei getrennte NVMe zu je 954 GB.
GEMESSEN AN EINEM ECHTEN LAUF, keinem Trockentest: 4630 Eintraege, 335,9 MB,
auf beiden lokalen Zielen und in der Nextcloud dieselbe Pruefsumme
FFCE4D0A7310... -- identisch.
Geprueft wird mit der Pruefsumme und nicht mit der Dateigroesse: eine
abgebrochene Kopie auf eine volle Platte hat oft genau die richtige Laenge und
trotzdem Nullen am Ende. Schlaegt das Kopieren oder der Vergleich fehl, ist der
ganze Lauf gescheitert (exit 1, ok:false) -- ein Ziel, das still ausfaellt, ist
genau das, wogegen das hier gebaut ist.
Der Bericht traegt jetzt zweit:true. Fehlt das Feld, ist KEIN zweites Ziel
eingerichtet -- es steht absichtlich nicht als false da, denn das laese sich wie
"hat nicht geklappt".
Die Statusseite zaehlt die Orte mit: "sicherung ok an 3 Orten" statt nur "ok".
Eine Sicherung, die es nur einmal gibt, ist gruen und trotzdem eine, die ein
Plattenausfall mitnimmt -- das gehoert dorthin, wo jemand hinsieht.
Der Pfad wird beim Speichern auf seine FORM geprueft (Laufwerksbuchstabe oder
UNC-Freigabe): der Dienst laeuft im Container und kann ihn nicht nachschlagen,
aber ein relativer Pfad ist mit Sicherheit ein Tippfehler -- und ein Tippfehler
in einem Sicherungsziel faellt sonst erst auf, wenn man die Sicherung braucht.
Nachgemessen: "backup/woauchimmer" -> 400 mit Text.
Und zurueckspielen.ps1 sagt im Kopf, was zu tippen ist, wenn genau diese Platte
das Problem ist. Das ist der Fall, fuer den das Ganze da ist, und niemand soll
ihn um vier Uhr nachts erst herleiten muessen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
463a821199 |
feat: der Dienst heisst media.d4rkst3r.de -- beide Namen liefern weiter aus
PUBLIC_URL umgestellt. FILES_HOST ist leer, der Dienst achtet also gar nicht
auf den Hostnamen, und in NPM stehen beide auf DEMSELBEN Host -- die drei
^~-Bloecke gelten damit fuer beide. Nachgemessen: unter beiden Namen health 200
und dasselbe Bild, bytegleich.
Nachgezogen:
7 Stellen in den Ressourcen d4rk_phone/config.lua (2)
d4rk_photostudio/config.upload.lua
d4rk_photostudio/config.upload.example.lua (2)
d4rk_photostudio/settings.json (2)
2 Watchdog-Eintraege im Bot inklusive Anzeigename
16 Stellen in Doku/Werkzeugen README, docs/API.md, Home.md,
npm-advanced.conf, proxy-pruefen.ps1,
uebernehmen.ps1, .env.example
Die Freigabe-Links zeigen von selbst auf den neuen Namen -- sie werden aus
PUBLIC_URL gebaut und stehen nirgends gespeichert. Die alten funktionieren
weiter.
DIE ROADMAP BEHAELT DEN ALTEN NAMEN, an sechzehn Stellen und mit Absicht: dort
steht die Geschichte, warum er ueberhaupt "fivemanage" hiess. Sie zu ersetzen
machte aus einer Begruendung Unsinn.
ZWEI DINGE, DIE DABEI AUFFIELEN.
Cloudflare stand vor dem neuen Namen und brach die WebP-Auslieferung: der
Cache-Schluessel kennt Accept nicht, also bekam ein Aufrufer OHNE
"Accept: image/webp" die WebP-Fassung aus dem Zwischenspeicher -- unsere
eigenen Bytes unter einem .png-Namen. Nach dem Umstellen auf DNS-only gemessen
und in Ordnung: ohne Accept image/png 12295 B, mit Accept image/webp 2270 B,
Server: openresty, vary: Accept, dreimal hintereinander stabil.
Und der Lua-Syntaxtest schlug bei d4rk_phone/config.lua fehl -- an Zeile 93,
`customModel = \`prop_player_phone_02\``. Das ist kein Schaden, sondern eine
CfxLua-Erweiterung: Backticks sind dort ein joaat-Hash und in Standard-Lua 5.4
ein Syntaxfehler. 85 davon stecken in der Datei. luac -p taugt fuer
CfxLua-Dateien also nicht; geprueft wurde stattdessen, dass genau die zwei
URL-Zeilen anders sind.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
3f706b27e1 |
feat: /status fuer die Statusseite -- und der Fehler, den es dabei gefunden hat
/health gab es und bleibt, wie es ist: app.get('/health', c => c.json({ok:true})).
Diese Zeile beweist genau eines -- der Prozess nimmt Anfragen an. Daran haengt
der HEALTHCHECK des Containers, und DORT ist billig richtig: eine schwere
Pruefung, die bei einer langsamen Platte einmal ausfaellt, liesse Docker den
Container neu starten, also genau dann, wenn er unter Last steht.
Fuer eine Statusseite ist das zu duenn -- sie stuende auf Gruen, waehrend die
Platte voll ist und kein Upload mehr angenommen wird.
/status sieht deshalb wirklich nach: Datenbank (eine echte Abfrage, nicht "die
Datei ist da"), Platte (schreiben UND wieder loeschen, der einzige Beweis),
Ausliefern (eine zufaellige Datei aus der Datenbank auf der Platte nachmessen),
Bestand, Platz und Sicherung. 200 wenn der Dienst sein Geschaeft tut, 503 wenn
nicht; ?streng=1 laesst auch eine Beeintraechtigung rot werden -- WELCHES von
beiden richtig ist, weiss nur, wer die Statusseite betreibt.
Oeffentlich, aber wortkarg: keine Dateizahlen, Groessen, Pfade, Tokennamen,
Benutzer. Und mit einer Zehn-Sekunden-Bremse -- ein oeffentlicher Endpunkt, der
auf die Platte schreibt, waere sonst ein Verstaerker.
UND DABEI FIEL EIN FEHLER IN MEINEM EIGENEN ZURUECKSPIEL-SKRIPT AUF.
Der harte Weg sollte an einem Wegwerf-Container geprueft werden. Der traf
zufaellig auf ein GEBRAUCHTES Volume, und die Zahlen waren eindeutig:
media.db aus dem Archiv, mit altem WAL daneben : 0 Zeilen
dieselbe Datei ohne die beiden Begleiter : 4452 Zeilen
Dateien auf der Platte : 4452
zurueckspielen.ps1 entfernte media.db, aber NICHT media.db-wal und
media.db-shm. Die liegen bei einem echten Zurueckspielen immer da -- der
laufende Dienst arbeitet im WAL-Modus. SQLite spielt das WAL der ALTEN
Datenbank ueber die NEUE, und heraus kommt der schlimmste denkbare Zustand: der
Dienst kommt hoch, /health ist gruen, die Mediathek ist leer, waehrend alle
Dateien danebenliegen.
Drei Konsequenzen:
1. Die Aufraeumzeile steht jetzt an EINER Stelle und nimmt media.db-wal,
media.db-shm und *.tmp mit. Uebung und Ernstfall fahren denselben Befehl --
zwei Fassungen waeren zwei, von denen die geuebte die harmlosere ist.
2. Die Uebung TAEUSCHT JETZT EINE BESTEHENDE INSTALLATION VOR, bevor sie
zurueckspielt: Container starten, warten bis media.db-wal daliegt, stoppen,
und erst dann einspielen. In ein leeres Volume zu spielen probt den Fall,
der nie eintritt.
3. /status erkennt den Zustand selbst -- "kein Eintrag in der Datenbank, aber
Dateien auf der Platte". Am kaputten Container gemessen:
/health sagt: HTTP 200
/status sagt: HTTP 503
Der geuebte Lauf danach, ueber eine vorgetaeuschte Installation:
im Volume liegt jetzt: files media.db media.db-shm media.db-wal
4452 Medieneintraege, 4452 Dateien -- gleich viele
ok items/shushi.png · items/weedbud_1.png · items/cc-castella.png
Die Uebung ist bestanden.
Gefunden beim Ueben und nicht im Ernstfall. Genau dafuer gibt es sie.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
338f8e59cf |
feat: die Sicherung laesst sich jetzt zurueckspielen -- und wurde es auch
Bis hierhin war sie GEPRUEFT, nicht ERPROBT. sichern.ps1 packt sein Archiv aus,
oeffnet die Datenbank, zaehlt, laedt hoch, holt zurueck und vergleicht die
Pruefsumme. Das beantwortet "ist die Datei heil?".
Es beantwortet nicht die Frage, auf die es ankommt: "kommt daraus wieder ein
laufender Dienst?" Dazwischen liegen Handgriffe, die man sonst zum ersten Mal
an dem Abend macht, an dem man sie unter Druck macht -- wie das Volume heisst,
dass der Container stehen muss, wem die Dateien gehoeren muessen, dass thumbs/
und webp/ abgeleitet sind und neu gerechnet werden wollen.
Ein Backup, das man nie zurueckgespielt hat, ist kein Backup, sondern eine
Datei.
DIE UEBUNG IST DER STANDARD, und sie ist der eigentliche Gewinn: Wegwerf-
Volume anlegen, Archiv hineinspielen, einen ZWEITEN Container davorhaengen
(ohne veroeffentlichten Port -- der Uebungslauf soll sich mit nichts anlegen,
was schon laeuft), auf /health warten, nachzaehlen, und dann der Teil, auf den
es ankommt: drei zufaellige Dateien DURCH den Dienst holen und ihr SHA-256
gegen den vergleichen, der in der Datenbank steht. Danach alles abraeumen, im
finally -- ein liegengebliebenes Uebungs-Volume mit 300 MB findet sonst niemand
wieder.
Gelaufen, nicht behauptet:
4452 Medieneintraege, 316,3 MB, 2 Token, 2 Benutzer, 0 im Papierkorb
4452 Dateien -- gleich viele wie Eintraege
ok items/velum2.png 113567 B
ok items/flesh-dolphin-2star.png 3623 B
ok items/burgermeat.png 4269 B
Die Uebung ist bestanden.
Der echte Dienst lief die ganze Zeit weiter und war danach unveraendert
(health 200, dasselbe Volume, 4542 Dateien).
DER ERNSTFALL verlangt ein getipptes ZURUECKSPIELEN und sagt vorher, was
passiert. Er loescht thumbs/ und webp/ mit: Abgeleitetes stehenzulassen waere
schlimmer als es neu zu rechnen -- die Galerie zeigte sonst die Vorschau einer
Datei, die dort nicht mehr liegt. Loeschen und Einspielen laufen in EINEM
Container-Aufruf, damit dazwischen niemand den Dienst wieder startet. Geht
etwas schief, sagt das Skript, dass der Container steht, und wie man ihn
startet.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
914f2fb854 |
docs: der NPM-Block zum Einfuegen, und ein Skript, das sagt ob er sitzt
docs/npm-advanced.conf enthaelt genau das, was in NPM unter Advanced ->
Custom Nginx Configuration gehoert: die drei ^~-Bloecke fuer /f/, /t/ und
/api/, samt Begruendung als Kommentar. Die ganze Datei ist einfuegbar --
nginx kennt '#'. Syntax gegengeprueft mit `nginx -t` in einem
Wegwerf-Container, und zwar mit DIESEM Dateiinhalt und nicht mit einer
abgetippten Kurzfassung.
/s/ steht bewusst NICHT drin: die Schluessel bestehen aus einem festen
Alphabet ohne Punkt, eine Freigabe-Adresse endet also nie auf eine
Bildendung und faellt gar nicht erst in die assets.conf-Regel. /assets/
ebenfalls nicht -- die Dateinamen der gebauten Oberflaeche tragen einen Hash
und aendern sich nie unter demselben Namen, dort ist der Zwischenspeicher
richtig.
tools/proxy-pruefen.ps1 misst danach, ob es gewirkt hat, und sagt sonst, was
zu tun ist. Beim Schreiben hat es gleich eine SCHWAECHE IN SICH SELBST
gezeigt: die erste Fassung prueft "/api/ ohne Token -> 401" und meldete
Erfolg -- der 401 war aber nur ein zwischengespeicherter, den der Proxy an
alle ausliefert. Gemessen bekam ihn sogar ein Aufrufer MIT gueltigem Token.
Verlaesslich ist nur das Paar auf einer FRISCHEN, zufaelligen Adresse:
GET /api/exists/probe-xyz.webp mit Token -> 200
GET /api/exists/probe-xyz.webp ohne Token -> 401 gut
-> 200 Block fehlt
Aktueller Stand, gemessen:
[ok] /f/ Cache-Control public, max-age=300
[!!] /t/ Cache-Control max-age=10608
[ok] /api/ mit Token -> 200 HTTP 200
[!!] /api/ dieselbe Adresse ohne Token HTTP 200 -- der Proxy gibt sie weiter
[ok] /api/exists?pfad ohne Token -> 401 HTTP 401
Die letzte Zeile ist der Grund, warum die Abfrageform gebaut wurde: sie ist
dicht, ohne von dieser Proxy-Einstellung abzuhaengen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
06e924daf5 |
fix: die Sicherung liess den Papierkorb aus -- ein Versprechen, das sie nicht halten konnte
GEFUNDEN BEIM NACHSEHEN, nicht beim Suchen: das Sicherungsskript packte
tar czf ... -C /data files -C /data/.sicherung media.db
also die Bilder und die Datenbank. Die EINTRAEGE des Papierkorbs stehen in
media.db und waren damit laengst gesichert -- die DATEIEN lagen unter
/data/papierkorb und waren es nicht.
Nach einem Zurueckspielen haette der Papierkorb also Zeilen gezeigt, deren
"zurueckholen" ins Leere greift: die Oberflaeche verspricht etwas, das die
Platte nicht hergibt. Genau die Sorte stiller Abweichung, wegen der dieses
Projekt Sicherungen ueberhaupt gegenprueft.
Er ist jetzt im Archiv, und er kostet fast nichts: was darin liegt, ist
hoechstens 30 Tage alt, danach raeumt der Dienst selbst auf. Die Abfrage
`if [ -d /data/papierkorb ]` davor ist noetig -- hat noch nie jemand etwas
geloescht, gibt es den Ordner gar nicht, und tar braeche mit "Cannot stat" ab.
Ein `mkdir` als Ausweg ginge nicht: das Volume haengt nur lesend.
Gemessen an einem echten Lauf, nicht angenommen:
d4rk_media-2026-08-11-2307.tar.gz -- 311,9 MB
in der Sicherung: 4452 Medieneintraege, 4452 Dateien, 2 Token, 2 Benutzer
hochgeladen (201), zurueckgeholt und verglichen: identisch
Inhalt: 4459 files/ 1 media.db 1 papierkorb/
UND DER PAPIERKORB SAGT ES JETZT SELBST. Die Liste prueft je Zeile, ob die
Datei wirklich noch daliegt (hoechstens 500 stat-Aufrufe), und zeigt fehlende
durchgestrichen samt Erklaerung. Die Sicherung nimmt ihn zwar inzwischen mit --
aber ein Papierkorb, der etwas verspricht, muss es halten koennen, und wenn
nicht, soll er es SAGEN statt es beim Druecken herauszufinden.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
44ad7dc6b3 |
feat: Umbenennen, Fivemanage-Sprache, Freigabe-Links, sichtbare Zahlen
UMBENENNEN gab es gar nicht -- verschieben ja, umbenennen nirgends. Jetzt fuer
Datei und Ordner. Die WARNUNG ist dabei der eigentliche Teil: beim Verschieben
wandert eine Datei, beim Umbenennen aendert sich IHRE ADRESSE, und die steht
womoeglich in einem Skript, das niemand mehr im Kopf hat. Der Dialog zeigt
deshalb die Abrufzahl und beim Ordner die Zahl der betroffenen Dateien, BEVOR
gedrueckt wird -- eine Datei mit viertausend Abrufen umzubenennen ist etwas
anderes als eine mit null, und der Dienst ist die einzige Stelle, die den
Unterschied kennt.
Am echten Fall geprueft, dem Kollisionsfund: items/coiloverss.png (hielt die
+-Fassung) -> items/coiloverss-plus.png, alte Adresse 404, neue 200 mit 4375 B,
Vorschau mitgewandert. Ordner: probe -> beispiele, 5 Dateien, alle Pfade in
einer Transaktion umgeschrieben.
Ein Fallstrick dabei: thumbPath haengt ".webp" an. Fuer eine Datei richtig, fuer
einen ORDNER Unsinn -- der Vorschauordner heisst thumbs/vehicles und nicht
thumbs/vehicles.webp. Dafuer gibt es jetzt moveFolder.
DIE 93 KOLLISIONEN SIND ERLEDIGT. tools/kollisionen.ps1 rechnet dieselbe
Zaehmung auf der Quelle nach und zeigt, was zusammenfaellt -- es aendert nichts,
damit die Entscheidung auf Zahlen steht. Ergebnis: 91 von 93 sind dieselbe Datei
zweimal (WEAPON_SMG.png und weapon_smg.png, byteweise gleich gross), da fehlte
nichts. Echt verloren waren ZWEI, beide mit + im Namen. Beide nachgeholt.
FIVEMANAGE-SPRACHE. POST /api/image|video|audio (v1) und POST /api/v3/file (v3),
Schluessel nackt im Authorization-Kopf. Auf einem laufenden Server stecken die
Fivemanage-Aufrufe in einem Dutzend fremder Ressourcen; sie alle umzuschreiben
tut niemand, und deshalb bliebe dieser Dienst ungenutzt daneben stehen. So ist
der Umzug eine Zeile je Skript: die Adresse.
Beide Formen sind AUS ECHTEM CODE abgelesen und nicht geraten -- fivemanage/sdk
fuer v3, Awleks/Devm-Camera fuer den aelteren Weg ueber screenshot-basic.
Gemessen: v1 -> {url,id,path}, v3 -> {status:"ok",data:{id,url}}, ohne
Schluessel 401, ein Video an /api/image -> 415 mit Grund.
Dabei bin ich in eine Falle gelaufen, vor der im eigenen Repo ein Kommentar
warnt: die Token-Wache hing an use('*'), und der Einhaengepunkt ist /api -- also
galt sie auch fuer /api/dash daneben. Das Dashboard bekam 401 auf die ANMELDUNG.
Wortwoertlich derselbe Fehler steht in upload.ts als Kommentar, weil er dort
schon einmal passiert ist. Gemerkt hat es der Gegentest, nicht der Kopf.
FREIGABE-LINKS. /s/<schluessel> zeigt einen Ordner ohne Anmeldung. Was dabei
ausdruecklich dabeisteht, in der Karte und in der Rueckfrage vor dem
Zurueckziehen: FREIGEGEBEN WIRD DIE LISTE, NICHT DER INHALT. Die Dateien sind
ohnehin oeffentlich; ein zurueckgezogener Link macht sie nicht wieder privat, er
nimmt nur die Uebersicht weg. Ohne diesen Satz zieht jemand einen Link zurueck
und glaubt, etwas sei verschwunden.
Die Antwort ist abgemessen: Name, Groesse, Art, Adresse, Laenge. NICHT Hash,
Token, Zeitpunkte, Abrufzahlen, IDs -- nichts davon braucht, wer einen Katalog
ansieht, und jedes davon waere eine Auskunft ueber den Betrieb. Ein unbekannter
Schluessel und ein zurueckgezogener geben dieselbe Antwort.
ZAHLEN, DIE SCHON DA WAREN. Sortieren nach Abrufen und nach "zuletzt geholt";
die Kachel zeigt dann auch diese Zahl statt Groesse und Datum, denn nach etwas
zu ordnen, das man nirgends sieht, ist eine Reihenfolge ohne Begruendung. Und je
Token, was damit abgelegt wurde: media.token_id wird seit dem ersten Tag
geschrieben und war NIRGENDS zu sehen. Gemessen: d4rk_photostudio haelt 810
Dateien / 99,5 MB, 3640 liegen ohne Token da.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
e4a125e1c6 |
fix: ein Upload durfte den ganzen Dienst umbringen -- und tat es
BEIM UEBERNEHMEN VON 3670 DATEIEN STARB DER DIENST. Nicht einmal, sondern in
Schleife: acht Neustarts, nach aussen 502 vom Proxy.
TypeError [ERR_INVALID_STATE]: ReadableStream is already closed
at ReadableByteStreamController.close (node:internal/webstreams/...)
DIE URSACHE WAR MEINE EIGENE AENDERUNG von vor einer Stunde. Um PowerShells
Formular-Rumpf zu vertragen, hatte ich `c.req.parseBody()` durch
`c.req.raw.formData()` ersetzt -- und damit Hono den Rumpf weggenommen, den es
selbst verwaltet. Beim Aufraeumen der Antwort schliesst dann jemand einen
Strom, der schon zu ist. Die Ausnahme faellt in einem Microtask an, also
AUSSERHALB jedes try/catch, und Node beendet den Prozess.
Beim Test mit 57 Dateien fiel das nicht auf. Bei 3670 schon.
Jetzt geht der Normalfall wieder ueber Hono; der eigene Weg gilt nur noch fuer
die Antwort mit Anfuehrungszeichen an der Grenzmarkierung, fuer die er gedacht
war.
UND EIN NETZ DARUNTER: process.on('uncaughtException') faengt, was ausserhalb
jedes try/catch anfaellt, und laesst den Dienst weiterlaufen. Die Abwaegung
steht im Code: das ist NICHT in jedem Fall richtig -- der Prozess kann danach
kaputt sein. Hier ueberwiegt das Weiterlaufen, weil dieser Dienst keinen
Zustand im Speicher haelt (alles in SQLite und auf der Platte) und ein Dienst,
der wegen EINER Anfrage fuer alle weg ist, der schlechtere Tausch waere. Laut
wird es trotzdem.
DER SCHADEN WAR REPARIERBAR, und zwar mit dem Werkzeug von heute Nachmittag:
206 Dateien waren durch, 2 lagen auf der Platte OHNE Datensatz (geschrieben,
dann starb der Prozess vor dem Eintrag), dazu eine verwaiste Vorschau. Der
Verwaisten-Finder hat sie gefunden, "aufnehmen" hat sie eingetragen -- keine
Datei verloren.
Und die Fehlermeldung im Uebernahmeskript verschwieg die Ursache: sie meldete
"Conversion from JSON failed ... Unexpected character <", obwohl die Wahrheit
ein 502 war. Jetzt wird der HTTP-Code mitgelesen, bei 502 sauber abgebrochen
und gesagt, dass ein neuer Lauf einfach weitermacht.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
5d35381952 |
feat: Dateien aus der Nextcloud uebernehmen
tools/uebernehmen.ps1 holt einen Nextcloud-Ordner in den Dienst. Unterordner
bleiben erhalten, -Probe zeigt erst nur, was passieren wuerde.
UEBER DAS DASHBOARD UND NICHT UEBER DIE TOKEN-API. Nextcloud-Dateien heissen
"Brand Logo (final).PNG" -- checkPath lehnt das mit Recht ab. Der
Dashboard-Weg biegt den Namen bereits vorhersagbar zurecht. Dieselbe Regel an
einer Stelle ist besser als dieselbe Regel an zweien, von denen eine irgendwann
abweicht.
WIEDERHOLBAR ueber overwrite=false: was schon da ist, meldet der Dienst je
Datei als "Pfad ist belegt". Nachgemessen mit 57 Marken -- zweiter Lauf: 0
uebernommen, 57 waren schon da.
DREI FEHLER BEIM BAUEN, alle durch Messen gefunden:
1. Ein kaputter Formular-Rumpf ergab einen NACKTEN 500, aufgefangen nur von der
Auffanglinie. Jetzt 400 mit Grund.
2. PowerShells `Invoke-RestMethod -Form` scheitert am Dienst. Ich hatte die
Ursache zuerst falsch: nicht (nur) die Grenzmarkierung in
Anfuehrungszeichen, die RFC 2046 erlaubt und die wir jetzt vertragen --
sondern der Rumpf selbst. Mitgeschnitten:
Content-Disposition: form-data; name=file; filename=a.png
RFC 7578 verlangt name="file" MIT Anfuehrungszeichen, .NET laesst sie weg,
und Nodes Parser besteht darauf. Das steht im RUMPF; dafuer braeuchte es
einen eigenen Multipart-Parser, und der waere unverhaeltnismaessig. Die
Werkzeuge nehmen deshalb curl.exe -F.
3. PowerShell-Falle: curl.exe gibt zeilenweise ein ARRAY zurueck, und
-match/-notmatch darauf FILTERT, statt zu pruefen. Ein nicht-leeres Ergebnis
gilt als wahr -- damit meldete das Skript "Kein Verzeichnis gefunden" bei
einer tadellosen Antwort mit HTTP 207. Jetzt wird vorher zusammengefuegt,
auch in sichern.ps1, wo dieselbe Falle nur deshalb nicht zuschlug, weil die
Ausgabe eine Zeile hat.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
66c5de1e31 |
docs: das Wiki gibt es jetzt -- und es kommt aus docs/
Gitea legt das Wiki-Repo erst mit der ersten Seite an; Klon und Push antworten davor mit 500, obwohl has_wiki auf true steht. Angelegt ueber die API (POST /api/v1/repos/<besitzer>/<repo>/wiki/new) -- danach ist es ein gewoehnliches Git-Repo und laesst sich klonen wie jedes andere. https://git.d4rkst3r.de/D4rkst3r/d4rk_media/wiki DIE QUELLE BLEIBT docs/ IM REPO. Ein Wiki hat keinen Zusammenhang mit dem Code: niemand sieht, ob die Anleitung noch zu dem passt, was der Dienst tut. Unter docs/ wandert sie im selben Commit mit der Aenderung, die sie beschreibt. tools/wiki.ps1 traegt sie danach hinaus -- EINSEITIG, und die Wiki-Seite sagt das auch: wer dort tippt, verliert es beim naechsten Lauf. Unschoen, aber ehrlicher als zwei Quellen, die auseinanderlaufen. Zwei Seiten: Home (Zuschnitt, was der Dienst kann, was zu tun ist, wenn etwas nicht geht) und API (Endpunkte, Kopfzeilen, Fehlerantworten, Grenzen -- und die beiden Proxy-Fallen von heute). Nachgemessen: beide Seiten sind ohne Anmeldung lesbar (200), ein zweiter Lauf des Skripts erkennt "bereits aktuell", und eine Aenderung kommt an. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
4e613dd40d |
feat: ein Einstellungen-Tab -- und die Sicherung meldet sich zurueck
Die Zugangsdaten der Sicherung standen in einer Textdatei auf der Platte. Jetzt
stehen sie im Panel, zusammen mit der Discord-Einrichtung, die vom Konto
dorthin umgezogen ist.
DER EIGENTLICHE GEWINN IST NICHT DIE BEQUEMLICHKEIT. Eine Textdatei sagt
niemandem, ob die Sicherung heute Nacht gelaufen ist -- und eine Sicherung, die
still aufhoert zu laufen, ist der eigentliche Schaden, nicht die eine Nacht, in
der sie fehlt. Das Skript schreibt sein Ergebnis deshalb nach jedem Lauf in die
Datenbank, und die Karte zeigt es als Erstes: wann, ob erfolgreich, wie viele
Dateien, wie gross, und ob es in der Nextcloud gelandet ist oder nur lokal.
Nachgemessen: {"dateien":228,"groesse":12063419,"hoch":false,"ok":true}.
DAS PASSWORT GEHT NICHT UEBER DIE API. Das Skript laeuft auf dem Wirt, nicht im
Dienst -- es liest die Werte per `docker exec` DIREKT aus media.db. Ueber HTTP
geht das Passwort nur beim Eintragen im Browser, und das ueber TLS. Zurueck gibt
die API nur "hatPasswort: true", wie beim Discord-Geheimnis.
nextcloud.txt neben den Sicherungen gilt weiter und hat VORRANG: wer sie
angelegt hat, soll nicht suchen muessen, warum sie ploetzlich ignoriert wird.
Zwei Fussangeln beim Melden, beide beim Bauen aufgefallen:
- Der JSON-Text geht ueber die STANDARDEINGABE in den Container und nicht als
Argument -- Anfuehrungszeichen ueberleben den Weg durch zwei Shells nicht.
- Auch dieses Hier-Dokument braucht das -replace "`r": dieselbe CRLF-Falle wie
bei der Pruefung.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
74cf56ce81 |
feat: eine Startseite, und die Sicherung geht in die Nextcloud
STARTSEITE. Wer das Dashboard oeffnet, landete bisher in der Galerie -- mit 228 Kacheln und ohne Antwort auf die erste Frage, die man beim Oeffnen hat: laeuft alles? Jetzt zuerst eine Uebersicht: Zahlen, was heute dazukam, ein Bilderstreifen der letzten Uploads, die letzten acht Schritte aus dem Verlauf, die groessten Ordner, die Zugaenge -- und die ADRESSVORLAGE zum Kopieren, weil das die haeufigste Frage ueberhaupt ist. Die Galerie zieht auf /galerie um. Alles in EINER Antwort (/api/dash/uebersicht, gemessen 14 ms). Vier Einzelabfragen waeren vier Runden ueber den Proxy und koennten auseinander- laufen: Dateizahl aus der einen und letzte Ereignisse aus der naechsten Antwort sind zwei verschiedene Zeitpunkte. Der Router zog dabei nach src/router.ts um. Der Grund ist mehr als Geschmack: die Startseite braucht eine Navigationsfunktion, die anderen Seiten nicht -- und sobald EINE Seite eine Eigenschaft mehr hat, passt die gemeinsame Liste in App.tsx nicht mehr. Genau daran ist der erste Versuch gescheitert (TypeScript hat es gefangen). Als Modulfunktion hat wieder jede Seite dieselbe Signatur. SICHERUNG IN DIE NEXTCLOUD. Bisher lagen die Archive auf derselben Platte wie die Daten -- gegen einen Plattenausfall half das gar nicht. Jetzt gehen sie per WebDAV hinauf, mit curl statt rclone: Nextcloud spricht WebDAV, curl spricht WebDAV, und curl ist ohnehin da. Auch dort wird NACHGEZAEHLT: die Kopie wird zurueckgeholt und die Pruefsumme verglichen. Ein PUT, der 201 sagt, hat nichts bewiesen -- abgeschnittene Uebertragungen sehen genauso aus. Und ausgeduennt wird ebenfalls, sonst waechst die Nextcloud still voll. Die Zugangsdaten liegen NEBEN den Sicherungen (C:\backup\d4rk_media\ nextcloud.txt), nicht im Repo: sie gehoeren zur Maschine, nicht zum Code. Fehlt die Datei, sagt das Skript das einmal und sichert lokal weiter -- eine Sicherung, die wegen eines fehlenden Passworts GAR NICHT stattfindet, waere der schlechtere Tausch. Dabei einmal reingefallen: die Pruefung brach mit "sh: set: Illegal option -" ab, nachdem ein Werkzeug die Datei mit CRLF gespeichert hatte -- die Shell im Container liest dann "set -e\r". Sieht aus wie ein Tippfehler und ist keiner. Das Skript entfernt die Wagenruecklaeufe jetzt selbst. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
aa2575bdf4 |
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> |