// server.js (Node.js backend) const express = require('express'); const multer = require('multer'); const cors = require('cors'); const fs = require('fs'); const path = require('path'); const bcrypt = require('bcrypt'); const jwt = require('jsonwebtoken'); const app = express(); const port = process.env.PORT || 3000; const secretKey = 'your-secret-key'; // Replace with a strong, randomly generated secret key const usersFilePath = 'users.json'; // File to store user data // --- Middleware --- app.use(cors({ origin: 'http://localhost:8080' // Replace with your frontend's origin (e.g., your domain) })); // Enable CORS for your frontend origin app.use(express.json()); app.use(express.static('public')); // --- File Upload Configuration --- const storage = multer.diskStorage({ destination: (req, file, cb) => { const uploadPath = 'uploads/'; fs.mkdirSync(uploadPath, { recursive: true }); cb(null, uploadPath); }, filename: (req, file, cb) => { const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9); const ext = path.extname(file.originalname); cb(null, file.fieldname + '-' + uniqueSuffix + ext); } }); const upload = multer({ storage: storage }); // --- Load Users from File (Database Simulation) --- let users = []; function loadUsers() { try { const data = fs.readFileSync(usersFilePath, 'utf8'); users = JSON.parse(data); } catch (error) { console.log('No existing users file found or error loading users. Starting with an empty user list.'); users = []; // Start with an empty array if the file doesn't exist or has an error } } function saveUsers() { try { fs.writeFileSync(usersFilePath, JSON.stringify(users, null, 2), 'utf8'); // Pretty print the JSON console.log('Users saved to file.'); } catch (error) { console.error('Error saving users to file:', error); } } loadUsers(); // Load users when the server starts // --- Helper Functions --- function generateToken(user) { const payload = { userId: user.id, email: user.email, name: user.name }; const options = { expiresIn: '1h' }; return jwt.sign(payload, secretKey, options); } // --- API Endpoints --- // 1. Registration app.post('/register', async (req, res) => { try { const { name, email, password, age, medicalHistory } = req.body; // Input validation (enhanced) if (!name || name.length < 2) { return res.status(400).json({ message: 'Name must be at least 2 characters long.' }); } if (!email || !email.includes('@')) { return res.status(400).json({ message: 'Invalid email format.' }); } if (!password || password.length < 6) { return res.status(400).json({ message: 'Password must be at least 6 characters long.' }); } // Check if the email is already registered if (users.find(user => user.email === email)) { return res.status(400).json({ message: 'Email already exists.' }); } // Hash the password const hashedPassword = await bcrypt.hash(password, 10); // Create a new user object const newUser = { id: users.length > 0 ? users[users.length - 1].id + 1 : 1, // Simple ID generation name, email, password: hashedPassword, age, medicalHistory, analysisHistory: [] // Initialize analysis history }; // Add the new user to the users array users.push(newUser); saveUsers(); // Save the updated user list to the file res.status(201).json({ message: 'User registered successfully.' }); } catch (error) { console.error('Registration error:', error); res.status(500).json({ message: 'Registration failed. Please try again later.' }); } }); // 2. Login app.post('/login', async (req, res) => { try { const { email, password } = req.body; // Find the user by email const user = users.find(user => user.email === email); // Check if the user exists if (!user) { return res.status(401).json({ message: 'Invalid credentials.' }); } // Compare the provided password with the hashed password const passwordMatch = await bcrypt.compare(password, user.password); // Check if the passwords match if (!passwordMatch) { return res.status(401).json({ message: 'Invalid credentials.' }); } // Generate a JWT const token = generateToken(user); // Respond with the token res.status(200).json({ token: token, message: 'Login successful' }); } catch (error) { console.error('Login error:', error); res.status(500).json({ message: 'Login failed.' }); } }); // 3. Authentication Middleware function authenticateToken(req, res, next) { const authHeader = req.headers['authorization']; const token = authHeader && authHeader.split(' ')[1]; if (!token) { return res.status(401).json({ message: 'Unauthorized: No token provided.' }); } jwt.verify(token, secretKey, (err, user) => { if (err) { console.error('Token verification error:', err); return res.status(403).json({ message: 'Forbidden: Invalid token.' }); } req.user = user; next(); }); } // 4. Get User Data (Protected Route) app.get('/user', authenticateToken, (req, res) => { try { const userId = req.user.userId; const user = users.find(user => user.id === userId); if (!user) { return res.status(404).json({ message: 'User not found.' }); } // Return user data (excluding the password) const userData = { name: user.name, email: user.email, age: user.age, medicalHistory: user.medicalHistory, analysisHistory: user.analysisHistory || [] // Get analysis history }; res.status(200).json(userData); } catch (error) { console.error('Error fetching user data:', error); res.status(500).json({ message: 'Failed to fetch user data.' }); } }); // 5. Image Analysis app.post('/analyze', authenticateToken, upload.single('image'), async (req, res) => { try { if (!req.file) { return res.status(400).json({ message: 'No image uploaded.' }); } const imagePath = req.file.path; const userId = req.user.userId; // --- Placeholder for Model Prediction --- const glaucomaProbability = Math.random(); const otherDiseases = { 'Diabetic Retinopathy': Math.random(), 'Macular Degeneration': Math.random() }; const analysisDescription = glaucomaProbability > 0.7 ? 'Possible signs of glaucoma detected. Consult an ophthalmologist.' : 'No significant signs of glaucoma detected.'; // --- Store Analysis History --- const userIndex = users.findIndex(user => user.id === userId); if (userIndex !== -1) { users[userIndex].analysisHistory.push({ date: new Date(), imagePath: imagePath, glaucoma_probability: glaucomaProbability, analysis_description: analysisDescription, other_diseases: otherDiseases }); saveUsers(); // Save the updated user list to the file } else { console.warn(`User with ID ${userId} not found for analysis history update.`); } // --- Respond with the analysis results --- res.status(200).json({ glaucoma_probability: glaucomaProbability, analysis_description: analysisDescription, other_diseases: otherDiseases, image_path: imagePath // Send the image path back to the client }); } catch (error) { console.error('Analysis error:', error); res.status(500).json({ message: 'Image analysis failed. Please try again later.' }); } }); // 6. Contact Form (Placeholder) app.post('/contact', (req, res) => { const { name, email, message } = req.body; if (!name || !email || !message) { return res.status(400).json({ message: 'All fields are required.' }); } console.log('Contact form submission:', { name, email, message }); res.status(200).json({ message: 'Your message has been sent (simulated).' }); }); // --- Start the Server --- app.listen(port, () => { console.log(`Server listening on port ${port}`); });