mirror of
https://github.com/snapotter-hq/SnapOtter.git
synced 2026-08-03 07:46:42 +02:00
fix: harden Docker image and async job responses
Harden Docker runtime packaging, preserve async job response semantics, fix Redis subscriber startup connections, clear lint warnings, and harden enterprise S3 object body handling.
This commit is contained in:
@@ -14,7 +14,7 @@
|
||||
import { eq } from "drizzle-orm";
|
||||
import type Redis from "ioredis";
|
||||
import { db, schema } from "../db/index.js";
|
||||
import { createRedisConnection, sharedRedis } from "./connection.js";
|
||||
import { createRedisSubscriberConnection, sharedRedis } from "./connection.js";
|
||||
import { getQueue } from "./queues.js";
|
||||
import { bullPrefix, POOLS } from "./types.js";
|
||||
|
||||
@@ -39,7 +39,7 @@ const CANCEL_CHANNEL = () => `${bullPrefix()}:cancel`;
|
||||
let subscriber: Redis | null = null;
|
||||
|
||||
export async function startCancelListener(): Promise<void> {
|
||||
subscriber = createRedisConnection();
|
||||
subscriber = createRedisSubscriberConnection();
|
||||
subscriber.on("error", (err) => {
|
||||
console.error("Cancel listener subscriber error", err);
|
||||
});
|
||||
|
||||
@@ -20,6 +20,18 @@ export function createRedisConnection(): Redis {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a Redis connection used only for pub/sub subscriptions.
|
||||
* Subscriber sockets cannot run regular commands once subscribed, so disable
|
||||
* ioredis ready checks that issue INFO during reconnects.
|
||||
*/
|
||||
export function createRedisSubscriberConnection(): Redis {
|
||||
return new Redis(env.REDIS_URL, {
|
||||
maxRetriesPerRequest: null,
|
||||
enableReadyCheck: false,
|
||||
});
|
||||
}
|
||||
|
||||
// ioredis 5.11 vs BullMQ's bundled 5.10 type mismatch
|
||||
export function createBullMQConnection(): ConnectionOptions {
|
||||
return createRedisConnection() as unknown as ConnectionOptions;
|
||||
|
||||
@@ -95,8 +95,8 @@ const CHANNEL = async () => {
|
||||
|
||||
/** Subscribe so a setting change on any replica refreshes this process's cache. */
|
||||
export async function startAnalyticsGateListener(): Promise<void> {
|
||||
const { createRedisConnection } = await import("../jobs/connection.js");
|
||||
gateSubscriber = createRedisConnection();
|
||||
const { createRedisSubscriberConnection } = await import("../jobs/connection.js");
|
||||
gateSubscriber = createRedisSubscriberConnection();
|
||||
gateSubscriber.on("error", (err) => console.error("Analytics gate subscriber error", err));
|
||||
await gateSubscriber.subscribe(await CHANNEL());
|
||||
gateSubscriber.on("message", () => {
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
export type AsyncAcceptedPayload = {
|
||||
jobId: string;
|
||||
async: true;
|
||||
progressJobId?: string;
|
||||
artifactJobId?: string;
|
||||
};
|
||||
|
||||
export function buildAsyncAcceptedPayload(
|
||||
artifactJobId: string,
|
||||
clientJobId?: string | null,
|
||||
): AsyncAcceptedPayload {
|
||||
const progressJobId = clientJobId && clientJobId.length > 0 ? clientJobId : artifactJobId;
|
||||
|
||||
if (progressJobId === artifactJobId) {
|
||||
return { jobId: artifactJobId, async: true };
|
||||
}
|
||||
|
||||
return {
|
||||
jobId: progressJobId,
|
||||
progressJobId,
|
||||
artifactJobId,
|
||||
async: true,
|
||||
};
|
||||
}
|
||||
@@ -13,7 +13,7 @@
|
||||
import { eq } from "drizzle-orm";
|
||||
import type { FastifyInstance, FastifyReply, FastifyRequest } from "fastify";
|
||||
import { db, schema } from "../db/index.js";
|
||||
import { createRedisConnection, sharedRedis } from "../jobs/connection.js";
|
||||
import { createRedisSubscriberConnection, sharedRedis } from "../jobs/connection.js";
|
||||
import { bullPrefix } from "../jobs/types.js";
|
||||
import { getSecurityHeaders } from "../lib/csp.js";
|
||||
|
||||
@@ -228,11 +228,11 @@ export function publishEphemeral(
|
||||
|
||||
type FrameCallback = (json: string) => void;
|
||||
const sseListeners = new Map<string, Set<FrameCallback>>();
|
||||
let sseSubscriber: ReturnType<typeof createRedisConnection> | null = null;
|
||||
let sseSubscriber: ReturnType<typeof createRedisSubscriberConnection> | null = null;
|
||||
|
||||
function ensureSubscriber(): void {
|
||||
if (sseSubscriber) return;
|
||||
sseSubscriber = createRedisConnection();
|
||||
sseSubscriber = createRedisSubscriberConnection();
|
||||
// ioredis auto-resubscribes after reconnects; the handler keeps connection
|
||||
// errors observable without crashing (ioredis silentEmits, but be explicit).
|
||||
sseSubscriber.on("error", (err) => {
|
||||
|
||||
@@ -25,6 +25,7 @@ import { InputValidationError } from "../modality/contract.js";
|
||||
import { inputHandlerFor } from "../modality/input-handler.js";
|
||||
import { MediaInputHandler, type MediaInputKind } from "../modality/media-input.js";
|
||||
import { requireToolAccess } from "../permissions.js";
|
||||
import { buildAsyncAcceptedPayload } from "./async-response.js";
|
||||
import { updateSingleFileProgress } from "./progress.js";
|
||||
|
||||
/** Context passed to tool process functions for cooperative cancellation, scratch storage, and progress. */
|
||||
@@ -553,7 +554,7 @@ export function createToolRoute<T>(app: FastifyInstance, config: ToolRouteConfig
|
||||
|
||||
// Long tools never block the HTTP request (spec 4.5): straight to SSE.
|
||||
if (shouldSkipSyncWindow(toolMeta?.executionHint)) {
|
||||
return reply.status(202).send({ jobId: clientJobId || jobId, async: true });
|
||||
return reply.status(202).send(buildAsyncAcceptedPayload(jobId, clientJobId));
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -590,7 +591,7 @@ export function createToolRoute<T>(app: FastifyInstance, config: ToolRouteConfig
|
||||
...result.resultPayload,
|
||||
});
|
||||
}
|
||||
return reply.status(202).send({ jobId: clientJobId || jobId, async: true });
|
||||
return reply.status(202).send(buildAsyncAcceptedPayload(jobId, clientJobId));
|
||||
} catch (err) {
|
||||
// Keep the full error (incl. raw ffmpeg/tool stderr) in server logs,
|
||||
// but return only a user-safe detail to the client.
|
||||
|
||||
@@ -20,6 +20,7 @@ import { getObjectBuffer, putObject } from "../../lib/object-storage.js";
|
||||
import { resolveOutputFormat } from "../../lib/output-format.js";
|
||||
import { receiveUpload } from "../../lib/upload-stream.js";
|
||||
import { getAuthUser } from "../../plugins/auth.js";
|
||||
import { buildAsyncAcceptedPayload } from "../async-response.js";
|
||||
import { registerToolProcessFn } from "../tool-factory.js";
|
||||
|
||||
const settingsSchema = z.object({
|
||||
@@ -241,8 +242,6 @@ export function registerAiCanvasExpand(app: FastifyInstance) {
|
||||
await putObject(inputKey, fileBuffer);
|
||||
}
|
||||
|
||||
const progressJobId = clientJobId || jobId;
|
||||
|
||||
await enqueueToolJob({
|
||||
jobId,
|
||||
toolId,
|
||||
@@ -256,7 +255,7 @@ export function registerAiCanvasExpand(app: FastifyInstance) {
|
||||
kind: "ai-tool",
|
||||
});
|
||||
|
||||
return reply.status(202).send({ jobId: progressJobId, async: true });
|
||||
return reply.status(202).send(buildAsyncAcceptedPayload(jobId, clientJobId));
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ import { isToolInstalled } from "../../lib/feature-status.js";
|
||||
import { type TranscriptSegment, toSrt, toVtt } from "../../lib/subtitle-format.js";
|
||||
import { receiveUpload } from "../../lib/upload-stream.js";
|
||||
import { getAuthUser } from "../../plugins/auth.js";
|
||||
import { buildAsyncAcceptedPayload } from "../async-response.js";
|
||||
|
||||
const settingsSchema = z.object({
|
||||
language: z
|
||||
@@ -157,8 +158,6 @@ export function registerAutoSubtitles(app: FastifyInstance) {
|
||||
return reply.status(400).send({ error: "Settings must be valid JSON" });
|
||||
}
|
||||
|
||||
const progressJobId = clientJobId || jobId;
|
||||
|
||||
await enqueueToolJob({
|
||||
jobId,
|
||||
toolId,
|
||||
@@ -172,7 +171,7 @@ export function registerAutoSubtitles(app: FastifyInstance) {
|
||||
kind: "ai-tool",
|
||||
});
|
||||
|
||||
return reply.status(202).send({ jobId: progressJobId, async: true });
|
||||
return reply.status(202).send(buildAsyncAcceptedPayload(jobId, clientJobId));
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import { decodeToSharpCompat, needsCliDecode } from "../../lib/format-decoders.j
|
||||
import { decodeHeic } from "../../lib/heic-converter.js";
|
||||
import { receiveUpload } from "../../lib/upload-stream.js";
|
||||
import { getAuthUser } from "../../plugins/auth.js";
|
||||
import { buildAsyncAcceptedPayload } from "../async-response.js";
|
||||
|
||||
const HEX_RE = /^#[0-9a-fA-F]{6}$/;
|
||||
|
||||
@@ -216,8 +217,6 @@ export function registerBackgroundReplace(app: FastifyInstance) {
|
||||
await putObject(inputKey, fileBuffer);
|
||||
}
|
||||
|
||||
const progressJobId = clientJobId || jobId;
|
||||
|
||||
await enqueueToolJob({
|
||||
jobId,
|
||||
toolId,
|
||||
@@ -231,7 +230,7 @@ export function registerBackgroundReplace(app: FastifyInstance) {
|
||||
kind: "ai-tool",
|
||||
});
|
||||
|
||||
return reply.status(202).send({ jobId: progressJobId, async: true });
|
||||
return reply.status(202).send(buildAsyncAcceptedPayload(jobId, clientJobId));
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import { decodeToSharpCompat, needsCliDecode } from "../../lib/format-decoders.j
|
||||
import { decodeHeic } from "../../lib/heic-converter.js";
|
||||
import { receiveUpload } from "../../lib/upload-stream.js";
|
||||
import { getAuthUser } from "../../plugins/auth.js";
|
||||
import { buildAsyncAcceptedPayload } from "../async-response.js";
|
||||
|
||||
const settingsSchema = z.object({
|
||||
intensity: z.number().int().min(1).max(100).default(50),
|
||||
@@ -186,8 +187,6 @@ export function registerBlurBackground(app: FastifyInstance) {
|
||||
await putObject(inputKey, fileBuffer);
|
||||
}
|
||||
|
||||
const progressJobId = clientJobId || jobId;
|
||||
|
||||
await enqueueToolJob({
|
||||
jobId,
|
||||
toolId,
|
||||
@@ -201,7 +200,7 @@ export function registerBlurBackground(app: FastifyInstance) {
|
||||
kind: "ai-tool",
|
||||
});
|
||||
|
||||
return reply.status(202).send({ jobId: progressJobId, async: true });
|
||||
return reply.status(202).send(buildAsyncAcceptedPayload(jobId, clientJobId));
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import { getObjectBuffer, putObject } from "../../lib/object-storage.js";
|
||||
import { resolveOutputFormat } from "../../lib/output-format.js";
|
||||
import { receiveUpload } from "../../lib/upload-stream.js";
|
||||
import { getAuthUser } from "../../plugins/auth.js";
|
||||
import { buildAsyncAcceptedPayload } from "../async-response.js";
|
||||
import { registerToolProcessFn } from "../tool-factory.js";
|
||||
|
||||
const settingsSchema = z.object({
|
||||
@@ -163,8 +164,6 @@ export function registerBlurFaces(app: FastifyInstance) {
|
||||
await putObject(inputKey, fileBuffer);
|
||||
}
|
||||
|
||||
const progressJobId = clientJobId || jobId;
|
||||
|
||||
await enqueueToolJob({
|
||||
jobId,
|
||||
toolId,
|
||||
@@ -178,7 +177,7 @@ export function registerBlurFaces(app: FastifyInstance) {
|
||||
kind: "ai-tool",
|
||||
});
|
||||
|
||||
return reply.status(202).send({ jobId: progressJobId, async: true });
|
||||
return reply.status(202).send(buildAsyncAcceptedPayload(jobId, clientJobId));
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ import { getObjectBuffer, putObject } from "../../lib/object-storage.js";
|
||||
import { resolveOutputFormat } from "../../lib/output-format.js";
|
||||
import { receiveUpload } from "../../lib/upload-stream.js";
|
||||
import { getAuthUser } from "../../plugins/auth.js";
|
||||
import { buildAsyncAcceptedPayload } from "../async-response.js";
|
||||
import { registerToolProcessFn } from "../tool-factory.js";
|
||||
|
||||
const settingsSchema = z.object({
|
||||
@@ -162,8 +163,6 @@ export function registerColorize(app: FastifyInstance) {
|
||||
await putObject(inputKey, fileBuffer);
|
||||
}
|
||||
|
||||
const progressJobId = clientJobId || jobId;
|
||||
|
||||
await enqueueToolJob({
|
||||
jobId,
|
||||
toolId,
|
||||
@@ -177,7 +176,7 @@ export function registerColorize(app: FastifyInstance) {
|
||||
kind: "ai-tool",
|
||||
});
|
||||
|
||||
return reply.status(202).send({ jobId: progressJobId, async: true });
|
||||
return reply.status(202).send(buildAsyncAcceptedPayload(jobId, clientJobId));
|
||||
});
|
||||
|
||||
// Register in the pipeline/batch registry
|
||||
|
||||
@@ -17,6 +17,7 @@ import { decodeHeic } from "../../lib/heic-converter.js";
|
||||
import { getObjectBuffer, putObject } from "../../lib/object-storage.js";
|
||||
import { receiveUpload } from "../../lib/upload-stream.js";
|
||||
import { getAuthUser } from "../../plugins/auth.js";
|
||||
import { buildAsyncAcceptedPayload } from "../async-response.js";
|
||||
import { registerToolProcessFn } from "../tool-factory.js";
|
||||
|
||||
const settingsSchema = z.object({
|
||||
@@ -156,8 +157,6 @@ export function registerEnhanceFaces(app: FastifyInstance) {
|
||||
await putObject(inputKey, fileBuffer);
|
||||
}
|
||||
|
||||
const progressJobId = clientJobId || jobId;
|
||||
|
||||
await enqueueToolJob({
|
||||
jobId,
|
||||
toolId,
|
||||
@@ -171,7 +170,7 @@ export function registerEnhanceFaces(app: FastifyInstance) {
|
||||
kind: "ai-tool",
|
||||
});
|
||||
|
||||
return reply.status(202).send({ jobId: progressJobId, async: true });
|
||||
return reply.status(202).send(buildAsyncAcceptedPayload(jobId, clientJobId));
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ import { getObjectBuffer, putObject } from "../../lib/object-storage.js";
|
||||
import { resolveOutputFormat } from "../../lib/output-format.js";
|
||||
import { receiveUpload } from "../../lib/upload-stream.js";
|
||||
import { getAuthUser } from "../../plugins/auth.js";
|
||||
import { buildAsyncAcceptedPayload } from "../async-response.js";
|
||||
|
||||
const settingsSchema = z.object({
|
||||
format: z
|
||||
@@ -157,8 +158,6 @@ export function registerEraseObject(app: FastifyInstance) {
|
||||
await putObject(imageKey, imageBuffer);
|
||||
}
|
||||
|
||||
const progressJobId = clientJobId || jobId;
|
||||
|
||||
// Enqueue with both image and mask as inputRefs; the worker handler
|
||||
// reads them via getObjectBuffer.
|
||||
await enqueueToolJob({
|
||||
@@ -174,7 +173,7 @@ export function registerEraseObject(app: FastifyInstance) {
|
||||
kind: "ai-tool",
|
||||
});
|
||||
|
||||
return reply.status(202).send({ jobId: progressJobId, async: true });
|
||||
return reply.status(202).send(buildAsyncAcceptedPayload(jobId, clientJobId));
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ import { decodeHeic } from "../../lib/heic-converter.js";
|
||||
import { getObjectBuffer, putObject } from "../../lib/object-storage.js";
|
||||
import { receiveUpload } from "../../lib/upload-stream.js";
|
||||
import { getAuthUser } from "../../plugins/auth.js";
|
||||
import { buildAsyncAcceptedPayload } from "../async-response.js";
|
||||
import { registerToolProcessFn } from "../tool-factory.js";
|
||||
|
||||
const settingsSchema = z.object({
|
||||
@@ -167,8 +168,6 @@ export function registerNoiseRemoval(app: FastifyInstance) {
|
||||
await putObject(inputKey, fileBuffer);
|
||||
}
|
||||
|
||||
const progressJobId = clientJobId || jobId;
|
||||
|
||||
await enqueueToolJob({
|
||||
jobId,
|
||||
toolId,
|
||||
@@ -182,7 +181,7 @@ export function registerNoiseRemoval(app: FastifyInstance) {
|
||||
kind: "ai-tool",
|
||||
});
|
||||
|
||||
return reply.status(202).send({ jobId: progressJobId, async: true });
|
||||
return reply.status(202).send(buildAsyncAcceptedPayload(jobId, clientJobId));
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ import { formatZodErrors, stripInternalPaths } from "../../lib/errors.js";
|
||||
import { isToolInstalled } from "../../lib/feature-status.js";
|
||||
import { receiveUpload } from "../../lib/upload-stream.js";
|
||||
import { getAuthUser } from "../../plugins/auth.js";
|
||||
import { buildAsyncAcceptedPayload } from "../async-response.js";
|
||||
|
||||
const settingsSchema = z.object({
|
||||
quality: z.enum(["fast", "balanced", "best"]).default("balanced"),
|
||||
@@ -122,8 +123,6 @@ export function registerOcrPdf(app: FastifyInstance) {
|
||||
return reply.status(400).send({ error: "Settings must be valid JSON" });
|
||||
}
|
||||
|
||||
const progressJobId = clientJobId || jobId;
|
||||
|
||||
await enqueueToolJob({
|
||||
jobId,
|
||||
toolId,
|
||||
@@ -137,6 +136,6 @@ export function registerOcrPdf(app: FastifyInstance) {
|
||||
kind: "ai-tool",
|
||||
});
|
||||
|
||||
return reply.status(202).send({ jobId: progressJobId, async: true });
|
||||
return reply.status(202).send(buildAsyncAcceptedPayload(jobId, clientJobId));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ import { decodeHeic } from "../../lib/heic-converter.js";
|
||||
import { getObjectBuffer, putObject } from "../../lib/object-storage.js";
|
||||
import { receiveUpload } from "../../lib/upload-stream.js";
|
||||
import { getAuthUser } from "../../plugins/auth.js";
|
||||
import { buildAsyncAcceptedPayload } from "../async-response.js";
|
||||
import { registerToolProcessFn } from "../tool-factory.js";
|
||||
|
||||
const settingsSchema = z.object({
|
||||
@@ -155,8 +156,6 @@ export function registerRedEyeRemoval(app: FastifyInstance) {
|
||||
await putObject(inputKey, fileBuffer);
|
||||
}
|
||||
|
||||
const progressJobId = clientJobId || jobId;
|
||||
|
||||
await enqueueToolJob({
|
||||
jobId,
|
||||
toolId,
|
||||
@@ -170,7 +169,7 @@ export function registerRedEyeRemoval(app: FastifyInstance) {
|
||||
kind: "ai-tool",
|
||||
});
|
||||
|
||||
return reply.status(202).send({ jobId: progressJobId, async: true });
|
||||
return reply.status(202).send(buildAsyncAcceptedPayload(jobId, clientJobId));
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ import { decodeHeic } from "../../lib/heic-converter.js";
|
||||
import { getObjectBuffer, putObject } from "../../lib/object-storage.js";
|
||||
import { receiveUpload } from "../../lib/upload-stream.js";
|
||||
import { getAuthUser } from "../../plugins/auth.js";
|
||||
import { buildAsyncAcceptedPayload } from "../async-response.js";
|
||||
import { registerToolProcessFn } from "../tool-factory.js";
|
||||
|
||||
const settingsSchema = z.object({
|
||||
@@ -206,8 +207,6 @@ export function registerRemoveBackground(app: FastifyInstance) {
|
||||
await putObject(inputKey, fileBuffer);
|
||||
}
|
||||
|
||||
const progressJobId = clientJobId || jobId;
|
||||
|
||||
// Enqueue on the AI pool
|
||||
await enqueueToolJob({
|
||||
jobId,
|
||||
@@ -223,7 +222,7 @@ export function registerRemoveBackground(app: FastifyInstance) {
|
||||
});
|
||||
|
||||
// AI tools always return 202 (no sync window)
|
||||
return reply.status(202).send({ jobId: progressJobId, async: true });
|
||||
return reply.status(202).send(buildAsyncAcceptedPayload(jobId, clientJobId));
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ import { getObjectBuffer, putObject } from "../../lib/object-storage.js";
|
||||
import { resolveOutputFormat } from "../../lib/output-format.js";
|
||||
import { receiveUpload } from "../../lib/upload-stream.js";
|
||||
import { getAuthUser } from "../../plugins/auth.js";
|
||||
import { buildAsyncAcceptedPayload } from "../async-response.js";
|
||||
import { registerToolProcessFn } from "../tool-factory.js";
|
||||
|
||||
const settingsSchema = z.object({
|
||||
@@ -187,8 +188,6 @@ export function registerRestorePhoto(app: FastifyInstance) {
|
||||
await putObject(inputKey, fileBuffer);
|
||||
}
|
||||
|
||||
const progressJobId = clientJobId || jobId;
|
||||
|
||||
await enqueueToolJob({
|
||||
jobId,
|
||||
toolId: "restore-photo",
|
||||
@@ -202,7 +201,7 @@ export function registerRestorePhoto(app: FastifyInstance) {
|
||||
kind: "ai-tool",
|
||||
});
|
||||
|
||||
return reply.status(202).send({ jobId: progressJobId, async: true });
|
||||
return reply.status(202).send(buildAsyncAcceptedPayload(jobId, clientJobId));
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ import { getObjectBuffer } from "../../lib/object-storage.js";
|
||||
import { receiveUpload } from "../../lib/upload-stream.js";
|
||||
import { inputHandlerFor } from "../../modality/input-handler.js";
|
||||
import { getAuthUser } from "../../plugins/auth.js";
|
||||
import { buildAsyncAcceptedPayload } from "../async-response.js";
|
||||
|
||||
const TOOL_ID = "sign-pdf";
|
||||
const MAX_PLACEMENTS = 100;
|
||||
@@ -158,7 +159,7 @@ export function registerSignPdf(app: FastifyInstance) {
|
||||
savedFileId: result.savedFileId,
|
||||
});
|
||||
}
|
||||
return reply.status(202).send({ jobId: clientJobId || jobId, async: true });
|
||||
return reply.status(202).send(buildAsyncAcceptedPayload(jobId, clientJobId));
|
||||
} catch (err) {
|
||||
request.log.error({ err, toolId: TOOL_ID }, "sign-pdf processing failed");
|
||||
return reply.status(422).send({
|
||||
|
||||
@@ -12,6 +12,7 @@ import { isToolInstalled } from "../../lib/feature-status.js";
|
||||
import { type TranscriptSegment, toSrt, toVtt } from "../../lib/subtitle-format.js";
|
||||
import { receiveUpload } from "../../lib/upload-stream.js";
|
||||
import { getAuthUser } from "../../plugins/auth.js";
|
||||
import { buildAsyncAcceptedPayload } from "../async-response.js";
|
||||
|
||||
const settingsSchema = z.object({
|
||||
language: z
|
||||
@@ -145,8 +146,6 @@ export function registerTranscribeAudio(app: FastifyInstance) {
|
||||
return reply.status(400).send({ error: "Settings must be valid JSON" });
|
||||
}
|
||||
|
||||
const progressJobId = clientJobId || jobId;
|
||||
|
||||
await enqueueToolJob({
|
||||
jobId,
|
||||
toolId,
|
||||
@@ -160,7 +159,7 @@ export function registerTranscribeAudio(app: FastifyInstance) {
|
||||
kind: "ai-tool",
|
||||
});
|
||||
|
||||
return reply.status(202).send({ jobId: progressJobId, async: true });
|
||||
return reply.status(202).send(buildAsyncAcceptedPayload(jobId, clientJobId));
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import { decodeHeic } from "../../lib/heic-converter.js";
|
||||
import { getObjectBuffer, putObject } from "../../lib/object-storage.js";
|
||||
import { receiveUpload } from "../../lib/upload-stream.js";
|
||||
import { getAuthUser } from "../../plugins/auth.js";
|
||||
import { buildAsyncAcceptedPayload } from "../async-response.js";
|
||||
import { registerToolProcessFn } from "../tool-factory.js";
|
||||
|
||||
const TOOL_ID = "transparency-fixer";
|
||||
@@ -252,8 +253,6 @@ export function registerTransparencyFixer(app: FastifyInstance) {
|
||||
await putObject(inputKey, fileBuffer);
|
||||
}
|
||||
|
||||
const progressJobId = clientJobId || jobId;
|
||||
|
||||
await enqueueToolJob({
|
||||
jobId,
|
||||
toolId: TOOL_ID,
|
||||
@@ -267,7 +266,7 @@ export function registerTransparencyFixer(app: FastifyInstance) {
|
||||
kind: "ai-tool",
|
||||
});
|
||||
|
||||
return reply.status(202).send({ jobId: progressJobId, async: true });
|
||||
return reply.status(202).send(buildAsyncAcceptedPayload(jobId, clientJobId));
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ import { getObjectBuffer, putObject } from "../../lib/object-storage.js";
|
||||
import { resolveOutputFormat } from "../../lib/output-format.js";
|
||||
import { receiveUpload } from "../../lib/upload-stream.js";
|
||||
import { getAuthUser } from "../../plugins/auth.js";
|
||||
import { buildAsyncAcceptedPayload } from "../async-response.js";
|
||||
import { registerToolProcessFn } from "../tool-factory.js";
|
||||
|
||||
const settingsSchema = z.object({
|
||||
@@ -214,8 +215,6 @@ export function registerUpscale(app: FastifyInstance) {
|
||||
await putObject(inputKey, fileBuffer);
|
||||
}
|
||||
|
||||
const progressJobId = clientJobId || jobId;
|
||||
|
||||
await enqueueToolJob({
|
||||
jobId,
|
||||
toolId,
|
||||
@@ -229,7 +228,7 @@ export function registerUpscale(app: FastifyInstance) {
|
||||
kind: "ai-tool",
|
||||
});
|
||||
|
||||
return reply.status(202).send({ jobId: progressJobId, async: true });
|
||||
return reply.status(202).send(buildAsyncAcceptedPayload(jobId, clientJobId));
|
||||
});
|
||||
|
||||
// Register in the pipeline/batch registry so this tool can be used
|
||||
|
||||
Reference in New Issue
Block a user