mirror of
https://github.com/jakejarvis/sofa.git
synced 2026-08-29 02:45:39 -04:00
Migrate the entire monorepo from Biome 2.4.7 to oxlint 1.56.0 (linter) and oxfmt 0.41.0 (formatter) for faster lint/format and broader rule coverage. - Add `.oxlintrc.json` with React, TypeScript, unicorn, import plugins and correctness/suspicious categories - Add `.oxfmtrc.json` with 2-space indent, import sorting, and Tailwind class sorting (all 30+ custom className attributes migrated) - Add `docs/.oxlintrc.json` and `docs/.oxfmtrc.json` with Next.js plugin - Update all 12 workspace package.json scripts: `oxlint`, `oxfmt`, `oxfmt --check` - Add `format:check` turbo task and CI step - Update VS Code settings/extensions to use `oxc.oxc-vscode` - Update CI path triggers from `biome.json` to new config files - Remove all `biome-ignore` comments and fix shadowed variables - Delete `biome.json` and `docs/biome.json` - Reformat entire codebase with oxfmt Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
65 lines
1.8 KiB
TypeScript
65 lines
1.8 KiB
TypeScript
import { Hono } from "hono";
|
|
|
|
import type { WebhookEvent } from "@sofa/core/webhooks";
|
|
import {
|
|
parseEmbyPayload,
|
|
parseJellyfinPayload,
|
|
parsePlexPayload,
|
|
processWebhook,
|
|
} from "@sofa/core/webhooks";
|
|
import { db } from "@sofa/db/client";
|
|
import { eq } from "@sofa/db/helpers";
|
|
import { integrations } from "@sofa/db/schema";
|
|
import { createLogger } from "@sofa/logger";
|
|
|
|
const log = createLogger("webhooks");
|
|
|
|
const app = new Hono();
|
|
|
|
app.post("/:token", async (c) => {
|
|
const token = c.req.param("token");
|
|
|
|
// Look up connection by token — this IS the auth
|
|
const connection = db.select().from(integrations).where(eq(integrations.token, token)).get();
|
|
|
|
if (!connection || !connection.enabled) {
|
|
// Always return 200 to avoid retry storms from media servers
|
|
return c.json({ ok: true });
|
|
}
|
|
|
|
// Only webhook-type integrations are handled here
|
|
if (connection.type !== "webhook") {
|
|
return c.json({ ok: true });
|
|
}
|
|
|
|
const provider = connection.provider as "plex" | "jellyfin" | "emby";
|
|
|
|
try {
|
|
let event: WebhookEvent | null;
|
|
if (provider === "plex") {
|
|
const formData = await c.req.raw.formData();
|
|
event = parsePlexPayload(formData as unknown as FormData);
|
|
} else if (provider === "emby") {
|
|
const body = await c.req.json();
|
|
event = parseEmbyPayload(body);
|
|
} else {
|
|
const body = await c.req.json();
|
|
event = parseJellyfinPayload(body);
|
|
}
|
|
|
|
if (!event) {
|
|
// Not a relevant event type — silently ignore
|
|
return c.json({ ok: true });
|
|
}
|
|
|
|
await processWebhook(connection.id, connection.userId, provider, event);
|
|
} catch (err) {
|
|
// Swallow errors — never return non-200 to media servers
|
|
log.debug("Webhook processing failed:", err);
|
|
}
|
|
|
|
return c.json({ ok: true });
|
|
});
|
|
|
|
export default app;
|