chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
import { Worker as RedisWorker } from "@trigger.dev/redis-worker";
|
||||
import { z } from "zod";
|
||||
import { env } from "~/env.server";
|
||||
import { logger } from "~/services/logger.server";
|
||||
import { singleton } from "~/utils/singleton";
|
||||
import { ssoController } from "~/services/sso.server";
|
||||
import { applyDirectorySyncEffects } from "~/services/directorySyncEffects.server";
|
||||
|
||||
// Dedicated worker for inbound account-management webhooks. The webhook
|
||||
// proxy route verifies the signature via the plugin and enqueues the
|
||||
// parsed event here; this worker calls back into the plugin to apply the
|
||||
// DB writes. The plugin owns the vendor-specific logic; the webapp owns
|
||||
// the queue runtime (this file), mirroring `commonWorker.server.ts`.
|
||||
//
|
||||
// Vendor-neutral by design: the catalog/job names and payload shape carry
|
||||
// no provider identity.
|
||||
const PayloadSchema = z
|
||||
.object({
|
||||
id: z.string(),
|
||||
event: z.string(),
|
||||
data: z.unknown(),
|
||||
})
|
||||
// Zod v3 treats a `z.unknown()` field as optional, so a payload missing
|
||||
// `data` entirely would otherwise validate. Require the key's presence
|
||||
// at runtime via refine — `.nonoptional()` is a v4-only API and isn't
|
||||
// available on the classic `z` import this repo pins.
|
||||
.refine((payload) => "data" in payload, {
|
||||
message: "data is required",
|
||||
path: ["data"],
|
||||
});
|
||||
|
||||
function initializeWorker() {
|
||||
const redisOptions = {
|
||||
keyPrefix: "accounts-webhook:worker:",
|
||||
host: env.COMMON_WORKER_REDIS_HOST,
|
||||
port: env.COMMON_WORKER_REDIS_PORT,
|
||||
username: env.COMMON_WORKER_REDIS_USERNAME,
|
||||
password: env.COMMON_WORKER_REDIS_PASSWORD,
|
||||
enableAutoPipelining: true,
|
||||
...(env.COMMON_WORKER_REDIS_TLS_DISABLED === "true" ? {} : { tls: {} }),
|
||||
};
|
||||
|
||||
const worker = new RedisWorker({
|
||||
name: "accounts-webhook-worker",
|
||||
redisOptions,
|
||||
catalog: {
|
||||
"account.webhook.event": {
|
||||
schema: PayloadSchema,
|
||||
visibilityTimeoutMs: 30_000,
|
||||
retry: { maxAttempts: 5 },
|
||||
},
|
||||
},
|
||||
concurrency: {
|
||||
workers: 2,
|
||||
tasksPerWorker: 4,
|
||||
limit: 8,
|
||||
},
|
||||
pollIntervalMs: 1_000,
|
||||
immediatePollIntervalMs: 50,
|
||||
shutdownTimeoutMs: 30_000,
|
||||
jobs: {
|
||||
"account.webhook.event": async ({ payload }) => {
|
||||
// The plugin returns a Result; throw on error so the worker
|
||||
// retries (a resolved err would otherwise be silently dropped).
|
||||
// `z.unknown()` infers `data?: unknown`, but the contract's
|
||||
// SsoWebhookEvent requires `data: unknown` — restate the fields
|
||||
// explicitly so the optional-vs-required shapes line up.
|
||||
const result = await ssoController.processWebhookEvent({
|
||||
id: payload.id,
|
||||
event: payload.event,
|
||||
data: payload.data,
|
||||
});
|
||||
if (result.isErr()) {
|
||||
throw new Error(`account webhook processing failed: ${result.error}`);
|
||||
}
|
||||
// Directory-sync events return membership effects to apply against
|
||||
// public.* tables (the plugin never writes those). A throw here
|
||||
// bubbles to the worker for retry; effects are idempotent.
|
||||
await applyDirectorySyncEffects(result.value.effects);
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Only poll on worker-role instances (same gate as commonWorker) and
|
||||
// only when the feature is enabled (no plugin loaded otherwise).
|
||||
if (env.COMMON_WORKER_ENABLED === "true" && env.SSO_ENABLED) {
|
||||
logger.debug(`👨🏭 Starting accounts webhook worker at host ${env.COMMON_WORKER_REDIS_HOST}`);
|
||||
worker.start();
|
||||
}
|
||||
|
||||
return worker;
|
||||
}
|
||||
|
||||
export const accountsWebhookWorker = singleton("accountsWebhookWorker", initializeWorker);
|
||||
Reference in New Issue
Block a user