Files
remanso-jetstream/server.ts
Julien Calixte 373b7a6777 feat: add webhook_subscription table and CRUD endpoints
- Migration: CREATE TABLE webhook_subscription (id, did, method, url) with index on did
- db.ts: addWebhookSubscription and deleteWebhooksByDid helpers
- server.ts: POST /:did/webhooks (201) and DELETE /:did/webhooks (204)
2026-02-25 22:46:45 +01:00

96 lines
2.5 KiB
TypeScript

import { Application, Router } from "@oak/oak";
import {
addWebhookSubscription,
deleteWebhooksByDid,
getNotes,
getNotesByDid,
} from "./src/data/db.ts";
import { log } from "./src/log.ts";
const router = new Router();
const PAGINATION = 20;
router.get("/", (ctx) => {
ctx.response.body = "Hello world";
});
router.get("/notes", (ctx) => {
const cursor = ctx.request.url.searchParams.get("cursor") ?? undefined;
const limit = Number(ctx.request.url.searchParams.get("limit")) || PAGINATION;
ctx.response.body = getNotes(cursor, limit);
});
router.get("/:did/notes", (ctx) => {
const { did } = ctx.params;
const cursor = ctx.request.url.searchParams.get("cursor") ?? undefined;
const limit = Number(ctx.request.url.searchParams.get("limit")) || PAGINATION;
ctx.response.body = getNotesByDid(did, cursor, limit);
});
router.post("/:did/webhooks", async (ctx) => {
const { did } = ctx.params;
const body = await ctx.request.body.json();
const { method, url } = body ?? {};
if (!method || !url) {
ctx.response.status = 400;
ctx.response.body = { error: "method and url are required" };
return;
}
const subscription = addWebhookSubscription({ did, method, url });
ctx.response.status = 201;
ctx.response.body = subscription;
});
router.delete("/:did/webhooks", (ctx) => {
const { did } = ctx.params;
deleteWebhooksByDid(did);
ctx.response.status = 204;
});
// router.delete("/:did/:rkey", async (ctx) => {
// const { did, rkey } = ctx.params;
// let verifiedDid: string;
// try {
// verifiedDid = await authenticateRequest(
// ctx.request.headers.get("Authorization"),
// );
// } catch {
// ctx.response.status = 401;
// ctx.response.body = { error: "Unauthorized" };
// return;
// }
// if (verifiedDid !== did) {
// ctx.response.status = 403;
// ctx.response.body = { error: "You can only delete your own notes" };
// return;
// }
// deleteNote({ did, rkey });
// ctx.response.status = 204;
// })
const app = new Application();
app.use(async (ctx, next) => {
ctx.response.headers.set("Access-Control-Allow-Origin", "*");
ctx.response.headers.set(
"Access-Control-Allow-Methods",
"GET, POST, PUT, DELETE, OPTIONS",
);
ctx.response.headers.set(
"Access-Control-Allow-Headers",
"Content-Type, Authorization",
);
if (ctx.request.method === "OPTIONS") {
ctx.response.status = 204;
return;
}
await next();
});
app.use(router.routes());
app.use(router.allowedMethods());
log("[server] listening on port 8080");
app.listen({ port: 8080 });