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>
77 lines
2.4 KiB
TypeScript
77 lines
2.4 KiB
TypeScript
import { NextResponse } from 'next/server';
|
|
import { withAdmin } from '@/lib/admin/withAdmin';
|
|
import { getConfig } from '@/lib/config';
|
|
import { immich } from '@/lib/immich';
|
|
import {
|
|
albumPaths,
|
|
buildAltTextReport,
|
|
pickImmichWebUrl,
|
|
type AltTextAlbumInput,
|
|
} from '@/lib/admin/alt-text';
|
|
|
|
/**
|
|
* Immich's *External domain* setting, or null when unset or unreadable. A
|
|
* failure here must not cost the report — the links just fall back.
|
|
*/
|
|
async function immichExternalDomain(apiUrl: string, apiKey: string, timeoutMs: number) {
|
|
try {
|
|
const res = await fetch(`${apiUrl}/server/config`, {
|
|
headers: { 'x-api-key': apiKey, Accept: 'application/json' },
|
|
signal: AbortSignal.timeout(timeoutMs),
|
|
});
|
|
if (!res.ok) return null;
|
|
const body = (await res.json()) as { externalDomain?: string };
|
|
return body.externalDomain ?? null;
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Alt-text coverage of every published album, for the diagnostics page.
|
|
*
|
|
* Goes through `immich.getAlbum()`, so it shares the album cache with the
|
|
* public site: a check right after browsing the gallery costs no Immich calls.
|
|
* An album that fails to load is counted, not fatal — the rest still report.
|
|
*/
|
|
export const GET = withAdmin(async () => {
|
|
const config = getConfig();
|
|
if (config.needsCredentials) {
|
|
return NextResponse.json({ error: 'Not configured' }, { status: 503 });
|
|
}
|
|
|
|
const paths = albumPaths(config.subpages);
|
|
const inputs: AltTextAlbumInput[] = [];
|
|
let unreadable = 0;
|
|
|
|
const [albums, externalDomain] = await Promise.all([
|
|
Promise.all(config.albums.map((id) => immich.getAlbum(id).catch(() => undefined))),
|
|
immichExternalDomain(config.immich.apiUrl, config.immich.apiKey, config.immichTimeoutMs),
|
|
]);
|
|
const immichLink = pickImmichWebUrl(externalDomain, config.immich.apiUrl);
|
|
albums.forEach((album, i) => {
|
|
if (!album) {
|
|
// null is "Immich says it does not exist" — the doctor's album-ids check
|
|
// already reports that. undefined is a failed request.
|
|
if (album === undefined) unreadable++;
|
|
return;
|
|
}
|
|
inputs.push({
|
|
id: album.id,
|
|
name: album.albumName,
|
|
path: paths.get(config.albums[i]) ?? '/',
|
|
assets: album.assets,
|
|
});
|
|
});
|
|
|
|
return NextResponse.json(
|
|
{
|
|
...buildAltTextReport(inputs, config.exif.caption),
|
|
unreadable,
|
|
immichUrl: immichLink.url,
|
|
immichUrlSource: immichLink.source,
|
|
},
|
|
{ headers: { 'Cache-Control': 'no-store' } },
|
|
);
|
|
});
|