- 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
- 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
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>
- 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>
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>
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>
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>
Upgraded `encodeAssetId` and `decodeAssetId` to use `aes-256-gcm` (Authenticated Encryption) instead of `aes-256-cbc`.
AES-CBC lacks authentication, leaving it potentially susceptible to padding oracle attacks or ciphertext manipulation.
The new token adds a `v2:` prefix which strictly enforces AES-GCM while supporting legacy fallback for cached urls.
Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
- Added comprehensive `aria-label` to the parent `<Link>` in `SubpageGridView`.
- Hid redundant textual child elements using `aria-hidden="true"`.
- Removed redundant `alt` text from decorative thumbnail `Image`.
- Removed unused `albumMap` prop in `AlbumGrid`.
Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
- Implement an in-memory `Map` to cache parsed YAML objects.
- Use `structuredClone` to prevent caller mutations from corrupting the cached object state.
- Implement negative caching for missing files.
Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
Avoids unnecessary O(N) memory allocation and copy by returning `Buffer.from(...)` directly since Buffer inherits from Uint8Array in Node.js.
Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
Add Promise deduplication (request coalescing) to the `getAssetInfo`
method in `ImmichClient` (`lib/immich.ts`) to prevent concurrent
identical requests from firing multiple times to the Immich server.
This is particularly helpful when calling `Promise.all` on multiple
assets concurrently (e.g. hero images on the homepage or multiple
album cover placeholder hashes).
Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
* Added aria-hidden="true" to decorative SVG icons within interactive elements (buttons, links) in Lightbox, ThemeToggle, Footer, BackLink, and MapView.
* Added role="alert" and aria-live="polite" to the MapView loading indicator.
* Documented learning in .Jules/palette.md
Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
Adds an `aria-label` to the `BackLink` component to improve screen reader accessibility. By default, it was an icon next to text that functioned as a single link, but providing an explicit `aria-label` makes its target clearer.
Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
Adds `role="dialog"`, `aria-modal="true"`, and `aria-label` to the `.overlay` container to improve screen reader accessibility. Also adds `aria-expanded` and `aria-controls` to the EXIF info toggle button, linking it to the newly ID'd `#exif-panel`.
Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>