Anleitung aus docs/ — 2026-08-12 08:26
+117
-3
@@ -212,12 +212,49 @@ Tokens werden im Dashboard angelegt. Der Klartext wird **genau einmal** gezeigt
|
||||
— danach steht nur noch sein Hash in der Datenbank, und auch der Dienst kann
|
||||
ihn nicht mehr herausgeben.
|
||||
|
||||
Ein Token kann zwei Fesseln haben:
|
||||
Ein Token kann **sechs** Fesseln haben — alle im Dashboard einstellbar, auch
|
||||
nachträglich (`PATCH`, ohne dass sich der Schlüssel ändert):
|
||||
|
||||
| Fessel | Wirkung |
|
||||
|---|---|
|
||||
| Präfix | darf nur unterhalb dieses Pfades schreiben, z. B. `vehicles/` |
|
||||
| darf löschen | ohne dieses Recht antwortet `DELETE` mit 403 |
|
||||
| größte Datei | schärfer als `MAX_UPLOAD_MB`; darüber 413 |
|
||||
| Kontingent | wieviel dieser Token **insgesamt** halten darf; darüber 413 |
|
||||
| erlaubte Arten | Bilder, Video, Ton, Dokumente, Anderes; sonst 403 |
|
||||
| gültig bis | danach 401 mit dem Datum im Satz |
|
||||
|
||||
> **Das Kontingent ist etwas anderes als die Größe einer Datei.** Ein Token mit
|
||||
> „höchstens 2 MB je Bild" konnte die Platte trotzdem füllen — es brauchte nur
|
||||
> genug Bilder. Gezählt wird über das, was wirklich liegt, nicht über einen
|
||||
> mitlaufenden Zähler:
|
||||
>
|
||||
> ```
|
||||
> Kontingent erschoepft: dieser Token haelt 12 KB von 20 KB,
|
||||
> und diese Datei braucht 12 KB.
|
||||
> ```
|
||||
|
||||
### ⚠️ Bilder können kleiner zurückkommen, als sie hingingen
|
||||
|
||||
Ein Token kann eine **größte Kantenlänge** tragen. Ist sie gesetzt und das Bild
|
||||
größer, rechnet der Dienst es herunter, **bevor** er es ablegt:
|
||||
|
||||
```
|
||||
3000 × 2000 hingeschickt → 512 × 341 abgelegt, 82 KB → 3 KB
|
||||
```
|
||||
|
||||
Für ein Skript heißt das zweierlei:
|
||||
|
||||
1. **`size` in der Antwort ist die Größe der abgelegten Datei**, nicht die der
|
||||
geschickten.
|
||||
2. **`sha256` ebenso.** Wer den Hash vor dem Upload selbst rechnet und mit der
|
||||
Antwort vergleicht, bekommt bei gesetzter Kantenlänge einen Unterschied —
|
||||
und der ist kein Fehler. Der Hash gehört zu dem, was liegt; alles andere
|
||||
wäre gelogen.
|
||||
|
||||
Kleinere Bilder werden **nicht** aufgeblasen, und Video, Ton und Dokumente
|
||||
bleiben unberührt. Ohne gesetzte Kantenlänge passiert gar nichts — der
|
||||
Standard ist „ablegen, wie es kommt".
|
||||
|
||||
---
|
||||
|
||||
@@ -251,6 +288,14 @@ Nimmt die Datei in einer von **drei Rumpfformen** entgegen:
|
||||
}
|
||||
```
|
||||
|
||||
Trägt der Token eine Kantenlänge und wurde wirklich gerechnet, kommt ein Feld
|
||||
dazu — sonst steht es gar nicht erst da, denn ein Feld, das immer dasteht, sagt
|
||||
nichts:
|
||||
|
||||
```json
|
||||
"verkleinert": { "von": "3000x2000", "auf": "512x341" }
|
||||
```
|
||||
|
||||
### Was schiefgehen kann
|
||||
|
||||
| Antwort | Grund |
|
||||
@@ -259,7 +304,7 @@ Nimmt die Datei in einer von **drei Rumpfformen** entgegen:
|
||||
| 403 | Der Token darf dort nicht schreiben (Präfix-Fessel) |
|
||||
| 400 | Pfad unzulässig, Rumpf leer, kaputtes Base64 |
|
||||
| 409 | `X-Overwrite: false` und der Pfad ist belegt |
|
||||
| 413 | größer als `MAX_UPLOAD_MB` (Standard 64) |
|
||||
| 413 | größer als `MAX_UPLOAD_MB` (Standard 64), **oder** größer als die Grenze dieses Tokens, **oder** sein Kontingent ist erschöpft — welches, steht im Satz |
|
||||
|
||||
Jede Fehlerantwort ist JSON mit einem Feld `error` und einem **Satz auf
|
||||
Deutsch**. Ein nackter 500 ohne Text ist genau der Fehler, wegen dem dieser
|
||||
@@ -349,14 +394,83 @@ Papierkorb. Die tägliche Sicherung hilft nur bis zum letzten Lauf.
|
||||
|
||||
---
|
||||
|
||||
## Die Wege des Dashboards
|
||||
|
||||
Diese brauchen eine **Sitzung** und keinen Token — sie sind für die Oberfläche
|
||||
gedacht. Hier stehen sie, weil man sie auch mit `curl` und einem Cookie
|
||||
benutzen kann, wenn man etwas einmalig braucht.
|
||||
|
||||
| Weg | Wozu |
|
||||
|---|---|
|
||||
| `POST /api/dash/upload-url` | Der Dienst holt die Datei selbst von einer Adresse. Siehe unten. |
|
||||
| `POST /api/dash/fehlende` | Muster + Namensliste → was fehlt, was überzählig ist |
|
||||
| `POST /api/dash/media/ersetzen` | Suchen und Ersetzen über Pfade. **Ohne `ausfuehren: true` wird nur gerechnet und gezeigt.** |
|
||||
| `POST /api/dash/media/umbenennen` | Eine Datei; die Antwort nennt ihre Abrufzahl |
|
||||
| `POST /api/dash/folders/umbenennen` | Ein Ordner mit allem darin, in einer Transaktion |
|
||||
| `PATCH /api/dash/tokens/:id` | Grenzen ändern. Weggelassene Felder bleiben, `null` löscht eine Grenze. |
|
||||
| `GET /api/dash/verwaltung` | Wer hat wann was eingestellt |
|
||||
| `GET /api/dash/verlauf/export` | Verlauf als CSV; `?was=verwaltung` für den anderen. Semikolon + CRLF, höchstens 20 000 Zeilen. |
|
||||
| `GET /api/dash/einstellungen/export` | Alle Einstellungen als JSON. **Ohne Geheimnisse**, es sei denn `?geheim=1`. |
|
||||
| `POST /api/dash/einstellungen/import` | Zurück. Leere Werte überschreiben nichts. |
|
||||
| `GET/POST/DELETE /api/dash/freigaben` | Nur-Lesen-Links auf einen Ordner |
|
||||
| `POST /api/dash/maintenance/thumbs` \| `/webp` | Abgeleitetes nachziehen, **in Runden zu 300** |
|
||||
|
||||
### POST /api/dash/upload-url
|
||||
|
||||
```json
|
||||
{ "url": "https://…/bild.png", "ordner": "items", "name": "optional.png" }
|
||||
```
|
||||
|
||||
**Das ist die vorsichtigste Stelle im ganzen Dienst.** Ein Server, der eine vom
|
||||
Benutzer genannte Adresse abruft, ist ein Angriff mit eigenem Namen — deshalb
|
||||
wird geprüft, und zwar die **aufgelöste IP** und nicht der Name:
|
||||
|
||||
| abgelehnt | Beispiel |
|
||||
|---|---|
|
||||
| alles außer http/https | `file://`, `gopher://` |
|
||||
| andere Anschlüsse als 80/443 | `http://host:9101/` |
|
||||
| Rückschleife und Heimnetz | `127.0.0.1`, `::1`, `10.x`, `172.16–31.x`, `192.168.x`, `169.254.x`, `0.0.0.0` |
|
||||
| öffentliche Namen auf private Adressen | `127.0.0.1.nip.io` |
|
||||
|
||||
Der letzte Punkt ist der eigentliche: ein auflösbarer Name, der auf eine private
|
||||
Adresse zeigt, ist der Standardweg um jeden Namensfilter herum. **Und jede
|
||||
Umleitung wird einzeln neu geprüft** — `fetch` folgen zu lassen wäre genau dort
|
||||
die Lücke.
|
||||
|
||||
---
|
||||
|
||||
## Grenzen
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| Größte Datei | 64 MB (`MAX_UPLOAD_MB`) |
|
||||
| Größte Datei | 64 MB (`MAX_UPLOAD_MB`), je Token weiter einschränkbar |
|
||||
| Kontingent je Token | einstellbar, ohne Grenze wenn leer |
|
||||
| Pfadlänge | 200 Zeichen, höchstens 8 Ebenen |
|
||||
| Anmeldeversuche | 5 frei, danach Sperre ab 30 s (verdoppelt bis 15 min) |
|
||||
| Sitzung | 30 Tage |
|
||||
| Papierkorb | 30 Tage **und** höchstens 20 GB (`PAPIERKORB_MAX_MB`) — was zuerst greift, greift |
|
||||
|
||||
Der Papierkorb hat zwei Grenzen, weil eine nicht reicht: wer 200 GB löscht,
|
||||
hält sie dreißig Tage lang doppelt. Beim Ausleeren fliegt erst, was zu alt ist,
|
||||
dann das **Älteste**, bis der Deckel wieder passt. Die Zahl steht in der
|
||||
Antwort von `GET /api/dash/papierkorb` als `maxBytes`.
|
||||
|
||||
---
|
||||
|
||||
## Meldungen nach Discord
|
||||
|
||||
Vier Anlässe, alle einzeln abschaltbar: `sicherung`, `verwaiste`, `platte`,
|
||||
`upload`.
|
||||
|
||||
**`upload` meldet nicht sofort.** Jeder Upload schiebt eine Frist von zwei
|
||||
Minuten nach hinten; erst wenn es ruhig bleibt, geht **eine** Nachricht raus —
|
||||
mit Anzahl, Summe, Absendern und den ersten fünf Pfaden.
|
||||
|
||||
Das ist keine Bequemlichkeit, sondern der Grund, warum die Funktion überhaupt
|
||||
brauchbar ist: ein Serienlauf des Fotostudios legt 900 Bilder ab. 900
|
||||
Nachrichten wären keine Meldung mehr, sondern ein Ausfall des Kanals — Discord
|
||||
drosselt Webhooks, und wer danach eine *echte* Meldung bekommt, sieht sie nicht
|
||||
mehr.
|
||||
|
||||
---
|
||||
|
||||
|
||||
+177
@@ -0,0 +1,177 @@
|
||||
# Was noch ginge
|
||||
|
||||
Stand 12.08.2026. Diese Liste ist **nicht** eine Sammlung von allem, was
|
||||
denkbar wäre — sie ist das, was beim Bauen und Messen wirklich aufgefallen ist.
|
||||
Was hier steht, hat einen Grund; was keinen hatte, steht am Ende unter
|
||||
„Verworfen, mit Begründung".
|
||||
|
||||
Sortiert nach dem, was ich zuerst täte.
|
||||
|
||||
---
|
||||
|
||||
## 1 · Sicher wertvoll
|
||||
|
||||
### ~~Die Oberfläche auf dem Telefon~~ — abgelehnt am 12.08.2026
|
||||
|
||||
> Vom Betreiber verworfen: das Dashboard wird nicht mobil benutzt. Der Rest des
|
||||
> Abschnitts bleibt stehen, damit die Messung nicht verlorengeht, falls sich das
|
||||
> je ändert.
|
||||
|
||||
**Gemessen:** 18 Umbruchpunkte (`sm:`/`lg:`/`xl:`) im ganzen Frontend. Die
|
||||
Galerie ist ein Raster und passt sich an — die **vier Tabellen** in Verlauf,
|
||||
Token, Konten und Papierkorb nicht. Auf einem Telefon laufen sie seitlich
|
||||
heraus.
|
||||
|
||||
Der Umbau ist keine Kosmetik: Karten statt Tabellenzeilen unterhalb einer
|
||||
Breite, so wie es die Galerie schon macht. Betrifft vier Dateien.
|
||||
|
||||
**Lohnt sich nur, wenn du das Dashboard mobil aufmachst.** Sonst ist es
|
||||
Arbeit für einen Bildschirm, den niemand benutzt.
|
||||
|
||||
### Sitzungen beenden können
|
||||
|
||||
**Gemessen:** 86 offene Sitzungen, davon 82 von `admin` — meine Testanmeldungen
|
||||
von heute. Sie laufen nach 30 Tagen aus, und `pruneSessions` räumt stündlich
|
||||
die abgelaufenen weg. Was fehlt, ist ein Knopf **„alle anderen Sitzungen
|
||||
beenden"** im Kontomenü.
|
||||
|
||||
Das ist mehr als Ordnung: wer sich an einem fremden Rechner angemeldet hat und
|
||||
es später merkt, hat heute keine Möglichkeit, das zurückzunehmen — außer das
|
||||
Passwort zu ändern und zu hoffen, dass das die Sitzungen mitnimmt (tut es
|
||||
nicht, sie hängen an einer eigenen Tabelle).
|
||||
|
||||
### Was der Verlauf nicht sieht
|
||||
|
||||
**Gemessen:** `events` kennt `upload`, `replace`, `delete`, `move`, `rename` —
|
||||
alles über **Dateien**. Nichts über **Einstellungen**. Wer den Discord-Webhook
|
||||
geändert hat, wer einen Token angelegt oder gelöscht hat, wer ein Konto
|
||||
entfernt hat: steht nirgends.
|
||||
|
||||
Bei zwei Konten ist das verschmerzbar. Sobald ein drittes dazukommt — und über
|
||||
die Discord-Rolle entsteht es von selbst — ist es die erste Frage, die man
|
||||
stellt, wenn etwas anders ist als gestern.
|
||||
|
||||
---
|
||||
|
||||
## 2 · Nützlich, wenn der Fall eintritt
|
||||
|
||||
### Massen-Umbenennen
|
||||
|
||||
Suchen und Ersetzen über Pfade, mit Vorschau vor dem Zuschlagen. Bei 3578 Items
|
||||
ist eine Umbenennung von Hand keine.
|
||||
|
||||
Der Anlass wäre etwa: `items/` nach `inventar/` verschieben, oder eine
|
||||
Namenskonvention ändern. Heute geht das nur Datei für Datei oder gar nicht.
|
||||
|
||||
**Die Warnung wäre dieselbe wie beim Ordner-Umbenennen** — jede alte Adresse
|
||||
gibt danach 404 —, nur mal tausend.
|
||||
|
||||
### Bilder beim Ablegen verkleinern
|
||||
|
||||
Eine Obergrenze für die Kantenlänge, einstellbar je Token. Wer ein 6000 × 4000
|
||||
großes Foto hochlädt, bekommt es auf, sagen wir, 2048 gerechnet.
|
||||
|
||||
**Gemessen und deshalb keine Dringlichkeit:** ein 12000 × 12000 großes PNG hat
|
||||
den Dienst *nicht* in Verlegenheit gebracht — Vorschau in 374 ms, Speicher bei
|
||||
47 MB von 31 GB. libvips arbeitet kachelweise und dekodiert nie das ganze Bild.
|
||||
Es geht also um Plattenplatz und Ladezeit beim Ausliefern, nicht um Stabilität.
|
||||
|
||||
### Fehlende Bilder finden
|
||||
|
||||
Eine Liste hereinreichen (Fahrzeugmodelle, Item-Namen) und beantwortet
|
||||
bekommen: **wozu fehlt ein Bild?** Heute beantwortet das niemand — das
|
||||
Fotostudio weiß, was es aufgenommen hat, der Dienst weiß, was liegt, und die
|
||||
Differenz rechnet niemand aus.
|
||||
|
||||
Das wäre ein Endpunkt, der eine Liste entgegennimmt und die fehlenden
|
||||
zurückgibt. Klein, und die Antwort auf „warum zeigt das Handy bei manchen
|
||||
Autos kein Bild".
|
||||
|
||||
### Einstellungen aus- und einlesen
|
||||
|
||||
Discord-Zugang, Sicherungsziel, Vorlagen, Meldungsanlässe — alles steht in
|
||||
`settings` und ist beim Neuaufsetzen von Hand nachzutragen. Ein Export als JSON
|
||||
(**ohne** die Geheimnisse, oder mit einer ausdrücklichen Ansage) macht aus
|
||||
einer Stunde Klickerei fünf Minuten.
|
||||
|
||||
---
|
||||
|
||||
## 3 · Wäre schön, drängt nicht
|
||||
|
||||
### ~~Verlauf als CSV~~ — gebaut am 12.08.2026
|
||||
|
||||
`GET /api/dash/verlauf/export`, wahlweise `?was=verwaltung`. Der Knopf sitzt in
|
||||
der Reiterleiste und holt **den Verlauf, der gerade offen ist** — zwei Knöpfe
|
||||
nebeneinander wären die Frage „welchen von beiden?" an einer Stelle, wo die
|
||||
Antwort schon auf dem Bildschirm steht.
|
||||
|
||||
Semikolon und CRLF wie beim Bestands-Export, und aus demselben Grund: Excel in
|
||||
deutscher Einstellung nimmt bei Komma alles in eine Spalte und bei nacktem LF
|
||||
die halbe Datei in eine Zelle. Gemessen an der ausgelieferten Datei: 5364
|
||||
Zeilen, 5364 CRLF, **kein einziges nacktes LF**.
|
||||
|
||||
### ~~Meldung bei Upload~~ — gebaut am 12.08.2026
|
||||
|
||||
Mit Drossel, und die ist der eigentliche Inhalt. Jeder Upload schiebt eine
|
||||
Frist von zwei Minuten nach hinten; erst nach der Ruhe geht **eine** Nachricht
|
||||
raus, mit Anzahl, Summe, Absendern und den ersten fünf Pfaden.
|
||||
|
||||
Ohne das wäre die Funktion ein Schaden statt eines Nutzens: 900 Fahrzeugbilder
|
||||
ergäben 900 Nachrichten, Discord drosselt Webhooks, und wer danach eine echte
|
||||
Meldung bekommt, sieht sie nicht mehr. Ein Serienlauf ergibt jetzt eine Zeile,
|
||||
ein einzelnes Bildschirmfoto ergibt eine Zeile — beide sagen dasselbe, nur mit
|
||||
anderen Zahlen.
|
||||
|
||||
Der Anlass heißt `upload` und ist **abschaltbar wie die anderen drei**. Die
|
||||
Liste steht an zwei Stellen (Server und Oberfläche) — genau die Verdopplung,
|
||||
die bei `platte` schon einmal ein Kreuzchen verschluckt hat; deshalb steht
|
||||
jetzt in beiden ein Verweis auf die andere. Nachgemessen: vier geschickt, vier
|
||||
gespeichert.
|
||||
|
||||
### ~~Papierkorb mit Größendeckel~~ — gebaut am 12.08.2026
|
||||
|
||||
20 GB. Beim Ausleeren wird erst nach Alter geräumt (30 Tage) und dann nach
|
||||
Größe, **das Älteste zuerst** — dessen Versehen wäre am ehesten schon
|
||||
aufgefallen. Die Grenze steht in der Antwort von `/api/dash/papierkorb`, damit
|
||||
die Oberfläche sie nicht ein zweites Mal hinschreibt.
|
||||
|
||||
Bei 910 GB frei ist das heute keine Grenze, sondern eine Zusicherung: der
|
||||
Papierkorb kann nicht mehr unbemerkt zur zweiten Ablage werden.
|
||||
|
||||
### Mehrere Sicherungsziele — offen
|
||||
|
||||
Die Archive liegen auf derselben Platte wie die Daten, die Kopie in der
|
||||
Nextcloud ist die einzige Trennung.
|
||||
|
||||
---
|
||||
|
||||
## Verworfen, mit Begründung
|
||||
|
||||
Diese standen auf der Liste und sind **gemessen** wieder heruntergefallen. Sie
|
||||
stehen hier, damit sie nicht beim nächsten Mal wieder aufschlagen.
|
||||
|
||||
| Idee | Warum nicht |
|
||||
|---|---|
|
||||
| Doppelte Dateien zusammenführen | 5 Gruppen, **0,0 MB** verschwendet. Es gibt nichts zu holen. |
|
||||
| Aufräumen nach Alter | Alle vier Altersstufen im Bericht stehen auf **0 Dateien**. Es ist nichts alt. |
|
||||
| Grenze für Bildmaße als *Schutz* | 12000 × 12000 → 374 ms, 47 MB. libvips streamt, die Gefahr gibt es nicht. |
|
||||
| Plattenplatz-Wächter | **Gebaut.** 80/90/95 %, meldet nach Discord. |
|
||||
| Kontingent je Token | **Gebaut**, auf beiden Upload-Wegen geprüft. |
|
||||
| Eigene Statusseite bauen | `/status` reicht — der d4rkbot zeigt sie schon an. |
|
||||
|
||||
---
|
||||
|
||||
## Und was heute noch schnell repariert wurde
|
||||
|
||||
**`pruneEvents` lief nie von selbst.** Die Funktion gibt es seit dem ersten Tag
|
||||
und sie räumt Ereignisse älter als 180 Tage weg — aufgerufen wurde sie aber nur
|
||||
von `/maintenance/prune-sessions`, einem Weg, den nicht einmal die Oberfläche
|
||||
anbietet. Die dokumentierte Aufbewahrung griff damit **nie**, und die Tabelle
|
||||
wuchs für immer.
|
||||
|
||||
Zum Vergleich: `pruneSessions` hängt seit jeher an einem stündlichen Takt. Das
|
||||
eine war verdrahtet, das andere nicht, und der Unterschied fiel niemandem auf,
|
||||
weil beide in derselben Zeile stehen.
|
||||
|
||||
Gemessen: nach **einem** Tag mit Umzug und Serienlauf standen 5355 Zeilen in
|
||||
`events`. Läuft jetzt täglich, mit zwei Minuten Verzögerung nach dem Start.
|
||||
Reference in New Issue
Block a user