All files / src/lib/app/routes webhook.ts

100% Statements 11/11
100% Branches 8/8
100% Functions 3/3
100% Lines 11/11

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96                                                                                  23x                                       25x         25x                           25x 25x       25x   25x 10x 10x 10x   10x      
import type { Hono } from "hono";
import { env as honoEnv } from "hono/adapter";
import { createKickWebhookValidationMiddleware } from "../../functions/middleware";
import { recordError } from "../../functions/errors/logError";
import { getDb } from "../../prisma";
import { followEvent } from "../../events/follow";
import { livestreamStatusUpdate } from "../../events/livestream";
import {
  newSubscriber,
  giftedSubs,
  renewedSub,
} from "../../events/subscriptions";
import { kicksGifted } from "../../events/kicks";
import { chatHandler } from "../../events/chat";
import type { AppEnv, WebhookContext } from "../types";
import {
  dispatchWebhookEvent,
  type WebhookEventHandlers,
} from "../handlers/dispatchWebhookEvent";
import type { DbClient } from "../../prisma";
 
/**
 * Concrete functions used by the webhook route when no overrides are
 * provided.
 */
export interface WebhookRouteDependencies {
  recordError: typeof recordError;
  getDb: typeof getDb;
  handlers: WebhookEventHandlers;
}
 
/**
 * Optional overrides that allow tests and integrations to swap out webhook
 * dependencies such as persistence and event handlers.
 */
export interface WebhookRouteOverrides {
  recordError?: typeof recordError;
  getDb?: typeof getDb;
  handlers?: Partial<WebhookEventHandlers>;
}
 
const defaultDependencies: WebhookRouteDependencies = {
  recordError,
  getDb,
  handlers: {
    followEvent,
    livestreamStatusUpdate,
    newSubscriber,
    giftedSubs,
    renewedSub,
    kicksGifted,
    chatHandler,
  },
};
 
/**
 * Merges optional overrides with the production webhook dependencies.
 */
function resolveDependencies(
  overrides?: WebhookRouteOverrides
): WebhookRouteDependencies {
  const resolvedHandlers: WebhookEventHandlers = {
    ...defaultDependencies.handlers,
    ...(overrides?.handlers ?? {}),
  };
 
  return {
    recordError: overrides?.recordError ?? defaultDependencies.recordError,
    getDb: overrides?.getDb ?? defaultDependencies.getDb,
    handlers: resolvedHandlers,
  };
}
 
/**
 * Wires up the Kick webhook middleware and request handler.
 */
export function registerWebhookRoute(
  app: Hono<AppEnv>,
  overrides?: WebhookRouteOverrides
): void {
  const dependencies = resolveDependencies(overrides);
  const webhookMiddleware = createKickWebhookValidationMiddleware<AppEnv>(
    dependencies.recordError
  );
 
  app.use("/webhook", webhookMiddleware);
 
  app.post("/webhook", async (c: WebhookContext) => {
    const result = c.get("kickWebhook");
    const databaseUrl = honoEnv(c).DATABASE_URL ?? "";
    const db: DbClient = dependencies.getDb(databaseUrl);
 
    return dispatchWebhookEvent(result, db, c, dependencies.handlers);
  });
}