import dayjs from "dayjs"; import { Elysia } from "elysia"; import { generateTokenAndKey, resolveToken } from "./human"; import { err } from "./logging"; import { liveTable, WsIn, WsOut } from "./table"; import type { ExtractPropertyType, UnionKeys } from "@games/shared/types"; const api = new Elysia({ prefix: "/api" }) .post("/whoami", async ({ cookie: { token } }) => { let key: string | undefined; if (token.value == null || (key = resolveToken(token.value)) == null) { const [newToken, newKey] = generateTokenAndKey(); token.set({ value: newToken, expires: dayjs().add(1, "year").toDate(), httpOnly: true, }); return newKey; } return key; }) .derive(async ({ cookie: { token }, status }) => { const humanKey = token.value && resolveToken(token.value); return humanKey != null ? { humanKey } : status(401); }) .ws("/ws/:tableKey", { body: WsIn, response: WsOut, open({ data: { params: { tableKey }, humanKey, }, send, }) { const table = liveTable(tableKey); table.inputs.connectionChanges.emit({ humanKey, presence: "joined", }); Object.entries({ ...table.outputs.global, ...(table.outputs.player[humanKey] ?? {}), }).forEach(([type, stream]) => // @ts-ignore stream.onValue((v) => send({ [type]: v })) ); }, message( { data: { humanKey, params: { tableKey }, }, }, body ) { liveTable(tableKey).inputs.messages.emit({ ...body, humanKey }); }, close({ data: { params: { tableKey }, humanKey, }, }) { liveTable(tableKey).inputs.connectionChanges.emit({ humanKey, presence: "left", }); }, error(error) { err(error); }, }); export default api; export type Api = typeof api;