d125c6bf72ba0734c2703509f98336b6f87b1bf0
8
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
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>
|
||
|
|
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> |
||
|
|
c9cd8eaa84 |
feat: Token-Grenzen, Speicher nach Art, Aufbewahrungsbericht, Discord-Meldungen
KEINE TOKEN-ARTEN, SONDERN TOKEN-GRENZEN. Fivemanage hat Token-Typen; wir haben schon ein Rechtesystem, es steht nur auf einer anderen Achse (Praefix und Loeschrecht). Was wirklich fehlte, sind die drei Schrauben, die die Frage beantworten "was passiert, wenn DIESER Token abhandenkommt": Groessengrenze ein Token fuer Fahrzeugbilder braucht keine 64 MB erlaubte Arten nur Bilder, kein Video Ablaufdatum fuer eine Anbindung, die man mal ausprobiert Alle drei am laufenden Dienst durchgemessen: 600 KB gegen 512 KB -> 413 mit beiden Zahlen; ein mp4 gegen einen Bild-Token -> 403 mit Art UND MIME im Text; ausserhalb des Praefix -> 403; abgelaufen -> 401 mit DEM DATUM. "Abgelaufen" und "unbekannt" auseinanderzuhalten spart eine halbe Stunde Suche nach einem Tippfehler, den es nicht gibt. Mehr als die Dienstgrenze zu erlauben wird abgelehnt statt still gekappt -- es waere eine Behauptung, denn durchgelassen wird ohnehin die schaerfere. SPEICHER NACH ART: ein Balken fuer die Verhaeltnisse, eine Tabelle fuer die Zahlen, dazu die groessten Dateien. AUFBEWAHRUNG VORBEREITET, NICHT GEBAUT. Automatisches Loeschen zu bauen, bevor man weiss, was da liegt, ist der Weg, wie man Daten verliert. Stattdessen der Bericht davor: wie viel ist aelter als 7/30/90/365 Tage, was liegt doppelt (gleicher SHA-256 unter zwei Pfaden) und wie viel Platz das kostet. "Alles aelter als 90 Tage loeschen" ist eine Behauptung, solange niemand weiss, wie viel das waere. Diese Seite loescht nichts. DISCORD-MELDUNGEN -- und der Grund ist nicht Discord, sondern eine Luecke, die wir heute selbst gebaut haben: die Sicherung laeuft nachts um halb fuenf, und wenn sie aufhoert zu laufen, merkt es niemand. Der Dienst sieht jetzt STUENDLICH nach und meldet, wenn der letzte Lauf gescheitert oder aelter als 26 Stunden ist (24 waeren zu knapp, 48 zu spaet). Hoechstens EINMAL je Zustand -- eine Meldung, die stuendlich wiederkommt, wird weggeklickt, und dann auch die, die zaehlt. Ein Webhook und keine Bot-Anbindung: eine Adresse, kein Token, keine Berechtigungen. Die Adresse wird geprueft (nur echte discord.com-Webhooks), und der Testknopf steht gleich daneben -- ein Webhook, den man eintraegt und erst in drei Wochen im Fehlerfall ausprobiert, ist einer, der dann nicht geht. Aus deren Doku mitgenommen und NICHT gebaut: presigned URLs (kurzlebige signierte Adressen fuer Client-Uploads). Waere der richtige Weg fuer Spieler-Screenshots -- solange keine Spieler hochladen, ist es Vorrat. 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>
|
||
|
|
4f149e365f |
feat: Discord-Bild und -Name im Dashboard
Wer ueber Discord hereinkommt, sieht sein Bild in der Kopfzeile, auf der
Kontoseite und neben seinen Eintraegen im Verlauf.
DIE ADRESSE BAUT DER DIENST, nicht die Oberflaeche. Gespeichert wird nur der
Bildstempel; das URL-Schema von Discord steht an EINER Stelle (avatarUrl in
db.ts) und nicht in jeder Ansicht, die ein Bild zeigt. Es hat drei Faelle, und
alle drei stehen dort:
- Stempel faengt mit "a_" an -> bewegtes Bild, muss als .gif angefragt werden
- Stempel vorhanden -> .png mit gewuenschter Groesse
- kein Stempel -> Discords Ersatzbild, dessen Nummer sich aus
der ID ergibt: (id >> 22) % 6. Das ist die
NEUE Rechnung; die alte ueber den
Diskriminator gilt seit den Pseudonymen
nicht mehr. Gegengerechnet an drei IDs.
Bild und Anzeigename werden BEI JEDER ANMELDUNG nachgezogen: wer sein Bild
wechselt, saehe sonst monatelang das alte und fragte sich, ob er im richtigen
Konto sitzt. Der Benutzername im Dashboard bleibt dagegen, wie er ist -- an ihm
haengen die Eintraege im Verlauf.
Der Avatar-Baustein kennt drei Faelle und keinen kaputten: Bild, sonst die
Initialen auf einer Farbe, die sich aus dem Namen ergibt (damit sie beim
Neuladen nicht springt), und fuer einen Token einen Schluessel -- ein Token hat
kein Gesicht, ein Platzhalterbild daneben waere eine Behauptung. Laedt das Bild
nicht (CDN nicht erreichbar, Stempel veraltet), faellt es auf die Initialen
zurueck statt ein kaputtes Bildsymbol zu zeigen.
Nachgemessen: cdn.discordapp.com liefert das Ersatzbild (200, image/png), die
Form der Bildadresse stimmt, und ein Passwort-Konto bekommt sauber avatar:null.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
dffec7714c |
feat: Discord-Anmeldung -- im Panel einrichtbar, nicht in der .env
Auf Wunsch ueber die Oberflaeche statt ueber Umgebungsvariablen. Der Gewinn ist
nicht nur Bequemlichkeit: das Panel legt die REDIRECT-URI zum Kopieren hin, und
genau daran scheitert ein OAuth-Anlauf fast immer.
settings.ts Schluessel/Wert in SQLite, mit Umgebung als Rueckfall. Wer
schon Stack-Variablen gesetzt hat, behaelt sie; das Panel
uebersteuert. In der Umgebung bleibt, was den Dienst ueberhaupt
erreichbar macht (PUBLIC_URL, PORT, DATA_DIR) -- wer das per
Formular verstellen koennte, sperrt sich mit einem Klick aus.
discord.ts Der Weg in drei Schritten, ohne Bibliothek und OHNE BOT-TOKEN:
die Rolle wird mit dem Merkmal des BENUTZERS geholt
(guilds.members.read). Kein Bot, keine Bot-Rechte, kein zweites
Geheimnis, das ablaufen kann.
DAS GEHEIMNIS GEHT NIE HERAUS -- auch nicht an eine angemeldete Sitzung. Das
Panel bekommt nur "hatGeheimnis: true". Ein Formular, das den Client-Schluessel
im Klartext zurueckschickt, streut ihn in jeden Browser-Cache. Ein leeres Feld
heisst "unveraendert", nicht "loeschen"; zum Entfernen gibt es "-".
JEDER ABBRUCH IST SICHTBAR: kein weisser Bildschirm, sondern zurueck aufs
Formular mit dem Grund im Adressfeld -- "Du bist nicht auf diesem Server",
"Dir fehlt die noetige Rolle", oder bei 4xx vom Tausch der Hinweis auf die
Redirect-URI. Die IDs werden schon beim Speichern geprueft (17-20 Ziffern),
damit ein halber Link nicht erst beim Anmelden auffaellt.
Das Passwort bleibt als Notausgang. Ein ueber Discord angelegtes Konto bekommt
KEIN brauchbares Passwort ("nur-ueber-discord"): verifyPassword scheitert an
allem, was nicht dem scrypt-Format entspricht -- nachgesehen, nicht vermutet.
Dazu der Verlauf, wie gewuenscht: Suche nach Pfad (mit derselben
LIKE-Maskierung wie in der Galerie -- Suche nach "_" findet 0 statt allem),
waehlbare Seitengroesse (25/50/100/200, Standard 50) und mehr Luft zwischen
"Groesse" und "Von wem".
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
0b295a2302 |
feat: Verlauf -- wer hat wann was abgelegt, ersetzt oder geloescht
Neuer Reiter, und dahinter eine eigene Tabelle. WARUM NICHT EINFACH
media.created_at -- drei Gruende, jeder allein wuerde reichen:
1. media kennt nur den JETZIGEN Stand. Was geloescht wurde, ist weg, samt
der Frage, wer es geloescht hat.
2. Ein Ueberschreiben hebt updated_at an und ueberschreibt damit die
Auskunft, wann die Datei urspruenglich kam.
3. media.token_id ist beim Upload aus dem Dashboard NULL -- dort hat niemand
einen Token, sondern eine Sitzung. Das "von wem" stand also nirgends.
Das ist NICHT das Logging, das die ROADMAP ablehnt: dort geht es um
Anfrage-Protokolle samt ClickHouse. Hier sind es ein paar Zeilen je Upload in
derselben SQLite-Datei, und nach einem halben Jahr raeumt pruneEvents auf.
DER VERLAUF STARTET NICHT LEER. Beim ersten Start wird er aus dem Bestand
nachgetragen (68 Eintraege) -- ein Tab, der am ersten Tag leer ist, obwohl 68
Bilder in der Ablage liegen, sieht aus wie ein kaputter Tab. Die nachgetragenen
Eintraege sind als solche gekennzeichnet: Zeitpunkt stimmt, Urheber steht als
"vor der Aufzeichnung", weil ihn niemand mehr kennt. Die Oberflaeche sagt das
unter der Tabelle, statt es zu verschweigen.
logEvent wirft nie: ein Verlauf, der einen Upload scheitern laesst, waere die
Buchhaltung, die das Geschaeft verhindert.
Nachgemessen: Upload, Ersetzen und Loeschen aus dem Dashboard erscheinen als
user:admin, die Uploads des Fotostudios als token:d4rk_photostudio; beim
Loeschen wird die Groesse VOR dem Loeschen geholt, sonst stuende dort nichts.
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>
|