const bcrypt = require("bcrypt"); const db = require("../config/database"); // Database connection const fs = require("fs"); const jwt = require("jsonwebtoken"); const nlp = require("compromise"); const nodemailer = require("nodemailer"); const sender_port = process.env.SENDER_PORT; const sender_security = process.env.SENDER_SECURITY; const path = require('path') const { emitEvent } = require("../services/secondaryWebsocket"); console.log('sender port : ', sender_port); console.log('sender security : ', sender_security); // zoho transporter const transporter = nodemailer.createTransport({ host: "smtp.zoho.com", port: 465, secure: true, auth: { user: "kavya.j@tech4biz.io", // Your Zoho email address pass: "8pQfkBw8gbrz", // Your Zoho App Password (not your account password) } }); exports.initWebSocket = (io) => { io.on("connection", (socket) => { console.log("Hospital connected:", socket.id); socket.on("register_hospital", (hospitalCode) => { hospitalSockets[hospitalCode] = socket; console.log(`Hospital ${hospitalCode} registered.`); }); socket.on("disconnect", () => { const hospitalCode = Object.keys(hospitalSockets).find( (key) => hospitalSockets[key] === socket ); if (hospitalCode) delete hospitalSockets[hospitalCode]; console.log(`Hospital ${hospitalCode} disconnected.`); }); }); }; exports.uploadIdPhoto = async (req, res) => { try { // Extracting user ID and hospital code const userId = parseInt(req.params.id, 10); // User ID from route const { hospital_code } = req.user; // Authenticated user's hospital code // Check if user ID is valid if (isNaN(userId)) { console.error("Invalid User ID:", userId); return res.status(400).json({ error: "Invalid user ID" }); } // Check if a file was uploaded if (!req.file) { console.error("No file uploaded"); return res.status(400).json({ error: "No file uploaded" }); } const photoPath = `/uploads/id_photos/${req.file.filename}`; // Verify app user is part of the same hospital const query = ` SELECT hospital_code FROM app_users WHERE id = ? AND hospital_code = ? `; const result = await db.query(query, [userId, hospital_code]); if (result.length === 0) { console.error( "Authorization failed for user ID:", userId, "with hospital_code:", hospital_code ); return res .status(403) .json({ error: "You are not authorized to upload ID for this user" }); } // Update the ID photo path in the database await db.query("UPDATE app_users SET id_photo_url = ? WHERE id = ?", [ photoPath, userId, ]); res.status(200).json({ message: "ID photo uploaded successfully!", id_photo_url: photoPath, }); } catch (error) { console.error("Error uploading ID photo:", error.message); res.status(500).json({ error: "Internal server error" }); } }; exports.updateSettings = async (req, res) => { try { const userId = req.user.id; // Assuming user is authenticated and userId is available const { pin, pin_enabled, remember_me } = req.body; // Validate pin (if provided) if (pin && (typeof pin !== 'string' || pin.length !== 4)) { return res.status(400).json({ error: 'Invalid PIN format. Must be a 4-digit string.' }); } // Determine expiry based on remember_me let expiresIn = '5h'; let expiryTimestamp = new Date(); if (remember_me) { expiresIn = '30d'; expiryTimestamp.setDate(expiryTimestamp.getDate() + 30); } else { expiryTimestamp.setHours(expiryTimestamp.getHours() + 5); } // Generate new access token const payload = { id: userId, role: 'AppUser' }; const accessToken = jwt.sign(payload, process.env.JWT_ACCESS_TOKEN_SECRET, { expiresIn: expiresIn, }); // Update user settings and access token const query = `UPDATE app_users SET pin_number = ?, pin_enabled = ?, remember_me = ?, access_token = ?, access_token_expiry = ? WHERE id = ?`; const result=await db.query(query, [pin, pin_enabled, remember_me, accessToken, expiryTimestamp, userId]); if (result.affectedRows > 0) { console.log("Table updated successfully."); } else { console.log("Table not updated. No matching user found."); } res.status(200).json({ message: 'Settings updated successfully.', accessToken }); } catch (error) { console.error('Error updating settings:', error); res.status(500).json({ error: 'Internal Server Error' }); } }; exports.hitlike = async (req, res) => { try { // Extract user ID and session ID from the request const { session_id, id } = req.body; const log_id = id; const app_user_id = req.user.id; // Check if both app_user_id and session_id are provided if (!app_user_id || !session_id || !log_id) { return res.status(400).json({ status: 'error', message: 'app user id and session id and log id are required', }); } // Query to toggle the is_liked value const toggleQuery = ` UPDATE interaction_logs SET is_liked = NOT is_liked WHERE app_user_id = ? AND session_id = ? AND id = ? `; // Execute the query const result = await db.query(toggleQuery, [app_user_id, session_id,log_id]); // Check if any rows were affected if (result.affectedRows > 0) { return res.status(200).json({ status: 'success', message: 'Like updated successfully', data: { app_user_id, session_id, is_liked: result.changedRows > 0 ? 1 : 0, // 1 if changed, 0 if not }, }); } else { return res.status(404).json({ status: 'error', message: 'No matching record found to toggle', }); } } catch (error) { console.error('Error during like toggle:', error); return res.status(500).json({ status: 'error', message: 'Internal server error', }); } }; exports.hitFlag = async (req, res) => { try { // Extract user ID and session ID from the request const { session_id, logid } = req.body; const log_id = logid; const app_user_id = req.user.id; // Check if both app_user_id and session_id are provided if (!app_user_id || !session_id || !log_id) { return res.status(400).json({ status: 'error', message: 'app user id and session id and log id are required', }); } // Query to toggle the is_liked value const toggleQuery = ` UPDATE interaction_logs SET is_flagged = NOT is_flagged WHERE app_user_id = ? AND session_id = ? AND id = ? `; // Execute the query const result = await db.query(toggleQuery, [app_user_id, session_id,log_id]); // Check if any rows were affected if (result.affectedRows > 0) { return res.status(200).json({ status: 'success', message: 'Updated successfully', data: { app_user_id, session_id }, }); } else { return res.status(404).json({ status: 'error', message: 'No matching record found to toggle', }); } } catch (error) { console.error('Error during like toggle:', error); return res.status(500).json({ status: 'error', message: 'Internal server error', }); } }; exports.addReportText = async (req, res) => { try { const { session_id, report_text } = req.body; const app_user_id = req.user.id; // Validate input if (!app_user_id || !session_id || typeof report_text !== 'string') { return res.status(400).json({ status: 'error', message: 'app_user_id, session_id, logid, and report_text are required', }); } // Update query const updateQuery = ` UPDATE interaction_logs SET report_text = ? WHERE app_user_id = ? AND session_id = ? `; // Execute update const result = await db.query(updateQuery, [report_text, app_user_id, session_id]); // Handle response if (result.affectedRows > 0) { return res.status(200).json({ status: 'success', message: 'Session reported successfully. Admin will review your report', data: { app_user_id, session_id, report_text }, }); } else { return res.status(404).json({ status: 'error', message: 'No matching record found to update report text', }); } } catch (error) { console.error('Error updating report text:', error); return res.status(500).json({ status: 'error', message: 'Internal server error', }); } }; exports.updateChecked = async (req,res) =>{ try { const app_user_id = req.params.id; if (!["Admin", 8].includes(req.user.role)) { return res.status(403).json({ error: "Unauthorized to acknowledge" }); } const updateQuery = ` UPDATE app_users SET checked = ? WHERE id = ? `; const result = await db.query(updateQuery, [1, app_user_id]); if (result.affectedRows > 0) { return res.status(200).json({ status: 'success', message: 'Acknowledged successfully', }); } else { return res.status(404).json({ status: 'error', message: 'No matching record found to update checked', }); } } catch (error) { console.error("Error updating checked:", error); return res.status(500).json({ error: "Internal server error" }); } } exports.signup = async (req, res) => { try { const { pin, pin_status,remember_me, email, password, hospital_code, username } = req.body; if (!email || !password || !hospital_code || !username) { return res.status(400).json({ error: "Email, password, username, and hospital code are required", }); } const pin_enabled = (pin_status === undefined || pin_status === '') ? 0 : pin_status; const remember_me_ = (remember_me === undefined || remember_me === '') ? 0 : remember_me; // Check if the hospital_code exists in the `hospitals` table const hospitalQuery = "SELECT hospital_code FROM hospitals WHERE hospital_code = ?"; const hospitalResult = await db.query(hospitalQuery, [hospital_code]); if (hospitalResult.length === 0) { return res.status(404).json({ error: "Invalid hospital code" }); } // Check if the email is already in use const userQuery = "SELECT id FROM app_users WHERE email = ?"; const userResult = await db.query(userQuery, [email]); if (userResult.length > 0) { return res.status(400).json({ error: "Email already in use" }); } // Check if a file was uploaded if (!req.file) { return res.status(400).json({ error: "ID photo url is required" }); } // Hash the password const hashPassword = await bcrypt.hash(password, 10); // Insert the new app user into the `app_users` table const insertQuery = ` INSERT INTO app_users (email, hash_password, hospital_code, status, username, pin_number, pin_enabled, remember_me ) VALUES (?, ?, ?, 'Pending', ?, ?, ?,?) `; const result = await db.query(insertQuery, [ email, hashPassword, hospital_code, username, pin, // Set pin to null if not provided pin_enabled, remember_me_ ]); const userId = result.insertId; // Get the new user's ID const filePath = `/uploads/id_photos/${req.file.filename}`; // Correct file path // Update the ID photo path in the database await db.query( "UPDATE app_users SET id_photo_url = ?, upload_status = ? WHERE id = ?", [filePath, "1", userId] ); // ✅ Notify hospitals in the SECONDARY WebSocket instance emitEvent("new_user", { message: `A new user has been created for hospital ${hospital_code}.`, user: { id: userId, username, email, hospital_code: hospital_code }, }); res.status(201).json({ message: "User signed up and ID photo uploaded successfully!", data: { id: userId, email, hospital_code, username, id_photo_url: filePath, pin: pin || null, pin_status : pin_status, remember_me: remember_me }, }); } catch (error) { console.error("Error during signup and ID photo upload:", error.message); res.status(500).json({ error: "Internal server error" }); } }; exports.login = async (req, res) => { try { const { email, password } = req.body; let remember_me; if (!email || !password) { return res.status(400).json({ error: "Email and password are required" }); } console.log("email----",email) // Check if the user exists const query = "SELECT * FROM app_users WHERE email = ?"; const result = await db.query(query, [email]); console.log("result---",result) if (result.length === 0) { return res.status(404).json({ error: "User not found" }); } const user = result[0]; remember_me = user.remember_me console.log("user-data----------------------",user); const hospitalData = await db.query("SELECT * FROM hospitals WHERE hospital_code = ?", [user.hospital_code]); // if (hospitalData.publicSignupEnabled) { // throw new Error("Hospital not found"); // } console.log("hospitalData---",hospitalData) let actual_status; actual_status = user.status if(hospitalData[0].publicSignupEnabled){ user.status = "Active" } if (user.status === "Pending" && !hospitalData[0].publicSignupEnabled) { return res.status(404).json({ error: "Your Account is Under Review" }); } if (user.status === "Inactive" && !hospitalData[0].publicSignupEnabled) { return res.status(404).json({ error: "Contact admin your account is not approved" }); } // Validate the password const validPassword = await bcrypt.compare(password, user.hash_password); if (!validPassword) { return res.status(401).json({ error: "Invalid email or password" }); } // Determine expiry based on remember_me let expiresIn = "5h"; let expiryTimestamp = new Date(); let rememberMeValue = remember_me; if (rememberMeValue === undefined) rememberMeValue = user.remember_me; if (rememberMeValue === true || rememberMeValue === 1 || rememberMeValue === "1" || rememberMeValue === "true") { const updateQuery = ` UPDATE app_users SET remember_me = ? WHERE id = ? `; await db.query(updateQuery, [remember_me,user.id]); expiresIn = "30d"; expiryTimestamp.setDate(expiryTimestamp.getDate() + 30); } else { expiryTimestamp.setHours(expiryTimestamp.getHours() + 5); } // Generate a new access token const payload = { id: user.id, email: user.email, role: "AppUser" }; const accessToken = jwt.sign(payload, process.env.JWT_ACCESS_TOKEN_SECRET, { expiresIn: expiresIn, }); // SQL query to fetch hospital data based on the provided hospital_code const query_hospital = ` SELECT * FROM hospitals WHERE hospital_code = ?; `; // Run the query const result_hospital = await db.query(query_hospital, [user.hospital_code]); // hospital users const queryHospitalUsr = ` SELECT * FROM hospital_users WHERE hospital_code = ?; `; // Run the query const resultHospitalUsr = await db.query(queryHospitalUsr, [user.hospital_code]); const hospitalUser = resultHospitalUsr[0]; if (!hospitalUser) { return res.status(404).json({ error: "Hospital user not found" }); } // Update the access token and expiry in the database const updateQuery = ` UPDATE app_users SET access_token = ?, access_token_expiry = ?, remember_me = ? WHERE id = ? `; await db.query(updateQuery, [accessToken, expiryTimestamp, (rememberMeValue === true || rememberMeValue === 1 || rememberMeValue === "1" || rememberMeValue === "true") ? 1 : 0, user.id]); // Send response res.status(200).json({ message: "Login successful", user: { id: user.id, email: user.email, pin: user.pin_number, pin_enabled: user.pin_enabled, hospital_code: user.hospital_code, hospital_id : hospitalData[0].id, status: user.status, actualStatus: actual_status, hospital_name: result_hospital[0].name_hospital, primary_color: result_hospital[0].primary_color, secondary_color: result_hospital[0].secondary_color, upload_status: user.upload_status, id_photo_url: user.id_photo_url, primary_admin_email: result_hospital[0].primary_admin_email, mobile_number: result_hospital[0].mobile_number, username: user.username, logo_url: resultHospitalUsr[0].profile_photo_url, query_title: user.query_title, remember_me: (rememberMeValue === true || rememberMeValue === 1 || rememberMeValue === "1" || rememberMeValue === "true") ? 1 : 0 }, accessToken, }); } catch (error) { console.error("Error during login:", error.message); res.status(500).json({ error: "Internal server error" }); } }; exports.checkPin = async (req, res) => { try { const { email, pin } = req.body; if (!email || !pin) { return res.status(400).json({ error: "Email and Pin are required" }); } // Check if the user exists const query = "SELECT * FROM app_users WHERE email = ?"; const result = await db.query(query, [email]); if (result.length === 0) { return res.status(404).json({ error: "User not found" }); } const user = result[0]; const hospitalData = await db.query("SELECT * FROM hospitals WHERE hospital_code = ?", [user.hospital_code]); if (user.status === "Pending" && !hospitalData[0].publicSignupEnabled) { throw new Error("Your Account is Under Review"); } if (user.status === "Inactive" && !hospitalData[0].publicSignupEnabled) { throw new Error("Contact admin your account is not approved"); } // Check PIN if PIN status is enabled if (pin !== user.pin_number) { return res.status(401).json({ error: "Invalid PIN", requires_pin: true, message: "Incorrect PIN. Please try again" }); } // Send response res.status(200).json({ message: "Pin verified successfully", }); } catch (error) { console.error("Error during login:", error.message); res.status(500).json({ error: "Internal server error" }); } }; exports.logout = async (req, res) => { try { const userId = req.user.id; // Assuming the user is authenticated via middleware // Check if the user exists const query = "SELECT * FROM app_users WHERE id = ?"; const result = await db.query(query, [userId]); if (result.length === 0) { return res.status(404).json({ error: "User not found" }); } // Invalidate the access token in the database const updateQuery = ` UPDATE app_users SET access_token = NULL, access_token_expiry = NULL WHERE id = ? `; await db.query(updateQuery, [userId]); res.status(200).json({ message: "Logout successful" }); } catch (error) { console.error("Error during logout:", error.message); res.status(500).json({ error: "Internal server error" }); } }; // controller.js exports.getAppUsersByHospitalCode = async (req, res) => { try { // Extract the hospital_code from the token (req.user object) const userId = req.user.id; if (!userId) { return res.status(400).json({ error: "User not found for the token" }); } // Query to fetch hospital users based on the hospital_code const query = ` SELECT id, email, status,hospital_code FROM app_users WHERE id = ?; `; const result = await db.query(query, [userId]); if (result.length === 0) { return res .status(404) .json({ error: "No users found for the specified Token" }); } const hospitalData = await db.query("SELECT * FROM hospitals WHERE hospital_code = ?", [result[0].hospital_code]); if(hospitalData[0].publicSignupEnabled){ result[0].status = 'Active' } // Send the response with the users data res.status(200).json({ message: "Hospital users fetched successfully", user: result[0], }); } catch (error) { console.error("Error fetching hospital users:", error.message); res.status(500).json({ error: "Internal server error" }); } }; exports.approveUserId = async (req, res) => { try { const appUserId = req.params.id; // App user ID to approve const { role } = req.user; // Authenticated user's details const { Action } = req.body; const { hospital_code } = req.user; if (!req.body.Action) { return res.status(400).json({ error: "Action is required" }); } if (!["Superadmin", "Admin", 8].includes(role)) { return res.status(403).json({ error: "Unauthorized to approve IDs" }); } const query = ` SELECT hospital_code FROM app_users WHERE id = ? AND hospital_code = ? `; const result = await db.query(query, [appUserId, hospital_code]); if (result.length === 0) { return res .status(403) .json({ error: "User does not belong to your hospital" }); } // Approve the ID if (Action == "Reject") { await db.query('UPDATE app_users SET status = "Inactive" WHERE id = ?', [ appUserId, ]); res.status(200).json({ message: "ID Rejected successfully" }); } else if (Action == "Approve") { await db.query('UPDATE app_users SET status = "Active" WHERE id = ?', [ appUserId, ]); res.status(200).json({ message: "ID approved successfully" }); } } catch (error) { console.error("Error approving ID:", error.message); res.status(500).json({ error: "Internal server error" }); } }; exports.getAppUsers = async (req, res) => { try { const { role } = req.user; // Ensure the user has the proper role if (!["Superadmin", "Admin", 9, 8].includes(role)) { return res.status(403).json({ error: "Unauthorized to view app users" }); } // Query to fetch app users const query = ` SELECT * FROM app_users; `; const users = await db.query(query); if (users.length === 0) { return res .status(404) .json({ message: "No app users found for this hospital" }); } res.status(200).json({ message: "App users fetched successfully", data: users, }); } catch (error) { console.error("Error fetching app users:", error.message); res.status(500).json({ error: "Internal server error" }); } }; exports.getAppUserByHospitalId = async (req, res) => { try { const { role } = req.user; const { id } = req.params; // Extract user ID from request parameters // Log authenticated user details // Ensure the user has the proper role if (!["Superadmin", "Admin", 8, 9].includes(role)) { return res.status(403).json({ error: "Unauthorized to view app users" }); } // fetching hospital code from hospital id const query1 = `SELECT * FROM hospitals WHERE id = ?`; const result1 = await db.query(query1, [id]); const hospital_code = result1[0].hospital_code; console.log("result1----", result1); // Query to fetch the app user by hospital_code const query = `SELECT * FROM app_users WHERE hospital_code = ?`; const users = await db.query(query, [hospital_code]); if (users.length === 0) { return res.status(404).json({ message: "App users not found" }); } res.status(200).json({ message: "App user fetched successfully", data: users, // Return single user object }); } catch (error) { console.error("Error fetching app user:", error.message); res.status(500).json({ error: "Internal server error" }); } }; exports.deleteAppUser = async (req, res) => { try { const { id } = req.params; const { hospital_code } = req.user; // Check if the user exists const appUserquery = "SELECT * FROM app_users WHERE id = ?"; const users = await db.query(appUserquery, [id]); if (users.length === 0) { return res .status(404) .json({ message: "No app users found for this hospital" }); } // Ensure only Admin or Superadmin can delete users if (!["Admin", "Superadmin", 8, 7,"AppUser"].includes(req.user.role)) { return res.status(403).json({ error: "Unauthorized to delete users" }); } // Ensure user belongs to the same hospital const query = "SELECT hospital_code,id_photo_url FROM app_users WHERE id = ? AND hospital_code = ?"; const result = await db.query(query, [id, hospital_code]); if (!result || result.length === 0) { return res .status(403) .json({ error: "User does not belong to your hospital" }); } // Unlink (delete) the associated file if it exists and is not null if (result[0].id_photo_url) { const filePath = path.join(__dirname, '..','..', 'uploads', result[0].id_photo_url.replace(/^\/uploads\//, '')); fs.access(filePath, fs.constants.F_OK, (err) => { if (err) { console.error(`File not found: ${filePath}`); return; } fs.unlink(filePath, (err) => { if (err) { console.error('Error deleting file:', err.message); } else { console.log('File deleted successfully:', filePath); } }); }); } else { console.log('No file to delete - id_photo_url is null'); } // delete feedback await db.query( 'DELETE FROM feedback WHERE sender_type = ? AND sender_id = ?', ['appuser', id] // Fixed: should be 'id' not 'req.user.id' ); // Delete the user const deleteResult = await db.query("DELETE FROM app_users WHERE id = ?", [ id, ]); if (deleteResult.affectedRows === 0) { return res.status(404).json({ error: "User not found" }); } res.status(200).json({ message: "User deleted successfully" }); } catch (error) { console.error("Error deleting user:", error.message); res.status(500).json({ error: "Internal server error" }); } }; // query title CRUD exports.updateQueryTitle = async (req, res) => { const id = req.user.id; const { query_title } = req.body; if (!query_title) { return res.status(400).json({ error: "query_title is required" }); } try { const result = await db.query( "UPDATE app_users SET query_title = ? WHERE id = ?", [query_title, id] ); if (result.affectedRows === 0) { return res.status(404).json({ error: "User not found" }); } res.json({ message: "Query title updated successfully" }); } catch (error) { console.error("Error updating query title:", error); res.status(500).json({ error: "Internal server error" }); } }; // Delete query_title for a user by user ID exports.getShortTitle = (req, res) => { const { question } = req.body; console.log("Question: ", question); if (!question) return res.status(400).json({ error: "No question provided" }); let shortTitle = nlp(question).sentences().toTitleCase().out("text"); // Remove unnecessary question words shortTitle = shortTitle .replace( /^(What|How|Why|When|Where|Which|Who|Is|Are|Do|Does|Can|Could)\s+/i, "" ) .trim(); if (!shortTitle) return res.status(400).json({ error: "Invalid question format" }); console.log("short question:", shortTitle); res.json({ query_title: shortTitle }); }; exports.deleteQueryTitle = async (req, res) => { const id = req.user.id; try { const result = await db.query( "UPDATE app_users SET query_title = NULL WHERE id = ?", [id] ); if (result.affectedRows === 0) { return res.status(404).json({ error: "User not found" }); } res.json({ message: "Query title deleted successfully" }); } catch (error) { console.error("Error deleting query title:", error); res.status(500).json({ error: "Internal server error" }); } }; exports.sendOtp = async (req, res) => { try { const { email } = req.body; // Validate email input if (!email) { return res.status(400).json({ error: "Email is required" }); } // Check if user exists const user = await db.query( "SELECT id, username, hospital_code FROM app_users WHERE email = ?", [email] ); if (!user.length) { return res.status(404).json({ error: "User not found" }); } console.log("user", user); const userId = user[0].id; const hospital_code = user[0].hospital_code; // fetch hospital name of app_user const hospital = await db.query( "SELECT name_hospital FROM hospitals WHERE hospital_code = ?", [hospital_code] ); console.log("hospital result : ", hospital[0].name_hospital); // Generate a 6-digit OTP const otp = Math.floor(100000 + Math.random() * 900000).toString(); const expiresAt = new Date(Date.now() + 2 * 60 * 60 * 1000); // OTP expires in 1 hour console.log("otp : ", otp, "expiresAt: ", expiresAt); // Store OTP in database await db.query( "UPDATE app_users SET otp_code = ?, otp_expires_at = ? WHERE id = ?", [otp, expiresAt, userId] ); // // Send OTP via email const info = await sendMail( email, hospital[0].name_hospital, user[0].username, otp ); res.json({ message: "OTP sent successfully", email_status: info.response }); } catch (error) { console.error("Error sending OTP:", error); res.status(500).json({ error: "Internal server error" }); } }; exports.changePassword = async (req, res) => { try { const { email, otp, new_password } = req.body; console.log("email : ", email); // Validate inputs if (!email || !otp || !new_password) { return res .status(400) .json({ error: "Email, OTP, and new password are required" }); } // Fetch OTP from the database const user = await db.query( "SELECT id, otp_code, otp_expires_at FROM app_users WHERE email = ?", [email] ); if (!user.length) { return res.status(404).json({ error: "User not found" }); } const userData = user[0]; // ✅ Check if OTP matches if (userData.otp_code !== otp) { return res.status(400).json({ error: "Invalid OTP" }); } // ✅ Check if OTP is expired if (new Date() > new Date(userData.otp_expires_at)) { return res.status(400).json({ error: "OTP expired. Request a new one." }); } // ✅ Hash the new password const hashedPassword = await bcrypt.hash(new_password, 10); // ✅ Update password in DB & clear OTP await db.query( "UPDATE app_users SET hash_password = ?, otp_code = NULL, otp_expires_at = NULL WHERE id = ?", [hashedPassword, userData.id] ); res.json({ message: "Password changed successfully!" }); } catch (error) { console.error("Error resetting password:", error); res.status(500).json({ error: "Internal server error" }); } }; exports.sendPinOtp = async (req, res) => { try { const { email } = req.body; // Validate email input if (!email) { return res.status(400).json({ error: "Email is required" }); } // Check if user exists const user = await db.query( "SELECT id, username, hospital_code FROM app_users WHERE email = ?", [email] ); if (!user.length) { return res.status(404).json({ error: "User not found" }); } console.log("user", user); const userId = user[0].id; const hospital_code = user[0].hospital_code; // fetch hospital name of app_user const hospital = await db.query( "SELECT name_hospital FROM hospitals WHERE hospital_code = ?", [hospital_code] ); console.log("hospital result : ", hospital[0].name_hospital); // Generate a 6-digit OTP const otp = Math.floor(100000 + Math.random() * 900000).toString(); const expiresAt = new Date(Date.now() + 2 * 60 * 60 * 1000); // OTP expires in 1 hour console.log("otp : ", otp, "expiresAt: ", expiresAt); // Store OTP in database await db.query( "UPDATE app_users SET pin_otp = ?, pin_otp_expiry = ? WHERE id = ?", [otp, expiresAt, userId] ); // // Send OTP via email const info = await sendMail( email, hospital[0].name_hospital, user[0].username, otp ); res.json({ message: "OTP sent successfully", email_status: info.response }); } catch (error) { console.error("Error sending OTP:", error); res.status(500).json({ error: "Internal server error" }); } }; exports.changePinByOtp = async (req, res) => { try { const { email, otp, new_pin } = req.body; console.log("email : ", email); // Validate inputs if (!email || !otp || !new_pin) { return res .status(400) .json({ error: "Email, OTP, and new pin are required" }); } // Fetch OTP from the database const user = await db.query( "SELECT id, pin_otp, pin_otp_expiry FROM app_users WHERE email = ?", [email] ); if (!user.length) { return res.status(404).json({ error: "User not found" }); } const userData = user[0]; // ✅ Check if OTP matches if (userData.pin_otp !== otp) { return res.status(400).json({ error: "Invalid OTP" }); } // ✅ Check if OTP is expired if (new Date() > new Date(userData.otp_expires_at)) { return res.status(400).json({ error: "OTP expired. Request a new one." }); } // ✅ Hash the new password // ✅ Update password in DB & clear OTP await db.query( "UPDATE app_users SET pin_number = ?, pin_otp = NULL, pin_otp_expiry = NULL WHERE id = ?", [new_pin, userData.id] ); res.json({ message: "Pin changed successfully!" }); } catch (error) { console.error("Error resetting pin:", error); res.status(500).json({ error: "Internal server error" }); } }; async function sendMail(email, hospital_name, username, otp) { const mailOptions = { from: "kavya.j@tech4biz.io", // Sender's email to: email, // Recipient's email subject: "Spurrinai Login Credentials", // Email subject html: ` Reset Your Password or Pin - Spurrinai Medical Platform
${hospital_name}

