Files
sofa/app/api/webhooks/[token]/route.ts
T
2026-03-03 19:25:44 -05:00

62 lines
1.6 KiB
TypeScript

import { eq } from "drizzle-orm";
import type { NextRequest } from "next/server";
import { NextResponse } from "next/server";
import { db } from "@/lib/db/client";
import { webhookConnections } from "@/lib/db/schema";
import { createLogger } from "@/lib/logger";
import type { WebhookEvent } from "@/lib/services/webhooks";
import {
parseJellyfinPayload,
parsePlexPayload,
processWebhook,
} from "@/lib/services/webhooks";
const log = createLogger("webhooks");
export async function POST(
req: NextRequest,
{ params }: { params: Promise<{ token: string }> },
) {
const { token } = await params;
// Look up connection by token — this IS the auth
const connection = db
.select()
.from(webhookConnections)
.where(eq(webhookConnections.token, token))
.get();
if (!connection || !connection.enabled) {
// Always return 200 to avoid retry storms from media servers
return NextResponse.json({ ok: true });
}
try {
let event: WebhookEvent | null;
if (connection.provider === "plex") {
const formData = await req.formData();
event = parsePlexPayload(formData);
} else {
const body = await req.json();
event = parseJellyfinPayload(body);
}
if (!event) {
// Not a relevant event type — silently ignore
return NextResponse.json({ ok: true });
}
await processWebhook(
connection.id,
connection.userId,
connection.provider,
event,
);
} catch (err) {
// Swallow errors — never return non-200 to media servers
log.debug("Webhook processing failed:", err);
}
return NextResponse.json({ ok: true });
}