diff --git a/docs/API.md b/docs/API.md index 38e84fe..fe8cb45 100644 --- a/docs/API.md +++ b/docs/API.md @@ -235,11 +235,33 @@ heraus wäre entweder Mixed Content oder ginge ins Leere. --- -## GET /api/exists/<pfad> +## GET /api/exists?pfad=<pfad> Für „nur fehlende" in einem Serienlauf: eine winzige Antwort gegen 130 KB Upload. +> **Diese Form und nicht mehr `/api/exists/`** — der Grund ist +> nachgemessen und unangenehm. Die alte Form endet auf `.webp`, und Nginx +> Proxy Managers `assets.conf` greift auf **jede** URL mit Bildendung: +> +> ```nginx +> location ~* ^.*\.(css|js|jpe?g|gif|png|webp|...)$ { +> proxy_cache public-cache; +> proxy_ignore_headers Set-Cookie Cache-Control Expires ...; +> proxy_cache_valid any 30m; +> } +> ``` +> +> Der Proxy legt die berechtigte Antwort weg und liefert sie danach **an jeden +> aus, auch ohne Token** — gemessen: 200 mit voller Auskunft, `Authorization` +> gar nicht gesetzt. Unser eigenes `Cache-Control` hilft nicht, +> `proxy_ignore_headers` wirft es weg. Und es geht auch andersherum: landet +> zuerst ein `401` im Zwischenspeicher, bekommen ihn 30 Minuten lang alle. +> +> Die Abfrageform endet nie auf eine Bildendung und ist deshalb **von sich aus** +> dicht — ohne von einer Zeile in einer fremden Oberfläche abzuhängen. Die alte +> Form bleibt, weil Skripte sie benutzen; neu gebaut wird mit dieser. + ```json { "exists": true, "path": "vehicles/adder.webp", "url": "…", "size": 132760, "sha256": "…", "updated_at": 1786466462488 } diff --git a/server/src/routes/upload.ts b/server/src/routes/upload.ts index 9c1c3e1..279c88f 100644 --- a/server/src/routes/upload.ts +++ b/server/src/routes/upload.ts @@ -86,6 +86,11 @@ function artVon(mime: string): string { uploadRoutes.use('/upload', requireToken) uploadRoutes.use('/media/*', requireToken) +// BEIDE Formen hinter der Wache. Die Abfrageform steht als eigener Eintrag da: +// '/exists/*' trifft sie NICHT, weil dort kein Schraegstrich mehr folgt -- und +// eine Auskunft ohne Token waere genau das Loch, das die Abfrageform schliessen +// soll. +uploadRoutes.use('/exists', requireToken) uploadRoutes.use('/exists/*', requireToken) /** Den Rumpf einsammeln, in welcher der drei Formen er auch kommt. */ @@ -285,11 +290,40 @@ uploadRoutes.delete('/media/:pfad{.+}', async (c) => { }) /** Nachsehen, ob es etwas schon gibt — damit ein Lauf "nur fehlende" - * beantworten kann, ohne 900 Bilder hochzuladen. */ -uploadRoutes.get('/exists/:pfad{.+}', (c) => { + * beantworten kann, ohne 900 Bilder hochzuladen. + * + * ZWEI FORMEN, UND DIE ZWEITE IST DIE WICHTIGE: + * + * GET /api/exists/vehicles/adder.webp die alte, mit Pfad + * GET /api/exists?pfad=vehicles/adder.webp die neue, mit Abfrage + * + * Der Grund ist NACHGEMESSEN und unangenehm. Nginx Proxy Manager bringt eine + * `assets.conf` mit, deren Regel auf JEDE URL greift, die auf .webp, .png, .js + * ... endet: + * + * location ~* ^.*\.(css|js|jpe?g|gif|png|webp|...)$ { + * proxy_cache public-cache; + * proxy_ignore_headers Set-Cookie Cache-Control Expires ...; + * proxy_cache_valid any 30m; + * } + * + * Die ERSTE Form endet auf ".webp" und faellt damit hinein. Der Proxy legt die + * Antwort weg -- und liefert sie danach an JEDEN aus, auch ohne Token. + * Gemessen: 200 mit vollstaendiger Auskunft, Authorization-Kopf gar nicht + * gesetzt. Unser eigenes `Cache-Control` hilft nicht, `proxy_ignore_headers` + * wirft es weg. + * + * Ein `location ^~ /api/`-Block im Proxy schliesst das -- aber eine + * Zugangspruefung, die an einer Zeile in einer fremden Oberflaeche haengt, ist + * keine. Die ZWEITE Form endet nie auf eine Bildendung (die Abfrage zaehlt + * fuer die Regel nicht mit) und ist deshalb von sich aus dicht. + * + * Die alte Form bleibt, weil Skripte sie benutzen -- aber die Dokumentation + * nennt nur noch die neue. */ +function existsAntwort(c: Context<{ Variables: Vars }>, roh: string) { let path: string try { - path = checkPath(c.req.param('pfad')) + path = checkPath(roh) } catch { return c.json({ exists: false }, 200) } @@ -302,4 +336,12 @@ uploadRoutes.get('/exists/:pfad{.+}', (c) => { ? { exists: true, path, url: publicUrlFor(path), ...row } : { exists: false, path }, ) +} + +uploadRoutes.get('/exists', (c) => { + const roh = c.req.query('pfad') ?? c.req.query('path') ?? '' + if (!roh) return c.json({ error: 'Abfrage "pfad" fehlt' }, 400) + return existsAntwort(c, roh) }) + +uploadRoutes.get('/exists/:pfad{.+}', (c) => existsAntwort(c, c.req.param('pfad'))) diff --git a/server/ui/src/pages/Api.tsx b/server/ui/src/pages/Api.tsx index 1161fef..52b0df4 100644 --- a/server/ui/src/pages/Api.tsx +++ b/server/ui/src/pages/Api.tsx @@ -131,7 +131,7 @@ curl -X POST ${B}/api/upload \\ marke="curl-exists" text={`# Gibt es das schon? — für "nur fehlende" in einem Serienlauf curl -H "Authorization: Bearer d4rk_…" \\ - ${B}/api/exists/vehicles/adder.webp + ${B}/api/exists?pfad=vehicles/adder.webp # → {"exists":true,"url":"${F}/vehicles/adder.webp","size":…}`} />