import { ZodSchema } from "zod"; import { generateSessionToken } from "../helpers"; import { Room } from "./room"; import { WebSocketInterface } from "./WebSocketAdapter"; type RouteHandler = (context: Context) => void; export interface Route { name: string; schema: ZodSchema; handler: RouteHandler; } export class User { static userIdLength: number = 32; id: string; username?: string; socket: WebSocketInterface; currentRoom?: Room; constructor(socket: WebSocketInterface) { this.id = generateSessionToken(32); this.socket = socket; this.username = "Unknown"; } } export interface Context { ws: WebSocketInterface; user: User; payload: T; server: HapticLinkServer; } export class HapticLinkServer { routes: { [key: string]: Route }; rooms: { [key: string]: Room }; users: Map; constructor() { this.routes = {}; this.users = new Map(); this.rooms = {}; } removeUser(ws: WebSocketInterface): boolean { const user = this.users.get(ws); if (!user) return false; if (user.currentRoom) { user.currentRoom.removeUserById(user.id); } return true; } addRoute(name: string, schema: ZodSchema, handler: RouteHandler): boolean { if (name in this.routes) { return false; } this.routes[name] = { name, schema, handler, }; return true; } handleRoute(ws: WebSocketInterface, message: string) { // Parse JSON let payload: any; try { payload = JSON.parse(message); } catch (e) { return ws.send(JSON.stringify({ error: "message not in JSON format" })); } if (typeof payload != "object" || !Object.keys(payload).includes("route")) { return ws.send(JSON.stringify({ error: "missing route" })); } if (!(payload.route in this.routes)) { return ws.send(JSON.stringify({ error: "route not found" })); } const route = this.routes[payload.route]; delete payload.route; let user: User; if (this.users.has(ws)) { user = this.users.get(ws)!; } else { const newUser = new User(ws); this.users.set(ws, newUser); user = newUser; } let context: Context = { ws, payload, server: this, user, }; if (!route) { return ws.send(JSON.stringify({ error: "invalid route" })); } if (route.schema.safeParse(payload).success) { route.handler(context); } else { return ws.send(JSON.stringify({ error: "invalid payload format" })); } } }