class SocketManager { constructor(io) { this.io = io; this.namespaces = new Map(); this.clients = new Map(); this.io.on('connection', socket => {}); } add(namespace, exists) { if (this.namespaces.has(namespace)) return this.namespaces.get(namespace); const nsp = this.io.of(namespace); const clients = new Map(); this.clients.set(namespace, clients); nsp.on('connection', socket => { const { objectGuid, sAMAccountName } = socket.handshake.auth || {}; if (!objectGuid) return; clients.set(objectGuid, { socket, userName: sAMAccountName }); // console.log(`${sAMAccountName} [${objectGuid}] connected to ${namespace}`); socket.on('disconnect', () => { clients.delete(objectGuid); // console.log(`${sAMAccountName} [${objectGuid}] disconnected from ${namespace}`); }); }); this.namespaces.set(namespace, nsp); return nsp; } async addAsync(namespace, exists) { return new Promise(resolve => { this.add(namespace, exists) // simulierter async Init (z. B. DB) setTimeout(resolve, 0); }); } /** * Broadcast in namespace * @param {string} namespace - Namespace-Name * @param {string} event - Event-Name * @param {any} data - Event-Data */ broadcast(namespace, event, data) { const nsp = this.namespaces.get(namespace); if (nsp) nsp.emit(event, data); } /** * Event-Handler für einen Namespace registrieren * @param {string} namespace - Namespace-Name * @param {string} event - Event-Name * @param {Function} callback - Callback mit (socket, data) */ // on(namespace, event, callback) { // const nsp = this.namespaces.get(namespace); // if (!nsp) { // console.warn(`Namespace ${namespace} doesn't exist`); // return; // } // nsp.on('connection', (socket) => { // socket.on(event, (data) => { // // übergibt socket als Kontext, damit sendTo automatisch weiß, wer anfragte // callback(socket, data, (responseEvent, responseData, targetGuid = null) => { // this.sendTo(namespace, targetGuid, responseEvent, responseData, socket); // }); // }); // }); // } on(namespace, event, callback) { const nsp = this.namespaces.get(namespace); if (!nsp) { console.warn(`Namespace ${namespace} doesn't exist`); return; } nsp.on('connection', (socket) => { socket.on(event, (data) => callback(socket, data)); }); } registerClient(namespace, socket) { const nsp = this.namespaces.get(namespace); if (!nsp) { console.warn(`Namespace ${namespace} does not exist`); return; } const auth = socket.handshake.auth; const objectGuid = auth.objectGuid; const sAMAccountName = auth.sAMAccountName; if (!objectGuid) { console.warn('Socket has no objectGuid, cannot register'); return; } socket.customId = objectGuid; // In Map speichern this.clients.set(objectGuid, { userName: sAMAccountName, socketId: socket.id, socket: socket }); console.log(`${sAMAccountName} [${objectGuid}] registered to namespace ${namespace}`); // Disconnect-Handler socket.on('disconnect', () => { this.clients.delete(objectGuid); console.log(`${sAMAccountName} [${objectGuid}] disconnected from namespace ${namespace}`); }); }; /** * Entfernt einen Client aus dem Namespace * @param {string} objectGuid - GUID des Clients * @param {string} [namespace] - optional, um Namespace-spezifisch zu loggen */ unregisterClient(objectGuid) { try { const client = this.clients.get(objectGuid); if (!client) return; // Optional: Socket trennen client.socket.disconnect(); this.clients.delete(objectGuid); const name = client.userName; console.log(`${name} [${objectGuid}] manuell entfernt`); } catch (err) { console.log(err); } } /** * Send a personal message to ObjectGUID * @param {string} namespace - Namespace-Name * @param {string} objectGuid - Socket-ID * @param {string} event - Event-Name * @param {any} data - Event-Data * @param {object} [senderSocket] - optionaler Absender-Socket */ sendTo(namespace, objectGuid, event, data, senderSocket = null) { const clients = this.clients.get(namespace); if (!clients) return; // Antwort an Absender if (!objectGuid && senderSocket) { senderSocket.emit(event, data); return; } const client = clients.get(objectGuid); if (!client?.socket) return; client.socket.emit(event, data); } } module.exports = SocketManager; // const WebSocket = require("ws"); // const url = require("url"); // class SocketManager { // constructor() { // this.namespaces = new Map(); // "/admin" → { clients:Set, handlers:Map } // this.clients = new Map(); // objectGuid → { userName, socket } // // Default-Namespaces wie bei Socket.IO // this.add("/"); // this.add("/admin"); // } // /** // * Namespace erstellen // */ // add(namespace) { // if (this.namespaces.has(namespace)) return this.namespaces.get(namespace); // const ns = { // clients: new Set(), // handlers: new Map() // event → callback // }; // this.namespaces.set(namespace, ns); // return ns; // } // /** // * Upgrade (HTTP → WebSocket) pro Namespace auswerten // */ // handleUpgrade(namespace, req, socket, head) { // if (!this.namespaces.has(namespace)) { // socket.destroy(); // return; // } // const ns = this.namespaces.get(namespace); // const wss = new WebSocket.Server({ noServer: true }); // wss.handleUpgrade(req, socket, head, ws => { // this._onConnection(namespace, ws, req); // }); // } // /** // * Verbindung herstellen + authentifizieren // */ // _onConnection(namespace, socket, req) { // const params = new URLSearchParams(url.parse(req.url).query); // const objectGuid = params.get("objectGuid"); // const sAMAccountName = params.get("sAMAccountName") || "Unknown"; // socket.namespace = namespace; // socket.objectGuid = objectGuid; // socket.userName = sAMAccountName; // // Helper zum JSON-Senden // socket.sendJSON = (obj) => socket.send(JSON.stringify(obj)); // const ns = this.namespaces.get(namespace); // ns.clients.add(socket); // if (objectGuid) { // this.clients.set(objectGuid, { userName: sAMAccountName, socket: socket }); // console.log(`${sAMAccountName} [${objectGuid}] connected to [${namespace}]`); // } // // Nachrichten-Handling // socket.on("message", raw => { // let msg; // try { msg = JSON.parse(raw); } // catch { return; } // const event = msg.event; // const data = msg.data; // const cb = ns.handlers.get(event); // if (cb) cb(socket, data); // }); // // Disconnect // socket.on("close", () => { // ns.clients.delete(socket); // if (objectGuid) { // this.clients.delete(objectGuid); // console.log(`${sAMAccountName} [${objectGuid}] disconnected from namespace ${namespace}`); // } // }); // } // /** // * Events registrieren wie socket.on("event") // */ // on(namespace, event, callback) { // const ns = this.namespaces.get(namespace); // if (!ns) return console.warn(`Namespace ${namespace} doesn't exist`); // ns.handlers.set(event, callback); // } // /** // * Broadcast in Namespace // */ // broadcast(namespace, event, data) { // const ns = this.namespaces.get(namespace); // if (!ns) return; // for (const client of ns.clients) { // if (client.readyState === WebSocket.OPEN) { // client.sendJSON({ event, data }); // } // } // } // /** // * Persönliche Nachricht an GUID // */ // sendTo(namespace, objectGuid, event, data, sender = null) { // if (!objectGuid && sender) { // sender.sendJSON({ event, data }); // return; // } // const client = this.clients.get(objectGuid); // if (!client) return; // if (client.socket.readyState === WebSocket.OPEN) { // client.socket.sendJSON({ event, data }); // } // } // /** // * Client manuell entfernen // */ // unregisterClient(objectGuid) { // const client = this.clients.get(objectGuid); // if (!client) return; // client.socket.close(4000, "manual kick"); // this.clients.delete(objectGuid); // console.log(`${client.userName} [${objectGuid}] manually removed`); // } // } // module.exports = SocketManager;