avallef commited on
Commit
35fc662
·
1 Parent(s): 0beae8c

Added websocket to wrapper for easier testing

Browse files
server/src/index.ts CHANGED
@@ -3,6 +3,7 @@ import * as http from "http";
3
  import * as WebSocket from "ws";
4
  import { HapticLinkServer } from "./socket/hapticLinkServer";
5
  import { registerRoutes } from "./socket/routes";
 
6
 
7
  const port: number = parseInt(process.env.PORT as string, 10) || 3000;
8
 
@@ -24,18 +25,19 @@ registerRoutes(hapticLink);
24
  // data for later requests such as an ID
25
  wss.on("connection", (ws: WebSocket) => {
26
  console.log("Client Connected");
 
27
 
28
  ws.on("message", (message: string) => {
29
  console.log(`Received Message: ${message}`);
30
- hapticLink.handleRoute(ws, message);
31
  });
32
 
33
  // When a user disconnects, their account is removed, and they are removed from all groups
34
  ws.on("close", () => {
35
- hapticLink.removeUser(ws);
36
  })
37
  ws.on("error", () => {
38
- hapticLink.removeUser(ws);
39
  })
40
 
41
  ws.send("Welcome");
 
3
  import * as WebSocket from "ws";
4
  import { HapticLinkServer } from "./socket/hapticLinkServer";
5
  import { registerRoutes } from "./socket/routes";
6
+ import WebSocketWrapper from "./socket/WebSocketAdapter";
7
 
8
  const port: number = parseInt(process.env.PORT as string, 10) || 3000;
9
 
 
25
  // data for later requests such as an ID
26
  wss.on("connection", (ws: WebSocket) => {
27
  console.log("Client Connected");
28
+ const wsw = new WebSocketWrapper(ws);
29
 
30
  ws.on("message", (message: string) => {
31
  console.log(`Received Message: ${message}`);
32
+ hapticLink.handleRoute(wsw, message);
33
  });
34
 
35
  // When a user disconnects, their account is removed, and they are removed from all groups
36
  ws.on("close", () => {
37
+ hapticLink.removeUser(wsw);
38
  })
39
  ws.on("error", () => {
40
+ hapticLink.removeUser(wsw);
41
  })
42
 
43
  ws.send("Welcome");
server/src/socket/WebSocketAdapter.ts ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { WebSocket } from "ws";
2
+
3
+ export default class WebSocketWrapper {
4
+ ws: WebSocket
5
+
6
+ constructor(ws: WebSocket) {
7
+ this.ws = ws;
8
+ }
9
+
10
+ send(data: string | Buffer) {
11
+ this.ws.send(data);
12
+ }
13
+
14
+ get OPEN() {
15
+ return this.ws.OPEN;
16
+ }
17
+
18
+ get CLOSED() {
19
+ return this.ws.CLOSED;
20
+ }
21
+
22
+ get CLOSING() {
23
+ return this.ws.CLOSING;
24
+ }
25
+ }
server/src/socket/hapticLinkServer.ts CHANGED
@@ -1,39 +1,39 @@
1
- import * as WebSocket from "ws";
2
  import { ZodSchema } from "zod";
3
  import { generateSessionToken } from "../helpers";
4
  import { Room } from "./room";
 
5
 
6
  type RouteHandler<T> = (context: Context<T>) => void;
7
 
8
  export interface Route<T> {
9
  name: string;
10
- schema: ZodSchema<T>
11
  handler: RouteHandler<T>;
12
  }
13
 
14
  export class User {
15
  id: string;
16
  username?: string;
17
- socket: WebSocket;
18
  currentRoom?: Room;
19
 
20
- constructor(socket: WebSocket) {
21
  this.id = generateSessionToken(16);
22
  this.socket = socket;
23
  }
24
  }
25
 
26
  export interface Context<T> {
27
- ws: WebSocket;
28
  user: User;
29
  payload: T;
30
  server: HapticLinkServer;
31
  }
32
 
33
  export class HapticLinkServer {
34
- routes: { [key: string]: Route<any> }
35
- rooms: { [key: string]: Room }
36
- users: Map<WebSocket, User>;
37
 
38
  constructor() {
39
  this.routes = {};
@@ -41,7 +41,7 @@ export class HapticLinkServer {
41
  this.rooms = {};
42
  }
43
 
44
- removeUser(ws: WebSocket): boolean {
45
  const user = this.users.get(ws);
46
  if (!user) return false;
47
  if (user.currentRoom) {
@@ -50,30 +50,39 @@ export class HapticLinkServer {
50
  return true;
51
  }
52
 
53
- addRoute<T>(name: string, schema: ZodSchema<T>, handler: RouteHandler<T>): boolean {
 
 
 
 
54
  if (name in this.routes) {
55
- return false
56
  }
57
 
58
  this.routes[name] = {
59
  name,
60
  schema,
61
- handler
62
- }
63
 
64
  return true;
65
  }
66
 
67
- handleRoute(ws: WebSocket, message: string) {
68
  // Parse JSON
69
  let payload: any;
70
  try {
71
  payload = JSON.parse(message);
72
  } catch (e) {
73
- return ws.send(JSON.stringify({ error: "message not in JSON format" }));
 
 
74
  }
75
 
76
- if (typeof payload != "object" || !Object.keys(payload).includes("route")) {
 
 
 
77
  return ws.send(JSON.stringify({ error: "missing route" }));
78
  }
79
 
@@ -81,7 +90,6 @@ export class HapticLinkServer {
81
  return ws.send(JSON.stringify({ error: "route not found" }));
82
  }
83
 
84
-
85
  const route = this.routes[payload.route];
86
  delete payload.route;
87
 
@@ -99,8 +107,8 @@ export class HapticLinkServer {
99
  ws,
100
  payload,
101
  server: this,
102
- user
103
- }
104
 
105
  if (route && route.schema.safeParse(payload).success) {
106
  route.handler(context);
 
 
1
  import { ZodSchema } from "zod";
2
  import { generateSessionToken } from "../helpers";
3
  import { Room } from "./room";
4
+ import WebSocketWrapper from "./WebSocketAdapter";
5
 
6
  type RouteHandler<T> = (context: Context<T>) => void;
7
 
8
  export interface Route<T> {
9
  name: string;
10
+ schema: ZodSchema<T>;
11
  handler: RouteHandler<T>;
12
  }
13
 
14
  export class User {
15
  id: string;
16
  username?: string;
17
+ socket: WebSocketWrapper;
18
  currentRoom?: Room;
19
 
20
+ constructor(socket: WebSocketWrapper) {
21
  this.id = generateSessionToken(16);
22
  this.socket = socket;
23
  }
24
  }
25
 
26
  export interface Context<T> {
27
+ ws: WebSocketWrapper;
28
  user: User;
29
  payload: T;
30
  server: HapticLinkServer;
31
  }
32
 
33
  export class HapticLinkServer {
34
+ routes: { [key: string]: Route<any> };
35
+ rooms: { [key: string]: Room };
36
+ users: Map<WebSocketWrapper, User>;
37
 
38
  constructor() {
39
  this.routes = {};
 
41
  this.rooms = {};
42
  }
43
 
44
+ removeUser(ws: WebSocketWrapper): boolean {
45
  const user = this.users.get(ws);
46
  if (!user) return false;
47
  if (user.currentRoom) {
 
50
  return true;
51
  }
52
 
53
+ addRoute<T>(
54
+ name: string,
55
+ schema: ZodSchema<T>,
56
+ handler: RouteHandler<T>
57
+ ): boolean {
58
  if (name in this.routes) {
59
+ return false;
60
  }
61
 
62
  this.routes[name] = {
63
  name,
64
  schema,
65
+ handler,
66
+ };
67
 
68
  return true;
69
  }
70
 
71
+ handleRoute(ws: WebSocketWrapper, message: string) {
72
  // Parse JSON
73
  let payload: any;
74
  try {
75
  payload = JSON.parse(message);
76
  } catch (e) {
77
+ return ws.send(
78
+ JSON.stringify({ error: "message not in JSON format" })
79
+ );
80
  }
81
 
82
+ if (
83
+ typeof payload != "object" ||
84
+ !Object.keys(payload).includes("route")
85
+ ) {
86
  return ws.send(JSON.stringify({ error: "missing route" }));
87
  }
88
 
 
90
  return ws.send(JSON.stringify({ error: "route not found" }));
91
  }
92
 
 
93
  const route = this.routes[payload.route];
94
  delete payload.route;
95
 
 
107
  ws,
108
  payload,
109
  server: this,
110
+ user,
111
+ };
112
 
113
  if (route && route.schema.safeParse(payload).success) {
114
  route.handler(context);