const db = require("../config/database");
const hospitalService = require("../services/hospitalService");
const jwt = require("jsonwebtoken");
const bcrypt = require("bcrypt");
const nodemailer = require("nodemailer");
const path = require("path");
const sender_mail = process.env.mail;
const back_url = process.env.BACK_URL;
const fs = require("fs");
const tokenService = require('../services/tokenService');
const transporter = nodemailer.createTransport({
host: "smtp.zoho.com", // Zoho SMTP Server
port: 465, // Use 465 for SSL or 587 for TLS
secure: true, // Set to true for port 465, false for port 587
auth: {
user: "kavya.j@tech4biz.io", // Your Zoho email address
pass: "8pQfkBw8gbrz", // Your Zoho App Password (not your account password)
}
// tls: {
// minVersion: "TLSv1.2",
// ciphers: "SSLv3",
// },
});
function generateHospitalCode() {
const length = 12;
const characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
let code = "";
for (let i = 0; i < length; i++) {
code += characters.charAt(Math.floor(Math.random() * characters.length));
}
return code;
}
exports.createHospital = async (req, res) => {
try {
const {
name_hospital,
subdomain,
primary_admin_email,
primary_admin_password,
primary_color,
secondary_color,
logo_url,
admin_name,
mobile_number,
location,
super_admin_id, // This comes from request body
} = req.body;
// ✅ Step 1: Ensure the logged-in SuperAdmin is the one creating the hospital
if (req.user.id !== super_admin_id) {
return res.status(403).json({
error:
"Unauthorized: You cannot create a hospital for another SuperAdmin",
});
}
// check email if already exists
const spurrinEmailQuery =
"SELECT email from super_admins WHERE email = ?";
const spurrinEmailResult = await db.query(spurrinEmailQuery, [primary_admin_email]);
if (spurrinEmailResult.length > 0) {
return res.status(403).json({ error: "Email already exists!" });
}
const hsptUsrEmailQuery =
"SELECT email from hospital_users WHERE email = ?";
const hsptUsrEmailResult = await db.query(hsptUsrEmailQuery, [primary_admin_email]);
if (hsptUsrEmailResult.length > 0) {
return res.status(403).json({ error: "Email already exists!" });
}
// ✅ Step 2: Generate a unique hospital code
let hospitalCode;
let isUnique = false;
while (!isUnique) {
hospitalCode = generateHospitalCode();
const codeExists = await db.query(
"SELECT COUNT(*) as count FROM hospitals WHERE hospital_code = ?",
[hospitalCode]
);
if (codeExists[0].count === 0) {
isUnique = true;
}
}
// ✅ Step 3: Validate if the SuperAdmin exists in the database
const superAdminQuery =
"SELECT id, access_token FROM super_admins WHERE id = ?";
const superAdminResult = await db.query(superAdminQuery, [super_admin_id]);
if (superAdminResult.length === 0) {
return res.status(400).json({ error: "Invalid super_admin_id" });
}
const superAdmin = superAdminResult[0];
// ✅ Step 4: Ensure the access token used in the request matches the one stored in the database
if (superAdmin.access_token !== req.headers.authorization.split(" ")[1]) {
return res.status(403).json({
error:
"Unauthorized: Access token does not match the SuperAdmin's token in the database",
});
}
// ✅ Step 5: Hash the primary admin's password
const hashedPassword = await bcrypt.hash(primary_admin_password, 10);
// ✅ Step 6: Send an email to notify
const mailOptions = {
from: 'kavya.j@tech4biz.io', // Sender's email
to: primary_admin_email, // Recipient's email
subject: "Spurrinai Login Credentials", // Email subject
html: `
Welcome Email
Greetings, ${admin_name},
Congratulations! Your hospital, ${name_hospital}, has been successfully onboarded to
Spurrinai. We are excited to have you on board and look forward to supporting your
hospital's needs.
Please find your hospital's login credentials below to access the platform:
Hospital Name
${name_hospital}
Username
${primary_admin_email}
Temporary Password
${primary_admin_password}
For your security, we recommend changing your password immediately after logging in.
Log In and Change Password
`,
};
const insertHospitalQuery = `
INSERT INTO hospitals (
name_hospital,
subdomain,
primary_admin_email,
primary_admin_password,
primary_color,
secondary_color,
logo_url,
status,
onboarding_status,
admin_name,
mobile_number,
location,
super_admin_id,
hospital_code,
type
) VALUES (?, ?, ?, ?, ?, ?, ?, 'Active', 'Pending', ?, ?, ?, ?,?,NULL)
`;
const hospitalResult = await db.query(insertHospitalQuery, [
name_hospital,
subdomain,
primary_admin_email,
hashedPassword,
primary_color,
secondary_color,
logo_url,
admin_name,
mobile_number,
location,
super_admin_id,
hospitalCode,
]);
// ✅ Step 7: Get the new hospital ID
const hospitalId = hospitalResult.insertId;
// ✅ Step 8: Generate a refresh token for the hospital's primary admin
// Step 1: Insert the primary admin into the hospital_users table
const insertUserQuery = `
INSERT INTO hospital_users (
hospital_id,
email,
hash_password,
role_id,
is_default_admin,
requires_onboarding,
password_reset_required,
phone_number,
status,
hospital_code,
name,
type,
location
) VALUES (?, ?, ?, ?, TRUE, TRUE, TRUE, ?, 'Active', ?, ?, NULL,?)
`;
// Assuming 7 is the role ID for Superadmin
const roleId = 7;
const insertResult = await db.query(insertUserQuery, [
hospitalId,
primary_admin_email,
hashedPassword,
roleId,
mobile_number,
hospitalCode,
admin_name,
location
]);
console.log("insert result", insertResult);
// Step 2: Retrieve the inserted user ID
const insertedUserId = insertResult.insertId;
const payload = { id: insertedUserId, email: primary_admin_email, role: roleId };
const accessToken = tokenService.generateAccessToken(payload);
const expiryTimestamp = new Date();
expiryTimestamp.setHours(expiryTimestamp.getHours() + 5); // Add 5 hours
// Step 3: Generate the refresh token
const refreshTokenPayload = {
id: insertedUserId,
email: primary_admin_email,
role: "Superadmin",
};
const refreshToken = jwt.sign(
refreshTokenPayload,
process.env.JWT_REFRESH_TOKEN_SECRET
);
// Step 4: Update the refresh token in the database
const updateRefreshTokenQuery = `UPDATE hospital_users SET refresh_token = ?, access_token = ?, access_token_expiry= ? WHERE id = ?`;
await db.query(updateRefreshTokenQuery, [refreshToken, accessToken, expiryTimestamp, insertedUserId]);
if (!primary_admin_email) {
return res.status(400).json({ error: "Recipient email is required" });
}
const responseData = {
message: "Hospital and Primary SuperAdmin created successfully!",
hospital: {
id: hospitalId,
name_hospital,
subdomain,
primary_admin_email,
primary_color,
secondary_color,
logo_url,
admin_name,
mobile_number,
location,
super_admin_id,
hospitalCode
},
refreshToken,
};
// 3. Send email in a separate try-catch
try {
const info = await transporter.sendMail(mailOptions);
responseData.emailInfo = info.response;
} catch (emailError) {
console.error("Email sending failed:", emailError.message);
responseData.emailInfo = "Email sending failed: " + emailError.message;
}
// 4. Send final response
res.status(201).json(responseData);
} catch (error) {
console.error("Error creating hospital and admin:", error.message);
res.status(500).json({ error: error.message });
}
};
exports.uploadLogo = (req, res) => {
try {
const logoUrl = req.file ? req.file.path : null;
res
.status(200)
.json({ message: "Logo uploaded successfully!", logo_url: logoUrl });
} catch (error) {
res.status(500).json({ error: error.message });
}
};
exports.getHospitalList = async (req, res) => {
try {
// Ensure we are filtering by the authenticated Spurrinadmin
const superAdminId = req.user.id; // Extract authenticated Spurrinadmin ID
const query = "SELECT * FROM hospitals";
const hospitals = await db.query(query);
res.status(200).json({
message: "Hospital list fetched successfully!",
data: hospitals,
});
} catch (error) {
console.error("Error fetching hospital list:", error.message);
res.status(500).json({ error: "Internal server error" });
}
};
exports.getHospitalById = async (req, res) => {
try {
const { id } = req.params; // Extract the hospital ID from route parameters
// Query the database to fetch the hospital by ID
const query = "SELECT * FROM hospitals WHERE id = ?";
const result = await db.query(query, [id]);
// Check if the hospital exists
if (result.length === 0) {
return res.status(404).json({ error: "Hospital not found" });
}
res.status(200).json({
message: "Hospital fetched successfully!",
data: result[0], // Return the first (and only) result
});
} catch (error) {
console.error("Error fetching hospital by ID:", error.message);
res.status(500).json({ error: "Internal server error" });
}
};
exports.updateHospital = async (req, res) => {
try {
const { id } = req.params; // Get the hospital ID from the route parameter
const updateData = req.body; // Get the fields to update from the request body
// Fetch the hospital to validate ownership
const hospitalQuery = "SELECT id hospital_code, super_admin_id FROM hospitals WHERE id = ?";
const hospitalResult = await db.query(hospitalQuery, [id]);
const hospitalQueryUsr = "SELECT id FROM hospital_users WHERE hospital_id = ?";
const hospitalResultUsr = await db.query(hospitalQueryUsr, [id]);
// console.log("requested user", req.user);
if (hospitalResult.length === 0) {
return res.status(404).json({ error: "Hospital not found" });
}
console.log('hspt id----', req.user.id)
console.log('db hspt id', hospitalResultUsr[0])
if (req.user.id !== hospitalResultUsr[0].id && req.user.id !== hospitalResult[0].super_admin_id) {
return res
.status(403)
.json({ error: "You can only edit the hospital you have created" });
}
const validColumns = new Set([
"name_hospital",
"primary_admin_password",
"primary_color",
"secondary_color",
"logo_url",
"status",
"onboarding_status",
"admin_name",
"mobile_number",
"location",
"super_admin_id",
]);
// Ensure no extra fields are present
for (const key of Object.keys(updateData)) {
if (!validColumns.has(key)) {
return res.status(400).json({ error: `Invalid field or cannot update: ${key}` });
}
}
// Build the SQL SET clause dynamically
const fields = [];
const values = [];
for (const [key, value] of Object.entries(updateData)) {
fields.push(`${key} = ?`);
values.push(value);
}
values.push(id); // Add the hospital ID to the values array for the WHERE clause
// Construct the SQL query
const query = `UPDATE hospitals SET ${fields.join(", ")} WHERE id = ?`;
const result = await db.query(query, values);
const queryhspt = `SELECT * FROM hospitals WHERE id = ?`;
const resulthspt = await db.query(queryhspt, [id]);
// Check if any row was updated
if (result.affectedRows === 0) {
return res
.status(404)
.json({ error: "Hospital not found or no changes made" });
}
res
.status(200)
.json({ message: "Hospital updated successfully!", data: resulthspt });
} catch (error) {
console.error("Error updating hospital:", error.message);
res.status(500).json({ error: "Internal server error" });
}
};
// exports.updateHospital = async (req, res) => {
// try {
// const { id } = req.params; // Get the hospital ID from the route parameter
// const updateData = req.body; // Get the fields to update from the request body
// // Fetch the hospital to validate ownership
// // const hospitalQuery = "SELECT hospital_code, super_admin_id FROM hospitals WHERE id = ?";
// // const hospitalResult = await db.query(hospitalQuery, [id]);
// // console.log("requested user", req.user);
// // if (hospitalResult.length === 0) {
// // return res.status(404).json({ error: "Hospital not found" });
// // }
// // if (req.user.id !== hospitalResult[0].super_admin_id) {
// // return res
// // .status(403)
// // .json({ error: "You can only edit the hospital you have created" });
// // }
// const validColumns = new Set([
// "name_hospital",
// "subdomain",
// "primary_admin_email",
// "primary_admin_password",
// "primary_color",
// "secondary_color",
// "logo_url",
// "status",
// "onboarding_status",
// "admin_name",
// "mobile_number",
// "location",
// "super_admin_id",
// "hospital_code",
// ]);
// // Ensure no extra fields are present
// for (const key of Object.keys(updateData)) {
// if (!validColumns.has(key)) {
// return res.status(400).json({ error: `Invalid field: ${key}` });
// }
// }
// // Build the SQL SET clause dynamically
// const fields = [];
// const values = [];
// for (const [key, value] of Object.entries(updateData)) {
// fields.push(`${key} = ?`);
// values.push(value);
// }
// values.push(id); // Add the hospital ID to the values array for the WHERE clause
// // Construct the SQL query
// const query = `UPDATE hospitals SET ${fields.join(", ")} WHERE id = ?`;
// const result = await db.query(query, values);
// const queryhspt = `SELECT * FROM hospitals WHERE id = ?`;
// const resulthspt = await db.query(queryhspt, [id]);
// // Check if any row was updated
// if (result.affectedRows === 0) {
// return res
// .status(404)
// .json({ error: "Hospital not found or no changes made" });
// }
// res
// .status(200)
// .json({ message: "Hospital updated successfully!", data: resulthspt });
// } catch (error) {
// console.error("Error updating hospital:", error.message);
// res.status(500).json({ error: "Internal server error" });
// }
// };
exports.deleteHospital = async (req, res) => {
const { id } = req.params;
console.log("user details ", req.user);
const hospitalId = id;
if (!id) {
return res.status(400).json({ error: "hospital ID is required" });
}
// Fetch the hospital to validate ownership
const hospitalQuery = "SELECT hospital_code, super_admin_id FROM hospitals WHERE id = ?";
const hospitalResult = await db.query(hospitalQuery, [id]);
console.log("requested user", req.user);
if (hospitalResult.length === 0) {
return res.status(404).json({ error: "Hospital not found" });
}
if (req.user.id !== hospitalResult[0].super_admin_id) {
return res
.status(403)
.json({ error: "You can only delete the hospital you have created" });
}
// Ensure the authenticated user is either Admin or Superadmin
if (!["Spurrinadmin", 6].includes(req.user.role)) {
return res
.status(403)
.json({ error: "You are not authorized to delete hospitals" });
}
// Check for dependent records
const [qaCount] = await db.query(
"SELECT COUNT(*) AS count FROM questions_answers WHERE document_id IN (SELECT id FROM documents WHERE hospital_id = ?)",
[hospitalId]
);
const [qaPageCount] = await db.query(
"SELECT COUNT(*) AS count FROM document_pages WHERE document_id IN (SELECT id FROM documents WHERE hospital_id = ?)",
[hospitalId]
);
const [metadataCount] = await db.query(
"SELECT COUNT(*) AS count FROM document_metadata WHERE document_id IN (SELECT id FROM documents WHERE hospital_id = ?)",
[hospitalId]
);
const [documentsCount] = await db.query(
"SELECT COUNT(*) AS count FROM documents WHERE hospital_id = ?",
[hospitalId]
);
const [appUsersCount] = await db.query(
"SELECT COUNT(*) AS count FROM app_users WHERE hospital_code = (SELECT hospital_code FROM hospitals WHERE id = ?)",
[hospitalId]
);
console.log('qaCount', qaCount, '\nqaPageCount', qaPageCount, '\nmetadataCount', metadataCount, '\ndocumentsCount', documentsCount, '\nappUsersCount', appUsersCount);
// // If any dependent records exist, block deletion
// if (qaCount.count > 0 || metadataCount.count > 0 || documentsCount.count > 0 || appUsersCount.count > 0 || qaPageCount.count > 0) {
// return res
// .status(403)
// .json({ error: "Can not delete hospital dependent records found" });
// }
try {
// Unlink (delete) the associated file if it exists
// Fetch all documents related to the hospital
const documents = await db.query(
"SELECT id, file_url FROM documents WHERE hospital_id = ?",
[id]
);
console.log("doc ids ", documents);
// Delete document files dynamically
for (const document of documents) {
if (document.file_url) {
const filePath = path.join(
__dirname,
"..",
"uploads",
document.file_url.replace(/^\/uploads\//, "")
);
try {
await fs.promises.access(filePath, fs.constants.F_OK);
await fs.promises.unlink(filePath);
console.log("File deleted successfully:", filePath);
} catch (err) {
console.error(`Error deleting or accessing file ${filePath}: ${err.message}`);
}
}
}
// Delete document-related records first
await db.query(
"DELETE FROM questions_answers WHERE document_id IN (SELECT id FROM documents WHERE hospital_id = ?)",
[id]
);
console.log('Deleted questions_answers successfully');
await db.query(
"DELETE FROM document_metadata WHERE document_id IN (SELECT id FROM documents WHERE hospital_id = ?)",
[id]
);
console.log('Deleted document_metadata successfully');
await db.query(
"DELETE FROM document_pages WHERE document_id IN (SELECT id FROM documents WHERE hospital_id = ?)",
[id]
);
console.log('Deleted document_pages successfully');
await db.query("DELETE FROM onboarding_steps WHERE user_id IN (SELECT id from hospital_users WHERE hospital_code = ?)", [
hospitalResult[0].hospital_code,
]);
console.log('Deleted onboarding steps successfully');
// Now delete the documents themselves
await db.query("DELETE FROM documents WHERE hospital_id = ?", [id]);
console.log('Deleted documents successfully');
// Now delete hospital_users AFTER documents (because of FK uploaded_by)
await db.query("DELETE FROM hospital_users WHERE hospital_code = ?", [
hospitalResult[0].hospital_code,
]);
console.log('Deleted hospital_users successfully');
// Then delete app_users
await db.query("DELETE FROM app_users WHERE hospital_code = ?", [
hospitalResult[0].hospital_code,
]);
console.log('Deleted app users successfully');
// And finally interaction_logs
await db.query("DELETE FROM interaction_logs WHERE hospital_code = ?", [
hospitalResult[0].hospital_code,
]);
console.log('Deleted interaction logs successfully');
} catch (error) {
console.error("Error deleting dependent records:", error.message);
return res.status(500).json({ error: "Failed to delete dependent records" });
}
// Now delete the hospital
const deleteQuery = "DELETE FROM hospitals WHERE id = ?";
const result = await db.query(deleteQuery, [id]);
if (result.affectedRows === 0) {
return res.status(404).json({ message: "hospital not found" });
}
res.status(200).json({ message: "Hospital deleted successfully!" });
};
exports.getAllHospitalUsers = async (req, res) => {
try {
// Fetch all users from the hospital_users table
const hospitalUsers = await db.query(`
SELECT
u.id,
u.hospital_id,
u.email,
u.role_id,
r.name AS role_name,
u.status,
u.created_at,
u.updated_at
FROM
hospital_users u
JOIN
roles r
ON
u.role_id = r.id
`);
res.status(200).json({
message: "Hospital users fetched successfully!",
data: hospitalUsers,
});
} catch (error) {
console.error("Error fetching hospital users:", error.message);
res.status(500).json({ error: "Internal server error" });
}
};
// get colors
exports.getColorsFromHospital = async (req, res) => {
try {
const { id } = req.user; // Get the hospital ID from the route parameter
console.log("requested user", req.user);
// Ensure no extra fields are present
if (!["Superadmin", 7].includes(req.user.role)) {
return res
.status(403)
.json({ error: "You are not authorized to access hospital's colors!!" });
}
const queryhspt_users = `SELECT hospital_id FROM hospital_users WHERE id = ?`;
const resulthspt_users = await db.query(queryhspt_users, [id]);
const queryhspt = `SELECT primary_color, secondary_color FROM hospitals WHERE id = ?`;
const resulthspt = await db.query(queryhspt, [
resulthspt_users[0].hospital_id,
]);
console.log("resulthspt", resulthspt);
if (resulthspt.length === 0) {
return res.status(404).json({ error: "Hospital not found" });
}
// Check if any row was updated
res.status(200).json({
message: "Hospital colors fetched successfully!",
data: resulthspt,
});
} catch (error) {
console.error("Error fetching hospital:", error.message);
res.status(500).json({ error: "Internal server error" });
}
};
// change password of super_admins/hospitals only spurrin have access to do so
exports.changePassword = async (req, res) => {
try {
const id = req.user.id; // Get the user ID from the route parameter
const { new_password } = req.body; // Get the new password from the request body
const authHeader = req.headers.authorization; // Get the token from the Authorization header
// Validate input
if (!new_password) {
return res.status(400).json({ error: "New password is required" });
}
// Ensure the token is present
if (!authHeader || !authHeader.startsWith("Bearer ")) {
return res.status(401).json({ error: "Authorization token is required" });
}
const token = authHeader.split(" ")[1]; // Extract token from "Bearer "
let decodedToken;
try {
decodedToken = jwt.verify(token, process.env.JWT_ACCESS_TOKEN_SECRET); // Decode the token
} catch (err) {
return res.status(401).json({ error: "Invalid or expired token" });
}
// Ensure the decoded token's user ID matches the route parameter
if (parseInt(id, 10) !== decodedToken.id) {
return res
.status(403)
.json({ error: "Token user does not match the requested user" });
}
// Convert ID to integer and validate
const numericId = parseInt(id, 10);
if (isNaN(numericId)) {
return res.status(400).json({ error: "Invalid user ID" });
}
// Fetch the user from the database to ensure they exist
const userQuery = `
SELECT id FROM app_users WHERE id = ?
`;
const [userResult] = await db.query(userQuery, [numericId]);
if (!userResult || userResult.length === 0) {
return res.status(404).json({ error: "User not found" });
}
// Hash the new password
const hashedNewPassword = await bcrypt.hash(new_password, 10);
// Update the password in the database
const updatePasswordQuery = `
UPDATE app_users SET hash_password = ? WHERE id = ?
`;
await db.query(updatePasswordQuery, [hashedNewPassword, numericId]);
res.status(200).json({ message: "Password updated successfully!" });
} catch (error) {
console.error("Error updating password:", error.message, error.stack);
res.status(500).json({ error: "Internal server error" });
}
};
const crypto = require("crypto");
function generateRandomPassword(length = 12) {
return crypto
.randomBytes(Math.ceil(length / 2))
.toString("hex")
.slice(0, length);
}
// Generate and print the password
const randomPassword = generateRandomPassword();
console.log("Generated Password:", randomPassword);
exports.sendTempPassword = 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, primary_admin_email ,name_hospital,admin_name FROM hospitals WHERE primary_admin_email = ?",
[email]
);
if (!user.length) {
return res.status(404).json({ error: "User not found" });
}
const hsptuser = await db.query(
"SELECT id, email FROM hospital_users WHERE email = ?",
[email]
);
if (!hsptuser.length) {
return res.status(404).json({ error: "User not found" });
}
const hsptId = user[0].id;
const hsptUsrId = hsptuser[0].id;
const randomPassword = generateRandomPassword();
const hashedPassword = await bcrypt.hash(randomPassword, 10);
const expiresAt = new Date(Date.now() + 2 * 60 * 60 * 1000); // OTP expires in 1 hour
const type = "temp";
await db.query(
"UPDATE hospitals SET temporary_password = ?, expires_at = ?, type = ? WHERE id = ?",
[randomPassword, expiresAt, type, hsptId]
);
await db.query(
"UPDATE hospital_users SET temporary_password = ?, expires_at = ?, type = ? WHERE id = ?",
[randomPassword, expiresAt, type, hsptUsrId]
);
// // // Send OTP via email
const info = await sendMail(
email,
user[0].name_hospital,
user[0].admin_name,
randomPassword
);
res.json({ message: "temporary password generated successfully",email_status: info.response });
} catch (error) {
console.error("Error sending OTP:", error);
res.status(500).json({ error: "Internal server error" });
}
};
exports.changeTempPassword = async (req, res) => {
try {
const { email, temp_password, new_password } = req.body;
// Validate inputs
if (!email || !temp_password || !new_password) {
return res.status(400).json({
error: "Email, Temporary password, and new password are required",
});
}
const user = await db.query(
"SELECT id, temporary_password, expires_at, type FROM hospitals WHERE primary_admin_email = ?",
[email]
);
if (!user.length) {
return res.status(404).json({ error: "User not found" });
}
const hsptuser = await db.query(
"SELECT id, temporary_password, expires_at, type FROM hospital_users WHERE email = ?",
[email]
);
if (!hsptuser.length) {
return res.status(404).json({ error: "User not found" });
}
const isMatch = temp_password === user[0].temporary_password;
// check the password match
if (!isMatch) {
return res.status(400).json({ error: "Invalid temporary password" });
}
// Check if temporary password is expired
if (new Date() > new Date(user[0].expires_at)) {
return res
.status(400)
.json({ error: "temporary password 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 hospitals SET primary_admin_password = ?, expires_at = ? ,type = NULL, temporary_password = NULL WHERE id = ?",
[hashedPassword, new Date(Date.now()), user[0].id]
);
await db.query(
"UPDATE hospital_users SET hash_password = ?, expires_at = ? ,type = NULL, temporary_password = NULL WHERE id = ?",
[hashedPassword, new Date(Date.now()), hsptuser[0].id]
);
res.json({ message: "Password changed successfully!" });
} catch (error) {
console.error("Error resetting password:", error);
res.status(500).json({ error: "Internal server error" });
}
};
async function sendMail(email, hospital_name, adminName, randomPassword) {
const mailOptions = {
from: "kavya.j@tech4biz.io", // Sender's email
to: email, // Recipient's email
subject: "Spurrinai temporary password", // Email subject
html: `
Reset Your Password - Spurrinai Medical Platform
Hello ${adminName},
We received a request to reset the password for your account on the Spurrinai healthcare platform. For your security reasons, please verify this action.
Your temporary password:
${randomPassword}
use same password to generate new password
Copy 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);
return info; // Return the info object for further processing if needed
} catch (error) {
console.error(`Error sending email to ${email}:`, error);
return error
}
}
exports.updateHospitalName = async (req, res) => {
console.log("requested user", req.user);
const hospital_user_id = req.user.id;
const { hospital_name } = req.body;
// Ensure the authenticated user is either Admin or Superadmin
if (!["Superadmin", 7].includes(req.user.role)) {
return res
.status(403)
.json({ error: "You are not authorized to update admins name" });
}
const queryuser = 'SELECT hospital_id FROM hospital_users WHERE id = ?';
const [rows] = await db.execute(queryuser, [hospital_user_id]);
console.log(rows[0]?.hospital_id);
const query = "UPDATE hospital_name SET name = ? WHERE id = ?";
const values = [hospital_name, rows[0]?.hospital_id];
await db.query(query, values);
res.json({ message: "Name changed successfully!" });
}
// password change for admins and viewers
exports.sendTemporaryPassword = async (req, res) => {
try {
const { email } = req.body;
// Validate email input
if (!email) {
return res.status(400).json({ error: "Email is required" });
}
const hsptuser = await db.query(
"SELECT id,hospital_id, hash_password, name, expires_at, type FROM hospital_users WHERE email = ? AND role_id IN (8, 9)",
[email]
);
if (!hsptuser.length) {
return res.status(404).json({ error: "User not found" });
}
const hsptUsrId = hsptuser[0].id;
const hsptId = hsptuser[0].hospital_id;
const randomPassword = generateRandomPassword();
const hashedPassword = await bcrypt.hash(randomPassword, 10);
const expiresAt = new Date(Date.now() + 2 * 60 * 60 * 1000); // OTP expires in 1 hour
const type = "temp";
await db.query(
"UPDATE hospital_users SET hash_password = ?, expires_at = ?, type = ? WHERE id = ?",
[hashedPassword, expiresAt, type, hsptUsrId]
);
const hspt = await db.query(
"SELECT name_hospital FROM hospitals WHERE id = ?",
[hsptId]
);
// // // Send OTP via email
const info =await sendMail(
email,
hspt[0].name_hospital,
hsptuser[0].name,
randomPassword
);
res.json({ message: "temporary password gerated successfully",email_status: info.response });
} catch (error) {
console.error("Error sending OTP:", error);
res.status(500).json({ error: "Internal server error" });
}
};
exports.changeTempPasswordAdminsViewers = async (req, res) => {
try {
const { email, temp_password, new_password } = req.body;
console.log("incoming data ", req.body);
// Validate inputs
if (!email || !temp_password || !new_password) {
return res.status(400).json({
error: "Email, Temporary password, and new password are required",
});
}
// Ensure the user has the proper role
const hsptuser = await db.query(
"SELECT id, temporary_password, expires_at, type FROM hospital_users WHERE email = ? AND role_id IN (8, 9)",
[email]
);
if (!hsptuser.length) {
return res.status(404).json({ error: "Email not found" });
}
const isMatch = await bcrypt.compare(
temp_password,
hsptuser[0].temporary_password
);
// Check if temporary password matches
if (!isMatch) {
return res.status(400).json({ error: "Invalid temporary password" });
}
// Check if temporary password is expired
if (new Date() > new Date(hsptuser[0].expires_at)) {
return res
.status(400)
.json({ error: "temporary password 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 hospital_users SET hash_password = ?, expires_at = ? ,type = NULL, temporary_password = NULL WHERE id = ?",
[hashedPassword, new Date(Date.now()), hsptuser[0].id]
);
res.json({ message: "Password changed successfully!" });
} catch (error) {
console.error("Error resetting password:", error);
res.status(500).json({ error: "Internal server error" });
}
};
// check app user notification
exports.checkNewAppUser = async (req, res) => {
if (!["Superadmin", "Admin", 7, 8].includes(req.user.role)) {
return res
.status(403)
.json({ error: "You are not authorized" });
}
try {
const { hospital_code } = req.body;
// Validate inputs
if (!hospital_code) {
return res.status(400).json({ error: "hospital code is required" });
}
// notify hospital only if notification is not checked
const appUser = await db.query(
"SELECT * FROM app_users WHERE hospital_code = ? AND checked = 0",
[hospital_code]
);
if (!appUser.length) {
return res.status(404).json({ error: "No new user found" });
}
res.json({ message: "new notification found", appUser });
} catch (error) {
console.error("Error checking new notification:", error);
res.status(500).json({ error: "Internal server error" });
}
};
exports.updateAppUserChecked = async (req, res) => {
if (!["Superadmin", "Admin", 7, 8].includes(req.user.role)) {
return res
.status(403)
.json({ error: "You are not authorized" });
}
try {
const id = req.params.id;
if (!id) {
return res.status(400).json({ error: "User ID is required" });
}
const result = await db.query("UPDATE app_users SET checked = 1 WHERE id = ?", [id]);
if (result.affectedRows === 0) {
return res.status(404).json({ error: "User not found or already checked" });
}
res.json({ message: "User checked status updated successfully", updatedUserId: id });
} catch (error) {
console.error("Error updating checked status:", error);
res.status(500).json({ error: "Internal server error" });
}
};
exports.interactionLogs = async (req, res) => {
const { hospital_code, app_user_id } = req.body;
if (!["Superadmin", 7].includes(req.user.role)) {
return res.status(403).json({ error: "You are not authorized" });
}
try {
// Validate inputs
if (!hospital_code && !app_user_id) {
return res
.status(400)
.json({ error: "hospital code or app user id is required" });
}
let intLogs;
// Build dynamic query based on inputs
let baseQuery = `
SELECT il.*, au.email, au.username
FROM interaction_logs il
LEFT JOIN app_users au ON il.app_user_id = au.id
WHERE 1=1
`;
const params = [];
if (hospital_code) {
baseQuery += ` AND il.hospital_code = ?`;
params.push(hospital_code);
}
if (app_user_id) {
baseQuery += ` AND il.app_user_id = ?`;
params.push(app_user_id);
}
intLogs = await db.query(baseQuery, params);
if (!intLogs.length) {
return res.status(200).json({ error: "No logs found" });
}
res.json({ message: "log data found", intLogs });
} catch (error) {
console.error("Error fetching logs:", error);
res.status(500).json({ error: "Internal server error" });
}
};
exports.updatePublicSignup = async (req, res) => {
try {
const { id } = req.params;
const { enabled } = req.body;
// Validate input
if (typeof enabled !== 'boolean') {
return res.status(400).json({
error: "Invalid input. 'enabled' must be a boolean value"
});
}
console.log("req user-----", req.user)
// Check if user has permission
if (!["Spurrinadmin", "Superadmin", 7, 6].includes(req.user.role)) {
return res.status(403).json({
error: "You are not authorized to update public signup settings"
});
}
// If user is Superadmin, verify they own the hospital
if (req.user.role === "Superadmin") {
const hospital = await db.query(
"SELECT id FROM hospitals WHERE id = ?",
[id]
);
if (!hospital.length) {
return res.status(403).json({
error: "hospital not found"
});
}
if (id != req.user.hospital_id) {
return res.status(403).json({
message: "You can only update public signup settings for your own hospital"
});
}
}
const result = await db.query(
'UPDATE hospitals SET publicSignupEnabled = ? WHERE id = ?',
[enabled, id]
);
if (result.affectedRows > 0) {
res.status(200).json({
status: 'success',
message: 'Hospital signup settings updated successfully.',
data: {
id,
publicSignupEnabled: enabled
}
});
}
else {
res.status(404).json({
status: 'error',
message: 'Hospital not found or no changes made.',
data: null
});
}
} catch (error) {
console.error("Error updating public signup setting:", error);
if (error instanceof ValidationError) {
res.status(400).json({ error: error.message });
} else {
res.status(500).json({ error: "Internal server error" });
}
}
};
exports.getPublicSignup = async (req, res) => {
try {
const { id } = req.params;
// Check if user has permission
if (!["Spurrinadmin", "Superadmin", 7, 6].includes(req.user.role)) {
return res.status(403).json({
error: "You are not authorized to update public signup settings"
});
}
// If user is Superadmin, verify they own the hospital
if (req.user.role === "Superadmin") {
const hospital = await db.query(
"SELECT id FROM hospitals WHERE id = ?",
[id]
);
if (!hospital.length) {
return res.status(403).json({
error: "hospital not found"
});
}
if (id != req.user.hospital_id) {
return res.status(403).json({
message: "You can only get public signup settings for your own hospital"
});
}
}
const result = await db.query(
'SELECT publicSignupEnabled from hospitals WHERE id = ?',
[id]
);
console.log("result----",result)
if (result.length > 0) {
res.status(200).json({
message: 'data fetched successfully.',
result
});
}
else {
res.status(200).json({
status: 'Not found',
message: 'Hospital not found or no changes made.',
});
}
} catch (error) {
console.error("Error updating public signup setting:", error);
if (error instanceof ValidationError) {
res.status(400).json({ error: error.message });
} else {
res.status(500).json({ error: "Internal server error" });
}
}
};