// Producer-side helper for the toolctrl.file-preview BullMQ queue. Web routes // call enqueueFilePreview(...) after a successful upload (and on the "regenerate // preview" action) to trigger asynchronous preview generation in the worker. // Mirrors fileScanQueue.server.ts so the queue lifecycle is the same shared // pattern. Unlike the scan queue there is no enable-flag: preview generation is // always desired; if Redis is unreachable the enqueue is a no-op and the file // simply keeps previewStatus=pending until re-triggered. import { buildFilePreviewJob, enqueueJob, queueNames, resolveRedisConnectionOptions, resolveRedisConnectionOptionsFromProcessEnv, type RedisConnectionEnv, type TypedQueue, } from "@toolctrl/queue"; export type EnqueueFilePreviewInput = { fileId: string; tenantId: string; bucket: string; storageKey: string; mimeType: string; metadata?: Record; }; export type EnqueueFilePreviewResult = { enqueued: true; jobId?: string } | { enqueued: false; reason: "queue-unavailable" }; type Dependencies = { env?: RedisConnectionEnv; queue?: TypedQueue; }; export async function enqueueFilePreview(input: EnqueueFilePreviewInput, dependencies: Dependencies = {}): Promise { let queue = dependencies.queue; let shouldCloseQueue = false; const connection = queue ? undefined : dependencies.env ? resolveRedisConnectionOptions({ env: dependencies.env }) : resolveRedisConnectionOptionsFromProcessEnv(); if (!queue && !connection) { return { enqueued: false, reason: "queue-unavailable" }; } if (!queue && connection) { const queueRuntimeModule = "@toolctrl/queue/runtime"; const { createQueue } = (await import(/* @vite-ignore */ queueRuntimeModule)) as typeof import("@toolctrl/queue/runtime"); queue = createQueue(queueNames.filePreview, { connection }); shouldCloseQueue = true; } if (!queue) { return { enqueued: false, reason: "queue-unavailable" }; } try { const descriptor = buildFilePreviewJob({ fileId: input.fileId, tenantId: input.tenantId, bucket: input.bucket, storageKey: input.storageKey, mimeType: input.mimeType, metadata: input.metadata, }); const job = await enqueueJob(queue, descriptor, { tenantId: input.tenantId }); return { enqueued: true, jobId: typeof job.id === "string" ? job.id : undefined }; } finally { if (shouldCloseQueue && queue) { await queue.close().catch(() => {}); } } }