Compare commits
No commits in common. "main" and "codex/qiniu-direct-delivery-20261112" have entirely different histories.
main
...
codex/qini
@ -4,7 +4,7 @@ import { fetchImageBytes, cacheImageBytes } from "./image-byte-cache";
|
||||
import { normalizeCanvasDownloadAsset } from "@/lib/canvas/photoshop-safe-download";
|
||||
import { claimImageDownloadTask, saveImageDownloadAsset, getImageDownloadTasks, hasPendingImageDownloads } from "./image-archive-db";
|
||||
import { uploadFile } from "./service";
|
||||
import { getReadyQiniuImageDownload, prepareQiniuWebpDownload } from "./qiniu-image-archive";
|
||||
import { getReadyQiniuImageDownload } from "./qiniu-image-archive";
|
||||
import { getDownloadFailure } from "@/lib/canvas/download-error";
|
||||
import { createLogger } from "@/lib/observability/logger";
|
||||
import { trackRuntimeWork } from "@/lib/observability/active-work";
|
||||
@ -21,11 +21,6 @@ export async function prepareImageArchiveDownload(id: string) {
|
||||
await saveImageDownloadAsset(id, ready);
|
||||
return;
|
||||
}
|
||||
const converted = await prepareQiniuWebpDownload(task.targetUrl, task.id).catch(() => undefined);
|
||||
if (converted) {
|
||||
await saveImageDownloadAsset(id, converted);
|
||||
return;
|
||||
}
|
||||
const source = await fetchImageBytes(task.targetUrl);
|
||||
phase = "convert";
|
||||
const asset = await normalizeCanvasDownloadAsset({
|
||||
|
||||
@ -91,28 +91,6 @@ export async function archiveImageThroughQiniu(sourceUrl: string, archiveId: str
|
||||
|
||||
// The archive task owns this object key, including retries after a timeout.
|
||||
const pathname = `uploads/ai-generated-archive-${archiveId}`;
|
||||
return fetchImageIntoQiniu(sourceUrl, pathname, target!);
|
||||
}
|
||||
|
||||
/** Prepare uploaded WebP images inside Kodo, retaining their embedded colour profile. */
|
||||
export async function prepareQiniuWebpDownload(url: string, taskId: string) {
|
||||
const target = await resolveS3UploadTarget();
|
||||
const config = target?.config;
|
||||
if (config?.provider !== "qiniu-kodo" || config.region !== "cn-east-1" || !config.publicUrl) return;
|
||||
const base = buildS3PublicUrl(config, "");
|
||||
if (!url.startsWith(base) || !/^uploads\/(?:[\w.-]+\/)*[\w.-]+\.webp$/i.test(url.slice(base.length))) return;
|
||||
// No resizing or lossy re-encoding; apply orientation before removing EXIF.
|
||||
const source = `${url}?imageMogr2/auto-orient/format/png/strip/meta-keep-list/${encodeQiniu("icc")}`;
|
||||
const result = await fetchImageIntoQiniu(source, `uploads/canvas-download-${taskId}.png`, target!);
|
||||
return result.downloadAsset;
|
||||
}
|
||||
|
||||
async function fetchImageIntoQiniu(
|
||||
sourceUrl: string,
|
||||
pathname: string,
|
||||
target: NonNullable<Awaited<ReturnType<typeof resolveS3UploadTarget>>>,
|
||||
) {
|
||||
const config = target.config;
|
||||
const path = `/fetch/${encodeQiniu(sourceUrl)}/to/${encodeQiniu(`${config.bucket}:${pathname}`)}`;
|
||||
const contentType = "application/x-www-form-urlencoded";
|
||||
const signingData = `POST ${path}\nHost: ${QINIU_EAST_CHINA_IO}\nContent-Type: ${contentType}\n\n`;
|
||||
|
||||
@ -2,7 +2,7 @@ import { defineConfig } from "@playwright/test";
|
||||
|
||||
export default defineConfig({
|
||||
testDir: "./routes",
|
||||
testMatch: /(?:qiniu-direct-paths|image-delivery-qiniu|image-archive-flow|original-image-download|canvas-download-speed|canvas-download-recovery)\.test\.ts/,
|
||||
testMatch: /(?:qiniu-direct-paths|image-delivery-qiniu|image-archive-flow|original-image-download|canvas-download-speed)\.test\.ts/,
|
||||
workers: 1,
|
||||
reporter: "list",
|
||||
timeout: 30_000,
|
||||
|
||||
@ -124,7 +124,7 @@ test("requested downloads are prioritized with two workers before scanning backg
|
||||
},
|
||||
"./image-byte-cache": { fetchImageBytes: async () => { active++; maximum = Math.max(maximum, active); await new Promise<void>(resolve => releases.push(resolve)); active--; return { buffer: bytes, contentType: "image/png" }; }, cacheImageBytes: async () => {} },
|
||||
"@/lib/canvas/photoshop-safe-download": { normalizeCanvasDownloadAsset: async () => ({ ok: true, bytes, contentType: "image/png" }) },
|
||||
"./qiniu-image-archive": { getReadyQiniuImageDownload: async () => undefined, prepareQiniuWebpDownload: async () => undefined },
|
||||
"./qiniu-image-archive": { getReadyQiniuImageDownload: async () => undefined },
|
||||
"./service": { uploadFile() { throw new Error("Compatible PNG must retain its object"); } },
|
||||
})("lib/storage/image-download-preparation.ts");
|
||||
worker.scheduleImageDownloadPreparation("one"); worker.scheduleImageDownloadPreparation("two"); worker.scheduleImageDownloadPreparation("three");
|
||||
|
||||
@ -137,7 +137,7 @@ test("PNG preparation failure leaves the original archived and schedules a retry
|
||||
const module = serverLoader({
|
||||
"./image-archive-db": { claimImageDownloadTask: async () => ({ id: "task", targetUrl: "https://cdn.example/original.png", metadata: { downloadAsset: { attempts: 2 } } }), saveImageDownloadAsset: async (_id: string, asset: any) => { saved = asset; } },
|
||||
"./image-byte-cache": { fetchImageBytes: async () => { throw new Error("storage offline"); } },
|
||||
"./qiniu-image-archive": { getReadyQiniuImageDownload: async () => undefined, prepareQiniuWebpDownload: async () => undefined },
|
||||
"./qiniu-image-archive": { getReadyQiniuImageDownload: async () => undefined },
|
||||
"./service": {},
|
||||
})("lib/storage/image-download-preparation.ts");
|
||||
await module.prepareImageArchiveDownload("task");
|
||||
|
||||
@ -113,54 +113,3 @@ test("saved preview URLs redirect to an archived CDN thumbnail without reading t
|
||||
expect(response.status).toBe(307);
|
||||
expect(response.headers.get("location")).toBe("https://cdn.example/uploads/a.png?imageView2/2/w/360/format/webp/q/88");
|
||||
});
|
||||
|
||||
test("uploaded WebP is converted inside Qiniu with orientation and ICC retention", async () => {
|
||||
const png = await sharp({ create: { width: 20, height: 12, channels: 4, background: "#22887780" } }).withIccProfile("p3").png().toBuffer();
|
||||
let calls = 0;
|
||||
const module = serverLoader({
|
||||
"@/lib/storage/service": { resolveS3UploadTarget: async () => ({ config }),
|
||||
buildS3PublicUrl: (_: unknown, key: string) => `${config.publicUrl}/${key}`,
|
||||
createS3Client: () => ({ destroy() {}, send: async (cmd: any) => {
|
||||
expect(cmd.input.Key).toBe("uploads/canvas-download-task-1.png");
|
||||
expect(cmd.input.Range).toBe("bytes=0-65535");
|
||||
return { $metadata: { httpStatusCode: 206 }, ContentType: "image/png", ContentLength: png.length,
|
||||
ContentRange: `bytes 0-${png.length - 1}/${png.length}`, Body: { transformToByteArray: async () => png } };
|
||||
} }) },
|
||||
fetch: async (url: string) => {
|
||||
calls++;
|
||||
const encoded = new URL(url).pathname.split("/fetch/")[1].split("/to/")[0];
|
||||
const source = Buffer.from(encoded, "base64url").toString();
|
||||
expect(source).toBe("https://cdn.example/uploads/direct/user/source.webp?imageMogr2/auto-orient/format/png/strip/meta-keep-list/aWNj");
|
||||
return Response.json({ key: "uploads/canvas-download-task-1.png", fsize: png.length, mimeType: "image/png" });
|
||||
},
|
||||
})("lib/storage/qiniu-image-archive.ts");
|
||||
const source = "https://cdn.example/uploads/direct/user/source.webp";
|
||||
const ready = await module.prepareQiniuWebpDownload(source, "task-1");
|
||||
expect(ready).toMatchObject({ status: "ready", url: "https://cdn.example/uploads/canvas-download-task-1.png", bytes: png.length });
|
||||
for (const excluded of [source + "?token=signed", source.replace("cdn.example", "other.example"), source.replace(".webp", ".png")]) {
|
||||
expect(await module.prepareQiniuWebpDownload(excluded, "task-1")).toBeUndefined();
|
||||
}
|
||||
expect(calls).toBe(1);
|
||||
});
|
||||
|
||||
test("download worker uses a cloud-prepared PNG and retains local conversion on cloud failure", async () => {
|
||||
const bytes = await sharp({ create: { width: 4, height: 4, channels: 4, background: "#bb442280" } }).png().toBuffer();
|
||||
let cloudFails = false, reads = 0;
|
||||
const saved: any[] = [];
|
||||
const task = { id: "webp", targetUrl: "https://cdn.example/uploads/a.webp", metadata: {} };
|
||||
const module = serverLoader({
|
||||
"./image-archive-db": { claimImageDownloadTask: async () => task, saveImageDownloadAsset: async (_: string, asset: any) => saved.push(asset) },
|
||||
"./qiniu-image-archive": { getReadyQiniuImageDownload: async () => undefined,
|
||||
prepareQiniuWebpDownload: async () => { if (cloudFails) throw Error("cloud temporarily unavailable"); return { status: "ready", url: "https://cdn.example/uploads/converted.png", bytes: bytes.length, contentType: "image/png" }; } },
|
||||
"./image-byte-cache": { fetchImageBytes: async () => { reads++; return { buffer: bytes, contentType: "image/png" }; }, cacheImageBytes: async () => undefined },
|
||||
"@/lib/storage/image-byte-cache": { onceImageBytes: async (_: string, fn: any) => fn() },
|
||||
"./service": { uploadFile: async () => ({ url: "https://cdn.example/uploads/fallback.png" }) },
|
||||
})("lib/storage/image-download-preparation.ts");
|
||||
await module.prepareImageArchiveDownload("webp");
|
||||
expect(reads).toBe(0);
|
||||
expect(saved[0]).toMatchObject({ status: "ready", url: "https://cdn.example/uploads/converted.png" });
|
||||
cloudFails = true;
|
||||
await module.prepareImageArchiveDownload("webp");
|
||||
expect(reads).toBe(1);
|
||||
expect(saved[1].status).toBe("ready");
|
||||
});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user