820f4835f1
Adds an `allow_downloads` boolean to `photo_categories` so admins can have different download policies per category — e.g. preview categories public, originals client-only. AND's with the event-level `allow_downloads`, so disabling at either level blocks downloads for that category's photos. Defaults to true so categories created before migration 135 keep working without admin intervention. Credit: 8digit/picpeak@928164b + @751ec75. ### Backend - **Migration 135**: additive `allow_downloads BOOLEAN NOT NULL DEFAULT true` on `photo_categories`, hasColumn-guarded + sane down. - **`adminCategories.js`**: PUT /:id accepts optional `allow_downloads` patch. - **`gallery.js`**: - `GET /:slug/photos` returns `allow_downloads` per category AND `category_allow_downloads` per photo. - `GET /:slug/download/:photoId` returns 403 when the photo's category disables downloads. - `GET /:slug/download-all` LEFT JOINs `photo_categories` and filters `whereNull(category_id) OR allow_downloads=true OR allow_downloads IS NULL`. The null check covers pre-migration-135 rows during the upgrade window. - `POST /:slug/download-selected` same filter pattern. ### Frontend - **`categories.service.ts`**: `updateCategory()` gains an optional `patch` argument carrying `{ allow_downloads }`. PhotoCategory interface gains the optional field. - **`EventCategoryManager.tsx`**: new toggle button next to the delete X. Green DownloadCloud icon when downloads are on, plain Download icon when off. Click toggles via the new mutation; toast confirms. - **`PhotoLightbox.tsx`**: `photoAllowsDownload = allowDownloads && currentPhoto?.category_allow_downloads !== false`. Hides the download button + blocks the 'D' keyboard shortcut + early-returns from handleDownload. - **Types**: Photo interface gains `category_allow_downloads`. - **i18n**: 5 new EN + DE entries for the toggle button toast + tooltip. No global-category surface change yet — global categories don't currently have a UI for the toggle. Admins can still flip the column directly via SQL or via a future global-categories editor. ### Test plan - [x] Backend syntax + TS check clean - [x] ESLint: no new warnings - [ ] Manual: admin → event detail → categories panel → click DownloadCloud icon → category flips, toast confirms - [ ] Manual: gallery (guest) → photo in disabled category → lightbox shows no download button, 'D' shortcut is a no-op - [ ] Manual: download-all on a gallery with one disabled category → ZIP excludes that category's photos - [ ] Manual: download-selected including a disabled-category photo → 404 (filtered out) and the response carries only the allowed selection - [ ] Manual: pre-migration-135 category (legacy row with NULL allow_downloads) → downloads still work (defaults true via fallback)