- apps/Backend/src/queue/: connection, queues, workers, processors - apps/Frontend/src/hooks/use-job-status.ts: WebSocket job progress hook - apps/Frontend/src/lib/socket.ts: shared Socket.IO singleton Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
56 lines
1.5 KiB
TypeScript
56 lines
1.5 KiB
TypeScript
import { Worker, Job } from "bullmq";
|
|
import { redisConnection } from "../connection";
|
|
import { OcrJobData } from "../queues";
|
|
import { io } from "../../socket";
|
|
import { runOcrProcessor } from "../processors/ocrProcessor";
|
|
|
|
function emitJobUpdate(
|
|
socketId: string | undefined,
|
|
jobId: string,
|
|
status: "active" | "completed" | "failed",
|
|
payload: Record<string, any>
|
|
) {
|
|
const event = "job:update";
|
|
const data = { jobId, jobType: "ocr", status, ...payload };
|
|
if (socketId && io) {
|
|
io.to(socketId).emit(event, data);
|
|
} else if (io) {
|
|
io.emit(event, data);
|
|
}
|
|
}
|
|
|
|
async function processOcrJob(job: Job<OcrJobData>) {
|
|
const { socketId, files } = job.data;
|
|
const jobId = job.id ?? job.name;
|
|
|
|
emitJobUpdate(socketId, jobId, "active", { message: "OCR processing started…" });
|
|
|
|
try {
|
|
const rows = await runOcrProcessor({ files });
|
|
emitJobUpdate(socketId, jobId, "completed", { result: { rows } });
|
|
return rows;
|
|
} catch (err: any) {
|
|
const errorMsg = err?.message ?? String(err);
|
|
emitJobUpdate(socketId, jobId, "failed", { error: errorMsg });
|
|
throw err;
|
|
}
|
|
}
|
|
|
|
export function startOcrWorker() {
|
|
const worker = new Worker<OcrJobData>("ocr-jobs", processOcrJob, {
|
|
connection: redisConnection,
|
|
concurrency: 2, // OCR service allows 2 concurrent
|
|
});
|
|
|
|
worker.on("completed", (job) => {
|
|
console.log(`[ocrWorker] job ${job.id} completed`);
|
|
});
|
|
|
|
worker.on("failed", (job, err) => {
|
|
console.error(`[ocrWorker] job ${job?.id} failed:`, err.message);
|
|
});
|
|
|
|
console.log("[ocrWorker] started");
|
|
return worker;
|
|
}
|