57 lines
1.5 KiB
TypeScript
57 lines
1.5 KiB
TypeScript
const IMAGE_PROXY_PATH = "/api/image-proxy";
|
|
|
|
type ImageProxyFormat = "webp" | "avif" | "jpeg" | "png";
|
|
|
|
export type ImageProxyOptions = {
|
|
width?: number;
|
|
quality?: number;
|
|
format?: ImageProxyFormat;
|
|
};
|
|
|
|
function clamp(value: number, min: number, max: number): number {
|
|
return Math.max(min, Math.min(max, value));
|
|
}
|
|
|
|
function normalizeWidth(width?: number): number | undefined {
|
|
if (!Number.isFinite(width) || !width) return undefined;
|
|
return clamp(Math.round(width), 64, 4096);
|
|
}
|
|
|
|
function normalizeQuality(quality?: number): number | undefined {
|
|
if (!Number.isFinite(quality) || !quality) return undefined;
|
|
return clamp(Math.round(quality), 40, 100);
|
|
}
|
|
|
|
function isAbsoluteHttpUrl(url: string): boolean {
|
|
return /^https?:\/\//i.test(url);
|
|
}
|
|
|
|
function isBypassUrl(url: string): boolean {
|
|
return (
|
|
!url ||
|
|
url.startsWith("data:") ||
|
|
url.startsWith("blob:") ||
|
|
url.startsWith(IMAGE_PROXY_PATH) ||
|
|
!isAbsoluteHttpUrl(url)
|
|
);
|
|
}
|
|
|
|
export function buildImageProxyUrl(url: string, options: ImageProxyOptions = {}): string {
|
|
const source = String(url || "").trim();
|
|
if (isBypassUrl(source)) {
|
|
return source;
|
|
}
|
|
|
|
const width = normalizeWidth(options.width);
|
|
const quality = normalizeQuality(options.quality);
|
|
const format = options.format;
|
|
|
|
const params = new URLSearchParams();
|
|
params.set("url", source);
|
|
if (width) params.set("w", String(width));
|
|
if (quality) params.set("q", String(quality));
|
|
if (format) params.set("f", format);
|
|
|
|
return `${IMAGE_PROXY_PATH}?${params.toString()}`;
|
|
}
|