From 9c62777f5b227119f6c62825fd310322f069fe25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Padez?= Date: Sat, 29 Aug 2026 21:26:19 +0100 Subject: [PATCH] thumbnails need the token in the query string MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit An cannot send an Authorization header, which is why user-middleware and the permission gate both already accept `?token=` — it is exactly what the file browser's own thumbnails do. Built without it, every picture 401'd while the layout looked perfectly correct: the space was reserved, the rows were right, and nothing appeared. The route was fine and had been tested with a bearer token, which is precisely the case the browser cannot reproduce. Co-Authored-By: Claude Opus 5 --- web/sections/GalleriesPanel.tsx | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/web/sections/GalleriesPanel.tsx b/web/sections/GalleriesPanel.tsx index 9c18566..9a3caab 100644 --- a/web/sections/GalleriesPanel.tsx +++ b/web/sections/GalleriesPanel.tsx @@ -4,6 +4,7 @@ import { ArrowDown, ArrowUp, FolderOpen, Images, Loader2, Plus, Trash2 } from 'l import { useClient } from 'hooks/useClient'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; +import { getHeaders } from 'hooks/useClient'; import { readableError } from '../errors'; import { MediaPicker } from '../MediaPicker'; @@ -27,6 +28,19 @@ import { MediaPicker } from '../MediaPicker'; type Item = { id: number; url: string; alt: string | null; sort_order: number }; type Gallery = { id: number; name: string; slug: string; items: Item[] }; +/** + * A thumbnail URL, authenticated by `?token=`. + * + * An `` cannot send an Authorization header, which is why `user-middleware` and the permission + * gate both accept the token in the query string — the same thing the file browser's own thumbnails do. + * Built without it first, and every picture 401'd while the layout looked perfectly correct. + */ +const thumbUrl = (path: string): string => { + const token = getHeaders()['Authorization']?.replace('Bearer ', '') ?? ''; + const encoded = path.split('/').map(encodeURIComponent).join('/'); + return `/api/fofos-admin/derived/${encoded}/320.webp?token=${encodeURIComponent(token)}`; +}; + export const GalleriesPanel = () => { const client = useClient(); const clientRef = useRef(client); @@ -203,7 +217,7 @@ export const GalleriesPanel = () => { and still sharp at this size on a retina screen. */}