From 5bfc6b8bd52e4315bb6198520b990c9f4624f3bc Mon Sep 17 00:00:00 2001 From: =bhagwanSingh Date: Wed, 12 Nov 2025 15:36:26 +0530 Subject: [PATCH 01/11] Socket Setup --- package-lock.json | 240 ++++++++++++++++++++++++++++- package.json | 1 + src/middlewares/auth.middleware.js | 5 +- src/models/vaModel.js | 6 +- src/routes/v1/va.routes.js | 8 +- src/server.js | 216 +++++++++++++++++++++++++- src/testApiVpnConnection.js | 32 ++++ views/va_live_tracker.ejs | 141 +++++++++++++++++ 8 files changed, 638 insertions(+), 11 deletions(-) create mode 100644 src/testApiVpnConnection.js create mode 100644 views/va_live_tracker.ejs diff --git a/package-lock.json b/package-lock.json index a0c011d..a773578 100644 --- a/package-lock.json +++ b/package-lock.json @@ -35,6 +35,7 @@ "puppeteer": "^24.10.1", "request": "^2.88.2", "require": "^2.4.20", + "socket.io": "^4.8.1", "uglify-js": "^3.19.3", "uuid": "^11.0.3", "winston": "^3.17.0", @@ -1975,12 +1976,27 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "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", + "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==", + "license": "MIT" + }, "node_modules/@tootallnate/quickjs-emscripten": { "version": "0.23.0", "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==", "license": "MIT" }, + "node_modules/@types/cors": { + "version": "2.8.19", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.19.tgz", + "integrity": "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/eslint": { "version": "9.6.1", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", @@ -2021,7 +2037,6 @@ "version": "22.10.6", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.6.tgz", "integrity": "sha512-qNiuwC4ZDAUNcY47xgaSuS92cjf8JbSUoaKS77bmLG1rU7MlATVSiw/IlrjtIyyskXBZ8KkNfjK/P5na7rgXbQ==", - "devOptional": true, "license": "MIT", "dependencies": { "undici-types": "~6.20.0" @@ -2728,6 +2743,15 @@ ], "license": "MIT" }, + "node_modules/base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", + "license": "MIT", + "engines": { + "node": "^4.5.0 || >= 5.9" + } + }, "node_modules/base64url": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/base64url/-/base64url-3.0.1.tgz", @@ -3866,6 +3890,88 @@ "once": "^1.4.0" } }, + "node_modules/engine.io": { + "version": "6.6.4", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.4.tgz", + "integrity": "sha512-ZCkIjSYNDyGn0R6ewHDtXgns/Zre/NT6Agvq1/WobF7JXgFff4SeDroKiCO3fNJreU9YG429Sc81o4w5ok/W5g==", + "license": "MIT", + "dependencies": { + "@types/cors": "^2.8.12", + "@types/node": ">=10.0.0", + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.7.2", + "cors": "~2.8.5", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.17.1" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/engine.io-parser": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", + "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/engine.io/node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/engine.io/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/engine.io/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/engine.io/node_modules/ws": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/enhanced-resolve": { "version": "5.18.0", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.0.tgz", @@ -8117,6 +8223,137 @@ "npm": ">= 3.0.0" } }, + "node_modules/socket.io": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.1.tgz", + "integrity": "sha512-oZ7iUCxph8WYRHHcjBEc9unw3adt5CmSNlppj/5Q4k2RIrhl8Z5yY2Xr4j9zj0+wzVZ0bxmYoGSzKJnRl6A4yg==", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.4", + "base64id": "~2.0.0", + "cors": "~2.8.5", + "debug": "~4.3.2", + "engine.io": "~6.6.0", + "socket.io-adapter": "~2.5.2", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/socket.io-adapter": { + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.5.tgz", + "integrity": "sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==", + "license": "MIT", + "dependencies": { + "debug": "~4.3.4", + "ws": "~8.17.1" + } + }, + "node_modules/socket.io-adapter/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io-adapter/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/socket.io-adapter/node_modules/ws": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/socket.io-parser": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", + "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", + "license": "MIT", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-parser/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io-parser/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/socket.io/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, "node_modules/socks": { "version": "2.8.5", "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.5.tgz", @@ -8632,7 +8869,6 @@ "version": "6.20.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", - "devOptional": true, "license": "MIT" }, "node_modules/unicode-canonical-property-names-ecmascript": { diff --git a/package.json b/package.json index 81f6408..4187f27 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,7 @@ "puppeteer": "^24.10.1", "request": "^2.88.2", "require": "^2.4.20", + "socket.io": "^4.8.1", "uglify-js": "^3.19.3", "uuid": "^11.0.3", "winston": "^3.17.0", diff --git a/src/middlewares/auth.middleware.js b/src/middlewares/auth.middleware.js index 0f1ea53..3c6e886 100644 --- a/src/middlewares/auth.middleware.js +++ b/src/middlewares/auth.middleware.js @@ -6,7 +6,10 @@ async function authValidator(req, res, next) { try { const accessToken = req.headers.token; if (!accessToken) return res.status(401).json({ success: false, message: "Not Authorized" }); - + // return res.status(200).json({ + // success: true, + // message: "Log Out Successfull", + // }); const decoded = jwt.verify(accessToken, process.env.JWT_SECRET_ACCESS_TOKEN); if (decoded && ("id" in decoded) && decoded.id !== null && ("email" in decoded)) { diff --git a/src/models/vaModel.js b/src/models/vaModel.js index 15e9105..8c32d0d 100644 --- a/src/models/vaModel.js +++ b/src/models/vaModel.js @@ -65,6 +65,10 @@ const projectSchema = new Schema({ type: Number, default: 0 }, + clientId: { + type: Types.ObjectId, + ref: "Client" + }, vaAssigned: [ { type: Types.ObjectId, @@ -204,7 +208,7 @@ const vaWorkTrackSchema = new Schema({ default: 0, }, }, - { timestamps: true } + { timestamps: true } ) /* ---------------------- Model Exports ---------------------- */ diff --git a/src/routes/v1/va.routes.js b/src/routes/v1/va.routes.js index 7e1398f..72076b4 100644 --- a/src/routes/v1/va.routes.js +++ b/src/routes/v1/va.routes.js @@ -43,9 +43,9 @@ vaRoutes.get("/clients/:clientId/projects/:projectId", va_clients_projects_callb // Getting Project List assigned to VA should be ACTIVE and add total logged hours added by VA -vaRoutes.get("/list/project/:projectId/:from?/:to?", va_projects_log_list_function); +vaRoutes.get("/list/projects/:projectId?/:from?/:to?", authValidator, va_projects_log_list_function); // GET Windows Tab Logged Data for VA IN Between Login and LOGOUT -vaRoutes.get("/report/project/:projectId/:from?/:to?", va_projects_report_function); +vaRoutes.get("/report/project/:projectId/:from?/:to?", authValidator, va_projects_report_function); vaRoutes.post("/log/project", authValidator, va_project_log_callback); // VA Logged IN / Logged Out in project @@ -56,8 +56,4 @@ vaRoutes.get("/log/workReport/", authValidator, va_log_work_report_callback); / vaRoutes.get("/log/project/:projectId/:from?/:to?", authValidator, va_log_callback); // VA / Project Audit Report // VA filter search: supports query params `name`, `email`, `from`, `to` - - - - export default vaRoutes; \ No newline at end of file diff --git a/src/server.js b/src/server.js index f71f4ed..e5016b5 100644 --- a/src/server.js +++ b/src/server.js @@ -1,7 +1,10 @@ import express from 'express'; +import { createServer } from 'http'; + import mongoose from "mongoose"; import bodyParser from "body-parser"; +import { Server } from 'socket.io'; import dotenv from "dotenv"; import cors from 'cors'; @@ -9,10 +12,18 @@ import path from 'path'; import { fileURLToPath } from 'url'; +import fs from "node:fs" + + import apiRoutes from './routes/index.js' dotenv.config() + const app = express(); + +const server = new createServer(app); +const io = new Server(server); + app.set("view engine", "ejs"); const PORT = process.env.PORT || 8000; @@ -40,9 +51,212 @@ app.get('/health', (req, res) => { res.status(201).send({ status: "Server is healthy" }); }) +app.get("/live", (req, res) => { + res.render("va_live_tracker") +}) + +// Handling Socket +import jwt from "jsonwebtoken"; +import { VAModel, ProjectModel, ClientModel } from './models/vaModel.js'; + + +const vaSocketRespectToClient = new Map(); + +io.use(async (socket, next) => { + try { + const token = socket.handshake.auth?.token; + const projectId = socket.handshake.auth?.projectId; + + console.log("🔐 Received token:", token); + + if (!token) { + return next(new Error("Not Authorized: Missing Token")); + } + + const decoded = jwt.verify(token, process.env.JWT_SECRET_ACCESS_TOKEN); + + if (!decoded?.email) { + return next(new Error("Invalid token payload")); + } + + /* ---------------------- 1️⃣ Check if VA ---------------------- */ + const vaUser = await VAModel.findOne({ "userDetails.email": decoded.email }); + const clientData = await ClientModel.findOne({ "projectIds": projectId }); + const vaProjClientId = clientData._id; + + if (vaUser && vaUser.accessToken === token) { + socket.data.role = "va"; + socket.data.vaId = vaUser._id.toString(); + socket.data.vaEmail = decoded.email; + + vaSocketRespectToClient.set(vaProjClientId, socket.id) + + console.log("✅ Authenticated VA:", vaUser.userDetails.email); + return next(); + } + + /* ---------------------- 2️⃣ Check if Client ---------------------- */ + const clientUser = await ClientModel.findOne({ "userDetails.email": decoded.email }); + + if (clientUser) { + socket.data.role = "client"; + socket.data.clientId = clientUser._id.toString(); + + // Get all VA IDs assigned to this client + socket.data.allowedVAs = clientUser.vaIds.map((va) => va.toString()); + + console.log("✅ Authenticated Client:", clientUser.userDetails.email); + return next(); + } + + return next(new Error("Not Authorized: Invalid User")); + } catch (error) { + console.error("❌ Socket Auth Error:", error.message); + next(new Error("Authentication failed: " + error.message)); + } +}); + + + +io.on("connection", (socket) => { + console.log("User connected:", socket.id); + + // Register role and ID (client or VA) + socket.on("register", async ({ role, id }) => { + if (role === "va") { + socket.data.role = "va"; + socket.data.vaId = id; + console.log(`VA registered: ${id}`); + } + + if (role === "client") { + socket.data.role = "client"; + socket.data.clientId = id; + + // 🧩 Get allowed VAs for this client + const client = await ClientModel.findById(id) + .populate("projectIds") // to access their projects + .populate("vaIds"); // optional if you track directly + + // Collect all VA IDs from their projects + const projectVAIds = new Set(); + for (const project of client.projectIds) { + const populated = await ProjectModel.findById(project._id).populate("vaAssigned"); + populated.vaAssigned.forEach((va) => projectVAIds.add(va._id.toString())); + } + + // Store allowed VA IDs in socket memory + socket.data.allowedVAs = Array.from(projectVAIds); + console.log(`Client ${id} can view VAs:`, socket.data.allowedVAs); + } + }); + + // When VA sends live image + socket.on("sendImage", async ({ token, image, title }) => { + try { + // Verify token + const decoded = jwt.verify(token, process.env.JWT_SECRET_ACCESS_TOKEN); + const vaEmail = decoded.email; + + // Find VA in DB + const vaUser = await VAModel.findOne({ "userDetails.email": vaEmail }); + if (!vaUser) { + console.log("❌ Unauthorized VA"); + return; + } + + const vaId = vaUser._id.toString(); + console.log("🟢 Received image from VA:", vaId); + + // Broadcast image only to allowed clients + for (let [_, clientSocket] of io.sockets.sockets) { + if ( + clientSocket.data.role === "client" && + clientSocket.data.allowedVAs?.includes(vaId) + ) { + clientSocket.emit("receiveImage", image, title); + } + } + } catch (err) { + console.error("❌ Token verification failed:", err.message); + } + }); + + socket.on("selectApp", ({ appTitle, clientId }) => { + console.log(`VA selected app: ${appTitle}`); + + // You can forward this info to the respective Electron client + socket.to(vaSocketRespectToClient[clientId]).emit("appToCapture", { appTitle }); + }); + + socket.on("getVisibleApps", async ({ token, apps }) => { + try { + // Verify token + const decoded = jwt.verify(token, process.env.JWT_SECRET_ACCESS_TOKEN); + const vaEmail = decoded.email; + + // Find VA in DB + const vaUser = await VAModel.findOne({ "userDetails.email": vaEmail }); + if (!vaUser) { + console.log("❌ Unauthorized VA"); + return; + } + + const vaId = vaUser._id.toString(); + console.log("🟢 Received Apps from VA:", vaId); + + // Broadcast image only to allowed clients + for (let [_, clientSocket] of io.sockets.sockets) { + if ( + clientSocket.data.role === "client" && + clientSocket.data.allowedVAs?.includes(vaId) + ) { + clientSocket.emit("receiveVisibleApps", apps); + } + } + } catch (err) { + console.error("❌ Token verification failed:", err.message); + } + }); + + + socket.on('disconnect', () => { + console.log('a user Disconnected'); + }); +}); + + + + +// io.on('connection', (socket) => { + +// socket.on("sendImage", (base64String) => { +// io.emit("receiveImage", base64String); +// }); + + +// socket.on('disconnect', () => { +// console.log('a user Disconnected'); +// }); +// }); + + // Express App Listening -app.listen(PORT, () => { +// app.listen(PORT, () => { +// mongoose +// .connect( +// process.env.MONGO_URI, +// ) +// .then(() => console.log("mongodb connected")) +// .catch((err) => { +// console.log(err.message); +// process.exit(1); +// }); +// console.log(`Express started on http://localhost:${PORT}`); +// }); + +server.listen(PORT, () => { mongoose .connect( process.env.MONGO_URI, diff --git a/src/testApiVpnConnection.js b/src/testApiVpnConnection.js new file mode 100644 index 0000000..2a6991f --- /dev/null +++ b/src/testApiVpnConnection.js @@ -0,0 +1,32 @@ +// testVpnApi.js +import axios from "axios"; + +const apiUrl = "http://165.140.240.203:3000/api"; +const apiKey = "hellovpn123"; + +const possiblePaths = [ + "/", "/v1", "/servers", "/status", "/vpn", "/health", "/ping", + "/nodes", "/list", "/connect", "/config", "/create", "/createConfig", + "/check", "/organizations", "/users" +]; + +async function testPaths() { + for (const path of possiblePaths) { + try { + const res = await axios.get(`${apiUrl}${path}`, { + headers: { "x-api-key": apiKey }, + timeout: 3000 + }); + console.log(`✅ ${path} → ${res.status} ${res.statusText}`); + console.log("Response:", res.data); + } catch (err) { + if (err.response) { + console.log(`❌ ${path} → ${err.response.status}`); + } else { + console.log(`⚠️ ${path} → No response`); + } + } + } +} + +testPaths(); diff --git a/views/va_live_tracker.ejs b/views/va_live_tracker.ejs new file mode 100644 index 0000000..dc1eb4d --- /dev/null +++ b/views/va_live_tracker.ejs @@ -0,0 +1,141 @@ + + + + + + Live Screen Feed + + + + + + +
+

Select an app to view

+ Waiting for image... +
+ + + + + + From 6358564131cb554c0c89f6007e63104eca0ccabd Mon Sep 17 00:00:00 2001 From: =bhagwanSingh Date: Wed, 12 Nov 2025 20:05:09 +0530 Subject: [PATCH 02/11] Socket Update and Client Login UI --- ngrok.yml | 9 + public/images/logo-white-vertical.svg | 1 + .../vaProjects/vaProjectLogList.controller.js | 8 +- src/server.js | 60 ++++- views/client_login.ejs | 253 ++++++++++++++++++ views/va_live_tracker.ejs | 232 ++++++++-------- 6 files changed, 436 insertions(+), 127 deletions(-) create mode 100644 ngrok.yml create mode 100644 public/images/logo-white-vertical.svg create mode 100644 views/client_login.ejs diff --git a/ngrok.yml b/ngrok.yml new file mode 100644 index 0000000..85ccd67 --- /dev/null +++ b/ngrok.yml @@ -0,0 +1,9 @@ +version: 2 +authtoken: 2gibp9BBceUJDZtNScqH5ivAUTA_2EVNSsmY6HkNYuY6aw7a4 +tunnels: + myapp: + proto: http + addr: 3000 + hostname: wired-terminally-muskrat.ngrok-free.app + # No bind_tls here! + # Optionally, you can specify 'scheme: https' diff --git a/public/images/logo-white-vertical.svg b/public/images/logo-white-vertical.svg new file mode 100644 index 0000000..c84b182 --- /dev/null +++ b/public/images/logo-white-vertical.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/controllers/va/vaProjects/vaProjectLogList.controller.js b/src/controllers/va/vaProjects/vaProjectLogList.controller.js index be7b7fb..a168c72 100644 --- a/src/controllers/va/vaProjects/vaProjectLogList.controller.js +++ b/src/controllers/va/vaProjects/vaProjectLogList.controller.js @@ -52,7 +52,13 @@ async function va_projects_log_list_function(req, res) { projectList = [project]; } else { - projectList = await ProjectModel.find({ vaAssigned: vaId }); + + const all = await ProjectModel.find().lean(); + console.log(all.map(p => p.vaAssigned)); + + console.log("VA ID:", vaId.toString()); + + projectList = await ProjectModel.find({ vaAssigned: vaUser._id }); } if (!projectList.length) { diff --git a/src/server.js b/src/server.js index 7907508..7474313 100644 --- a/src/server.js +++ b/src/server.js @@ -17,6 +17,8 @@ import fs from "node:fs" import apiRoutes from './routes/index.js' +process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; + dotenv.config() const app = express(); @@ -55,13 +57,19 @@ app.get('/health', (req, res) => { res.status(201).send({ status: "Server is healthy" }); }) -app.get("/live", (req, res) => { - res.render("va_live_tracker") +app.get("/client/login", (req, res) => { + res.render("client_login") +}) + +app.get("/live/:projectId", (req, res) => { + const { projectId } = req.params; + res.render("va_live_tracker", { title: "VA Live Tracker", projectId }) }) // Handling Socket import jwt from "jsonwebtoken"; import { VAModel, ProjectModel, ClientModel } from './models/vaModel.js'; +import { ObjectId } from 'mongodb/lib/bson.js'; const vaSocketRespectToClient = new Map(); @@ -70,6 +78,7 @@ io.use(async (socket, next) => { try { const token = socket.handshake.auth?.token; const projectId = socket.handshake.auth?.projectId; + const userType = socket.handshake.auth?.userType; console.log("🔐 Received token:", token); @@ -84,32 +93,59 @@ io.use(async (socket, next) => { } /* ---------------------- 1️⃣ Check if VA ---------------------- */ - const vaUser = await VAModel.findOne({ "userDetails.email": decoded.email }); - const clientData = await ClientModel.findOne({ "projectIds": projectId }); - const vaProjClientId = clientData._id; + let vaUser = null; + let clientData = null; + + if (userType === "va") { + vaUser = await VAModel.findOne({ "userDetails.email": decoded.email }); + clientData = await ProjectModel.findById(projectId).populate("clientId"); + } else if (userType === "client") { + clientData = await ProjectModel.findById(projectId).populate("clientId"); + } + + if (clientData == null) return next(new Error("Invalid Project ID")); + const vaProjClientId = clientData.clientId._id; if (vaUser && vaUser.accessToken === token) { socket.data.role = "va"; socket.data.vaId = vaUser._id.toString(); socket.data.vaEmail = decoded.email; - - vaSocketRespectToClient.set(vaProjClientId, socket.id) + + const a = vaProjClientId.toString(); + const b = clientData.clientId._id.toString(); + const c = JSON.stringify(clientData); + + console.log(a, b, c, "vaProjClientId vs clientData.clientId"); + + vaSocketRespectToClient.set(vaProjClientId.toString(), socket.id) console.log("✅ Authenticated VA:", vaUser.userDetails.email); return next(); } /* ---------------------- 2️⃣ Check if Client ---------------------- */ - const clientUser = await ClientModel.findOne({ "userDetails.email": decoded.email }); + // const clientUser = await ClientModel.findOne({ "userDetails.email": decoded.email }); + + // if (clientUser) { + // socket.data.role = "client"; + // socket.data.clientId = clientUser._id.toString(); + + // // Get all VA IDs assigned to this client + // socket.data.allowedVAs = clientUser.vaIds.map((va) => va.toString()); + + // console.log("✅ Authenticated Client:", clientUser.userDetails.email); + // return next(); + // } + const clientUser = clientData.clientId; if (clientUser) { socket.data.role = "client"; socket.data.clientId = clientUser._id.toString(); // Get all VA IDs assigned to this client - socket.data.allowedVAs = clientUser.vaIds.map((va) => va.toString()); + socket.data.allowedVAs = clientData.vaAssigned.map((va) => va.toString()); - console.log("✅ Authenticated Client:", clientUser.userDetails.email); + // console.log("✅ Authenticated Client:", clientUser.userDetails.email); return next(); } @@ -189,8 +225,10 @@ io.on("connection", (socket) => { socket.on("selectApp", ({ appTitle, clientId }) => { console.log(`VA selected app: ${appTitle}`); + const sssrrrr = vaSocketRespectToClient.get(clientId); + console.log(vaSocketRespectToClient.get(clientId), sssrrrr, "vaSocketRespectToClient[clientId]") // You can forward this info to the respective Electron client - socket.to(vaSocketRespectToClient[clientId]).emit("appToCapture", { appTitle }); + socket.to(vaSocketRespectToClient.get(clientId)).emit("appToCapture", { appTitle }); }); socket.on("getVisibleApps", async ({ token, apps }) => { diff --git a/views/client_login.ejs b/views/client_login.ejs new file mode 100644 index 0000000..1db99a0 --- /dev/null +++ b/views/client_login.ejs @@ -0,0 +1,253 @@ + + + + + + + DocVA | Login + + + + + + + + + + + + + \ No newline at end of file diff --git a/views/va_live_tracker.ejs b/views/va_live_tracker.ejs index dc1eb4d..3539a88 100644 --- a/views/va_live_tracker.ejs +++ b/views/va_live_tracker.ejs @@ -2,140 +2,142 @@ - - Live Screen Feed - + .no-image { + font-size: 14px; + color: #888; + } + - + -
-

Select an app to view

- Waiting for image... -
+
+

Select an app to view

+ Waiting for image... +
- - + + // 🟣 Receive images only for the selected app + socket.on("receiveImage", (base64, title) => { + if (currentApp && title === currentApp) { + liveImage.src = base64; + liveImage.classList.remove("no-image"); + } + }); + - + \ No newline at end of file From ea574004cead053e1204c5a25e48e624913a3697 Mon Sep 17 00:00:00 2001 From: =bhagwanSingh Date: Thu, 13 Nov 2025 17:56:49 +0530 Subject: [PATCH 03/11] socket and client login and forgot password update --- ngrok.yml | 9 -- public/images/logo-colored-vertical.svg | 1 + src/server.js | 3 + views/client_login.ejs | 48 +++++- views/forgot_password.ejs | 193 ++++++++++++++++++++++++ 5 files changed, 243 insertions(+), 11 deletions(-) delete mode 100644 ngrok.yml create mode 100644 public/images/logo-colored-vertical.svg create mode 100644 views/forgot_password.ejs diff --git a/ngrok.yml b/ngrok.yml deleted file mode 100644 index 85ccd67..0000000 --- a/ngrok.yml +++ /dev/null @@ -1,9 +0,0 @@ -version: 2 -authtoken: 2gibp9BBceUJDZtNScqH5ivAUTA_2EVNSsmY6HkNYuY6aw7a4 -tunnels: - myapp: - proto: http - addr: 3000 - hostname: wired-terminally-muskrat.ngrok-free.app - # No bind_tls here! - # Optionally, you can specify 'scheme: https' diff --git a/public/images/logo-colored-vertical.svg b/public/images/logo-colored-vertical.svg new file mode 100644 index 0000000..1a98240 --- /dev/null +++ b/public/images/logo-colored-vertical.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/server.js b/src/server.js index c5f660b..81465f2 100644 --- a/src/server.js +++ b/src/server.js @@ -63,6 +63,9 @@ app.get('/health', (req, res) => { app.get("/client/login", (req, res) => { res.render("client_login") }) +app.get("/client/forgot-password", (req, res) => { + res.render("forgot_password") +}) app.get("/live/:projectId", (req, res) => { const { projectId } = req.params; diff --git a/views/client_login.ejs b/views/client_login.ejs index 1db99a0..a4419ab 100644 --- a/views/client_login.ejs +++ b/views/client_login.ejs @@ -220,10 +220,10 @@