Files
immich-folio/app/api/admin/assets/route.ts
Ralksta a3a6319c29 release: v0.16.0
See CHANGELOG.md for the full v0.16.0 entry.

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: ralksta <ralksta@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Ralf <rohlenbo79@gmail.com>
Co-authored-by: Paul Thies <110185070+ImScheinox@users.noreply.github.com>
Co-authored-by: lancetm714 <108419425+lancetm714@users.noreply.github.com>
2026-09-22 14:12:13 +02:00

84 lines
2.4 KiB
TypeScript

/**
* Admin API: Browse Immich assets for the hero picker.
* Supports pagination and fetching favorites first.
*/
import { NextRequest, NextResponse } from 'next/server';
import { withAdmin } from '@/lib/admin/withAdmin';
import { getConfig } from '@/lib/config';
interface ImmichSearchResult {
assets: {
items: Array<{
id: string;
type: string;
originalFileName: string;
thumbhash: string | null;
fileCreatedAt: string;
isFavorite: boolean;
}>;
nextPage: string | null;
};
}
/** GET: Search/browse assets from Immich for hero image selection. */
export const GET = withAdmin(async (request: NextRequest) => {
const config = getConfig();
// Credentials, not `needsSetup`: this route is how the operator picks the
// albums that gallery.yaml is built from, so refusing to run until that file
// exists is a deadlock (#507).
if (config.needsCredentials) {
return NextResponse.json({ error: 'Immich not configured' }, { status: 503 });
}
const { searchParams } = new URL(request.url);
const page = parseInt(searchParams.get('page') || '1', 10);
const favoritesOnly = searchParams.get('favorites') === 'true';
const pageSize = 50;
try {
// Use Immich search/metadata endpoint to query assets
const body: Record<string, unknown> = {
type: 'IMAGE',
size: pageSize,
page,
order: 'desc',
isNotInAlbum: false,
};
if (favoritesOnly) {
body.isFavorite = true;
}
const res = await fetch(`${config.immich.apiUrl}/search/metadata`, {
method: 'POST',
headers: {
'x-api-key': config.immich.apiKey,
'Content-Type': 'application/json',
Accept: 'application/json',
},
body: JSON.stringify(body),
});
if (!res.ok) {
return NextResponse.json({ error: `Immich API returned ${res.status}` }, { status: 502 });
}
const data = (await res.json()) as ImmichSearchResult;
const assets = data.assets.items.map((asset) => ({
id: asset.id,
originalFileName: asset.originalFileName,
fileCreatedAt: asset.fileCreatedAt,
isFavorite: asset.isFavorite,
}));
return NextResponse.json({
assets,
nextPage: data.assets.nextPage ? page + 1 : null,
});
} catch (err) {
console.error('[Admin] Failed to fetch assets from Immich:', err);
return NextResponse.json({ error: 'Failed to connect to Immich' }, { status: 502 });
}
});