ai-chatbot/lib/storage/canvas-cdn-download.ts

31 lines
2.0 KiB
TypeScript

import "server-only";
import { getDefaultStorageConfig } from "@/lib/db/queries";
import { findImageArchiveDownloadTask } from "./image-archive-db";
import { extractImageProxySourceUrl, inferOriginalImageUrl } from "@/lib/image-url";
export async function resolveCanvasCdnDownload(urls: string[], filename: string) {
const sources = Array.from(new Set(urls.flatMap(url => {
const source = extractImageProxySourceUrl(url) || url;
return [source, inferOriginalImageUrl(source) || source];
})));
const task = await findImageArchiveDownloadTask(sources, true);
const asset = (task?.metadata as { downloadAsset?: { status?: string; url?: string; contentType?: string; bytes?: number } } | null)?.downloadAsset;
if (!task) return { task, direct: null, canPrepareDirect: false };
const config = await getDefaultStorageConfig();
if (config?.type !== "qiniu-kodo" || !config.publicUrl || !config.isEnabled) return { task, direct: null, canPrepareDirect: false };
const ready = asset?.status === "ready" && asset.url && asset.contentType === "image/png";
const source = ready ? asset.url : task.targetUrl;
if (!source) return { task, direct: null, canPrepareDirect: false };
const url = new URL(source);
const base = new URL(config.publicUrl);
const origin = process.env.CANVAS_QINIU_ORIGIN_URL;
if (origin && url.origin === new URL(origin).origin) url.host = base.host;
if (base.protocol !== "https:" || url.origin !== base.origin
|| !(url.pathname.startsWith(base.pathname.replace(/\/$/, "") + "/"))) return { task, direct: null, canPrepareDirect: false };
if (!ready) return { task, direct: null, canPrepareDirect: true };
// attname is interpreted by Kodo/CDN as Content-Disposition, including Chinese
// filenames. Keep all processing/auth parameters intact for correct cache keys.
url.searchParams.set("attname", filename);
return { task, direct: { delivery: "cdn", downloadUrl: url.toString(), filename, bytes: asset?.bytes || 0 }, canPrepareDirect: true };
}