fix: improve api/cron/webhookTriggers endpoint (#14188)
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
This commit is contained in:
co-authored by
CarinaWolli
parent
1132d9d0dc
commit
ed0cd30092
@@ -31,20 +31,35 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
lte: dayjs().toISOString(),
|
||||
},
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
jobName: true,
|
||||
payload: true,
|
||||
subscriberUrl: true,
|
||||
webhook: {
|
||||
select: {
|
||||
secret: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const fetchPromises: Promise<any>[] = [];
|
||||
|
||||
// run jobs
|
||||
for (const job of jobsToRun) {
|
||||
// Fetch the webhook configuration so that we can get the secret.
|
||||
const [appId, subscriberId] = job.jobName.split("_");
|
||||
let webhook = job.webhook;
|
||||
|
||||
let webhook;
|
||||
try {
|
||||
webhook = await prisma.webhook.findUniqueOrThrow({
|
||||
where: { id: subscriberId, appId: appId !== "null" ? appId : null },
|
||||
});
|
||||
} catch {
|
||||
logger.error(`Error finding webhook for subscriberId: ${subscriberId}, appId: ${appId}`);
|
||||
if (!webhook) {
|
||||
try {
|
||||
webhook = await prisma.webhook.findUniqueOrThrow({
|
||||
where: { id: subscriberId, appId: appId !== "null" ? appId : null },
|
||||
});
|
||||
} catch {
|
||||
logger.error(`Error finding webhook for subscriberId: ${subscriberId}, appId: ${appId}`);
|
||||
}
|
||||
}
|
||||
|
||||
const headers: Record<string, string> = {
|
||||
@@ -56,33 +71,15 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
headers["X-Cal-Signature-256"] = createWebhookSignature({ secret: webhook.secret, body: job.payload });
|
||||
}
|
||||
|
||||
try {
|
||||
await fetch(job.subscriberUrl, {
|
||||
fetchPromises.push(
|
||||
fetch(job.subscriberUrl, {
|
||||
method: "POST",
|
||||
body: job.payload,
|
||||
headers,
|
||||
});
|
||||
} catch (error) {
|
||||
console.log(`Error running webhook trigger (retry count: ${job.retryCount}): ${error}`);
|
||||
|
||||
// if job fails, retry again for 5 times.
|
||||
if (job.retryCount < 5) {
|
||||
await prisma.webhookScheduledTriggers.update({
|
||||
where: {
|
||||
id: job.id,
|
||||
},
|
||||
data: {
|
||||
retryCount: {
|
||||
increment: 1,
|
||||
},
|
||||
startAfter: dayjs()
|
||||
.add(5 * (job.retryCount + 1), "minutes")
|
||||
.toISOString(),
|
||||
},
|
||||
});
|
||||
return res.json({ ok: false });
|
||||
}
|
||||
}
|
||||
}).catch((error) => {
|
||||
console.error(`Webhook trigger for subscriber url ${job.subscriberUrl} failed with error: ${error}`);
|
||||
})
|
||||
);
|
||||
|
||||
const parsedJobPayload = JSON.parse(job.payload) as {
|
||||
id: number; // booking id
|
||||
@@ -122,6 +119,8 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
});
|
||||
}
|
||||
|
||||
Promise.allSettled(fetchPromises);
|
||||
|
||||
res.json({ ok: true });
|
||||
}
|
||||
|
||||
|
||||
@@ -297,6 +297,11 @@ export async function scheduleTrigger(
|
||||
payload,
|
||||
startAfter: triggerEvent === WebhookTriggerEvents.MEETING_ENDED ? booking.endTime : booking.startTime,
|
||||
subscriberUrl,
|
||||
webhook: {
|
||||
connect: {
|
||||
id: subscriber.id,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "WebhookScheduledTriggers" ADD COLUMN "webhookId" TEXT;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "WebhookScheduledTriggers" ADD CONSTRAINT "WebhookScheduledTriggers_webhookId_fkey" FOREIGN KEY ("webhookId") REFERENCES "Webhook"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
@@ -691,23 +691,24 @@ enum WebhookTriggerEvents {
|
||||
}
|
||||
|
||||
model Webhook {
|
||||
id String @id @unique
|
||||
userId Int?
|
||||
teamId Int?
|
||||
eventTypeId Int?
|
||||
id String @id @unique
|
||||
userId Int?
|
||||
teamId Int?
|
||||
eventTypeId Int?
|
||||
/// @zod.url()
|
||||
subscriberUrl String
|
||||
payloadTemplate String?
|
||||
createdAt DateTime @default(now())
|
||||
active Boolean @default(true)
|
||||
eventTriggers WebhookTriggerEvents[]
|
||||
user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
team Team? @relation(fields: [teamId], references: [id], onDelete: Cascade)
|
||||
eventType EventType? @relation(fields: [eventTypeId], references: [id], onDelete: Cascade)
|
||||
app App? @relation(fields: [appId], references: [slug], onDelete: Cascade)
|
||||
appId String?
|
||||
secret String?
|
||||
platform Boolean @default(false)
|
||||
subscriberUrl String
|
||||
payloadTemplate String?
|
||||
createdAt DateTime @default(now())
|
||||
active Boolean @default(true)
|
||||
eventTriggers WebhookTriggerEvents[]
|
||||
user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
team Team? @relation(fields: [teamId], references: [id], onDelete: Cascade)
|
||||
eventType EventType? @relation(fields: [eventTypeId], references: [id], onDelete: Cascade)
|
||||
app App? @relation(fields: [appId], references: [slug], onDelete: Cascade)
|
||||
appId String?
|
||||
secret String?
|
||||
platform Boolean @default(false)
|
||||
scheduledTriggers WebhookScheduledTriggers[]
|
||||
|
||||
@@unique([userId, subscriberUrl], name: "courseIdentifier")
|
||||
}
|
||||
@@ -989,6 +990,8 @@ model WebhookScheduledTriggers {
|
||||
startAfter DateTime
|
||||
retryCount Int @default(0)
|
||||
createdAt DateTime? @default(now())
|
||||
webhookId String?
|
||||
webhook Webhook? @relation(fields: [webhookId], references: [id], onDelete: Cascade)
|
||||
}
|
||||
|
||||
enum WorkflowTemplates {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as z from "zod"
|
||||
import * as imports from "../zod-utils"
|
||||
import { WebhookTriggerEvents } from "@prisma/client"
|
||||
import { CompleteUser, UserModel, CompleteTeam, TeamModel, CompleteEventType, EventTypeModel, CompleteApp, AppModel } from "./index"
|
||||
import { CompleteUser, UserModel, CompleteTeam, TeamModel, CompleteEventType, EventTypeModel, CompleteApp, AppModel, CompleteWebhookScheduledTriggers, WebhookScheduledTriggersModel } from "./index"
|
||||
|
||||
export const _WebhookModel = z.object({
|
||||
id: z.string(),
|
||||
@@ -23,6 +23,7 @@ export interface CompleteWebhook extends z.infer<typeof _WebhookModel> {
|
||||
team?: CompleteTeam | null
|
||||
eventType?: CompleteEventType | null
|
||||
app?: CompleteApp | null
|
||||
scheduledTriggers: CompleteWebhookScheduledTriggers[]
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -35,4 +36,5 @@ export const WebhookModel: z.ZodSchema<CompleteWebhook> = z.lazy(() => _WebhookM
|
||||
team: TeamModel.nullish(),
|
||||
eventType: EventTypeModel.nullish(),
|
||||
app: AppModel.nullish(),
|
||||
scheduledTriggers: WebhookScheduledTriggersModel.array(),
|
||||
}))
|
||||
|
||||
Reference in New Issue
Block a user