Files
games/pkg/server/src/api.ts
2025-09-08 22:45:42 -04:00

84 lines
1.7 KiB
TypeScript

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;