119 lines
3.5 KiB
TypeScript
119 lines
3.5 KiB
TypeScript
import "server-only";
|
|
|
|
import { archiveTemporaryImage } from "@/lib/storage/image-archive";
|
|
import {
|
|
getImageArchiveTaskStats,
|
|
getImageArchiveTasksToReplace,
|
|
getImageArchiveTasksToStore,
|
|
markImageArchiveTaskArchiving,
|
|
markImageArchiveTaskCompleted,
|
|
markImageArchiveTaskFailed,
|
|
markImageArchiveTaskReplaceFailed,
|
|
markImageArchiveTaskReplacing,
|
|
markImageArchiveTaskStored,
|
|
} from "@/lib/storage/image-archive-db";
|
|
import { replaceImageUrlInDatabase } from "@/lib/storage/image-url-replacement";
|
|
|
|
const DEFAULT_BATCH_SIZE = 10;
|
|
|
|
function getBatchSize() {
|
|
const parsed = Number(process.env.IMAGE_ARCHIVE_BATCH_SIZE || DEFAULT_BATCH_SIZE);
|
|
if (!Number.isFinite(parsed)) return DEFAULT_BATCH_SIZE;
|
|
return Math.max(1, Math.min(50, Math.round(parsed)));
|
|
}
|
|
|
|
export async function runImageArchiveJob() {
|
|
const batchSize = getBatchSize();
|
|
console.log(`[ImageArchiveJob] Start image archive job (batchSize=${batchSize})`);
|
|
|
|
const storeTasks = await getImageArchiveTasksToStore({ limit: batchSize });
|
|
let storeSucceeded = 0;
|
|
let storeFailed = 0;
|
|
|
|
for (const task of storeTasks) {
|
|
try {
|
|
await markImageArchiveTaskArchiving(task.id);
|
|
const archived = await archiveTemporaryImage(task.sourceUrl);
|
|
await markImageArchiveTaskStored({
|
|
id: task.id,
|
|
targetUrl: archived.url,
|
|
});
|
|
storeSucceeded += 1;
|
|
console.log("[ImageArchiveJob] stored", {
|
|
taskId: task.id,
|
|
bytes: archived.bytes,
|
|
sourceUrl: task.sourceUrl,
|
|
targetUrl: archived.url,
|
|
});
|
|
} catch (error) {
|
|
storeFailed += 1;
|
|
await markImageArchiveTaskFailed({ id: task.id, error });
|
|
console.error("[ImageArchiveJob] store failed", {
|
|
taskId: task.id,
|
|
sourceUrl: task.sourceUrl,
|
|
error: error instanceof Error ? error.message : String(error),
|
|
});
|
|
}
|
|
}
|
|
|
|
const remainingSlots = Math.max(1, batchSize - storeTasks.length);
|
|
const replaceTasks = await getImageArchiveTasksToReplace({
|
|
limit: remainingSlots,
|
|
});
|
|
let replaceSucceeded = 0;
|
|
let replaceFailed = 0;
|
|
|
|
for (const task of replaceTasks) {
|
|
if (!task.targetUrl) {
|
|
await markImageArchiveTaskReplaceFailed({
|
|
id: task.id,
|
|
error: new Error("Missing targetUrl for stored image archive task"),
|
|
});
|
|
replaceFailed += 1;
|
|
continue;
|
|
}
|
|
|
|
try {
|
|
await markImageArchiveTaskReplacing(task.id);
|
|
const result = await replaceImageUrlInDatabase(task.sourceUrl, task.targetUrl);
|
|
await markImageArchiveTaskCompleted({
|
|
id: task.id,
|
|
replacedCount: result.changed,
|
|
});
|
|
replaceSucceeded += 1;
|
|
console.log("[ImageArchiveJob] replaced", {
|
|
taskId: task.id,
|
|
sourceUrl: task.sourceUrl,
|
|
targetUrl: task.targetUrl,
|
|
replacedCount: result.changed,
|
|
});
|
|
} catch (error) {
|
|
replaceFailed += 1;
|
|
await markImageArchiveTaskReplaceFailed({ id: task.id, error });
|
|
console.error("[ImageArchiveJob] replace failed", {
|
|
taskId: task.id,
|
|
sourceUrl: task.sourceUrl,
|
|
targetUrl: task.targetUrl,
|
|
error: error instanceof Error ? error.message : String(error),
|
|
});
|
|
}
|
|
}
|
|
|
|
const stats = await getImageArchiveTaskStats();
|
|
return {
|
|
success: true,
|
|
message: "Image archive job completed",
|
|
store: {
|
|
total: storeTasks.length,
|
|
succeeded: storeSucceeded,
|
|
failed: storeFailed,
|
|
},
|
|
replace: {
|
|
total: replaceTasks.length,
|
|
succeeded: replaceSucceeded,
|
|
failed: replaceFailed,
|
|
},
|
|
stats,
|
|
};
|
|
}
|