225 lines
8.0 KiB
JavaScript
225 lines
8.0 KiB
JavaScript
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 }; |