This commit is contained in:
Ubuntu 2025-07-15 11:30:19 +05:30
parent fe45696573
commit 7c8185b8f3
10 changed files with 9877 additions and 110 deletions

34
chat.py
View File

@ -271,7 +271,7 @@ async def get_hospital_id(hospital_code):
CHUNK_SIZE = 4000
CHUNK_OVERLAP = 150
BATCH_SIZE = 1000
BATCH_SIZE = 250
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=CHUNK_SIZE,
@ -1681,6 +1681,11 @@ async def generate_answer_with_rag(
question, user_id, hospital_id, conversation_manager
)
# Check for table requests in both original question and contextual query
is_original_table_request = is_table_request(question)
is_contextual_table_request = is_table_request(contextual_query) if contextual_query != question else False
is_any_table_request = is_original_table_request or is_contextual_table_request
# Retrieve document context with strict relevance
doc_context = await get_relevant_context(contextual_query, hospital_id, doc_id)
@ -1737,17 +1742,6 @@ async def generate_answer_with_rag(
)
return {"answer": answer}, 200
# Check if question lacks relevant context
if not context or is_general_knowledge_question(question, context, conv_history):
logging.info("B")
logging.info("No relevant context or general knowledge question detected")
answer = "<p>No relevant information found in the hospital documents for this query.</p>"
if conversation_manager:
await conversation_manager.add_rag_interaction(
user_id, hospital_id, question, answer, session_id
)
return {"answer": answer}, 200
# Check follow-up status with stricter criteria
is_follow_up = False
if conv_history:
@ -1816,6 +1810,18 @@ async def generate_answer_with_rag(
logging.info(f"- SpaCy similarity: {similarity:.2f}")
logging.info(f"- Is follow-up: {is_follow_up}")
# Check if question lacks relevant context
if not is_follow_up:
if not context or is_general_knowledge_question(question, context, conv_history):
logging.info("B")
logging.info("No relevant context or general knowledge question detected")
answer = "<p>No relevant information found in the hospital documents for this query.</p>"
if conversation_manager:
await conversation_manager.add_rag_interaction(
user_id, hospital_id, question, answer, session_id
)
return {"answer": answer}, 200
# Generate answer with strict document-based instruction
prompt_template = f"""You are a document-based question-answering system. You must ONLY use the provided context and conversation history to answer the question. Do NOT use any external knowledge, assumptions, or definitions beyond the given context, even if the query seems familiar. If the context does not contain sufficient information to directly answer the question, respond ONLY with:
<p>No relevant information found in the hospital documents for this query.</p>
@ -1949,7 +1955,9 @@ def async_to_sync(coroutine):
loop.close()
@app.route("/flask-api", methods=["GET"])
@app.route("/flask-api/", methods=["GET"])
def health_check():
"""Health check endpoint"""
access_logger.info(f"Health check request received from {request.remote_addr}")

9587
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -25,6 +25,7 @@
"dependencies": {
"axios": "^1.7.9",
"bcrypt": "^5.1.1",
"child_process": "^1.0.2",
"compression": "^1.7.4",
"compromise": "^14.14.4",
"cors": "^2.8.5",
@ -44,6 +45,7 @@
"nodemailer": "^6.10.0",
"number-to-words": "^1.2.4",
"path": "^0.12.7",
"pm2": "^6.0.8",
"socket.io": "^4.8.1",
"stopword": "^3.1.4",
"string-similarity": "^4.0.4",

View File

@ -5,6 +5,8 @@ const path = require('path');
const dotenv = require('dotenv');
const helmet = require('helmet');
const initializeDatabase = require('./config/initDatabase');
const pm2 = require('pm2');
const fs = require('fs');
// Load environment variables
dotenv.config();
@ -127,6 +129,93 @@ async function startServer() {
res.send("SpurrinAI Backend is running!");
});
app.get('/logs', (req, res) => {
pm2.connect(err => {
if (err) {
return res.status(500).send(generateErrorPage('Failed to connect to PM2', err.message));
}
pm2.list((err, processes) => {
if (err) {
return res.status(500).send(generateErrorPage('Failed to list processes', err.message));
}
const logs = processes.map(proc => {
const logFilePath = path.join(process.env.HOME, '.pm2/logs', `${proc.name}-out.log`);
const errorFilePath = path.join(process.env.HOME, '.pm2/logs', `${proc.name}-error.log`);
const stdoutSize = fs.existsSync(logFilePath) ? fs.statSync(logFilePath).size : 0;
const stderrSize = fs.existsSync(errorFilePath) ? fs.statSync(errorFilePath).size : 0;
const maxSize = 10 * 1024 * 1024; // 10MB max size for progress bar
return {
name: proc.name,
logs: {
stdout: fs.existsSync(logFilePath) ? fs.readFileSync(logFilePath, 'utf8') : 'No logs available',
stderr: fs.existsSync(errorFilePath) ? fs.readFileSync(errorFilePath, 'utf8') : 'No error logs available',
stdoutProgress: Math.min((stdoutSize / maxSize) * 100, 100),
stderrProgress: Math.min((stderrSize / maxSize) * 100, 100)
}
};
});
const logsHtml = logs.map(log => `
<div class="log-container">
<h2>${log.name}</h2>
<div class="stdout">
<h3>Standard Output</h3>
<progress value="${log.logs.stdoutProgress}" max="100"></progress>
<pre>${log.logs.stdout}</pre>
</div>
<div class="stderr">
<h3>Error Output</h3>
<progress value="${log.logs.stderrProgress}" max="100"></progress>
<pre>${log.logs.stderr}</pre>
</div>
</div>
`).join('');
res.send(`
<html>
<head>
<title>PM2 Logs</title>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
h1 { color: #333; }
.log-container { margin-bottom: 20px; }
.log-container h2 { margin: 0; font-size: 1.2em; color: #444; }
.log-container pre { background: #f4f4f4; padding: 10px; border-radius: 5px; white-space: pre-wrap; word-wrap: break-word; }
.log-container progress { width: 100%; height: 20px; margin-bottom: 10px; }
.log-container .stdout { border-left: 5px solid #4CAF50; }
.log-container .stderr { border-left: 5px solid #f44336; }
</style>
</head>
<body>
<h1>PM2 Logs</h1>
${logsHtml}
</body>
</html>
`);
pm2.disconnect();
});
});
});
function generateErrorPage(title, message) {
return `
<html>
<head><title>${title}</title></head>
<body>
<h1>${title}</h1>
<p>${message}</p>
</body>
</html>
`;
}
console.log('checking automation!');
console.log('checking automation!');
console.log('checking automation!');

View File

@ -25,6 +25,8 @@ const config = {
'http://localhost:8081',
'http://testhospital.localhost:5174',
'http://testhospitaltwo.localhost:5174',
"https://spurrinai.info",
"http://spurrinai.info"
]
}
},

View File

@ -247,6 +247,10 @@ exports.signup = async (req, res) => {
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);
@ -267,10 +271,7 @@ exports.signup = async (req, res) => {
const userId = result.insertId; // Get the new user's ID
// Check if a file was uploaded
if (!req.file) {
return res.status(400).json({ error: "ID photo is required" });
}
const filePath = `/uploads/id_photos/${req.file.filename}`; // Correct file path
@ -341,15 +342,15 @@ exports.login = async (req, res) => {
user.status = "Active"
}
// if (user.status === "Pending" && !hospitalData[0].publicSignupEnabled) {
// return res.status(404).json({ message: "Your Account is Under Review" });
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({ message: "Contact admin your account is not approved" });
if (user.status === "Inactive" && !hospitalData[0].publicSignupEnabled) {
return res.status(404).json({ error: "Contact admin your account is not approved" });
// }
}
// Validate the password
@ -675,13 +676,10 @@ exports.deleteAppUser = async (req, res) => {
const { id } = req.params;
const { hospital_code } = req.user;
// Check if the user exists
// const [users] = await db.query('SELECT * FROM app_users WHERE id = ?', [id]); // ✅ Properly handle array
// if (!users || users.length === 0) {
// return res.status(404).json({ error: 'User not found' });
// }
const appUserquery = "SELECT * FROM app_users WHERE id = ?";
console.log('req.user------------------------------------------------------------------',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) {
@ -691,12 +689,10 @@ exports.deleteAppUser = async (req, res) => {
}
// Ensure only Admin or Superadmin can delete users
if (!["Admin", "Superadmin", 8, 7].includes(req.user.role)) {
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 = ?";
@ -709,23 +705,34 @@ exports.deleteAppUser = async (req, res) => {
.json({ error: "User does not belong to your hospital" });
}
// Unlink (delete) the associated file if it exists
const filePath = path.join(__dirname, '..','..', 'uploads', result[0].id_photo_url.replace(/^\/uploads\//, ''));
// 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) => {
fs.access(filePath, fs.constants.F_OK, (err) => {
if (err) {
console.error('Error deleting file:', err.message);
} else {
console.log('File deleted successfully:', filePath);
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,

View File

@ -409,3 +409,38 @@ exports.getForwardedFeedbacks = async (req, res) => {
res.status(500).json({ error: "Internal server error" });
}
};
exports.deleteAppUserFeedback = async (req, res) => {
try {
const feedbackId = req.params.id;
const user = req.user; // From auth middleware
// Validate that the feedback ID is provided
if (!feedbackId) {
return res.status(400).json({
error: 'Feedback ID is required',
});
}
if (
user.role !== 'Spurrinadmin' &&
user.role !== 6 &&
user.role !== 'Superadmin' &&
user.role !== 7
) {
return res.status(403).json({
error: 'You are not authorized!',
});
}
// Delete the feedback
await db.query('DELETE FROM feedback WHERE feedback_id = ?', [feedbackId]);
res.status(200).json({
message: 'Feedback deleted successfully',
});
} catch (error) {
console.error('Error deleting app user feedback:', error);
res.status(500).json({ error: 'Internal server error' });
}
};

View File

@ -491,10 +491,21 @@ exports.uploadLogo = (req, res) => {
};
exports.getHospitalList = async (req, res) => {
if (!["Spurrinadmin", 6].includes(req.user.role)) {
return res
.status(403)
.json({ error: "You are not authorized!" });
}
try {
// Ensure we are filtering by the authenticated Spurrinadmin
const superAdminId = req.user.id; // Extract authenticated Spurrinadmin ID
const query = "SELECT * FROM hospitals";
// Query to get all hospitals along with their associated super_admin_email
const query = `
SELECT h.*, sa.email AS super_admin_email
FROM hospitals h
LEFT JOIN super_admins sa ON h.super_admin_id = sa.id
`;
const hospitals = await db.query(query);
res.status(200).json({
@ -507,6 +518,7 @@ exports.getHospitalList = async (req, res) => {
}
};
exports.getHospitalById = async (req, res) => {
try {
const { id } = req.params; // Extract the hospital ID from route parameters

View File

@ -548,83 +548,106 @@ exports.editHospitalUser = async (req, res) => {
};
exports.deleteHospitalUser = async (req, res) => {
const { id } = req.params;
const requestorRole = req.user.role;
if (!id) {
return res.status(400).json({ error: 'User ID is required' });
}
console.log("user id------", id)
const hspt_user = await db.query(
"SELECT role_id FROM hospital_users WHERE id= ?",
[id]
);
console.log("hspt_user", hspt_user)
if (hspt_user.length === 0) {
return res.status(404).json({ error: 'Hospital user not found' });
}
const userRole = hspt_user[0].role_id;
if(userRole ==7 && requestorRole == 8){
return res.status(403).json({ error: 'Access denied. You cannot delete super admin' });
}
// Step 1: Validate the role of the requestor
if (!['Superadmin', 'Admin', 8, 7].includes(requestorRole)) {
return res.status(403).json({ error: 'Access denied. Only Superadmin and Admin can delete users.' });
}
try {
const { id } = req.params;
const requestorRole = req.user.role;
const hospitalId = req.user.hospital_id;
const requestorHospitalId = req.user.hospital_id; // Extracted from the authenticated user's token
// const { hospital_id, role_id, ...rest } = req.body;
console.log("user data,", req.user)
if (!id) {
return res.status(400).json({ error: 'User ID is required' });
}
// Step 1: Validate the role of the requestor
if (!['Superadmin', 'Admin', 8, 7].includes(requestorRole)) {
return res.status(403).json({ error: 'Access denied. Only Superadmin and Admin can delete users.' });
}
// Step 2: Validate the hospital_id
// if (String(hospital_id).toString().trim() !== String(requestorHospitalId).toString().trim()) {
// return res.status(403).json({
// error: 'Access denied. You can only delete members of your hospital.',
// });
// }
// 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 uploaded_by = ?)",
// 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 uploaded_by= ?",
[id]
);
const [qaPageCount] = await db.query(
"SELECT COUNT(*) AS count FROM document_pages WHERE document_id IN (SELECT id FROM documents WHERE uploaded_by = ?)",
[id]
);
const [metadataCount] = await db.query(
"SELECT COUNT(*) AS count FROM document_metadata WHERE document_id IN (SELECT id FROM documents WHERE uploaded_by = ?)",
[id]
);
const [documentsCount] = await db.query(
"SELECT COUNT(*) AS count FROM documents WHERE uploaded_by = ?",
[id]
);
// Delete document files dynamically
for (const document of documents) {
if (document.file_url) {
const filePath = path.join(
__dirname,
"..",
"uploads",
document.file_url.replace(/^\/uploads\//, "")
);
// point to be discussed or resolved
// 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 || qaPageCount.count > 0) {
return res
.status(403)
.json({ error: "Can not delete hospital dependent records found" });
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 uploaded_by = ?)",
[id]
);
console.log('Deleted questions_answers successfully');
await db.query(
"DELETE FROM document_metadata WHERE document_id IN (SELECT id FROM documents WHERE uploaded_by = ?)",
[id]
);
console.log('Deleted document_metadata successfully');
// then delete hospital user
const query = `DELETE FROM hospital_users WHERE id = ?`;
const result = await db.query(query, [id]);
await db.query(
"DELETE FROM document_pages WHERE document_id IN (SELECT id FROM documents WHERE uploaded_by = ?)",
[id]
);
console.log('Deleted document_pages successfully');
if (result.affectedRows === 0) {
return res.status(404).json({ error: 'Hospital user not found' });
}
// 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 uploaded_by = ?", [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 id = ?", [
id,
]);
console.log('Deleted hospital_user successfully');
return res.status(200).json({ message: "Hospital user deleted successfully" });
res.status(200).json({ message: 'Hospital user deleted successfully' });
} catch (error) {
console.error('Error deleting hospital user:', error.message);
res.status(500).json({ error: 'Internal server error' });
console.error("Error deleting dependent records:", error.message);
return res.status(500).json({ error: "Failed to delete dependent records" });
}
};
exports.getAccessToken = async (req, res) => {
const { refreshToken, user_id } = req.body;

View File

@ -20,4 +20,6 @@ router.post('/hospital/forward',upload.none(), authenticateToken, feedbacksContr
router.get('/admin/all', authenticateToken, feedbacksController.getAllFeedbacks);
router.get('/get-forwarded-feedbacks',authenticateToken,feedbacksController.getForwardedFeedbacks)
router.delete('/:id',authenticateToken,feedbacksController.deleteAppUserFeedback);
module.exports = router;