- 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
68 lines
2.1 KiB
TypeScript
68 lines
2.1 KiB
TypeScript
import { NextResponse } from 'next/server';
|
|
import { isAdminAuthenticated, isAdminEnabled } from '@/lib/admin/auth';
|
|
import { getConfig } from '@/lib/config';
|
|
|
|
interface ImmichAlbumSummary {
|
|
id: string;
|
|
albumName: string;
|
|
description: string;
|
|
albumThumbnailAssetId: string | null;
|
|
assetCount: number;
|
|
createdAt: string;
|
|
updatedAt: string;
|
|
}
|
|
|
|
/** GET: List ALL shared Immich albums (not just allowlisted ones). */
|
|
export async function GET() {
|
|
if (!isAdminEnabled()) {
|
|
return NextResponse.json({ error: 'Admin not enabled' }, { status: 403 });
|
|
}
|
|
if (!(await isAdminAuthenticated())) {
|
|
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
|
}
|
|
|
|
const config = getConfig();
|
|
if (config.needsSetup) {
|
|
return NextResponse.json({ error: 'Immich not configured' }, { status: 503 });
|
|
}
|
|
|
|
try {
|
|
const res = await fetch(`${config.immich.apiUrl}/albums?shared=true`, {
|
|
headers: {
|
|
'x-api-key': config.immich.apiKey,
|
|
Accept: 'application/json',
|
|
},
|
|
});
|
|
|
|
if (!res.ok) {
|
|
return NextResponse.json({ error: `Immich API returned ${res.status}` }, { status: 502 });
|
|
}
|
|
|
|
const albums = (await res.json()) as ImmichAlbumSummary[];
|
|
|
|
// Mark which albums are already configured
|
|
const configuredIds = new Set(config.albums);
|
|
const enriched = albums.map((album) => ({
|
|
id: album.id,
|
|
albumName: album.albumName,
|
|
description: album.description || '',
|
|
thumbnailAssetId: album.albumThumbnailAssetId,
|
|
assetCount: album.assetCount,
|
|
createdAt: album.createdAt,
|
|
updatedAt: album.updatedAt,
|
|
isConfigured: configuredIds.has(album.id),
|
|
}));
|
|
|
|
// Sort: configured first, then by name
|
|
enriched.sort((a, b) => {
|
|
if (a.isConfigured !== b.isConfigured) return a.isConfigured ? -1 : 1;
|
|
return a.albumName.localeCompare(b.albumName);
|
|
});
|
|
|
|
return NextResponse.json({ albums: enriched });
|
|
} catch (err) {
|
|
console.error('[Admin] Failed to fetch albums from Immich:', err);
|
|
return NextResponse.json({ error: 'Failed to connect to Immich' }, { status: 502 });
|
|
}
|
|
}
|