mirror of
https://github.com/KuekHaoYang/KVideo.git
synced 2026-08-12 23:33:43 +08:00
104 lines
2.6 KiB
TypeScript
104 lines
2.6 KiB
TypeScript
import fs from 'fs';
|
|
import path from 'path';
|
|
|
|
export const DEFAULT_SITE_ICON_PATH = '/icon.png';
|
|
const LEGACY_SITE_ICON_ROUTE = '/api/site-icon';
|
|
|
|
function getMimeType(filePath: string): string {
|
|
switch (path.extname(filePath).toLowerCase()) {
|
|
case '.avif':
|
|
return 'image/avif';
|
|
case '.gif':
|
|
return 'image/gif';
|
|
case '.ico':
|
|
return 'image/x-icon';
|
|
case '.jpg':
|
|
case '.jpeg':
|
|
return 'image/jpeg';
|
|
case '.png':
|
|
return 'image/png';
|
|
case '.svg':
|
|
return 'image/svg+xml';
|
|
case '.webp':
|
|
return 'image/webp';
|
|
default:
|
|
return 'application/octet-stream';
|
|
}
|
|
}
|
|
|
|
function getIconFileCandidates(filePath: string): string[] {
|
|
if (path.isAbsolute(filePath)) {
|
|
return [filePath];
|
|
}
|
|
|
|
const currentWorkingDirectory = process.cwd();
|
|
const candidates = [path.join(currentWorkingDirectory, filePath)];
|
|
const standaloneSuffix = `${path.sep}.next${path.sep}standalone`;
|
|
|
|
if (currentWorkingDirectory.endsWith(standaloneSuffix)) {
|
|
candidates.push(path.resolve(currentWorkingDirectory, '..', '..', filePath));
|
|
}
|
|
|
|
return [...new Set(candidates)];
|
|
}
|
|
|
|
function normalizeIconUrl(iconUrl?: string | null): string | null {
|
|
const value = iconUrl?.trim();
|
|
|
|
if (!value) {
|
|
return null;
|
|
}
|
|
|
|
if (value === LEGACY_SITE_ICON_ROUTE) {
|
|
console.warn('[SiteIcon] Ignoring legacy /api/site-icon path.');
|
|
return null;
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
async function resolveIconFileAsDataUrl(iconFile: string): Promise<string | null> {
|
|
const resolvedFilePaths = getIconFileCandidates(iconFile);
|
|
let lastError: unknown = null;
|
|
|
|
for (const resolvedFilePath of resolvedFilePaths) {
|
|
try {
|
|
const fileBuffer = await fs.promises.readFile(resolvedFilePath);
|
|
const mimeType = getMimeType(resolvedFilePath);
|
|
return `data:${mimeType};base64,${fileBuffer.toString('base64')}`;
|
|
} catch (error) {
|
|
lastError = error;
|
|
}
|
|
}
|
|
|
|
console.warn(
|
|
`[SiteIcon] Failed to read SITE_ICON_FILE from any supported path: ${resolvedFilePaths.join(', ')}`,
|
|
lastError,
|
|
);
|
|
return null;
|
|
}
|
|
|
|
export async function resolveSiteIconSrc(): Promise<string> {
|
|
const resolvedRuntimeIcon = normalizeIconUrl(process.env.SITE_ICON_RESOLVED_URL);
|
|
if (resolvedRuntimeIcon) {
|
|
return resolvedRuntimeIcon;
|
|
}
|
|
|
|
const runtimeIconUrl = normalizeIconUrl(
|
|
process.env.SITE_ICON_URL?.trim() || process.env.NEXT_PUBLIC_SITE_ICON_URL?.trim(),
|
|
);
|
|
if (runtimeIconUrl) {
|
|
return runtimeIconUrl;
|
|
}
|
|
|
|
const iconFile = process.env.SITE_ICON_FILE?.trim();
|
|
if (iconFile) {
|
|
const dataUrl = await resolveIconFileAsDataUrl(iconFile);
|
|
if (dataUrl) {
|
|
return dataUrl;
|
|
}
|
|
}
|
|
|
|
return DEFAULT_SITE_ICON_PATH;
|
|
}
|