schoolbox-ws/src/index.ts

142 lines
3.5 KiB
TypeScript

import { serveFile } from "@std/http/file-server";
import { WSHeartbeat } from "./heartbeat.ts";
import { Registration } from "./registration.ts";
import { parseCommand } from "./commandbuilder.ts";
import { Authentication } from "./auth.ts";
import { Device } from "./device.ts";
// deno-lint-ignore no-explicit-any
async function wsStartHandler(this: WebSocket, msg: MessageEvent<any>) {
const data = parseCommand(msg.data);
if (data === null) return;
switch (data.c as string) {
case "reg_start":
{
new Registration(this);
this.removeEventListener("message", wsStartHandler);
break;
}
case "auth_start": {
await new Authentication(this).authenticate(data);
this.removeEventListener("message", wsStartHandler);
break;
}
default:
break;
}
}
function handlePrompt(prompt: string) {
let tokens = prompt.match(/(?:[^\s"]+|"[^"]*")+/g) || [] as string[];
tokens = tokens.map((v) => v.replaceAll(/"([^"]*)"+/g, "$1"));
if (tokens.length === 0) return;
switch (tokens[0]) {
case "pin": {
const pin = +tokens[1];
if (pin === 0 || isNaN(pin)) return;
console.log(Registration.validate(pin) ? "great success" : "invalid pin");
break;
}
case "devices": {
console.debug(Device.devices);
break;
}
case "startsession": {
if (tokens.length < 3) return;
if (!(tokens[1] in Device.devices)) {
console.log("invalid uuid");
return;
}
const device = Device.devices[tokens[1]];
if (!device.connected) {
console.log("Device not connected.");
return;
}
device.startSession(tokens[2]);
console.log("great success");
break;
}
case "stopsession": {
if (tokens.length < 2) return;
if (!(tokens[1] in Device.devices)) {
console.log("invalid uuid");
return;
}
const device = Device.devices[tokens[1]];
if (!device.connected) {
console.log("Device not connected.");
return;
}
device.stopSession();
console.log("great success");
break;
}
case "votes": {
if (tokens.length < 2) return;
if (!(tokens[1] in Device.devices)) {
console.log("invalid uuid");
return;
}
const device = Device.devices[tokens[1]];
if (!device.connected) {
console.log("Device not connected.");
return;
}
console.log(device.latestVotes);
}
}
}
const WS_ROUTE = new URLPattern({ pathname: "/ws" });
Deno.serve((req) => {
if (WS_ROUTE.exec(req.url) === null) {
return serveFile(req, "./static/index.html");
}
if (req.headers.get("upgrade") != "websocket") {
return new Response(null, { status: 426 });
}
const { socket, response } = Deno.upgradeWebSocket(req);
new WSHeartbeat(socket);
socket.addEventListener("message", wsStartHandler);
return response;
});
const decoder = new TextDecoder();
console.info(`
Commands:
- pin <int>
used to activate device.
- devices
list devices
- startsession <uuid> "<text>"
starts a voting session
- stopsession <uuid>
stops a voting session
- votes <uuid>
grabs the latest votes.
`);
Deno.stdout.write(Uint8Array.from([62, 32])); // '> '
while (true) { // ja dit is scuffed, nee het werkt niet goed, nee ik ga er niks aan doen.
for await (const chunk of Deno.stdin.readable) {
const prompt = decoder.decode(chunk).trim();
if (prompt !== "") handlePrompt(prompt);
Deno.stdout.write(Uint8Array.from([62, 32])); // '> '
}
}