const path = require("path"); require("dotenv").config({ path: path.resolve(process.cwd(), `.env.${process.env.NODE_ENV || "development"}`) }); const { io } = require("../../server"); const { admin } = require("../firebase/firebase-handler"); const { isArray } = require("lodash"); const logger = require("../utils/logger"); const { default: CdkJobExport, CdkSelectedCustomer } = require("../cdk/cdk-job-export"); const CdkGetMakes = require("../cdk/cdk-get-makes").default; const CdkCalculateAllocations = require("../cdk/cdk-calculate-allocations").default; const { default: PbsExportJob, PbsSelectedCustomer } = require("../accounting/pbs/pbs-job-export"); const { PbsCalculateAllocationsAp, PbsExportAp } = require("../accounting/pbs/pbs-ap-allocations"); // Middleware for verifying tokens via Firebase authentication function socketAuthMiddleware(socket, next) { const token = socket.handshake.auth.token; if (!token) return next(new Error("Authentication error - no authorization token.")); admin .auth() .verifyIdToken(token) .then((user) => { socket.user = user; next(); }) .catch((error) => { next(new Error("Authentication error", JSON.stringify(error))); }); } // Register all socket events for a given socket connection function registerSocketEvents(socket) { socket.log_level = "TRACE"; createLogEvent(socket, "DEBUG", `Connected and Authenticated.`); // Register CDK-related socket events registerCdkEvents(socket); // Register PBS AR-related socket events registerPbsArEvents(socket); // Register PBS AP-related socket events registerPbsApEvents(socket); // Register room and broadcasting events registerRoomAndBroadcastEvents(socket); // Handle socket disconnection socket.on("disconnect", () => { createLogEvent(socket, "DEBUG", `User disconnected.`); }); } // CDK-specific socket events function registerCdkEvents(socket) { socket.on("cdk-export-job", (jobid) => CdkJobExport(socket, jobid)); socket.on("cdk-selected-customer", (selectedCustomerId) => { createLogEvent(socket, "DEBUG", `User selected customer ID ${selectedCustomerId}`); CdkSelectedCustomer(socket, selectedCustomerId); }); socket.on("cdk-get-makes", async (cdk_dealerid, callback) => { try { const makes = await CdkGetMakes(socket, cdk_dealerid); callback(makes); } catch (error) { createLogEvent(socket, "ERROR", `Error in cdk-get-makes WS call. ${JSON.stringify(error)}`); } }); socket.on("cdk-calculate-allocations", async (jobid, callback) => { const allocations = await CdkCalculateAllocations(socket, jobid); createLogEvent(socket, "DEBUG", `Allocations calculated.`); createLogEvent(socket, "TRACE", `Allocations details: ${JSON.stringify(allocations)}`); callback(allocations); }); } // PBS AR-specific socket events function registerPbsArEvents(socket) { socket.on("pbs-calculate-allocations", async (jobid, callback) => { const allocations = await CdkCalculateAllocations(socket, jobid); createLogEvent(socket, "DEBUG", `PBS AR allocations calculated.`); createLogEvent(socket, "TRACE", `Allocations details: ${JSON.stringify(allocations)}`); callback(allocations); }); socket.on("pbs-export-job", (jobid) => PbsExportJob(socket, jobid)); socket.on("pbs-selected-customer", (selectedCustomerId) => { createLogEvent(socket, "DEBUG", `PBS AR selected customer ID ${selectedCustomerId}`); PbsSelectedCustomer(socket, selectedCustomerId); }); } // PBS AP-specific socket events function registerPbsApEvents(socket) { socket.on("pbs-calculate-allocations-ap", async (billids, callback) => { const allocations = await PbsCalculateAllocationsAp(socket, billids); createLogEvent(socket, "DEBUG", `PBS AP allocations calculated.`); createLogEvent(socket, "TRACE", `Allocations details: ${JSON.stringify(allocations)}`); socket.apAllocations = allocations; callback(allocations); }); socket.on("pbs-export-ap", ({ billids, txEnvelope }) => { socket.txEnvelope = txEnvelope; PbsExportAp(socket, { billids, txEnvelope }); }); } // Room management and broadcasting events function registerRoomAndBroadcastEvents(socket) { socket.on("join-bodyshop-room", (bodyshopUUID) => { socket.join(bodyshopUUID); createLogEvent(socket, "DEBUG", `Client joined bodyshop room: ${bodyshopUUID}`); }); socket.on("leave-bodyshop-room", (bodyshopUUID) => { socket.leave(bodyshopUUID); createLogEvent(socket, "DEBUG", `Client left bodyshop room: ${bodyshopUUID}`); }); socket.on("broadcast-to-bodyshop", (bodyshopUUID, message) => { io.to(bodyshopUUID).emit("bodyshop-message", message); createLogEvent(socket, "INFO", `Broadcasted message to bodyshop ${bodyshopUUID}`); }); } // Logging helper functions function createLogEvent(socket, level, message) { if (LogLevelHierarchy(socket.log_level) >= LogLevelHierarchy(level)) { console.log(`[WS LOG EVENT] ${level} - ${new Date()} - ${socket.user.email} - ${socket.id} - ${message}`); socket.emit("log-event", { timestamp: new Date(), level, message }); logger.log("ws-log-event", level, socket.user.email, socket.recordid, { wsmessage: message }); if (socket.logEvents && isArray(socket.logEvents)) { socket.logEvents.push({ timestamp: new Date(), level, message }); } } } function createJsonEvent(socket, level, message, json) { if (LogLevelHierarchy(socket.log_level) >= LogLevelHierarchy(level)) { socket.emit("log-event", { timestamp: new Date(), level, message }); } logger.log("ws-log-event-json", level, socket.user.email, socket.recordid, { wsmessage: message, json }); if (socket.logEvents && isArray(socket.logEvents)) { socket.logEvents.push({ timestamp: new Date(), level, message }); } } function createXmlEvent(socket, xml, message, isError = false) { if (LogLevelHierarchy(socket.log_level) >= LogLevelHierarchy("TRACE")) { socket.emit("log-event", { timestamp: new Date(), level: isError ? "ERROR" : "TRACE", message: `${message}: ${xml}` }); } logger.log( isError ? "ws-log-event-xml-error" : "ws-log-event-xml", isError ? "ERROR" : "TRACE", socket.user.email, socket.recordid, { wsmessage: message, xml } ); if (socket.logEvents && isArray(socket.logEvents)) { socket.logEvents.push({ timestamp: new Date(), level: isError ? "ERROR" : "TRACE", message, xml }); } } // Log level hierarchy function LogLevelHierarchy(level) { const levels = { XML: 5, TRACE: 5, DEBUG: 4, INFO: 3, WARNING: 2, ERROR: 1 }; return levels[level] || 3; } // Socket.IO Middleware and Connection io.use(socketAuthMiddleware); io.on("connection", registerSocketEvents); // Export logging helpers exports.createLogEvent = createLogEvent; exports.createXmlEvent = createXmlEvent; exports.createJsonEvent = createJsonEvent;