From 83ab0c4ea768ef4ff0e7796b5660c897f73a08d4 Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Tue, 15 Jul 2025 15:16:11 +0200 Subject: [PATCH] admin side scan connection --- package-lock.json | 48 +++++++++++++++++++++++ package.json | 1 + src/enums/socketEnum.ts | 4 ++ src/middleware/authenticateSocket.ts | 1 - src/websocket/handleEvent.ts | 26 ++++++++++++- src/websocket/index.ts | 24 +++++++++++- src/websocket/scanner/index.ts | 58 +++++++++++++++++++++++++++- 7 files changed, 156 insertions(+), 6 deletions(-) create mode 100644 src/enums/socketEnum.ts diff --git a/package-lock.json b/package-lock.json index cfbc2a5..e0c92d4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -43,6 +43,7 @@ "validator": "^13.15.15" }, "devDependencies": { + "@socket.io/admin-ui": "^0.5.1", "@types/cors": "^2.8.19", "@types/express": "^5.0.3", "@types/ip": "^1.1.3", @@ -764,6 +765,39 @@ "node": ">=18" } }, + "node_modules/@socket.io/admin-ui": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@socket.io/admin-ui/-/admin-ui-0.5.1.tgz", + "integrity": "sha512-1dlGL2FGm6T+uL1e6iDvbo2eCINwvW7iVbjIblwh5kPPRM1SP8lmZrbFZf4QNJ/cqQ+JLcx49eXGM9WAB4TK7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/bcryptjs": "^2.4.2", + "bcryptjs": "^2.4.3", + "debug": "~4.3.1" + }, + "peerDependencies": { + "socket.io": ">=3.1.0" + } + }, + "node_modules/@socket.io/admin-ui/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, "node_modules/@socket.io/component-emitter": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", @@ -821,6 +855,13 @@ "devOptional": true, "license": "MIT" }, + "node_modules/@types/bcryptjs": { + "version": "2.4.6", + "resolved": "https://registry.npmjs.org/@types/bcryptjs/-/bcryptjs-2.4.6.tgz", + "integrity": "sha512-9xlo6R2qDs5uixm0bcIqCeMCE6HiQsIyel9KQySStiyqNl2tnj2mP3DX1Nf56MD6KMenNNlBBsy3LJ7gUEQPXQ==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/body-parser": { "version": "1.19.5", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", @@ -1421,6 +1462,13 @@ "node": ">=10.0.0" } }, + "node_modules/bcryptjs": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz", + "integrity": "sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ==", + "dev": true, + "license": "MIT" + }, "node_modules/bindings": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", diff --git a/package.json b/package.json index cf7f32c..695a152 100644 --- a/package.json +++ b/package.json @@ -59,6 +59,7 @@ "validator": "^13.15.15" }, "devDependencies": { + "@socket.io/admin-ui": "^0.5.1", "@types/cors": "^2.8.19", "@types/express": "^5.0.3", "@types/ip": "^1.1.3", diff --git a/src/enums/socketEnum.ts b/src/enums/socketEnum.ts new file mode 100644 index 0000000..4aed946 --- /dev/null +++ b/src/enums/socketEnum.ts @@ -0,0 +1,4 @@ +export enum SocketConnectionTypes { + scanner = "/scanner", + pscanner = "/public_scanner", +} diff --git a/src/middleware/authenticateSocket.ts b/src/middleware/authenticateSocket.ts index fe8af95..d8718c4 100644 --- a/src/middleware/authenticateSocket.ts +++ b/src/middleware/authenticateSocket.ts @@ -45,7 +45,6 @@ export default async function authenticateSocket(socket: Socket, next: Function) permissions: decoded.permissions, isWebApiRequest: decoded?.sub == "webapi_access_token", }); - socket.join("home"); next(); } catch (err) { diff --git a/src/websocket/handleEvent.ts b/src/websocket/handleEvent.ts index 0a484f2..c36c601 100644 --- a/src/websocket/handleEvent.ts +++ b/src/websocket/handleEvent.ts @@ -3,6 +3,8 @@ import { PermissionObject, PermissionType, PermissionSection, PermissionModule } import PermissionHelper from "../helpers/permissionHelper"; import ForbiddenRequestException from "../exceptions/forbiddenRequestException"; import { SocketMap } from "../storage/socketMap"; +import { SocketConnectionTypes } from "../enums/socketEnum"; +import SocketServer from "."; export type EventResponseType = { answer: any; @@ -23,7 +25,8 @@ type PermissionPass = section: PermissionSection; module?: PermissionModule; } - | "admin"; + | "admin" + | "noPermissionsRequired"; export let handleEvent = ( permissions: PermissionPass, @@ -37,7 +40,7 @@ export let handleEvent = ( if (!socketData.isOwner && !socketData.permissions.admin) { throw new ForbiddenRequestException(`missing admin permission`); } - } else { + } else if (permissions != "noPermissionsRequired") { if ( !socketData.isOwner && !PermissionHelper.can(socketData.permissions, permissions.type, permissions.section, permissions.module) @@ -58,6 +61,25 @@ export let handleEvent = ( }; }; +export let emitEvent = ( + event: EventResponseType & { namespace?: SocketConnectionTypes }, + socket: Socket, + io: Server +) => { + try { + const { answer, type, room, namespace } = event; + if (room === undefined || room == "") { + socket.emit(type, answer); + } else if (namespace === undefined) { + socket.to(room).emit(type, answer); + } else { + io.of(namespace).emit(type, answer); + } + } catch (e) { + socket.emit("error", e.message); + } +}; + /** socket.on( "event", diff --git a/src/websocket/index.ts b/src/websocket/index.ts index aac4c17..4f68826 100644 --- a/src/websocket/index.ts +++ b/src/websocket/index.ts @@ -1,9 +1,15 @@ import helmet from "helmet"; import { Server as httpServerType } from "http"; import { Server } from "socket.io"; +import { instrument } from "@socket.io/admin-ui"; + import authenticateSocket from "../middleware/authenticateSocket"; import checkSocketExists from "../middleware/checkSocketExists"; +import { SocketConnectionTypes } from "../enums/socketEnum"; + import base from "./base"; +import scanner from "./scanner"; +import pScanner from "./pScanner"; export default abstract class SocketServer { private static io: Server; @@ -17,17 +23,31 @@ export default abstract class SocketServer { }, }); + if (process.env.NODE_ENV) { + instrument(this.io, { + auth: false, + mode: "development", + }); + } + this.io.engine.use(helmet()); this.io - .of("/scanner") + .of(SocketConnectionTypes.scanner) .use(authenticateSocket) .on("connection", (socket) => { console.log("socket connection: ", socket.id); - socket.use((packet, next) => authenticateSocket(socket, next)); socket.use((packet, next) => checkSocketExists(socket, packet, next)); base(this.io, socket); + scanner(this.io, socket); }); + + this.io.of(SocketConnectionTypes.pscanner).on("connection", (socket) => { + console.log("socket connection: ", socket.id); + + base(this.io, socket); + pScanner(this.io, socket); + }); } } diff --git a/src/websocket/scanner/index.ts b/src/websocket/scanner/index.ts index c588e2c..0a8575e 100644 --- a/src/websocket/scanner/index.ts +++ b/src/websocket/scanner/index.ts @@ -1,7 +1,63 @@ import { Server, Socket } from "socket.io"; +import { emitEvent, handleEvent } from "../handleEvent"; +import { SocketConnectionTypes } from "../../enums/socketEnum"; export default (io: Server, socket: Socket) => { + socket.on( + "session:create", + handleEvent( + "noPermissionsRequired", + async (room: string) => { + socket.join(room); + return { + type: "status-session:create", + answer: { status: "success" }, + }; + }, + socket + ) + ); + + socket.on( + "session:close", + handleEvent( + "noPermissionsRequired", + async () => { + const rooms = Array.from(socket.rooms).filter((r) => r !== socket.id); + const room = rooms[0]; + socket.leave(room); + emitEvent( + { + type: "package-host_leave", + answer: "host_leave", + room: room, + namespace: SocketConnectionTypes.pscanner, + }, + socket, + io + ); + return { + type: "status-session:close", + answer: { status: "success" }, + }; + }, + socket + ) + ); + socket.on("disconnecting", () => { - // tell public client, that host left - connection will be closed + const rooms = Array.from(socket.rooms).filter((r) => r !== socket.id); + const room = rooms[0]; + io.of(SocketConnectionTypes.pscanner).in(room).disconnectSockets(); + emitEvent( + { + type: "package-host_leave", + answer: "host_leave", + room: room, + namespace: SocketConnectionTypes.pscanner, + }, + socket, + io + ); }); };