9e4132cc7e7ea6064c52005148f1bc030c9c22d7
11
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
381feade4e |
feat: fehlende Bilder finden, Massen-Umbenennen, Bilder verkleinern, Einstellungen mitnehmen
FEHLENDE BILDER. Die Frage, die bisher niemand beantwortet hat: das Fotostudio weiss, was es aufgenommen hat, der Dienst weiss, was liegt, und die Differenz rechnet keiner aus -- weshalb "das Handy zeigt bei manchen Autos kein Bild" erst im Spiel auffaellt. POST /api/dash/fehlende nimmt ein Muster mit genau EINEM Platzhalter und eine Liste (als Array oder als Textblock aus einem Textfeld) und gibt beide Richtungen zurueck: was fehlt, und was daliegt ohne in der Liste zu stehen. Beides bedeutet etwas anderes -- fehlende sind Arbeit fuers Fotostudio, ueberzaehlige sind Platz, den man zurueckbekommt. Gesucht wird KLEINGESCHRIEBEN, weil so abgelegt wird: wer "WEAPON_SMG" hereinreicht, meint weapon_smg.png und bekaeme sonst ein "fehlt", das gelogen waere. Gemessen an echten Namen: 6 geprueft, 4 da, 2 fehlen, 985 ueberzaehlig. MASSEN-UMBENENNEN mit Vorschau. Bei 3578 Items ist eine Umbenennung von Hand keine. DIE VORSCHAU IST DER EIGENTLICHE TEIL: was hier passiert, ist dieselbe Warnung wie beim Ordner-Umbenennen -- jede alte Adresse gibt danach 404 --, nur mal tausend. Geprueft wird VOR dem ersten Schritt: Kollisionen untereinander, belegte Ziele, unzulaessige Pfade. Und EIN Problem verhindert ALLES; eine halbe Umbenennung ist der Zustand, aus dem niemand mehr herausfindet, weil die Grenze nirgends steht. Gemessen: Vorschau ueber 134 Dateien fasst nichts an; eine erzwungene Kollision gibt 409 und laesst alle drei Dateien stehen; der saubere Lauf benennt zwei um, Vorschau wandert mit, ein Eintrag im Verlauf statt zwei. BILDER BEIM ABLEGEN VERKLEINERN, je Token einstellbar. NICHT als Schutz -- das waere gemessen falsch, ein 12000x12000-PNG kostet 374 ms und 47 MB, weil libvips kachelweise arbeitet. Es geht um Plattenplatz und Ladezeit: ein Bildschirmfoto ist 3840 breit und wird auf einem Handy auf 400 gezeigt. withoutEnlargement ist dabei entscheidend, sonst blaeht die Einstellung kleine Icons auf. DABEI WAERE MIR EIN ECHTER FEHLER UNTERLAUFEN: der sha256 wird weiter oben aus dem Original gerechnet (fuer den Ersatzpfad, wenn kein X-Path kam). Wird das Bild verkleinert, ist das eine ANDERE Datei -- ein Datensatz mit dem Hash des Originals waere schlicht falsch, und jede spaetere Gegenprobe verglichen gegen etwas, das nirgends liegt. Gemessen nach der Korrektur: 3000x2000 -> 512x341, 82 KB -> 3 KB, und Datenbank und Platte stimmen in Groesse und Hash ueberein. EINSTELLUNGEN MITNEHMEN. Export als JSON, Import zurueck. GEHEIMNISSE GEHEN STANDARDMAESSIG NICHT MIT: unter den Schluesseln liegen das Discord-Client-Geheimnis und das Nextcloud-Passwort, und eine Datei, die man "mal eben" exportiert und in einen Chat zieht, ist genau der Weg, auf dem so etwas abhandenkommt. Wer sie braucht, sagt ?geheim=1 -- und der Hinweis steht dann IN der Datei, nicht nur auf dem Bildschirm, von dem er beim Weiterreichen abfaellt. Der Export mit Geheimnissen steht im Verwaltungs-Verlauf. Beim Import ueberschreiben LEERE WERTE NICHTS: eine Datei ohne Geheimnisse -- der Normalfall -- traegt sie als leere Zeichenkette, und die darf ein vorhandenes Passwort nicht loeschen. Gemessen: nach einem Import mit leerem client_secret stehen die 32 Zeichen unveraendert da. Nicht mitgenommen wird sicherung.letzte -- das ist der Bericht des letzten Laufs, kein Einstellungswert. In eine frische Anlage eingespielt behauptete er dem Wachhund eine Sicherung, die es dort nie gab. 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>
|
||
|
|
d125c6bf72 |
feat: Vorschau fuer Video und Ton, Ordner anlegen dort, wo man es sucht
Mit den ersten echten Clips fiel auf, was bei 3600 Bildern nie auffiel: alles,
was kein Bild ist, bekam ein FRAGEZEICHEN auf die Kachel -- ausgerechnet das
Zeichen fuer "unbekannt" auf einer voellig gesunden MP4.
Jetzt: ein Standbild aus dem Clip (ein Zehntel hinein, weil viele mit Schwarz
oder einem Einblender beginnen und eine schwarze Kachel so aussagekraeftig ist
wie gar keine), bei Ton das eingebettete Titelbild, dazu ein Abspielzeichen und
die LAUFZEIT in der Ecke. Fehlt eine Vorschau, kommt ein Zeichen, das die ART
benennt, statt eines Fragezeichens.
DER PREIS IST FFMPEG IM ABBILD, und der wurde zweimal gemessen und zweimal
gedrueckt:
vorher 460 MB
apt-get install ffmpeg 1,11 GB
statisches ffmpeg + ffprobe 835 MB
statisches ffmpeg allein 648 MB
Debians Paket schaltet jeden Filter frei und zieht die Abhaengigkeiten mit:
libllvm15 (114 MB), Mesa samt libgl1-mesa-dri, libz3 und libflite1 -- ein
SPRACHSYNTHESIZER. Alles fuer Filter, die ein Dienst ohne Bildschirm nie
anfasst; --no-install-recommends half nicht, es sind harte Abhaengigkeiten.
ffprobe flog danach auch noch raus: 135 MB fuer eine einzige Aufgabe, die
Laenge zu nennen -- die steht auch im Kopf, den ffmpeg beim Oeffnen druckt.
Gegengeprueft an beiden Dateien, auf zwei Nachkommastellen identisch:
ffprobe 29.767967 | ffmpeg Duration: 00:00:29.77
ffprobe 645.642449 | ffmpeg Duration: 00:10:45.64
Der Preis dafuer ist ebenfalls zu benennen: die Fassung haengt jetzt an einer
Zeile im Dockerfile und nicht mehr an apt. Vertretbar, weil ffmpeg hier nur
eigene, bereits abgelegte Dateien anfasst und nicht am Netz haengt.
EIN FEHLER, DER ZWEIMAL DERSELBE WAR. "Keine Bildspur" ist bei Ton der
Normalfall -- die meisten MP3 tragen kein Titelbild. Der erste Anlauf erkannte
das am WORTLAUT der Fehlermeldung, und der ist zwischen den Fassungen anders:
ffmpeg 5.1 "Stream map '0:v:0' matches no streams."
ffmpeg 7.1 "Failed to set value '0:v:0' for option 'map'"
Ergebnis: eine gesunde MP3 wurde als "1 fehlgeschlagen" gezaehlt, und dann
sucht jemand einen Fehler, den es nicht gibt. Gelesen wird jetzt die
SPURENLISTE ("Stream #0:1: Video:"), die seit jeher so dasteht -- und die kommt
aus demselben Aufruf wie die Laenge, also ein Blick in die Datei statt drei.
Gemessen: fehler 0, ohneBild 1.
Beide Wege durchgeprueft. Video: 320x180, Durchschnittsfarbe RGB(97,140,155) --
echtes Bild und kein schwarzer Vorspann. Ton mit angehaengtem Titelbild:
Vorschau erzeugt, dauer 8.05, Kachel zeigt 0:08.
UND ZWEI ORDNER-LUECKEN, beide beim Benutzen gefunden.
"Wie lege ich neue Ordner an?" -- gar nicht, ausser man wusste es: der Knopf
sass NUR im Verschieben-Dialog. Wer einen leeren Ordner wollte, musste erst
Dateien auswaehlen und "Verschieben" druecken. Eine Funktion, die man nur ueber
eine andere Funktion erreicht, ist fuer den, der sie sucht, nicht vorhanden.
Jetzt steht sie unter dem Ordnerbaum, mit der Angabe, IN welchem Ordner sie
entsteht -- sonst tippt jemand "waffen", landet in "items/waffen" und sucht
danach an der Wurzel.
Zielordner beim Hochladen: vorher galt stillschweigend der in der Galerie
angeklickte. Richtig, solange man es weiss, und ein Fallstrick, sobald man es
nicht weiss -- wer nach einer Suche hochlaedt, steht in "Alle" und legt an der
Wurzel ab. Der angeklickte bleibt die Voreinstellung, ist jetzt aber sichtbar
und aenderbar, samt "neuer Ordner" direkt daneben.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
7b35ef5698 |
feat: Betrachter fuer Lesbares, und aus dem <video> wird ein Player
Bild, Video, Ton und PDF konnte der Betrachter schon. Was fehlte, war
ausgerechnet die Art, die man am ehesten nur lesen und nicht herunterladen
will: eine .md landete im Zweig "laesst sich hier nicht anzeigen".
LESBARES. Markdown gesetzt, .lua und .json eingefaerbt, CSV als Tabelle, alles
Uebrige mit Zeilennummern. Der Tokenizer ist DERSELBE wie fuer die Schnipsel
auf der API-Seite und nicht ein zweiter -- zwei waeren zwei, die auseinander
laufen. Die Zeilennummern liegen in einer eigenen Spalte: so nimmt ein
Markieren mit der Maus sie nicht mit.
Die CSV-Zerlegung kann Anfuehrungszeichen und doppelte darin. Das ist der Teil,
den ein split(';') falsch macht, und CSV aus Excel hat ihn regelmaessig; das
Trennzeichen wird aus der Kopfzeile geraten.
NICHT ALLES WIRD GEHOLT. Die Groesse steht in der Datenbank, also wissen wir
vorher, worauf wir uns einlassen: ueber 512 KB kommt per Bereichsanfrage nur
der Anfang, und das steht auch da. Ein Betrachter, der bei einem 400-MB-
Protokoll den Tab abschiesst, ist schlimmer als einer, der die Datei gar nicht
erst oeffnet. Gemessen: Range: bytes=0-49 -> 206, 50 Bytes.
MARKDOWN OHNE BIBLIOTHEK, und das ist kein Geiz. Der uebliche Weg heisst marked
plus DOMPurify und endet bei dangerouslySetInnerHTML -- damit haengt die
Sicherheit dieser Seite an der Frage, ob die Filterliste vollstaendig ist. Hier
entsteht NIE eine HTML-Zeichenkette: der Text wird zu React-Knoten, und ein
<script> in einer hochgeladenen .md erscheint als die acht Zeichen, die es ist.
Im ganzen ui/ steht kein einziges dangerouslySetInnerHTML.
Die eine Luecke, die React nicht schliesst, ist href: [klick](javascript:...)
kaeme durch. Zwoelf Faelle in Node durchgeprueft, alle bestanden -- http,
https, mailto und relative Ziele durch; javascript:, JavaScript:, mit
Leerzeichen davor, data:, vbscript:, file: abgelehnt und als Text stehen
gelassen.
DER PLAYER. Die Bedienelemente bleiben die des Browsers: ein selbstgebauter
Schieber sieht in jedem Browser anders falsch aus, kennt keine
Tastatursteuerung und keine Untertitel. Drumherum kam, was der Browser nicht
mitbringt.
Lautstaerke ueber Dateien hinweg -- <video> setzt sie bei jedem neuen Element
auf 1 zurueck, und wer sich durch zwanzig Clips klickt, stellt sie sonst
zwanzigmal leise, waehrend der einundzwanzigste ungefragt wieder hochfaehrt.
Weiter zum naechsten Stueck, abschaltbar, und nur wenn das Naechste auch etwas
zum Abspielen ist: nach einem Lied ungefragt bei einem Bild zu landen waere
schlechter als stehenzubleiben.
Tastatur an EINER Stelle: Leertaste haelt an, Pfeile springen fuenf Sekunden,
Umschalt+Pfeil wechselt die Datei. Bei allem anderen blaettern die Pfeile wie
bisher. Dass das so ist, steht als Zeile unter dem Bild -- eine unsichtbare
Sonderregel fuehlt sich an wie ein Fehler.
SERVER. Die Typtabelle kennt jetzt md, markdown, csv, lua, xml, yml, yaml,
toml, ini, cfg, sql und log -- alle als text/plain. Nicht als text/markdown
(braechte nichts) und erst recht nicht als text/html: wer einen Upload-Token
hat, koennte damit eine Seite unter unserem Namen veroeffentlichen. html, css
und js stehen deshalb weiterhin NICHT drin.
Der Betrachter entscheidet trotzdem nach der Endung und erst danach nach der
gemeldeten Art. Grund steht in der Datenbank: items/readme.md lag schon da und
trug application/octet-stream, weil sie vor der erweiterten Tabelle hochgeladen
wurde. Wer nur auf die Art schaut, zeigt die alte README nicht an und eine neue
schon -- und sucht den Unterschied an der falschen Stelle.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
40c446f859 |
feat: Papierkorb, Abrufzaehler, WebP ohne Adressaenderung, ZIP hochladen
Vier Dinge, alle am laufenden Dienst gemessen. PAPIERKORB. Loeschen war endgueltig -- bei einem Knopf "alle 3861 waehlen" direkt neben "loeschen" ist das die falsche Haerte, und die Sicherung half nur bis zum letzten naechtlichen Lauf. Die Datei wandert jetzt nach /data/papierkorb, der Datensatz in eine eigene Tabelle, nach 30 Tagen raeumt der Dienst stuendlich selbst auf. Der Ablagename traegt eine laufende Nummer: unter demselben Pfad koennen nacheinander verschiedene Dateien gelegen haben, und beide sollen zurueckholbar sein. Geprueft: loeschen -> oeffentlich 404, Eintrag im Papierkorb, zurueckholen -> 200 samt neu gerechneter Vorschau. ABRUFZAEHLER. media.abrufe und zuletzt_abgerufen. Gezaehlt wird im Speicher und alle 30 Sekunden weggeschrieben -- ein UPDATE je Kachel waeren bei einer Galerieseite 120 Schreibvorgaenge. Der 304 zaehlt mit (der Aufrufer WOLLTE die Datei, er hatte sie nur schon), HEAD nicht. Was vor dem Einbau lag, steht als "nie geholt" da, auch wenn es taeglich benutzt wurde -- die Karte im Speicherbericht sagt das auch dazu, statt eine Zahl zu zeigen, die luegt. WEBP OHNE ADRESSAENDERUNG. Der Pfad ist die Adresse: aus items/foo.png darf nicht items/foo.webp werden. Die sparsame Fassung liegt deshalb DANEBEN und wird unter DERSELBEN Adresse ausgeliefert, wenn der Aufrufer per Accept sagt, dass er WebP versteht. Nachgemessen an einer Datei: 6448 Bytes fuer curl, 2758 fuer einen Browser. Ueber den ganzen Bestand: 3606 Fassungen, 199,3 MB -> 33,1 MB, gespart 166,2 MB. Bei 29 lohnt WebP nicht -- dort bleibt es beim Original. Zwei Dinge haengen daran, beide gegengeprueft: "Vary: Accept" (ohne die Zeile legt ein Zwischenspeicher die WebP-Fassung fuer alle ab, auch fuer die, die sie nicht lesen koennen) und getrennte ETags mit "-w" (sonst bekaeme jemand auf seinen If-None-Match hin ein 304 fuer das falsche Bild). Gemessen: derselbe ETag mit Accept -> 304, ohne Accept -> 200 mit dem PNG. ZIP HOCHLADEN. Ein Archiv wird ausgepackt statt abgelegt, die Ordner darin bleiben erhalten und haengen sich hinter den gewaehlten Zielordner. fflate und nicht adm-zip: reines JavaScript, keine native Bibliothek -- dieses Projekt hat schon einen halben Abend an einer ABI-Nummer verloren. Grenzen: 5000 Eintraege, 256 MB entpackt, geprueft VOR dem Entpacken (eine Zip-Bombe waere sonst schon im Speicher). Zip-Slip gemessen: "../../../../etc/passwd" und "..\..\windows\hosts" abgelehnt, die harmlose Datei im selben Archiv abgelegt. UND EIN KNOPF, DER GELOGEN HAT. Der erste Nachruestlauf ueber alle Dateien brauchte mehr als 90 Sekunden -- genau da gibt Nginx Proxy Manager auf. Der Aufrufer sah einen 504, waehrend die Arbeit im Hintergrund weiterlief und fertig wurde. Ein Knopf, der Erfolg als Fehler meldet, ist schlimmer als einer ohne Rueckmeldung. Beide Wartungsknoepfe laufen deshalb in Runden zu 300 Stueck: der Server meldet "offen" und "fertig", die Oberflaeche ruft erneut auf und zeigt dabei "280 erzeugt, noch 129 offen ...". Gemessen: 7,8 s je Runde statt 90+ am Stueck. Der Vorschau-Knopf hatte dieselbe Wand und wurde mitgezogen, obwohl er noch nicht dagegengelaufen war. Nebenbei: der Verwaisten-Sucher kennt jetzt auch /data/webp (gleiche Schleife, Ordner im Ergebnis vorangestellt), Verschieben und Loeschen nehmen die sparsame Fassung mit, und das Loeschen eines Ordners raeumt thumbs/ und webp/ hinterher. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
67d2f4ec93 |
feat: verschieben, Ordner, Adressvorlagen, API-Seite mit Export
VERSCHIEBEN. Dateien in einen anderen Ordner legen -- mit einer Warnung, die
OBEN steht und nicht im Kleingedruckten: der Pfad IST die oeffentliche Adresse,
nach dem Verschieben fuehrt die alte URL ins Leere. Das ist kein Fehler,
sondern der Preis vorhersagbarer Adressen; man muss es nur vorher wissen.
Der Umzug ist ein `rename` und kein Kopieren-dann-Loeschen: auf derselben
Platte ist das unteilbar. Die Vorschau zieht mit, sonst zeigte die Galerie am
neuen Ort nichts und am alten das Bild einer Datei, die dort nicht mehr liegt.
Eine belegte Zieladresse wird NICHT still ueberschrieben -- da verschwaende
sonst eine fremde Datei, und der Verlauf zeigte nur einen Umzug.
Der Verlauf kennt dafuer eine neue Spalte `von` und eine neue Art `move`; ohne
sie stuende dort nur das Ziel und die Frage "wo war die vorher" waere nicht
mehr zu beantworten.
ORDNER anlegen und entfernen. Leere Ordner gibt es sonst nicht -- sie sind der
Teil eines Pfades vor dem letzten Schraegstrich, mehr nicht. Jetzt sind sie ein
echtes Verzeichnis, und /folders liest Tabelle und Platte zusammen. Ein Ordner
MIT Inhalt wird nicht mitgeloescht: das waere ein Knopf, der hunderte Dateien
wegraeumt, ohne sie zu zeigen.
ADRESSVORLAGEN. Der Bauplan einer oeffentlichen Adresse, benannt und
gespeichert (vehicles/{model}.webp). Sie werden auf der Startseite und der
API-Seite mit ausgefuellter Adresse gezeigt. Ohne Platzhalter oder mit ".." im
Muster wird abgelehnt -- beides nachgemessen.
API-SEITE. Anleitung fuer die eigenen Skripte, und der Punkt daran: die
Beispiele stehen MIT DER ECHTEN ADRESSE drin statt mit example.com. Wer
abschreibt, schreibt keinen Tippfehler ab. Dazu Export in drei Formen -- JSON,
CSV (Semikolon und CRLF, sonst steht in Excel alles in einer Spalte) und eine
fertige Lua-Tabelle Name -> Adresse, die man in eine Resource legt.
DREI FEHLER BEIM MESSEN GEFUNDEN:
- rm mit recursive:false wirft bei einem Verzeichnis EISDIR und sagt damit
etwas ganz anderes, als der Fall ist. rmdir scheitert von selbst, wenn der
Ordner nicht leer ist -- genau das gewuenschte Verhalten.
- In der Lua-Ausgabe war "\'" gemeint und "\'" geschrieben: in JavaScript ist
das schlicht ein Apostroph, der Backslash faellt weg. Ein Modellname mit
Apostroph haette die erzeugte Datei zerlegt.
- Der Export mit Ordnerfilter antwortete mit einem NACKTEN 500 (kaputtes
ESCAPE in der SQL-Zeile). Ausgerechnet das.
Deshalb hat der Dienst jetzt eine AUFFANGLINIE: app.onError gibt einen Text
statt "Internal Server Error". Wir haben das bisher an den bekannten Stellen
einzeln abgefangen -- und prompt hat ein Tippfehler die Luecke gefunden. Eine
Stelle schliesst sie fuer alles, woran heute niemand denkt.
Dazu: die Verlaufstabelle hat feste Spaltenbreiten (colgroup) und ueberall
dieselben Abstaende -- vorher hatten Kopf und Koerper verschiedene, und der
Pfad wurde abgeschnitten, obwohl daneben Platz frei war. Und die Anmeldeseite
hat zwei weiche Lichter statt einer leeren Flaeche.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
3ddab937e7 |
feat: hochladen aus dem Dashboard -- Bilder, Videos, Ton, PDF
POST /api/dash/upload, eigener Weg neben /api/upload. Der dortige haengt an
einem Bearer-Token; wer im Dashboard sitzt, hat eine SITZUNG und keinen Token,
und einen Token nur zum Hochladen anzulegen waere ein Geheimnis mehr, das
jemand verlieren kann.
Mehrere Dateien auf einmal, und JEDE bekommt ihr eigenes Ergebnis mit Pfad,
Groesse und Grund im Fehlerfall. Ein "3 von 5 hochgeladen" ohne die Namen der
zwei anderen ist genau die Sorte Rueckmeldung, wegen der wir hier neu bauen.
Eine kaputte Datei bricht die anderen nicht ab.
parseBody({ all: true }) ist noetig: ohne das liefert Hono bei mehreren Feldern
gleichen Namens nur das LETZTE, und aus fuenf Dateien wuerde eine.
DATEINAMEN WERDEN ZURECHTGEBOGEN, und zwar sichtbar. checkPath laesst
"Mein Video (final) 2.MP4" mit Recht nicht durch, also macht safeFilename
daraus "mein-video-final-2.mp4" -- Umlaute werden umschrieben und nicht
weggeworfen ("Grüße" -> "gruesse", nicht "gr-e"). Die Ergebnisliste zeigt den
neuen Pfad neben dem alten Namen.
BEIM MESSEN GEFUNDEN: bleibt vom Namen nichts uebrig -- "ЖУРНАЛ.txt",
" .png", "★★★.txt" --, hiessen bisher ALLE "datei.txt", und die zweite
Datei ueberschrieb die erste stillschweigend. Jetzt haengen die ersten acht
Stellen des SHA-256 daran: gleicher Inhalt ergibt denselben Namen,
verschiedener Inhalt verschiedene. Nachgemessen mit drei solchen Namen.
Der Fortschrittsbalken laeuft ueber XMLHttpRequest und nicht fetch: nur der
kennt upload.onprogress, und bei einem 40-MB-Video ist ein Balken der
Unterschied zwischen "es tut sich was" und "ist es abgestuerzt?".
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> |
||
|
|
5f7f17ce7e |
feat: der Dienst steht oeffentlich, und die Pfadmeldung sagt jetzt die Regel
Der Stack laeuft unter https://fivemanage.d4rkst3r.de, hinter NPM mit Let's-Encrypt-Zertifikat, Container auf Port 9101. DER NAME IST EINE UMKEHR, und sie steht ausfuehrlich im ROADMAP: hier stand, dass fivecdn und fivemanage wegfallen, weil wir kein Fivemanage betreiben. Das Argument gilt weiter -- es ist nur teurer als das, was dagegen steht. Der Name war bereits fertig verkabelt: DNS da, NPM-Host da, Zertifikat gueltig bis 09.11.2026, Ziel host.docker.internal:9101, und dieser Port wurde frei, als die Lite-Container gingen. Unser Stack auf 9101 kostet damit null Klicks. Der Wechsel auf media.d4rkst3r.de bleibt billig, weil keine Adresse in der Datenbank steht. Ueber die oeffentliche Adresse durchgemessen: Anmeldung durch echtes HTTPS (der Fall, den curl bisher nicht bezeugen konnte), Token mit Praefix-Fessel, 403 mit Text fuer anderswo/ und fuers Loeschen, Upload und Abruf Byte fuer Byte identisch. Und ein Befund aus dem Fotostudio-Anschluss: die Pfadmeldung war irrefuehrend. SEGMENT verlangt als ERSTES Zeichen einen Buchstaben oder eine Ziffer, die Meldung sprach aber nur von "nicht mit Punkt beginnend" -- ein fuehrender Unterstrich faellt ebenso durch. Kein erfundener Fall: das Fotostudio legt seine Leerbilder als "_plate" ab. Jetzt nennt die Meldung die Regel. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
5edbe6c45f |
feat: ein Name statt zwei, und PUBLIC_URL ist nur noch der Ursprung
fivecdn.d4rkst3r.de und fivemanage.d4rkst3r.de fallen weg -- wir betreiben kein Fivemanage, also tragen unsere Adressen auch nicht dessen Namen. Dashboard und API liegen an der Wurzel, die Dateien unter /f/. Der Ein-Namen-Betrieb war bis jetzt nicht ausdrueckbar. Der Kommentar in config.ts versprach "leer lassen, wenn alles unter einem Namen laufen soll", aber optional() faellt bei leerem Wert auf den Host aus PUBLIC_URL zurueck -- und dann gilt JEDE Anfrage als Anfrage an den Dateiwirt: kein Dashboard, keine API, /health gibt 404 und die Anmeldung 405 "hier gibt es nur Dateien". Nichts ist kaputt, und niemand kommt darauf. Deshalb ist der Ein-Namen-Betrieb jetzt der Standard und der zweite Name die Ansage. PUBLIC_URL ist nur noch der Ursprung; das /f haengt der Dienst selbst an. Wer es mitschriebe, bekaeme Adressen mit /f/f/, und das faellt erst auf, wenn das erste Bild fehlt. Praefix und Route haengen jetzt an derselben Stelle (config.filePrefix), damit die zurueckgegebene Adresse und die Route, die sie ausliefert, nicht auseinanderlaufen koennen. Beide Betriebsarten durchgemessen: mit einem Namen kommt .../f/vehicles/adder.png zurueck und liefert die Datei; mit gesetztem FILES_HOST kommt sie ohne Praefix, liegt unter dem Dateihost, ist am Dashboard-Namen zusaetzlich unter /f/ erreichbar, und unter dem Dateihost gibt es weiterhin keine API. Welche Betriebsart laeuft, sagt der Dienst in der zweiten Startzeile. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
e36db8c015 |
feat: eigener Medien-Dienst statt Fivemanage Lite — Server-Grundgeruest
Ein Container statt vier: Hono, SQLite, Dateien auf einer Platte. Kein
PostgreSQL, kein MinIO, kein ClickHouse. Das ist nicht die kleine Loesung,
sondern die, die man nicht pflegt — eine Sicherung ist ein cp -a.
Der Ausloeser war nicht der Funktionsumfang von Lite, sondern dessen
Zustand. web/src/utils/http-util.ts wirft jeden Fehler als new Error(...),
die Hooks pruefen auf "instanceof ApiError" — damit wird jeder Fehlschlag
lautlos verschluckt. "Create organization" meldet weder Erfolg noch
Misserfolg; das Ergebnis waren 19 gleichnamige Organisationen, die sich auch
nicht loeschen lassen, weil die Route fehlt. Von allem, was Lite darueber
hinaus kann, haben wir nichts gebraucht.
Zwei Hostnamen, ein Prozess: unter FILES_HOST gibt es ausschliesslich
Dateien — kein Dashboard, keine API, nichts anzumelden. Das haelt die
oeffentliche Adresse frei von Angriffsflaeche und die URLs huebsch, ohne
/f/-Praefix. Die Vorlage im Handy bleibt {model}.webp.
Der Pfad ist der Schluessel: X-Path legt die Datei genau dort ab, wo der
Aufrufer sie erwartet. Bei Lite haetten wir die zurueckgegebenen Adressen in
einer urls.json mitschleppen muessen — vorhersagbare URLs sind fuer das
Fotostudio die ganze Voraussetzung.
storage.ts bereinigt Pfade NICHT, es lehnt sie ab. Ein zurechtgebogener Pfad
legt die Datei unter einem anderen Namen ab als dem erwarteten, und das faellt
erst auf, wenn das Bild fehlt.
Typprueft. Noch nicht gestartet, noch nicht getestet — das ist der naechste
Schritt und steht in ROADMAP.md.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|