mirror of
https://github.com/bulwarkmail/webmail.git
synced 2026-10-31 00:12:39 +00:00
40 lines
1.7 KiB
TypeScript
40 lines
1.7 KiB
TypeScript
import { NextResponse } from 'next/server';
|
|
import { getThemeCSS, getThemeRegistry, getThemeSkin } from './plugin-registry';
|
|
import { logger } from '@/lib/logger';
|
|
|
|
/**
|
|
* Serve an enabled theme's CSS or skin. Clients put the theme's content hash
|
|
* in the URL, so the response may be revalidated freely but never served
|
|
* stale after an update (it used to be cached publicly for an hour).
|
|
*/
|
|
export async function serveThemeAsset(id: string, kind: 'css' | 'skin'): Promise<NextResponse> {
|
|
try {
|
|
if (!/^[a-z0-9][a-z0-9-]*[a-z0-9]$/.test(id)) {
|
|
return NextResponse.json({ error: 'Invalid theme ID' }, { status: 400 });
|
|
}
|
|
const registry = await getThemeRegistry();
|
|
const theme = registry.themes.find(t => t.id === id);
|
|
if (!theme) {
|
|
return NextResponse.json({ error: 'Theme not found' }, { status: 404 });
|
|
}
|
|
if (!theme.enabled) {
|
|
return NextResponse.json({ error: 'Theme is disabled' }, { status: 403 });
|
|
}
|
|
const body = kind === 'css' ? await getThemeCSS(id) : await getThemeSkin(id);
|
|
if (!body) {
|
|
return NextResponse.json({ error: kind === 'css' ? 'Theme CSS not found' : 'Theme has no skin' }, { status: 404 });
|
|
}
|
|
return new NextResponse(body, {
|
|
headers: {
|
|
'Content-Type': 'text/css; charset=utf-8',
|
|
'Cache-Control': 'private, no-cache',
|
|
...(theme.contentHash ? { ETag: `"${theme.contentHash}"` } : {}),
|
|
'X-Content-Type-Options': 'nosniff',
|
|
},
|
|
});
|
|
} catch (error) {
|
|
logger.error('Theme asset serve error', { kind, error: error instanceof Error ? error.message : 'Unknown error' });
|
|
return NextResponse.json({ error: 'Internal server error' }, { status: 500 });
|
|
}
|
|
}
|