diff --git a/server.ts b/server.ts index ab8ea49..449bcbd 100644 --- a/server.ts +++ b/server.ts @@ -6,6 +6,7 @@ import { getNotes, getNotesByDid, getNotesByDids, + listAllWebhooks, listWebhooksByDid, type WebhookVerb, } from "./src/data/db.ts"; @@ -39,6 +40,32 @@ const requireDidOwnership = async ( return true; }; +const ADMIN_DIDS = new Set( + (Deno.env.get("ADMIN_DIDS") ?? "") + .split(",") + .map((d) => d.trim()) + .filter(Boolean), +); + +const requireAdmin = async (ctx: AuthCtx): Promise => { + let verifiedDid: string; + try { + verifiedDid = await authenticateRequest( + ctx.request.headers.get("Authorization"), + ); + } catch { + ctx.response.status = 401; + ctx.response.body = { error: "Unauthorized" }; + return false; + } + if (!ADMIN_DIDS.has(verifiedDid)) { + ctx.response.status = 403; + ctx.response.body = { error: "Admin only" }; + return false; + } + return true; +}; + const router = new Router(); const PAGINATION = 20; @@ -117,6 +144,11 @@ router.post("/notes/feed", async (ctx) => { const ALLOWED_VERBS = ["create", "delete", "bulk-create"] as const; +router.get("/admin/webhooks", async (ctx) => { + if (!(await requireAdmin(ctx))) return; + ctx.response.body = listAllWebhooks(); +}); + router.get("/:did/webhooks", async (ctx) => { const { did } = ctx.params; if (!(await requireDidOwnership(ctx, did))) return; diff --git a/src/data/db.ts b/src/data/db.ts index e62fc3e..863b2c7 100644 --- a/src/data/db.ts +++ b/src/data/db.ts @@ -136,6 +136,12 @@ export const listWebhooksByDid = ( ).all>(did); }; +export const listAllWebhooks = (): Omit[] => { + return db.prepare( + "SELECT id, did, method, url, verb FROM webhook_subscription ORDER BY did, id", + ).all>(); +}; + export const getWebhooksByDidAndVerb = ( did: string, verb: WebhookVerb,