Socket Setup
This commit is contained in:
parent
7d73299822
commit
5bfc6b8bd5
|
|
@ -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": {
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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)) {
|
||||
|
|
|
|||
|
|
@ -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 ---------------------- */
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
216
src/server.js
216
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,
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
@ -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>
|
||||
Loading…
Reference in New Issue