Spaces:
Sleeping
Sleeping
File size: 2,920 Bytes
2b43822 e3cb794 2b43822 35fc662 2b43822 d32b1fb 2b43822 e3cb794 2b43822 e3cb794 34a23a6 2b43822 9369ecd 2b43822 e3cb794 2b43822 35fc662 e3cb794 2b43822 e3cb794 2b43822 307d8b2 2b43822 d32b1fb 9dffdbf 35fc662 2b43822 9dffdbf 35fc662 2b43822 e3cb794 2b43822 d32b1fb 2b43822 d32b1fb 2b43822 35fc662 2b43822 e1a2bcd 2b43822 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 |
import { ZodSchema } from "zod";
import { generateSessionToken } from "../helpers";
import { Room } from "./room";
import { WebSocketInterface } from "./WebSocketAdapter";
type RouteHandler<T> = (context: Context<T>) => void;
export interface Route<T> {
name: string;
schema: ZodSchema<T>;
handler: RouteHandler<T>;
}
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<T> {
ws: WebSocketInterface;
user: User;
payload: T;
server: HapticLinkServer;
}
export class HapticLinkServer {
routes: { [key: string]: Route<any> };
rooms: { [key: string]: Room };
users: Map<WebSocketInterface, User>;
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<T>(name: string, schema: ZodSchema<T>, handler: RouteHandler<T>): 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<any> = {
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" }));
}
}
}
|