Socket Setup

This commit is contained in:
=bhagwanSingh 2025-11-12 15:36:26 +05:30
parent 7d73299822
commit 5bfc6b8bd5
8 changed files with 638 additions and 11 deletions

240
package-lock.json generated
View File

@ -35,6 +35,7 @@
"puppeteer": "^24.10.1", "puppeteer": "^24.10.1",
"request": "^2.88.2", "request": "^2.88.2",
"require": "^2.4.20", "require": "^2.4.20",
"socket.io": "^4.8.1",
"uglify-js": "^3.19.3", "uglify-js": "^3.19.3",
"uuid": "^11.0.3", "uuid": "^11.0.3",
"winston": "^3.17.0", "winston": "^3.17.0",
@ -1975,12 +1976,27 @@
"url": "https://github.com/sponsors/sindresorhus" "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": { "node_modules/@tootallnate/quickjs-emscripten": {
"version": "0.23.0", "version": "0.23.0",
"resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz",
"integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==", "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==",
"license": "MIT" "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": { "node_modules/@types/eslint": {
"version": "9.6.1", "version": "9.6.1",
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz",
@ -2021,7 +2037,6 @@
"version": "22.10.6", "version": "22.10.6",
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.6.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.6.tgz",
"integrity": "sha512-qNiuwC4ZDAUNcY47xgaSuS92cjf8JbSUoaKS77bmLG1rU7MlATVSiw/IlrjtIyyskXBZ8KkNfjK/P5na7rgXbQ==", "integrity": "sha512-qNiuwC4ZDAUNcY47xgaSuS92cjf8JbSUoaKS77bmLG1rU7MlATVSiw/IlrjtIyyskXBZ8KkNfjK/P5na7rgXbQ==",
"devOptional": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"undici-types": "~6.20.0" "undici-types": "~6.20.0"
@ -2728,6 +2743,15 @@
], ],
"license": "MIT" "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": { "node_modules/base64url": {
"version": "3.0.1", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/base64url/-/base64url-3.0.1.tgz", "resolved": "https://registry.npmjs.org/base64url/-/base64url-3.0.1.tgz",
@ -3866,6 +3890,88 @@
"once": "^1.4.0" "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": { "node_modules/enhanced-resolve": {
"version": "5.18.0", "version": "5.18.0",
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.0.tgz", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.0.tgz",
@ -8117,6 +8223,137 @@
"npm": ">= 3.0.0" "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": { "node_modules/socks": {
"version": "2.8.5", "version": "2.8.5",
"resolved": "https://registry.npmjs.org/socks/-/socks-2.8.5.tgz", "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.5.tgz",
@ -8632,7 +8869,6 @@
"version": "6.20.0", "version": "6.20.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz",
"integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==",
"devOptional": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/unicode-canonical-property-names-ecmascript": { "node_modules/unicode-canonical-property-names-ecmascript": {

View File

@ -39,6 +39,7 @@
"puppeteer": "^24.10.1", "puppeteer": "^24.10.1",
"request": "^2.88.2", "request": "^2.88.2",
"require": "^2.4.20", "require": "^2.4.20",
"socket.io": "^4.8.1",
"uglify-js": "^3.19.3", "uglify-js": "^3.19.3",
"uuid": "^11.0.3", "uuid": "^11.0.3",
"winston": "^3.17.0", "winston": "^3.17.0",

View File

@ -6,7 +6,10 @@ async function authValidator(req, res, next) {
try { try {
const accessToken = req.headers.token; const accessToken = req.headers.token;
if (!accessToken) return res.status(401).json({ success: false, message: "Not Authorized" }); 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); const decoded = jwt.verify(accessToken, process.env.JWT_SECRET_ACCESS_TOKEN);
if (decoded && ("id" in decoded) && decoded.id !== null && ("email" in decoded)) { if (decoded && ("id" in decoded) && decoded.id !== null && ("email" in decoded)) {

View File

@ -65,6 +65,10 @@ const projectSchema = new Schema({
type: Number, type: Number,
default: 0 default: 0
}, },
clientId: {
type: Types.ObjectId,
ref: "Client"
},
vaAssigned: [ vaAssigned: [
{ {
type: Types.ObjectId, type: Types.ObjectId,

View File

@ -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 // 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 // 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 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 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` // VA filter search: supports query params `name`, `email`, `from`, `to`
export default vaRoutes; export default vaRoutes;

View File

@ -1,7 +1,10 @@
import express from 'express'; import express from 'express';
import { createServer } from 'http';
import mongoose from "mongoose"; import mongoose from "mongoose";
import bodyParser from "body-parser"; import bodyParser from "body-parser";
import { Server } from 'socket.io';
import dotenv from "dotenv"; import dotenv from "dotenv";
import cors from 'cors'; import cors from 'cors';
@ -9,10 +12,18 @@ import path from 'path';
import { fileURLToPath } from 'url'; import { fileURLToPath } from 'url';
import fs from "node:fs"
import apiRoutes from './routes/index.js' import apiRoutes from './routes/index.js'
dotenv.config() dotenv.config()
const app = express(); const app = express();
const server = new createServer(app);
const io = new Server(server);
app.set("view engine", "ejs"); app.set("view engine", "ejs");
const PORT = process.env.PORT || 8000; const PORT = process.env.PORT || 8000;
@ -40,9 +51,212 @@ app.get('/health', (req, res) => {
res.status(201).send({ status: "Server is healthy" }); 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 // 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 mongoose
.connect( .connect(
process.env.MONGO_URI, process.env.MONGO_URI,

View File

@ -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();

141
views/va_live_tracker.ejs Normal file
View File

@ -0,0 +1,141 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Live Screen Feed</title>
<style>
* {
box-sizing: border-box;
}
body {
margin: 0;
display: flex;
height: 100vh;
background: #111;
color: #fff;
font-family: 'Inter', sans-serif;
}
.sidebar {
width: 280px;
background: #1b1b1b;
border-right: 1px solid #333;
overflow-y: auto;
padding: 10px;
}
.app {
background: #222;
border: 1px solid #333;
border-radius: 8px;
padding: 10px 12px;
margin-bottom: 8px;
cursor: pointer;
transition: background 0.3s ease;
word-break: break-word;
}
.app:hover {
background: #333;
}
.app.active {
background: #6C63FF;
color: white;
}
.content {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
overflow: hidden;
position: relative;
}
h2 {
margin-bottom: 12px;
font-weight: 500;
}
img {
width: 90%;
max-width: 900px;
border: 2px solid #555;
border-radius: 10px;
object-fit: contain;
}
.no-image {
font-size: 14px;
color: #888;
}
</style>
</head>
<body>
<div class="sidebar">
<h3>Visible Apps</h3>
<div id="appsList"></div>
</div>
<div class="content">
<h2 id="selectedAppTitle">Select an app to view</h2>
<img id="liveImage" src="" alt="Waiting for image..." class="no-image" />
</div>
<script src="/socket.io/socket.io.js"></script>
<script>
const socket = io({
auth: {
token: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjY5MGIzMjhkMDkxMDFmNzRjZjUyNGMwNyIsImVtYWlsIjoiYmhhZ3dhbi5yQGhpY2dsb2JhbHNvbHV0aW9ucy5jb20iLCJuYW1lIjoiUm9oaXQiLCJpYXQiOjE3NjI4NTgxOTMsImV4cCI6MTc2Mjk0NDU5M30.UlHy3RU9W5LOid7smG7WzyNT7dnqhQT4qqzRrN1axmU"
},
});
const clientId = "691311fece83c986eb8a4e89";
socket.emit("register", { role: "client", id: clientId });
const appsList = document.getElementById("appsList");
const liveImage = document.getElementById("liveImage");
const selectedAppTitle = document.getElementById("selectedAppTitle");
let currentApp = null;
// 🟢 Receive visible apps from client
socket.on("receiveVisibleApps", (apps) => {
appsList.innerHTML = "";
if (!apps?.length) {
appsList.innerHTML = "<p>No visible apps</p>";
return;
}
apps.forEach(app => {
const div = document.createElement("div");
div.classList.add("app");
div.textContent = app.name || "Unnamed App";
div.onclick = () => {
document.querySelectorAll(".app").forEach(el => el.classList.remove("active"));
div.classList.add("active");
currentApp = app.name;
selectedAppTitle.textContent = app.name;
// 🟠 Tell server which app VA wants to monitor
socket.emit("selectApp", { appTitle: app.name, clientId });
};
appsList.appendChild(div);
});
});
// 🟣 Receive images only for the selected app
socket.on("receiveImage", (base64, title) => {
if (currentApp && title === currentApp) {
liveImage.src = base64;
liveImage.classList.remove("no-image");
}
});
</script>
</body>
</html>