diff --git a/server/src/routes/upload.ts b/server/src/routes/upload.ts index 6f163c8..b92b671 100644 --- a/server/src/routes/upload.ts +++ b/server/src/routes/upload.ts @@ -24,7 +24,7 @@ import type { Context, Next } from 'hono' import { config } from '../config.js' import { meldeUpload } from '../meldung.js' import { db, logEvent, now, type Media, type Token } from '../db.js' -import { fesselText, tokenAllows, tokenFromHeader } from '../auth.js' +import { fesselText, tokenAllows, tokenFromHeader, tokenPrefixes } from '../auth.js' import { PathError, checkPath, @@ -141,6 +141,7 @@ uploadRoutes.use('/media/*', requireToken) // '/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('/zugang', requireToken) uploadRoutes.use('/exists', requireToken) uploadRoutes.use('/exists/*', requireToken) @@ -425,6 +426,53 @@ function existsAntwort(c: Context<{ Variables: Vars }>, roh: string) { ) } +/** Was darf dieser Token eigentlich? + * + * WOZU. Das Fotostudio soll beim Einrichten eine AUSWAHL der Ordner zeigen + * statt eines Freitextfeldes -- und eine Auswahl, die etwas anbietet, das + * hinterher mit 403 abprallt, ist schlimmer als keine. + * + * Bisher erfuhr man die Fesseln nur, indem man dagegen lief: die + * 403-Meldung nennt sie. Das ist eine Auskunft ueber den eigenen Zugang, kein + * Geheimnis -- wer den Token hat, kennt seinen Umfang ohnehin, er muss ihn + * nur ausprobieren. Also lieber geradeheraus. + * + * Eine leere Liste heisst "ueberall", genau wie in der Datenbank. Das steht + * auch im Feld daneben, damit niemand "keine Ordner" liest. + */ +uploadRoutes.get('/zugang', (c) => { + const token = c.get('token') as Token + const fesseln = tokenPrefixes(token) + + // WELCHE ORDNER ZUR AUSWAHL STEHEN. + // + // Ist der Token gefesselt, sind es genau seine -- eine Auswahl, die etwas + // anbietet, das hinterher mit 403 abprallt, waere schlimmer als keine. + // + // Ist er es NICHT, waere die Antwort "ueberall" fuer ein Auswahlfeld + // wertlos. Dann kommen die Ordner, die es wirklich gibt. Das ist keine + // Preisgabe: wer ueberall schreiben darf, kommt an jeden davon ohnehin -- + // er muesste den Namen nur raten. + const vorhanden = ( + db + .prepare( + `SELECT DISTINCT substr(path, 1, instr(path, '/') - 1) AS o + FROM media WHERE instr(path, '/') > 0 ORDER BY o`, + ) + .all() as { o: string }[] + ).map((z) => z.o) + + return c.json({ + // Woraus das Auswahlfeld waehlen darf. + ordner: fesseln.length > 0 ? fesseln : vorhanden, + // Und ob daneben noch etwas anderes ginge -- dann darf das Feld auch + // einen freien Namen zulassen. + ueberall: fesseln.length === 0, + darfLoeschen: token.can_delete === 1, + name: token.name, + }) +}) + uploadRoutes.get('/exists', (c) => { const roh = c.req.query('pfad') ?? c.req.query('path') ?? '' if (!roh) return c.json({ error: 'Abfrage "pfad" fehlt' }, 400)