Commit Graph

196 Commits

Author SHA1 Message Date
dependabot[bot]
c5645a49fd chore(deps): bump the react group across 1 directory with 3 updates (#223)
Bumps the react group with 3 updates in the / directory: [react](https://github.com/facebook/react/tree/HEAD/packages/react), [@types/react](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react) and [react-dom](https://github.com/facebook/react/tree/HEAD/packages/react-dom).


Updates `react` from 19.2.5 to 19.2.6
- [Release notes](https://github.com/facebook/react/releases)
- [Changelog](https://github.com/facebook/react/blob/main/CHANGELOG.md)
- [Commits](https://github.com/facebook/react/commits/v19.2.6/packages/react)

Updates `@types/react` from 19.2.14 to 19.2.15
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react)

Updates `react-dom` from 19.2.5 to 19.2.6
- [Release notes](https://github.com/facebook/react/releases)
- [Changelog](https://github.com/facebook/react/blob/main/CHANGELOG.md)
- [Commits](https://github.com/facebook/react/commits/v19.2.6/packages/react-dom)

Updates `@types/react` from 19.2.14 to 19.2.15
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react)

---
updated-dependencies:
- dependency-name: "@types/react"
  dependency-version: 19.2.15
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: react
- dependency-name: "@types/react"
  dependency-version: 19.2.15
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: react
- dependency-name: react
  dependency-version: 19.2.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: react
- dependency-name: react-dom
  dependency-version: 19.2.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: react
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-31 10:03:14 +02:00
Ralksta
a91269eb42 🎨 Palette: Improve Lightbox counter accessibility (#235)
Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
2026-05-31 10:03:08 +02:00
Ralksta
746e9ae4c9 ⚡ Bolt: Memoize auth checks in Map API (#236)
Introduced a request-scoped `Map` (`authCache`) in `app/api/map/route.ts` to memoize `isAuthenticated` calls.
The `getMapData` loop iterates over all albums across all locations. Albums within the same subpage slug trigger repeated `isAuthenticated` checks, which execute expensive HMAC cryptographic operations, causing performance degradation for large datasets.
Eliminates redundant cryptographic operations during map data fetching, reducing CPU utilization and response times for the Map API.

Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
2026-05-31 10:03:03 +02:00
Ralksta
c2fa8e7596 🛡️ Sentinel: Fix Stored XSS via Content-Type spoofing in asset proxies (#237)
Sanitized the Content-Type header on image and video proxy routes (`/api/image/*`, `/api/video/*`). The content type is forced to lowercase, validated against a safe prefix (`image/`, `video/`, `audio/`), and restricted to block `svg` and `xml`. Any invalid type falls back to `application/octet-stream`. This prevents an attacker from uploading an SVG file containing a malicious script and triggering a Stored XSS attack via these proxy endpoints, which are excluded from the global CSP.

Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
2026-05-31 10:02:59 +02:00
ralksta
6b7fb09097 feat: use album hero image as grid thumbnail on subpage overview 2026-05-30 15:40:29 +02:00
ralksta
117685e47b fix: reload config without docker restart – mtime-based yaml cache instead of immortal in-memory cache 2026-05-30 15:38:55 +02:00
ralksta
19c3727afc feat: per-album hero image – picker from album assets, fullbleed banner on detail page 2026-05-30 15:33:43 +02:00
ralksta
af5da8f316 fix: use preview size for grid thumbnails to avoid blur on portrait images 2026-05-30 15:21:28 +02:00
ralksta
6f4cb467fd fix: revalidate all pages on admin save so hero/config changes appear immediately
Add revalidatePath('/', 'layout') calls to gallery save, settings save,
and reload routes. This tells Next.js to purge any cached page responses
so the next browser refresh picks up the new hero images and config.
2026-05-29 18:10:48 +02:00
ralksta
066e6f5af6 fix: asset picker grid overflow - images no longer stack on scroll 2026-05-29 18:03:00 +02:00
ralksta
e8e65e7172 feat(admin): add asset picker with favorites, drag-and-drop reordering, preview links, and keyboard shortcuts
- Asset Picker: Visual browser for hero images with Favorites tab (shown first)
  and All Photos tab. Includes infinite scroll and UUID fallback input for
  assets not easily found in the grid.
- Drag & Drop: All lists (hero images, standalone albums, subpage albums,
  subpage tiles) are now reorderable via drag handles using @dnd-kit.
- Preview Links: 'Preview Site' button in save bar, '↗ Site' in header, and
  per-subpage preview links with URL slug preview shown inline.
- Keyboard Shortcuts: ⌘+S / Ctrl+S to save in both Pages and Settings tabs.
- Unsaved Changes Guard: beforeunload warning prevents accidental data loss.
- New API route /api/admin/assets for browsing Immich assets with pagination
  and favorites filter via search/metadata endpoint.
2026-05-29 17:59:25 +02:00
ralksta
2e4cde4b6e fix: clear album cache on admin save so changes appear immediately
When saving gallery/settings via admin, the config cache was invalidated
but the Immich album cache (which stores resolved title overrides and
descriptions) was not cleared. This meant changes only appeared after
the TTL expired or a manual 'Reload' click.

Now both gallery and settings saves also call immich.invalidateAll()
to flush the album cache, making changes visible on next page load.
2026-05-29 17:47:08 +02:00
ralksta
3a8a890930 fix: resolve permission error when saving gallery config in Docker
The container was running as 'nextjs' (UID 1001) but bind-mounted
./content from the host has host-user ownership, making it unwritable.

Solution: start as root, fix /app/content ownership in entrypoint,
then drop privileges to nextjs via su-exec before running the app.
2026-05-29 17:40:02 +02:00
ralksta
4416502e14 feat: enlarge admin thumbnails for hero, album, and subpage grids
- Hero grid: 140px → 220px min tile width
- Album grid: 160px → 240px min tile width
- Subpage grid: 140px → 200px min tile width
- Picker thumbnails: 40px → 52px
- Admin main max-width: 960px → 1120px for more space
- Slightly larger overlay buttons and index badges
2026-05-29 17:37:34 +02:00
ralksta
f820a7b683 fix: use docker-compose.override.yml for server-specific config
- Remove :ro and network: host from base docker-compose.yml
- Add docker-compose.override.yml.example with network/healthcheck template
- Add docker-compose.override.yml to .gitignore

This allows git pull without conflicts on the server. Server-specific
settings (external networks, healthchecks) go in the override file.
2026-05-29 17:33:19 +02:00
ralksta
01dde84216 fix: remove read-only flag from content volume to allow admin saves
The content volume was mounted as :ro (read-only) which prevented
the admin panel from writing gallery.yaml and settings.yaml changes.
Removed the :ro flag so the admin panel can save config and create backups.
2026-05-29 17:30:28 +02:00
ralksta
0457a74dd1 feat: redesign subpages as collapsible tile grid for better overview
- Subpages shown as compact visual tiles in a grid (like folders)
- Each tile shows the first album's cover image + name + album count
- Click a tile to expand/edit that subpage's details below
- Active tile highlighted with accent border
- Only one subpage expanded at a time — no more endless scrolling
- Move arrows changed to ← → (horizontal) matching the grid layout
- Smooth slide-down animation when expanding a subpage
2026-05-29 17:29:26 +02:00
ralksta
502ca1d32c feat: redesign hero section as visual image grid with large previews
- Hero images shown as landscape tiles (16:10 aspect ratio) in a grid
- Full cover images instead of tiny thumbnails with UUID text
- Remove button appears on hover (red X, top-right corner)
- Slide number badge shown bottom-left for ordering reference
- Much more visual and recognizable at a glance
2026-05-29 17:26:54 +02:00
ralksta
276e9b3d68 feat: redesign admin album cards as grid tiles with large cover images
Replace the list-style album cards with a visual grid layout:
- Albums shown as tiles with square cover images (aspect-ratio: 1)
- Grid auto-fills with min 160px columns
- Edit/remove buttons appear as overlay on hover
- Album name and photo count shown below the cover
- Much more compact — multiple albums visible without scrolling
2026-05-29 17:24:53 +02:00
ralksta
b4fa37d680 fix: admin panel light/dark theme support, improved contrast, and backup confirmation
a) Admin panel now respects the site's light/dark theme toggle via CSS
   custom properties that respond to [data-theme='light'] on <html>.
   All hardcoded dark colors replaced with semantic CSS variables.

b) Improved contrast in dark mode: secondary text changed from #666/#555
   to #a0a0a0, muted text from #555 to #707070. All text is now clearly
   readable against dark backgrounds.

c) Backup system was already implemented in yaml-service.ts (creates
   timestamped backups in content/.backups/ before each save, keeps last
   10 versions). Now the UI shows a confirmation message that a backup
   was created after each successful save.
2026-05-29 17:20:56 +02:00
ralksta
4a0b57b932 feat: add thumbnail previews to admin panel for albums and hero images
- Add /api/admin/thumbnail/[id] proxy endpoint for serving Immich thumbnails
- Show album cover thumbnails in AlbumCard components (subpages + standalone)
- Show actual image thumbnails in AlbumPicker modal instead of emoji placeholders
- Show small thumbnail previews for hero image entries
- Add corresponding CSS styles for all thumbnail elements
2026-05-29 17:14:58 +02:00
ralksta
9b053e1648 fix: exclude /admin from CSP middleware to prevent script blocking
The admin panel is a static page with client-side React that gets
prerendered at build time. The nonce-based CSP from middleware doesn't
work for static pages because the nonce is generated per-request but
the HTML is served from cache without it. Exclude /admin from the
middleware matcher so its scripts can load normally.
2026-05-29 17:09:53 +02:00
ralksta
e723358514 chore: add Claude Code config and brainstorm notes 2026-05-29 17:05:18 +02:00
ralksta
5dffadbe3c feat: add video playback support in lightbox and photo grid
- Lightbox renders <video> with controls for video assets
- PhotoGrid shows video indicator overlay on video thumbnails
- Skip video preloading in lightbox (streams on demand)
- Add video-related CSS styles
- Update catch-all page to pass video URLs to components
2026-05-29 17:05:12 +02:00
ralksta
e9063d3a71 feat: add video streaming proxy and webhook cache invalidation
- Add /api/video/[id] route with Range header support for seeking
- Add /api/webhook route for Immich event-driven cache invalidation
- Add streamVideo() to ImmichClient with 206 Partial Content support
- Support album description overrides in gallery.yaml
- Add videoUrl() helper to lib/urls.ts
- Export cache.delete() and cache.clear() methods
2026-05-29 17:04:57 +02:00
ralksta
ce7c1742be docs: add admin panel documentation and update README
- Add docs/admin-panel.md with full guide (setup, features, security, Docker)
- Update README with admin panel feature listing and setup instructions
- Update gallery-config.md with tip about visual alternative
- Update CLAUDE.md with admin architecture and API routes
2026-05-29 17:04:41 +02:00
ralksta
9e52d7a0b8 feat: add visual admin panel with page builder and settings editor
- Add /admin route with password-protected visual page builder
- Page Builder: manage hero images, standalone albums, subpages, and sections
- Album Picker: browse all shared Immich albums with search and filtering
- Settings Editor: theme, grid, footer, legal, SEO configuration
- YAML service with atomic writes and automatic backups (content/.backups/)
- HMAC-signed admin sessions (24h expiry, HttpOnly cookies)
- API endpoints: /api/admin/{auth,gallery,settings,albums,reload}
- Enable with ADMIN_PASSWORD env var; disabled by default
- invalidateConfigCache() export for hot-reload after saves
2026-05-29 17:04:37 +02:00
ralksta
5887706775 fix: restore @ts-expect-error for request.ip in getClientIp
The property was removed from NextRequest types in Next.js 15+ but is
still populated by hosting platforms (Vercel, Netlify, etc.).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-06 12:15:33 +02:00
ralksta
771cee517a security: implement secure IP retrieval with TRUSTED_PROXIES support (closes #139) 2026-05-06 12:10:20 +02:00
Ralksta
3c5715e0b0 Merge PR #141
chore(deps): bump the nextjs group with 2 updates
2026-05-06 12:00:01 +02:00
Ralksta
773e62f579 Merge PR #142
chore(deps-dev): bump prettier from 3.8.2 to 3.8.3
2026-05-06 11:59:58 +02:00
Ralksta
3e310f142e Merge PR #147
🛡️ Sentinel: [MEDIUM] Fix TOCTOU vulnerability in file read
2026-05-06 11:59:55 +02:00
Ralksta
0aa775a08d Merge PR #160
🛡️ Sentinel: [security improvement]
2026-05-06 11:59:51 +02:00
Ralksta
fb32a8cabc Merge PR #164
🛡️ Sentinel: [HIGH] Fix scrypt lockout and timing fallback
2026-05-06 11:59:48 +02:00
Ralksta
3a4c65276d Merge PR #165
⚡ Bolt: [performance improvement] Optimize map bucketing memory and deduplicate getMapData promises
2026-05-06 11:59:45 +02:00
Ralksta
08207e2f16 Merge PR #163
🎨 Palette: Improve PhotoGrid screen reader accessibility
2026-05-06 11:59:42 +02:00
google-labs-jules[bot]
59321d88ca ⚡ Bolt: [performance improvement] Optimize map bucketing memory and deduplicate getMapData promises
Co-authored-by: ralksta <16941050+ralksta@users.noreply.github.com>
2026-05-05 10:44:12 +00:00
google-labs-jules[bot]
8ab1a3d5ea Fix scrypt hash generation in plaintext fallback
- Adjusted `generateScryptHash` parameter in plaintext fallback logic in `lib/auth.ts` to use `storedPassword` instead of the intermediate hashed value. This prevents users from getting locked out when they adopt the recommended hash.
- Enhanced plaintext fallback verification to hash `password` and `storedPassword` via keyed HMAC before executing `crypto.timingSafeEqual` to avoid potential timing attacks more securely.

Co-authored-by: ralksta <16941050+ralksta@users.noreply.github.com>
2026-05-05 10:34:52 +00:00
google-labs-jules[bot]
ebf60b12c3 Improve PhotoGrid screen reader accessibility
Co-authored-by: ralksta <16941050+ralksta@users.noreply.github.com>
2026-05-05 10:24:39 +00:00
google-labs-jules[bot]
71381342d6 🛡️ Sentinel: [security improvement]\n\nAdded rate limiting to the /api/health endpoint using the shared checkRateLimit utility. This prevents potential DoS attacks by restricting the number of requests per IP, utilizing the configured rateLimitRpm.
Co-authored-by: ralksta <16941050+ralksta@users.noreply.github.com>
2026-05-03 10:20:32 +00:00
google-labs-jules[bot]
8c573314d6 fix(about): resolve TOCTOU vulnerability by migrating to async file I/O
Co-authored-by: ralksta <16941050+ralksta@users.noreply.github.com>
2026-04-28 11:16:18 +00:00
dependabot[bot]
a344228fb9 chore(deps-dev): bump prettier from 3.8.2 to 3.8.3
Bumps [prettier](https://github.com/prettier/prettier) from 3.8.2 to 3.8.3.
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prettier/prettier/compare/3.8.2...3.8.3)

---
updated-dependencies:
- dependency-name: prettier
  dependency-version: 3.8.3
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-27 07:48:48 +00:00
dependabot[bot]
64eff62db4 chore(deps): bump the nextjs group with 2 updates
Bumps the nextjs group with 2 updates: [next](https://github.com/vercel/next.js) and [eslint-config-next](https://github.com/vercel/next.js/tree/HEAD/packages/eslint-config-next).


Updates `next` from 16.2.1 to 16.2.4
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/compare/v16.2.1...v16.2.4)

Updates `eslint-config-next` from 16.2.1 to 16.2.4
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/commits/v16.2.4/packages/eslint-config-next)

---
updated-dependencies:
- dependency-name: next
  dependency-version: 16.2.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: nextjs
- dependency-name: eslint-config-next
  dependency-version: 16.2.4
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: nextjs
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-27 07:48:20 +00:00
Ralksta
56f0305763 perf(map): chunk album fetching to prevent OOM errors (#138)
Replaced the unbounded `Promise.all` in `getMapData` with a chunked
fetching approach. This prevents Out of Memory crashes by limiting
the concurrent fetching of large arrays of assets to chunks of 10.

Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
2026-04-24 22:12:45 +02:00
Ralksta
f18d9ae3d9 🛡️ Sentinel: [CRITICAL] Fix rate limit DoS vulnerability (#136)
Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
2026-04-24 22:12:42 +02:00
Ralksta
93ac1c20e8 Add [role="button"]:focus-visible to app/tokens.css for proper keyboard focus indication on custom button elements. (#130)
Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
2026-04-21 22:52:08 +02:00
dependabot[bot]
a61c577a94 chore(deps-dev): bump @types/node from 20.19.37 to 20.19.39 (#84)
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 20.19.37 to 20.19.39.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 20.19.39
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-21 22:51:55 +02:00
Ralksta
431e7c0553 🛡️ Sentinel: [improvement] Add Content Security Policy Report-Only header (#86)
Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
2026-04-21 22:51:50 +02:00
Ralksta
006ba539ab fix(rate-limit): mitigate cache flooding vulnerability (#94)
Update in-memory rate limiter to fail secure (reject new requests) when maximum capacity is reached, rather than evicting the oldest tracking entry. This prevents an attacker from bypassing their rate limit by flooding the store with spoofed `x-forwarded-for` IPs to intentionally trigger cache eviction. Also cleaned up temporary files and updated tests to verify the behavior.

Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
2026-04-21 22:51:46 +02:00
Ralksta
bb7c1aa1bd Improve rate-limit headers in auth API (#102)
Destructures `remaining` and `resetAt` from `checkRateLimit` and adds standard rate-limiting headers (`Retry-After`, `X-RateLimit-Limit`, `X-RateLimit-Remaining`) to the 429 Too Many Requests response.

Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
2026-04-21 22:51:42 +02:00