import bcrypt from "bcrypt"; import jwt from "jsonwebtoken"; import { VAModel, LoggedModel, WorkTrackModel } from "../../../models/vaModel.js"; // function to close open work tracks and create logout async function closePreviousSession(vaId, lastLog) { try { const currentTime = new Date(); // Find all open work tracks for this VA and project (where to is not set or totalTime is 0) const openWorkTracks = await WorkTrackModel.find({ vaId, logId: lastLog._id, $or: [ { to: { $exists: false } }, { totalTime: 0 }, { to: null } ] }); // console.log(`Found ${openWorkTracks.length} open work tracks to close`); // Close all open work tracks for (const workTrack of openWorkTracks) { const fromTime = new Date(workTrack.from); const totalTimeMs = currentTime - fromTime; // Update the work track with end time and total time workTrack.to = currentTime; workTrack.totalTime = totalTimeMs; await workTrack.save(); // console.log(`Closed work track ${workTrack._id} with duration: ${totalTimeMs}ms`); } // Create a logout log entry const logoutLog = await LoggedModel.create({ loggedType: "LOGOUT", vaId, vaEmail: lastLog.vaEmail, // Use email from previous log projectId: lastLog.projectId, time: currentTime // Use same timestamp for consistent logging }); // console.log(`Created logout log: ${logoutLog._id}`); try { const salesforceUrl = `${process.env.SF_DOCTRACKER_API_URL}services/apexrest/docva/api/va-project-log`; const sfDataSync = { "_id": logoutLog._id, "loggedType": "LOGOUT", "vaId": vaId, "vaEmail": lastLog.vaEmail, "projectId": lastLog.projectId, "time": logoutLog.time, } const response = await fetch(salesforceUrl, { method: 'POST', headers: { 'Content-Type': 'application/json', // Add any required headers for Salesforce authentication // 'Authorization': 'Bearer ' + process.env.SF_ACCESS_TOKEN }, body: JSON.stringify(sfDataSync) }); if (!response.ok) { const errorText = await response.text(); throw new Error(`Salesforce API responded with status ${response.status}: ${errorText}`); } const result = await response.json(); // console.log("✅ Successfully synced VA data to Salesforce:", result); return { closedWorkTracks: openWorkTracks.length, logoutLogId: logoutLog._id, SyncSalesforce: result }; } catch (error) { console.error("Failed to sync with Salesforce:", error.message); console.log(error) // Create a custom error to distinguish Salesforce errors const salesforceError = new Error(`Salesforce sync failed: ${error.message}`); salesforceError.isSalesforceError = true; throw salesforceError; } // return { // closedWorkTracks: openWorkTracks.length, // logoutLogId: logoutLog._id, // }; } catch (error) { console.error("Error closing previous session:", error); throw error; } } // Login VA async function va_login_callback(req, res) { try { // const payload = req.body ? JSON.parse(req.body) : {}; const payload = req.body || {}; const { userDetails } = payload; if (!userDetails || !userDetails.email || !userDetails.password) { return res.status(400).json({ success: false, message: "Please provide email and password" }); } // Find user by email const user = await VAModel.findOne({ "userDetails.email": userDetails.email }); if (!user) { return res.status(404).json({ success: false, message: "Invalid credentials" }); } // const saltRounds = 10; // const hashedPassword = await bcrypt.hash(userDetails.password, saltRounds); // Compare password with stored hash const isMatch = await bcrypt.compare( userDetails.password, user.userDetails.password ); if (!isMatch) { return res.status(401).json({ success: false, message: "Invalid password" }); } // also on main login when user try to login into app then logout project and workModel track then allow login on same timestamp // Check if VA has any active sessions (LOGIN without LOGOUT) const activeSessions = await LoggedModel.find({ vaId: user._id }).sort({ time: -1 }); let closeResult = []; if (activeSessions.length > 0) { // Get the latest login to check if there's no corresponding logout const latestLog = activeSessions[0]; closeResult = await closePreviousSession(user._id, latestLog); } // Generate JWT accessToken with 24h expiry const accessToken = jwt.sign( { id: user._id, email: user.userDetails.email, name: user.userDetails.firstName, }, process.env.JWT_SECRET_ACCESS_TOKEN || "DocTrackerSecretKey", // { expiresIn: "24h" } ); // Generate JWT refreshToken with 24h expiry const refreshToken = jwt.sign( { id: user._id, email: user.userDetails.email, name: user.userDetails.firstName, }, process.env.JWT_SECRET_REFRESH_TOKEN || "DocTrackerSecretKeyRefreshToken", // { expiresIn: "30d" } ); await VAModel.updateOne({ "userDetails.email": userDetails.email }, { $set: { accessToken, refreshToken } }) let responseData = []; if (closeResult) { responseData = { success: true, message: "Login Successfull", accessToken, refreshToken, user: { id: user._id, name: user.userDetails.firstName, email: user.userDetails.email, }, previousSession: { closedWorkTracks: closeResult.closedWorkTracks, logoutLogId: closeResult.logoutLogId } } } else { responseData = { success: true, message: "Login Successfull", accessToken, refreshToken, user: { id: user._id, name: user.userDetails.firstName, email: user.userDetails.email, } } } // Return accessToken and user info return res.status(200).json(responseData); } catch (error) { console.error("va_register error:", error); return res.status(500).json({ success: false, message: "Internal Server Error" }); } } // Helper function to format session duration function formatSessionDuration(loginTime) { const now = new Date(); const login = new Date(loginTime); const durationMs = now - login; const hours = Math.floor(durationMs / (1000 * 60 * 60)); const minutes = Math.floor((durationMs % (1000 * 60 * 60)) / (1000 * 60)); const seconds = Math.floor((durationMs % (1000 * 60)) / 1000); return `${hours}h ${minutes}m ${seconds}s`; } export { va_login_callback };