72 lines
2.0 KiB
TypeScript
72 lines
2.0 KiB
TypeScript
import { createHmac } from "node:crypto";
|
|
import type { GenerationJob, WebhookLastStatus } from "@/lib/types";
|
|
|
|
export type JobWebhookPayload = {
|
|
jobId: string;
|
|
status: GenerationJob["status"];
|
|
capability: GenerationJob["capability"];
|
|
outputAssetIds: string[];
|
|
error?: GenerationJob["error"];
|
|
updatedAt: string;
|
|
};
|
|
|
|
const MAX_WEBHOOK_ATTEMPTS = 3;
|
|
|
|
export function buildJobWebhookPayload(job: GenerationJob): JobWebhookPayload {
|
|
return {
|
|
jobId: job.id,
|
|
status: job.status,
|
|
capability: job.capability,
|
|
outputAssetIds: job.outputAssetIds,
|
|
error: job.error,
|
|
updatedAt: job.updatedAt
|
|
};
|
|
}
|
|
|
|
export function signWebhookBody(body: string, secret = process.env.ZHINIAN_WEBHOOK_SECRET): string | undefined {
|
|
if (!secret?.trim()) return undefined;
|
|
return `sha256=${createHmac("sha256", secret.trim()).update(body).digest("hex")}`;
|
|
}
|
|
|
|
export async function deliverJobWebhook(job: GenerationJob): Promise<{
|
|
attempts: number;
|
|
lastStatus?: WebhookLastStatus;
|
|
}> {
|
|
if (!job.webhookUrl) return { attempts: job.webhookAttempts || 0 };
|
|
let attempts = job.webhookAttempts || 0;
|
|
let lastStatus: WebhookLastStatus | undefined = job.webhookLastStatus;
|
|
const payload = buildJobWebhookPayload(job);
|
|
const body = JSON.stringify(payload);
|
|
const signature = signWebhookBody(body);
|
|
|
|
while (attempts < MAX_WEBHOOK_ATTEMPTS) {
|
|
attempts += 1;
|
|
const attemptedAt = new Date().toISOString();
|
|
try {
|
|
const response = await fetch(job.webhookUrl, {
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
"User-Agent": "zhinian-aigc-webhook/1.0",
|
|
...(signature ? { "X-Zhinian-Signature": signature } : {})
|
|
},
|
|
body
|
|
});
|
|
lastStatus = {
|
|
ok: response.ok,
|
|
status: response.status,
|
|
attemptedAt
|
|
};
|
|
if (response.ok) break;
|
|
} catch (error) {
|
|
lastStatus = {
|
|
ok: false,
|
|
error: error instanceof Error ? error.message : String(error),
|
|
attemptedAt
|
|
};
|
|
}
|
|
}
|
|
|
|
return { attempts, lastStatus };
|
|
}
|