Spaces:
Runtime error
Runtime error
Merge pull request #13 from AnujPanthri/server_new_id
Browse files- server/package-lock.json +6 -0
- server/package.json +1 -0
- server/src/helpers.ts +11 -3
- server/src/socket/hapticLinkServer.ts +2 -1
- server/src/socket/room.ts +2 -1
- server/src/socket/routes/join_room.ts +13 -5
- server/test/helpers.spec.ts +2 -2
- server/test/socket/routes/join_room.spec.ts +22 -0
- server/test/socket/routes/send_touch.spec.ts +4 -2
server/package-lock.json
CHANGED
|
@@ -10,6 +10,7 @@
|
|
| 10 |
"license": "ISC",
|
| 11 |
"dependencies": {
|
| 12 |
"@types/ws": "^8.5.9",
|
|
|
|
| 13 |
"express": "^4.18.2",
|
| 14 |
"pino": "^8.16.2",
|
| 15 |
"ws": "^8.14.2",
|
|
@@ -341,6 +342,11 @@
|
|
| 341 |
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
| 342 |
"dev": true
|
| 343 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 344 |
"node_modules/base64-js": {
|
| 345 |
"version": "1.5.1",
|
| 346 |
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
|
|
|
|
| 10 |
"license": "ISC",
|
| 11 |
"dependencies": {
|
| 12 |
"@types/ws": "^8.5.9",
|
| 13 |
+
"base-x": "^4.0.0",
|
| 14 |
"express": "^4.18.2",
|
| 15 |
"pino": "^8.16.2",
|
| 16 |
"ws": "^8.14.2",
|
|
|
|
| 342 |
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
| 343 |
"dev": true
|
| 344 |
},
|
| 345 |
+
"node_modules/base-x": {
|
| 346 |
+
"version": "4.0.0",
|
| 347 |
+
"resolved": "https://registry.npmjs.org/base-x/-/base-x-4.0.0.tgz",
|
| 348 |
+
"integrity": "sha512-FuwxlW4H5kh37X/oW59pwTzzTKRzfrrQwhmyspRM7swOEZcHtDZSCt45U6oKgtuFE+WYPblePMVIPR4RZrh/hw=="
|
| 349 |
+
},
|
| 350 |
"node_modules/base64-js": {
|
| 351 |
"version": "1.5.1",
|
| 352 |
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
|
server/package.json
CHANGED
|
@@ -26,6 +26,7 @@
|
|
| 26 |
},
|
| 27 |
"dependencies": {
|
| 28 |
"@types/ws": "^8.5.9",
|
|
|
|
| 29 |
"express": "^4.18.2",
|
| 30 |
"pino": "^8.16.2",
|
| 31 |
"ws": "^8.14.2",
|
|
|
|
| 26 |
},
|
| 27 |
"dependencies": {
|
| 28 |
"@types/ws": "^8.5.9",
|
| 29 |
+
"base-x": "^4.0.0",
|
| 30 |
"express": "^4.18.2",
|
| 31 |
"pino": "^8.16.2",
|
| 32 |
"ws": "^8.14.2",
|
server/src/helpers.ts
CHANGED
|
@@ -1,7 +1,15 @@
|
|
| 1 |
import * as crypto from "crypto";
|
|
|
|
| 2 |
|
| 3 |
export function generateSessionToken(length: number): string {
|
| 4 |
-
if (length < 1) throw new Error("invalid length. length must be greater than 0")
|
| 5 |
-
|
| 6 |
-
|
|
|
|
|
|
|
| 7 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
import * as crypto from "crypto";
|
| 2 |
+
import basex from "base-x";
|
| 3 |
|
| 4 |
export function generateSessionToken(length: number): string {
|
| 5 |
+
if (length < 1) throw new Error("invalid length. length must be greater than 0");
|
| 6 |
+
const base31Tokens = "ABCDEFGHJKMNPQRSTUVWXYZ23456789";
|
| 7 |
+
const base31 = basex(base31Tokens);
|
| 8 |
+
|
| 9 |
+
const byteLength = Math.ceil((length * Math.log(256)) / Math.log(base31Tokens.length)) + 2;
|
| 10 |
|
| 11 |
+
const buffer = crypto.randomBytes(Math.ceil(byteLength));
|
| 12 |
+
const token = base31.encode(buffer);
|
| 13 |
+
|
| 14 |
+
return token.substring(0, length);
|
| 15 |
+
}
|
server/src/socket/hapticLinkServer.ts
CHANGED
|
@@ -12,13 +12,14 @@ export interface Route<T> {
|
|
| 12 |
}
|
| 13 |
|
| 14 |
export class User {
|
|
|
|
| 15 |
id: string;
|
| 16 |
username?: string;
|
| 17 |
socket: WebSocketInterface;
|
| 18 |
currentRoom?: Room;
|
| 19 |
|
| 20 |
constructor(socket: WebSocketInterface) {
|
| 21 |
-
this.id = generateSessionToken(
|
| 22 |
this.socket = socket;
|
| 23 |
}
|
| 24 |
}
|
|
|
|
| 12 |
}
|
| 13 |
|
| 14 |
export class User {
|
| 15 |
+
static userIdLength: number = 32
|
| 16 |
id: string;
|
| 17 |
username?: string;
|
| 18 |
socket: WebSocketInterface;
|
| 19 |
currentRoom?: Room;
|
| 20 |
|
| 21 |
constructor(socket: WebSocketInterface) {
|
| 22 |
+
this.id = generateSessionToken(32);
|
| 23 |
this.socket = socket;
|
| 24 |
}
|
| 25 |
}
|
server/src/socket/room.ts
CHANGED
|
@@ -9,10 +9,11 @@ export interface UserData {
|
|
| 9 |
}
|
| 10 |
|
| 11 |
export class Room {
|
|
|
|
| 12 |
id: string;
|
| 13 |
users: User[];
|
| 14 |
constructor(roomId?: string) {
|
| 15 |
-
this.id = roomId || generateSessionToken(
|
| 16 |
this.users = [];
|
| 17 |
}
|
| 18 |
|
|
|
|
| 9 |
}
|
| 10 |
|
| 11 |
export class Room {
|
| 12 |
+
static roomIdLength: number = 6;
|
| 13 |
id: string;
|
| 14 |
users: User[];
|
| 15 |
constructor(roomId?: string) {
|
| 16 |
+
this.id = roomId || generateSessionToken(Room.roomIdLength);
|
| 17 |
this.users = [];
|
| 18 |
}
|
| 19 |
|
server/src/socket/routes/join_room.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
| 1 |
import { z } from "zod";
|
| 2 |
-
import { Context
|
| 3 |
-
import { generateSessionToken } from "../../helpers";
|
| 4 |
import { Room } from "../room";
|
| 5 |
|
| 6 |
export interface JoinRoomPayload {
|
|
@@ -18,17 +17,26 @@ export function JoinRoomHandler(ctx: Context<JoinRoomPayload>) {
|
|
| 18 |
ctx.user.username = ctx.payload.username;
|
| 19 |
}
|
| 20 |
|
|
|
|
|
|
|
| 21 |
let room: Room;
|
| 22 |
|
| 23 |
if (ctx.payload.roomId && !ctx.server.rooms[ctx.payload.roomId]) {
|
| 24 |
// User sent roomId, but room doesn't exist
|
| 25 |
-
|
| 26 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 27 |
|
| 28 |
} else if (!ctx.payload.roomId) {
|
| 29 |
// Didn't include RoomID, creating new one
|
| 30 |
-
ctx.payload.roomId = generateSessionToken(12);
|
| 31 |
room = new Room(ctx.payload.roomId);
|
|
|
|
|
|
|
| 32 |
ctx.server.rooms[room.id] = room;
|
| 33 |
|
| 34 |
} else {
|
|
|
|
| 1 |
import { z } from "zod";
|
| 2 |
+
import { Context } from "../hapticLinkServer";
|
|
|
|
| 3 |
import { Room } from "../room";
|
| 4 |
|
| 5 |
export interface JoinRoomPayload {
|
|
|
|
| 17 |
ctx.user.username = ctx.payload.username;
|
| 18 |
}
|
| 19 |
|
| 20 |
+
if (ctx.payload.roomId) ctx.payload.roomId = ctx.payload.roomId.toUpperCase();
|
| 21 |
+
|
| 22 |
let room: Room;
|
| 23 |
|
| 24 |
if (ctx.payload.roomId && !ctx.server.rooms[ctx.payload.roomId]) {
|
| 25 |
// User sent roomId, but room doesn't exist
|
| 26 |
+
return ctx.ws.send(JSON.stringify({
|
| 27 |
+
"message": "join_room_response",
|
| 28 |
+
"status": "room doesn't exist",
|
| 29 |
+
}))
|
| 30 |
+
// Custom ID Code:
|
| 31 |
+
// room = new Room(ctx.payload.roomId);
|
| 32 |
+
// room.id = room.id.toUpperCase();
|
| 33 |
+
// ctx.server.rooms[room.id] = room;
|
| 34 |
|
| 35 |
} else if (!ctx.payload.roomId) {
|
| 36 |
// Didn't include RoomID, creating new one
|
|
|
|
| 37 |
room = new Room(ctx.payload.roomId);
|
| 38 |
+
room.id = room.id.toUpperCase();
|
| 39 |
+
ctx.payload.roomId = room.id;
|
| 40 |
ctx.server.rooms[room.id] = room;
|
| 41 |
|
| 42 |
} else {
|
server/test/helpers.spec.ts
CHANGED
|
@@ -8,12 +8,12 @@ describe('Helpers', () => {
|
|
| 8 |
|
| 9 |
// Since the token is hexadecimal, its string length should be twice the byte length
|
| 10 |
expect(token).to.be.a('string');
|
| 11 |
-
expect(token.length).to.equal(length
|
| 12 |
});
|
| 13 |
|
| 14 |
it('should generate a unique token each time', () => {
|
| 15 |
const tokens = new Set<string>();
|
| 16 |
-
const tokenCount =
|
| 17 |
const length = 16;
|
| 18 |
|
| 19 |
for (let i = 0; i < tokenCount; i++) {
|
|
|
|
| 8 |
|
| 9 |
// Since the token is hexadecimal, its string length should be twice the byte length
|
| 10 |
expect(token).to.be.a('string');
|
| 11 |
+
expect(token.length).to.equal(length);
|
| 12 |
});
|
| 13 |
|
| 14 |
it('should generate a unique token each time', () => {
|
| 15 |
const tokens = new Set<string>();
|
| 16 |
+
const tokenCount = 1000; // Generate a number of tokens to test uniqueness
|
| 17 |
const length = 16;
|
| 18 |
|
| 19 |
for (let i = 0; i < tokenCount; i++) {
|
server/test/socket/routes/join_room.spec.ts
CHANGED
|
@@ -43,6 +43,16 @@ describe("Join Room", () => {
|
|
| 43 |
expect(ctx.payload.roomId!).to.satisfy((id: string) => id.length > 3, "room id not generated");
|
| 44 |
expect(user.username).to.equal("test", "didn't set username");
|
| 45 |
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 46 |
|
| 47 |
it("should join existing room", () => {
|
| 48 |
testJoinRoom(1); // Initial user joins and creates room
|
|
@@ -58,4 +68,16 @@ describe("Join Room", () => {
|
|
| 58 |
JoinRoomHandler(ctx);
|
| 59 |
expect(server.rooms[ctx.payload.roomId!].users.length).to.equal(2, "user joined twice");
|
| 60 |
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 61 |
});
|
|
|
|
| 43 |
expect(ctx.payload.roomId!).to.satisfy((id: string) => id.length > 3, "room id not generated");
|
| 44 |
expect(user.username).to.equal("test", "didn't set username");
|
| 45 |
});
|
| 46 |
+
|
| 47 |
+
it("should fail to join not existant room", () => {
|
| 48 |
+
ctx.payload.roomId = "testid"
|
| 49 |
+
JoinRoomHandler(ctx);
|
| 50 |
+
const resData = (ws.sendData as string);
|
| 51 |
+
const res = JSON.parse(resData);
|
| 52 |
+
expect(res.message).to.eq("join_room_response", "didn't get server response");
|
| 53 |
+
const roomNowExists = server.rooms["testid"]
|
| 54 |
+
expect(roomNowExists).to.equal(undefined, "server allowed to join non-existant room");
|
| 55 |
+
});
|
| 56 |
|
| 57 |
it("should join existing room", () => {
|
| 58 |
testJoinRoom(1); // Initial user joins and creates room
|
|
|
|
| 68 |
JoinRoomHandler(ctx);
|
| 69 |
expect(server.rooms[ctx.payload.roomId!].users.length).to.equal(2, "user joined twice");
|
| 70 |
});
|
| 71 |
+
|
| 72 |
+
it("should have case insensitive IDs", () => {
|
| 73 |
+
testJoinRoom(1); // Initial user joins and creates room
|
| 74 |
+
|
| 75 |
+
// Set up second user to join the same room
|
| 76 |
+
ctx.payload.username = "test2";
|
| 77 |
+
ctx.user = user2;
|
| 78 |
+
ctx.ws = ws2;
|
| 79 |
+
ctx.payload.roomId = ctx.payload.roomId?.toLowerCase();
|
| 80 |
+
|
| 81 |
+
testJoinRoom(2); // Second user joins the existing room
|
| 82 |
+
})
|
| 83 |
});
|
server/test/socket/routes/send_touch.spec.ts
CHANGED
|
@@ -24,13 +24,13 @@ describe("Send Vibration", () => {
|
|
| 24 |
ctx = {
|
| 25 |
ws: ws,
|
| 26 |
user: user,
|
| 27 |
-
payload: { username: "test"
|
| 28 |
server: server,
|
| 29 |
};
|
| 30 |
ctx2 = {
|
| 31 |
ws: ws2,
|
| 32 |
user: user2,
|
| 33 |
-
payload: { username: "test2"
|
| 34 |
server: server,
|
| 35 |
};
|
| 36 |
|
|
@@ -54,6 +54,8 @@ describe("Send Vibration", () => {
|
|
| 54 |
|
| 55 |
it("should broadcast vibration", () => {
|
| 56 |
JoinRoomHandler(ctx)
|
|
|
|
|
|
|
| 57 |
JoinRoomHandler(ctx2)
|
| 58 |
ws.sendData = "";
|
| 59 |
ws2.sendData = "";
|
|
|
|
| 24 |
ctx = {
|
| 25 |
ws: ws,
|
| 26 |
user: user,
|
| 27 |
+
payload: { username: "test" },
|
| 28 |
server: server,
|
| 29 |
};
|
| 30 |
ctx2 = {
|
| 31 |
ws: ws2,
|
| 32 |
user: user2,
|
| 33 |
+
payload: { username: "test2" },
|
| 34 |
server: server,
|
| 35 |
};
|
| 36 |
|
|
|
|
| 54 |
|
| 55 |
it("should broadcast vibration", () => {
|
| 56 |
JoinRoomHandler(ctx)
|
| 57 |
+
const resData = JSON.parse(ws.sendData as string)
|
| 58 |
+
ctx2.payload.roomId = resData.roomId
|
| 59 |
JoinRoomHandler(ctx2)
|
| 60 |
ws.sendData = "";
|
| 61 |
ws2.sendData = "";
|