Reset Your Password or Pin

Hello ${username},

We received a request to reset the password for your account on the Spurrinai healthcare platform. For your security reasons, please verify this action.

Please enter this verification code:

${otp}

on the password reset screen

Reset Password
Note: This verification code will expire in 2 hours for security reasons.

If you did not request this password reset, please contact our IT security team immediately at info@spurrinai.com or call our support line at +1 (800) 555-1234.

`, }; try { const info = await transporter.sendMail(mailOptions); console.log("info: ", info); return info; } catch (error) { console.error(`Error sending email to ${email}:`, error); return error; } } // chat-sessions exports.getChatSessionsByAppUserID = async (req, res) => { try { console.log("user data", req.user) const { role } = req.user; // Ensure the user has the proper role // if (!['Superadmin', 'Admin', 9, 8].includes(role)) { // return res.status(403).json({ error: 'Unauthorized to view chats' }); // } if (!req.user.id) { return res.status(400).json({ error: "user id not found" }); } // Query to fetch app users const query = ` SELECT session_id, MIN(session_title) AS session_title, MIN(created_at) AS created_at FROM interaction_logs WHERE app_user_id = ${req.user.id} AND session_id IS NOT NULL GROUP BY session_id ORDER BY session_id DESC; `; const session_ids = await db.query(query); if (session_ids.length === 0) { return res.status(404).json({ message: 'No interaction logs found for this app user', data: session_ids }); } res.status(200).json({ message: 'Interaction logs fetched successfully', data: session_ids, }); } catch (error) { console.error('Error fetching interaction logs:', error.message); res.status(500).json({ error: 'Internal server error' }); } } exports.getChatForEachSession = async (req, res) => { try { console.log("user data", req.user) const session_id = req.params.session_id; console.log("session_id ", session_id) // Ensure the user has the proper role // if (!['Superadmin', 'Admin', 9, 8].includes(role)) { // return res.status(403).json({ error: 'Unauthorized to view chats' }); // } if (!req.user.id) { return res.status(400).json({ error: "user id not found" }); } // Query to fetch app users const query = ` SELECT * FROM interaction_logs WHERE app_user_id = ? AND session_id = ? AND status = 'Active'; `; const values = [req.user.id, session_id]; const interaction_logs = await db.query(query, values); if (interaction_logs.length === 0) { return res.status(404).json({ message: 'No interaction logs found for this app user or session', data: interaction_logs }); } res.status(200).json({ message: 'Interaction logs fetched successfully', data: interaction_logs, }); } catch (error) { console.error('Error fetching interaction logs:', error.message); res.status(500).json({ error: 'Internal server error' }); } } exports.deleteChatSessions = async (req, res) => { const id = req.user.id; const { session_id } = req.body console.log("session_id ", session_id) console.log("app_user_id ", id) try { const result = await db.query( "UPDATE interaction_logs SET session_id = NULL WHERE app_user_id = ? AND session_id = ?", [id, session_id] ); if (result.affectedRows === 0) { return res.status(404).json({ error: "No session found" }); } res.json({ message: "Chat session deleted successfully" }); } catch (error) { console.error("Error deleting query title:", error); res.status(500).json({ error: "Internal server error" }); } } exports.clearChatbasedOnSessions = async (req, res) => { const id = req.user.id; const { session_id } = req.body try { const result = await db.query( "UPDATE interaction_logs SET status = ? WHERE app_user_id = ? AND session_id = ?", ['Inactive', id, session_id] ); if (result.affectedRows === 0) { return res.status(404).json({ error: "No chat found" }); } res.json({ message: "Chat deleted successfully" }); } catch (error) { console.error("Error deleting chat:", error); res.status(500).json({ error: "Internal server error" }); } } exports.getChatByTime = async (req, res) => { try { console.log("user data", req.user) const { lastcreated_at } = req.body; // Ensure the user has the proper role // if (!['Superadmin', 'Admin', 9, 8].includes(role)) { // return res.status(403).json({ error: 'Unauthorized to view chats' }); // } if (!req.user.id) { return res.status(400).json({ error: "user id not found" }); } // Query to fetch app users let results console.log("last created at", lastcreated_at) if (!lastcreated_at) { // Handles null, undefined, empty string, etc. const query = ` SELECT * FROM interaction_logs WHERE app_user_id = ?; `; results = await db.query(query, [req.user.id]); } else { console.log("Executing with last_created_at:", lastcreated_at); const query = ` SELECT * FROM interaction_logs WHERE app_user_id = ? AND created_at > ?; `; results = await db.query(query, [req.user.id, lastcreated_at]); } if (results.length === 0) { return res.status(404).json({ message: 'No interaction logs found for this app user' }); } res.status(200).json({ message: 'Interaction logs fetched successfully', data: results, }); } catch (error) { console.error('Error fetching interaction logs:', error.message); res.status(500).json({ error: 'Internal server error' }); } } exports.checkEmailCode = async (req, res) => { try { const { email, hospital_code } = req.body; // Check if the email and code are provided if (!email || !hospital_code) { return res.status(400).json({ error: "Email and code are required" }); } console.log("received data", email, hospital_code) // Check if the email exists in the database const useremail = await db.query("SELECT * FROM app_users WHERE email = ?", [email]); console.log("user email", useremail) if (useremail.length) { return res.status(404).json({ error: "Email already in use" }); } // Check if the code is correct const usercode = await db.query("SELECT hospital_code FROM hospital_users WHERE hospital_code = ?", [hospital_code]); console.log("user code", usercode) if (!usercode.length) { return res.status(404).json({ error: "Please enter valid hospital code" }); } // Code is correct, return success response res.status(200).json({ message: "Email code is correct" }); } catch (error) { console.error("Error checking email code:", error); res.status(500).json({ error: "Internal server error" }); } } // get popular topics exports.getPopularTopics = async (req, res) => { let popularTopics = []; try { // Fetch top 4 most viewed questions console.log("req.user data : ", req.user) const rows = await getMappedPopularQuestionsAnswers(req.user.hospital_code); if (!rows) { popularTopics = [] } else { popularTopics = rows; } console.log('hospital data : ', rows) res.status(200).json({ success: true, data: popularTopics }); } catch (error) { console.error('Error fetching popular topics:', error); res.status(500).json({ success: false, message: 'Internal server error' }); } } const getMappedPopularQuestionsAnswers = async (hospitalCode) => { console.log("Hospital code is---", hospitalCode); try { const query = ` WITH question_frequency AS ( SELECT query, response, COUNT(*) as frequency FROM interaction_logs WHERE hospital_code = ? GROUP BY query, response HAVING COUNT(*) > 1 ORDER BY frequency DESC LIMIT 10 ), recent_questions AS ( SELECT il.*, COALESCE(qf.frequency, 1) as frequency FROM interaction_logs il LEFT JOIN question_frequency qf ON il.query = qf.query AND il.response = qf.response WHERE il.hospital_code = ? ORDER BY COALESCE(qf.frequency, 0) DESC, il.created_at DESC LIMIT 10 ) SELECT * FROM recent_questions; `; const rows = await db.query(query, [hospitalCode, hospitalCode]); console.log("Fetched questions before filtering:", rows); // If no data found, return message if (!rows || rows.length === 0) { return { message: "No interactions made in your hospital" }; } // Skip the row if either condition is true const filteredRows = rows.filter(row => !(row.query.toLowerCase().includes("yes") || row.response.includes("Please reply with 'yes'")) ); // If no data after filtering, return message if (filteredRows.length === 0) { return { message: "No interactions made in your hospital" }; } // Return only the top 4 filtered results return filteredRows.slice(0, 4); } catch (error) { console.error("Error fetching popular topics:", error.message); throw new Error("Internal server error"); } }; exports.changePin = async (req, res) => { try { const { current_pin, new_pin } = req.body; const userId = req.user.id; if (!current_pin || !new_pin) { return res.status(400).json({ error: "Current pin and new pin are required" }); } // Add PIN length validation if (new_pin.length !== 4 || !/^\d+$/.test(new_pin)) { return res.status(400).json({ error: "New PIN must be exactly 4 digits" }); } // Get user's current pin const userQuery = "SELECT pin_number FROM app_users WHERE id = ?"; const user = await db.query(userQuery, [userId]); if (!user.length) { return res.status(404).json({ error: "User not found" }); } // Verify current pin if (user[0].pin_number !== current_pin) { return res.status(400).json({ error: "Current pin is incorrect" }); } // Check if new pin is same as current pin if (current_pin === new_pin) { return res.status(400).json({ error: "New PIN cannot be the same as current PIN" }); } // Update to new pin await db.query( "UPDATE app_users SET pin_number = ? WHERE id = ?", [new_pin, userId] ); res.json({ message: "Pin changed successfully" }); } catch (error) { console.error("Error changing pin:", error); res.status(500).json({ error: "Internal server error" }); } }; exports.forgotPin = async (req, res) => { try { const { email } = req.body; if (!email) { return res.status(400).json({ error: "Email is required" }); } // Get user details const userQuery = ` SELECT au.id, au.username, au.pin_number, au.hospital_code, h.name_hospital FROM app_users au JOIN hospitals h ON au.hospital_code = h.hospital_code WHERE au.email = ? `; const user = await db.query(userQuery, [email]); if (!user.length) { return res.status(404).json({ error: "User not found" }); } const userData = user[0]; // Send pin via email const mailOptions = { from: "kavya.j@tech4biz.io", to: email, subject: "Your Spurrinai PIN", html: ` Your PIN - Spurrinai Medical Platform
${userData.name_hospital}

Your PIN Information

Hello ${userData.username},

Here is your PIN for the Spurrinai healthcare platform:

${userData.pin_number}

For security reasons, please do not share this PIN with anyone. If you did not request this information, please contact our support team immediately.

` }; await transporter.sendMail(mailOptions); res.json({ message: "PIN has been sent to your email" }); } catch (error) { console.error("Error in forgot pin:", error); res.status(500).json({ error: "Internal server error" }); } };