Files
pn-new-crm/src/lib/services/webhook-dispatch.ts
Matt Ciaccio 8699f81879
Some checks failed
Build & Push Docker Images / lint (push) Failing after 1m18s
Build & Push Docker Images / build-and-push (push) Has been skipped
chore(style): codebase em-dash sweep + minor layout polish
Replaces every em-dash and en-dash with regular ASCII hyphens
across comments, JSX strings, and dev-facing logs. Mostly cosmetic
but stops the inconsistent mix that crept in over the last few
months (some files used em-dashes in comments, others didn't,
some used both).

Bundles two small dashboard-layout tweaks that touch a couple of
already-modified files:
- (dashboard)/layout.tsx main padding goes from p-6 to pt-3 px-6
  pb-6 so page content sits closer to the topbar.
- Sidebar now receives the ports list it needs for the footer
  port switcher.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 22:57:01 +02:00

72 lines
2.1 KiB
TypeScript

import { and, eq, sql } from 'drizzle-orm';
import { db } from '@/lib/db';
import { webhooks, webhookDeliveries } from '@/lib/db/schema/system';
import { getQueue } from '@/lib/queue';
import { logger } from '@/lib/logger';
import { INTERNAL_TO_WEBHOOK_MAP } from '@/lib/services/webhook-event-map';
/**
* Translates an internal socket event to the outbound webhook event name,
* queries all active webhooks for the given port that are subscribed to that
* event, and enqueues a BullMQ delivery job for each one.
*
* This function is fire-and-forget - callers should use `void dispatchWebhookEvent(...)`.
*/
export async function dispatchWebhookEvent(
portId: string,
internalEvent: string,
payload: Record<string, unknown>,
): Promise<void> {
const webhookEvent = INTERNAL_TO_WEBHOOK_MAP[internalEvent];
if (!webhookEvent) {
// No mapping for this event - skip silently
return;
}
try {
// Query active webhooks for this port that subscribe to this event
const matchingWebhooks = await db
.select({ id: webhooks.id })
.from(webhooks)
.where(
and(
eq(webhooks.portId, portId),
eq(webhooks.isActive, true),
// Check if events array contains the webhook event
sql`${webhooks.events} @> ARRAY[${webhookEvent}]::text[]`,
),
);
if (matchingWebhooks.length === 0) {
return;
}
const queue = getQueue('webhooks');
for (const webhook of matchingWebhooks) {
// Create a pending delivery record before enqueueing
const [delivery] = await db
.insert(webhookDeliveries)
.values({
webhookId: webhook.id,
eventType: webhookEvent,
payload,
status: 'pending',
})
.returning({ id: webhookDeliveries.id });
await queue.add('deliver', {
webhookId: webhook.id,
portId,
event: webhookEvent,
deliveryId: delivery!.id,
payload,
});
}
} catch (err) {
// Never block callers - log and swallow
logger.error({ portId, internalEvent, webhookEvent, err }, 'Failed to dispatch webhook event');
}
}