admin side scan connection

This commit is contained in:
Julian Krauser 2025-07-15 15:16:11 +02:00
parent b29cdae088
commit 83ab0c4ea7
7 changed files with 156 additions and 6 deletions

48
package-lock.json generated
View file

@ -43,6 +43,7 @@
"validator": "^13.15.15" "validator": "^13.15.15"
}, },
"devDependencies": { "devDependencies": {
"@socket.io/admin-ui": "^0.5.1",
"@types/cors": "^2.8.19", "@types/cors": "^2.8.19",
"@types/express": "^5.0.3", "@types/express": "^5.0.3",
"@types/ip": "^1.1.3", "@types/ip": "^1.1.3",
@ -764,6 +765,39 @@
"node": ">=18" "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": { "node_modules/@socket.io/component-emitter": {
"version": "3.1.2", "version": "3.1.2",
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz",
@ -821,6 +855,13 @@
"devOptional": true, "devOptional": true,
"license": "MIT" "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": { "node_modules/@types/body-parser": {
"version": "1.19.5", "version": "1.19.5",
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz",
@ -1421,6 +1462,13 @@
"node": ">=10.0.0" "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": { "node_modules/bindings": {
"version": "1.5.0", "version": "1.5.0",
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",

View file

@ -59,6 +59,7 @@
"validator": "^13.15.15" "validator": "^13.15.15"
}, },
"devDependencies": { "devDependencies": {
"@socket.io/admin-ui": "^0.5.1",
"@types/cors": "^2.8.19", "@types/cors": "^2.8.19",
"@types/express": "^5.0.3", "@types/express": "^5.0.3",
"@types/ip": "^1.1.3", "@types/ip": "^1.1.3",

4
src/enums/socketEnum.ts Normal file
View file

@ -0,0 +1,4 @@
export enum SocketConnectionTypes {
scanner = "/scanner",
pscanner = "/public_scanner",
}

View file

@ -45,7 +45,6 @@ export default async function authenticateSocket(socket: Socket, next: Function)
permissions: decoded.permissions, permissions: decoded.permissions,
isWebApiRequest: decoded?.sub == "webapi_access_token", isWebApiRequest: decoded?.sub == "webapi_access_token",
}); });
socket.join("home");
next(); next();
} catch (err) { } catch (err) {

View file

@ -3,6 +3,8 @@ import { PermissionObject, PermissionType, PermissionSection, PermissionModule }
import PermissionHelper from "../helpers/permissionHelper"; import PermissionHelper from "../helpers/permissionHelper";
import ForbiddenRequestException from "../exceptions/forbiddenRequestException"; import ForbiddenRequestException from "../exceptions/forbiddenRequestException";
import { SocketMap } from "../storage/socketMap"; import { SocketMap } from "../storage/socketMap";
import { SocketConnectionTypes } from "../enums/socketEnum";
import SocketServer from ".";
export type EventResponseType = { export type EventResponseType = {
answer: any; answer: any;
@ -23,7 +25,8 @@ type PermissionPass =
section: PermissionSection; section: PermissionSection;
module?: PermissionModule; module?: PermissionModule;
} }
| "admin"; | "admin"
| "noPermissionsRequired";
export let handleEvent = ( export let handleEvent = (
permissions: PermissionPass, permissions: PermissionPass,
@ -37,7 +40,7 @@ export let handleEvent = (
if (!socketData.isOwner && !socketData.permissions.admin) { if (!socketData.isOwner && !socketData.permissions.admin) {
throw new ForbiddenRequestException(`missing admin permission`); throw new ForbiddenRequestException(`missing admin permission`);
} }
} else { } else if (permissions != "noPermissionsRequired") {
if ( if (
!socketData.isOwner && !socketData.isOwner &&
!PermissionHelper.can(socketData.permissions, permissions.type, permissions.section, permissions.module) !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( socket.on(
"event", "event",

View file

@ -1,9 +1,15 @@
import helmet from "helmet"; import helmet from "helmet";
import { Server as httpServerType } from "http"; import { Server as httpServerType } from "http";
import { Server } from "socket.io"; import { Server } from "socket.io";
import { instrument } from "@socket.io/admin-ui";
import authenticateSocket from "../middleware/authenticateSocket"; import authenticateSocket from "../middleware/authenticateSocket";
import checkSocketExists from "../middleware/checkSocketExists"; import checkSocketExists from "../middleware/checkSocketExists";
import { SocketConnectionTypes } from "../enums/socketEnum";
import base from "./base"; import base from "./base";
import scanner from "./scanner";
import pScanner from "./pScanner";
export default abstract class SocketServer { export default abstract class SocketServer {
private static io: Server; 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.engine.use(helmet());
this.io this.io
.of("/scanner") .of(SocketConnectionTypes.scanner)
.use(authenticateSocket) .use(authenticateSocket)
.on("connection", (socket) => { .on("connection", (socket) => {
console.log("socket connection: ", socket.id); console.log("socket connection: ", socket.id);
socket.use((packet, next) => authenticateSocket(socket, next));
socket.use((packet, next) => checkSocketExists(socket, packet, next)); socket.use((packet, next) => checkSocketExists(socket, packet, next));
base(this.io, socket); 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);
}); });
} }
} }

View file

@ -1,7 +1,63 @@
import { Server, Socket } from "socket.io"; import { Server, Socket } from "socket.io";
import { emitEvent, handleEvent } from "../handleEvent";
import { SocketConnectionTypes } from "../../enums/socketEnum";
export default (io: Server, socket: Socket) => { 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", () => { 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
);
}); });
}; };