/** * Serves the invite endpoints. Any signed-in user can invite someone, from * their profile page, and the invite link opens the `/register` page. * * - `POST /api/invites` — signed in. Makes an invite and answers its token. * - `GET /api/invites/:token` — public. Checks that an invite still works, * before the register page shows its form; 404 when it doesn't. * * @module */ import { Router } from "@oak/oak"; import { type APIResponse } from "../../shared/api.ts"; import { APIException } from "../lib/api-exception.ts"; import type { InviteResponse } from "../../shared/user.ts"; import { type AuthContext, authMiddleware } from "../middleware/auth.ts"; import { createInvite, validateInvite } from "../services/invite-service.ts"; /** * Mounted by `api/main.ts` behind the error, Open Graph and CORS middleware. * An `APIException` thrown by a handler becomes the JSON error response there. */ const router: Router = new Router({ prefix: "/api/invites" }); router.post("/", authMiddleware, (ctx: AuthContext) => { const responseBody: APIResponse = { success: true, data: { token: createInvite(ctx.state.user.userId) }, }; ctx.response.status = 201; ctx.response.body = responseBody; }); router.get("/:token", (ctx) => { // A used invite answers 404 too: the page only needs to know it won't work. try { validateInvite(ctx.params.token); const responseBody: APIResponse = { success: true, data: null }; ctx.response.body = responseBody; } catch { throw new APIException(404, "Invalid or expired invite"); } }); export default router;