ai-chatbot/lib/jobs/image-archive-job.ts

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,
};
}