diff --git a/public/images/downloads.png b/public/images/downloads.png new file mode 100644 index 0000000..e1f5720 Binary files /dev/null and b/public/images/downloads.png differ 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/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/public/images/mac-os-logo.png b/public/images/mac-os-logo.png new file mode 100644 index 0000000..605d0b3 Binary files /dev/null and b/public/images/mac-os-logo.png differ diff --git a/public/images/windows.png b/public/images/windows.png new file mode 100644 index 0000000..c98b377 Binary files /dev/null and b/public/images/windows.png differ diff --git a/src/controllers/va/vaProfile/vaRegister.controller.js b/src/controllers/va/vaProfile/vaRegister.controller.js index da2f8fb..e88cecf 100644 --- a/src/controllers/va/vaProfile/vaRegister.controller.js +++ b/src/controllers/va/vaProfile/vaRegister.controller.js @@ -50,7 +50,8 @@ async function va_register(req, res) { // Send data to Salesforce API after successful registration try { const salesforceUrl = `${process.env.SF_DOCTRACKER_API_URL}services/apexrest/docva/api/virtualassistant`; - + delete created.userDetails._id; + created.userDetails._id = created._id; // Include VA's MongoDB ID const response = await fetch(salesforceUrl, { method: 'POST', headers: { diff --git a/src/models/vaModel.js b/src/models/vaModel.js index 820be1f..dc2f6e9 100644 --- a/src/models/vaModel.js +++ b/src/models/vaModel.js @@ -92,7 +92,7 @@ const projectSchema = new Schema({ ], dedicatedHours: { type: String, - default: 0 + default: "0" }, status: { type: String, @@ -166,6 +166,7 @@ const clientDetailsSchema = new Schema({ enum: ["CHECKING", "SAVINGS"], type: String, }, + accountHolderName: { type: String, }, @@ -332,7 +333,7 @@ const vaWorkTrackSchema = new Schema({ default: 0, }, }, - { timestamps: true } + { timestamps: true } ) const passwordResetSchema = new mongoose.Schema({ diff --git a/src/server.js b/src/server.js index 7e80d5f..17cc29a 100644 --- a/src/server.js +++ b/src/server.js @@ -13,16 +13,32 @@ import cors from 'cors'; import path from 'path'; import { fileURLToPath } from 'url'; + import apiRoutes from './routes/index.js' + import { render_reset_form } from "./controllers/passwordReset.controllers.js"; import jwt from "jsonwebtoken"; const app = express(); + const server = new createServer(app); const io = new Server(server); +app.on('certificate-error', (event, webContents, url, error, certificate, callback) => { + console.log("⚠️ Certificate error:", url); + + // Allow for ngrok only + if (url.includes("ngrok")) { + event.preventDefault(); + callback(true); // approve the certificate + } else { + callback(false); // reject others + } +}); + + app.on('certificate-error', (event, webContents, url, error, certificate, callback) => { console.log("⚠️ Certificate error:", url); @@ -93,13 +109,18 @@ app.get('/varegister/:sf_token?', (req, res) => { }); app.get('/clientregister/:sf_token?', (req, res) => { + const { sf_token } = req.params; + try { + const decoded = jwt.verify(sf_token, process.env.JWT_SECRET_SF_TOKEN); - // portalId: RECORDid => 8990928342309 - // email - - // const { sf_token } = useParams(); - - res.render('va_client_register', { title: "Client Registration" }); + if (decoded) { + res.render('va_client_register', { title: "Client Registration" }); + } else { + res.render('not_found', { title: "Client Registration" }); + } + } catch (error) { + res.render('not_found', { title: "Client Registration" }); + } }); app.get('/auth/password/reset-form', render_reset_form); @@ -138,35 +159,28 @@ app.get('/client/virtual-assistants', async (req, res) => { } }); -// app.get('/client/timesheets', async (req, res) => { -// try { -// const timesheetData = await timesheetService.getClientTimesheets(req.user.id); -// res.render('client/dashboard-timesheets', timesheetData); -// } catch (error) { -// console.error('Timesheets error:', error); -// res.status(500).render('client/dashboard-timesheets', { error: 'Failed to load timesheets' }); -// } -// }); +app.get("/live/:projectId/:vaId", async (req, res) => { + const { projectId, vaId } = req.params; -// app.get('/client/invoices', async (req, res) => { -// try { -// const invoiceData = await invoiceService.getClientInvoices(req.user.id); -// res.render('client/dashboard-invoices', invoiceData); -// } catch (error) { -// console.error('Invoices error:', error); -// res.status(500).render('client/dashboard-invoices', { error: 'Failed to load invoices' }); -// } -// }); + if (!projectId || !vaId) { + return res.status(400).send({ "message": "Page not found" }); + } -// app.get('/client/referrals', async (req, res) => { -// try { -// const referralData = await referralService.getClientReferrals(req.user.id); -// res.render('client/dashboard-referrals', referralData); -// } catch (error) { -// console.error('Referrals error:', error); -// res.status(500).render('client/dashboard-referrals', { error: 'Failed to load referrals' }); -// } -// }); + let clientId = null; + try { + const clientRes = await ProjectModel.findById(projectId); + if (clientRes) { + clientId = clientRes.clientId.toString(); + } + } catch (err) { + console.error("Error fetching client for project:", err.message); + return; + } + + if (!clientId) return res.status(400).send({ "message": "Page not found" }); + + res.render("va_live_tracker", { title: "VA Live Tracker", projectId, vaId, clientId }) +}) app.get("/live/:projectId/:vaId", async (req, res) => { const { projectId, vaId } = req.params; @@ -193,16 +207,17 @@ app.get("/live/:projectId/:vaId", async (req, res) => { // Handling Socket import { VAModel, ProjectModel, ClientModel } from './models/vaModel.js'; -import { ObjectId } from 'mongodb/lib/bson.js'; - const vaSocketRespectToClient = new Map(); +// Handling Socket io.use(async (socket, next) => { try { const token = socket.handshake.auth?.token; const projectId = socket.handshake.auth?.projectId; const userType = socket.handshake.auth?.userType; + const vaId = socket.handshake.auth?.vaId; + console.log("🔐 Received token:", token); @@ -216,7 +231,7 @@ io.use(async (socket, next) => { return next(new Error("Invalid token payload")); } - /* ---------------------- 1️⃣ Check if VA ---------------------- */ + /* ---------------------- 1️⃣ Check if VA ---------------------- */ let vaUser = null; let clientData = null; @@ -241,13 +256,13 @@ io.use(async (socket, next) => { console.log(a, b, c, "vaProjClientId vs clientData.clientId"); - vaSocketRespectToClient.set(`${vaProjClientId.toString()}-${vaUser._id.toString()}`, socket.id) + vaSocketRespectToClient.set(`${vaProjClientId.toString()}-${vaUser._id.toString()}-${projectId.toString()}-va`, socket.id) console.log("✅ Authenticated VA:", vaUser.userDetails.email); return next(); } - /* ---------------------- 2️⃣ Check if Client ---------------------- */ + /* ---------------------- 2️⃣ Check if Client ---------------------- */ // const clientUser = await ClientModel.findOne({ "userDetails.email": decoded.email }); // if (clientUser) { @@ -268,6 +283,7 @@ io.use(async (socket, next) => { // Get all VA IDs assigned to this client socket.data.allowedVAs = clientData.vaAssigned.map((va) => va.toString()); + vaSocketRespectToClient.set(`${vaProjClientId.toString()}-${vaId.toString()}-${projectId.toString()}-client`, socket.id) // console.log("✅ Authenticated Client:", clientUser.userDetails.email); return next(); @@ -280,8 +296,6 @@ io.use(async (socket, next) => { } }); - - io.on("connection", (socket) => { console.log("User connected:", socket.id); @@ -316,7 +330,7 @@ io.on("connection", (socket) => { }); // When VA sends live image - socket.on("sendImage", async ({ token, image, title }) => { + socket.on("sendImage", async ({ token, image, title, projectId }) => { try { // Verify token const decoded = jwt.verify(token, process.env.JWT_SECRET_ACCESS_TOKEN); @@ -329,30 +343,42 @@ io.on("connection", (socket) => { return; } + const project = await ProjectModel.findById(projectId); + if (!project) { + console.log("❌ Invalid Project ID"); + return; + } + + const clientId = project.clientId.toString(); const vaId = vaUser._id.toString(); + const clientVaUniqueId = `${clientId}-${vaId}-${projectId}-client`; + const socketId = vaSocketRespectToClient.get(clientVaUniqueId); + 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); - } - } + // for (let [_, clientSocket] of io.sockets.sockets) { + // if ( + // clientSocket.data.role === "client" && + // clientSocket.data.allowedVAs?.includes(vaId) + // ) { + // clientSocket.emit("receiveImage", image, title); + // } + // } + + // Can be like clientId-projectId-vaId to uniquely identify + socket.to(socketId).emit("receiveImage", image, title); } catch (err) { console.error("❌ Token verification failed:", err.message); } }); - socket.on("selectApp", ({ appTitle, clientId }) => { + socket.on("selectApp", ({ appTitle, clientId, vaId, projectId }) => { console.log(`VA selected app: ${appTitle}`); - const sssrrrr = vaSocketRespectToClient.get(`${clientId}-${socket.data.vaId}`); - console.log(vaSocketRespectToClient.get(`${clientId}-${socket.data.vaId}`), sssrrrr, "vaSocketRespectToClient[clientId]") + console.log(vaSocketRespectToClient.get(`${clientId}-${vaId}-${projectId}-va`), "vaSocketRespectToClient[clientId]") // You can forward this info to the respective Electron client - socket.to(vaSocketRespectToClient.get(`${clientId}-${socket.data.vaId}`)).emit("appToCapture", { appTitle }); + socket.to(vaSocketRespectToClient.get(`${clientId}-${vaId}-${projectId}-va`)).emit("appToCapture", { appTitle }); }); socket.on("getVisibleApps", async ({ token, apps }) => { @@ -377,7 +403,7 @@ io.on("connection", (socket) => { clientSocket.data.role === "client" && clientSocket.data.allowedVAs?.includes(vaId) ) { - clientSocket.emit("receiveVisibleApps", apps); + clientSocket.emit("receiveVisibleApps", { apps, vaId }); } } } catch (err) { @@ -391,39 +417,10 @@ io.on("connection", (socket) => { }); }); - - -// 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, () => { -// 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}`); -// }); - app.use((req, res) => { - res.status(404).render("not_found", { - title: "404 - Page Not Found" - }); + res.status(404).render("not_found", { + title: "404 - Page Not Found" + }); }); server.listen(PORT, () => { diff --git a/src/testApiVpnConnection.js b/src/testApiVpnConnection.js index e985b2a..2dbf132 100644 --- a/src/testApiVpnConnection.js +++ b/src/testApiVpnConnection.js @@ -29,4 +29,35 @@ async function testPaths() { } } -testPaths(); \ No newline at end of file +testPaths();// 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/trackerExpress.zip b/trackerExpress.zip new file mode 100644 index 0000000..1051b92 Binary files /dev/null and b/trackerExpress.zip differ diff --git a/views/client/dashboard-dashboard.ejs b/views/client/dashboard-dashboard.ejs index 4fc3e5e..091dc6b 100644 --- a/views/client/dashboard-dashboard.ejs +++ b/views/client/dashboard-dashboard.ejs @@ -148,8 +148,7 @@