feat: implement album-level password protection

This commit is contained in:
ralksta
2026-04-07 23:04:03 +02:00
parent e739fdb518
commit 17398dea39
16 changed files with 168 additions and 98 deletions

View File

@@ -107,18 +107,26 @@ function toPhotoItems(assets: ImmichAsset[], showExif: boolean): PhotoItem[] {
}
/**
* Returns a PasswordGate element if the slug is protected and not yet
* authenticated, otherwise returns null. Centralises the duplicated
* cookie-check boilerplate.
* Returns a PasswordGate element if the key (slug or ID) is protected and
* not yet authenticated, otherwise returns null.
*/
async function gateIfProtected(slug: string): Promise<React.ReactElement | null> {
if (!isProtected(slug)) return null;
async function gateIfProtected(
key: string,
type: 'subpage' | 'album' = 'subpage',
titleOverride?: string,
): Promise<React.ReactElement | null> {
if (!isProtected(key, type)) return null;
const cookieStore = await cookies();
const getCookie = (name: string) => cookieStore.get(name)?.value;
if (isAuthenticated(slug, getCookie)) return null;
const subpageData = await immich.getSubpageAlbums(slug);
const title = subpageData?.subpage.name ?? slug;
return <PasswordGate slug={slug} title={title} />;
if (isAuthenticated(key, getCookie, type)) return null;
let title = titleOverride || key;
if (!titleOverride && type === 'subpage') {
const subpageData = await immich.getSubpageAlbums(key);
title = subpageData?.subpage.name ?? key;
}
return <PasswordGate slug={key} title={title} type={type} />;
}
export default async function PathPage({ params }: PathPageProps) {
@@ -162,6 +170,10 @@ export default async function PathPage({ params }: PathPageProps) {
const images = toPhotoItems(album.assets, config.exifOnHover);
// Password gate for protected albums
const albumGate = await gateIfProtected(album.id, 'album', album.albumName);
if (albumGate) return albumGate;
return (
<AlbumDetailView
album={album}
@@ -197,6 +209,10 @@ export default async function PathPage({ params }: PathPageProps) {
const images = toPhotoItems(album.assets, config.exifOnHover);
// Password gate for protected albums
const albumGate = await gateIfProtected(album.id, 'album', album.albumName);
if (albumGate) return albumGate;
return (
<AlbumDetailView
album={album}
@@ -241,6 +257,10 @@ export default async function PathPage({ params }: PathPageProps) {
const images = toPhotoItems(album.assets, config.exifOnHover);
// Password gate for protected albums
const albumGate = await gateIfProtected(album.id, 'album', album.albumName);
if (albumGate) return albumGate;
return (
<AlbumDetailView
album={album}