/** * Serves the chat's history to the chatbox (`ChatModal`). Messages are sent, * edited and deleted over the socket instead (`routes/ws.ts`). * * - `GET /api/chat?limit=&before=&beforeId=&after=&afterId=`, signed in. * Returns messages oldest first, at most `limit` (50 by default, 100 at * most). With no cursor, it returns the latest. With `before`, it returns * the page just older than that cursor, for "load older". With `after`, it * returns what follows it, for the resync after a reconnect. A cursor is a * message's ISO `created_at`, and its id breaks ties. Without `afterId`, * messages at the `after` timestamp itself are included. * * @module */ import { Router } from "@oak/oak"; import type { APIResponse } from "../../shared/api.ts"; import type { ChatMessage } from "../../shared/chat.ts"; import { authMiddleware } from "../middleware/auth.ts"; import { getChatMessagesAfter, getRecentChatMessages, } from "../services/chat-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/chat" }); router.get("/", authMiddleware, (ctx) => { const params = ctx.request.url.searchParams; const limitParam = params.get("limit"); const limit = limitParam ? Number(limitParam) : NaN; const cappedLimit = Number.isFinite(limit) ? limit : undefined; const before = params.get("before"); const after = params.get("after"); const messages = after ? getChatMessagesAfter( { createdAt: after, id: params.get("afterId") ?? undefined }, cappedLimit, ) : getRecentChatMessages( cappedLimit, before ? { createdAt: before, id: params.get("beforeId") ?? undefined } : undefined, ); const responseBody: APIResponse = { success: true, data: messages, }; ctx.response.body = responseBody; }); export default router;