mirror of
https://github.com/jakejarvis/sofa.git
synced 2026-08-29 02:45:39 -04:00
Add Emby as a third media server integration alongside Plex and Jellyfin. Emby webhooks use a similar payload format (JSON with nested Item object and ProviderIds). Also removes the mediaServerUsername field from all webhook connections since it was never used for authentication — the token-in-URL is the sole auth mechanism. This simplifies the connection UX to a single "Connect" button. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
66 lines
1.8 KiB
TypeScript
66 lines
1.8 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 {
|
|
parseEmbyPayload,
|
|
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 if (connection.provider === "emby") {
|
|
const body = await req.json();
|
|
event = parseEmbyPayload(body);
|
|
} 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 });
|
|
}
|