147 lines
2.7 KiB
TypeScript
147 lines
2.7 KiB
TypeScript
import { Elysia, t } from "elysia";
|
|
import {
|
|
getSimplePlayerView,
|
|
SimpleAction,
|
|
SimpleConfiguration,
|
|
SimpleGameState,
|
|
} from "./games/simple";
|
|
import { human } from "./human";
|
|
import dayjs from "dayjs";
|
|
import db from "./db";
|
|
import { liveTable, WsOut, WsIn } from "./table";
|
|
import { Human } from "@prisma/client";
|
|
import { combine } from "kefir";
|
|
|
|
const api = new Elysia({ prefix: "/api" })
|
|
.post("/whoami", async ({ cookie: { token } }) => {
|
|
let human: Human | null;
|
|
if (
|
|
token.value == null ||
|
|
(human = await db.human.findUnique({
|
|
where: {
|
|
token: token.value,
|
|
},
|
|
})) == null
|
|
) {
|
|
human = await db.human.create({
|
|
data: {},
|
|
});
|
|
token.set({
|
|
value: human.token,
|
|
expires: dayjs().add(1, "year").toDate(),
|
|
httpOnly: true,
|
|
});
|
|
}
|
|
|
|
return human.key;
|
|
})
|
|
.use(human)
|
|
.post(
|
|
"/setName",
|
|
({ body: { name }, humanKey }) =>
|
|
db.human.update({
|
|
where: {
|
|
key: humanKey,
|
|
},
|
|
data: {
|
|
name,
|
|
},
|
|
}),
|
|
{
|
|
body: t.Object({
|
|
name: t.String(),
|
|
}),
|
|
}
|
|
)
|
|
.get("/profile", ({ humanKey, query: { otherHumanKey } }) =>
|
|
db.human
|
|
.findFirst({ where: { key: otherHumanKey ?? humanKey } })
|
|
.then((human) => {
|
|
if (human == null) {
|
|
return null;
|
|
}
|
|
const { token, ...safeProfile } = human;
|
|
return safeProfile;
|
|
})
|
|
)
|
|
.get("/games", () => [{ key: "simple", name: "simple" }])
|
|
.ws("/ws/:tableKey", {
|
|
async open({
|
|
data: {
|
|
params: { tableKey },
|
|
humanKey,
|
|
},
|
|
send,
|
|
}) {
|
|
const table = liveTable<
|
|
SimpleConfiguration,
|
|
SimpleGameState,
|
|
SimpleAction
|
|
>(tableKey);
|
|
|
|
table.inputs.connectionChanges.emit({
|
|
humanKey,
|
|
presence: "joined",
|
|
});
|
|
|
|
table.outputs.playersPresent.onValue((players) =>
|
|
send({ players })
|
|
);
|
|
|
|
table.outputs.playersReady
|
|
.skipDuplicates()
|
|
.onValue((readys) => send({ playersReady: readys }));
|
|
|
|
combine(
|
|
[table.outputs.gameState],
|
|
[table.outputs.gameConfig],
|
|
(state, config) =>
|
|
state &&
|
|
config &&
|
|
getSimplePlayerView(config, state, humanKey)
|
|
)
|
|
.toProperty()
|
|
.onValue((view) => send({ view }));
|
|
},
|
|
|
|
response: WsOut,
|
|
body: WsIn,
|
|
|
|
message(
|
|
{
|
|
data: {
|
|
humanKey,
|
|
params: { tableKey },
|
|
},
|
|
},
|
|
body
|
|
) {
|
|
const {
|
|
inputs: { readys, actions, quits },
|
|
} = liveTable(tableKey);
|
|
|
|
if ("ready" in body) {
|
|
readys.emit({ humanKey, ...body });
|
|
} else if ("action" in body) {
|
|
actions.emit({ humanKey, ...body.action });
|
|
} else if ("quit" in body) {
|
|
quits.emit({ humanKey });
|
|
}
|
|
},
|
|
|
|
async close({
|
|
data: {
|
|
params: { tableKey },
|
|
humanKey,
|
|
},
|
|
}) {
|
|
liveTable(tableKey).inputs.connectionChanges.emit({
|
|
humanKey,
|
|
presence: "left",
|
|
});
|
|
},
|
|
});
|
|
|
|
export default api;
|
|
export type Api = typeof api;
|