const express = require('express'); const bcrypt = require('bcryptjs'); const jwt = require('jsonwebtoken'); const cors = require('cors'); const bodyParser = require('body-parser'); const app = express(); const PORT = 8080; const JWT_SECRET = 'your-secret-key'; // In production, use env var app.use(cors()); app.use(bodyParser.json()); // In-memory user store (for demo purposes) const users = []; // Password validation regex const passwordRegex = /^(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/; // Middleware to verify token function authenticateToken(req, res, next) { const authHeader = req.headers['authorization']; const token = authHeader && authHeader.split(' ')[1]; if (!token) return res.status(401).json({ error: 'Access token required' }); jwt.verify(token, JWT_SECRET, (err, user) => { if (err) return res.status(403).json({ error: 'Invalid token' }); req.user = user; next(); }); } // Auth routes app.post('/api/auth/register', async (req, res) => { const { email, username, password } = req.body; if (!email || !username || !password) { return res.status(400).json({ error: 'All fields are required' }); } // Check if password meets requirements if (!passwordRegex.test(password)) { return res.status(400).json({ error: 'Password must be at least 8 characters and include at least one uppercase letter, one number, and one symbol' }); } // Check if user already exists const existingUser = users.find(u => u.email === email || u.username === username); if (existingUser) { return res.status(400).json({ error: 'User already exists' }); } try { const passwordHash = await bcrypt.hash(password, 10); const user = { id: users.length + 1, email, username, passwordHash }; users.push(user); const token = jwt.sign({ id: user.id, email: user.email, username: user.username }, JWT_SECRET); res.json({ token, user: { id: user.id, email: user.email, username: user.username } }); } catch (error) { res.status(500).json({ error: 'Registration failed' }); } }); app.post('/api/auth/login', async (req, res) => { const { login, password } = req.body; // login can be email or username if (!login || !password) { return res.status(400).json({ error: 'Username and password are required' }); } const user = users.find(u => u.email === login || u.username === login); if (!user) { return res.status(401).json({ error: 'Invalid credentials' }); } try { const isValid = await bcrypt.compare(password, user.passwordHash); if (!isValid) { return res.status(401).json({ error: 'Invalid credentials' }); } const token = jwt.sign({ id: user.id, email: user.email, username: user.username }, JWT_SECRET); res.json({ token, user: { id: user.id, email: user.email, username: user.username } }); } catch (error) { res.status(500).json({ error: 'Login failed' }); } }); app.get('/api/auth/profile', authenticateToken, (req, res) => { res.json(req.user); }); // Stub routes for ebooks (to avoid errors) app.get('/api/ebooks', authenticateToken, (req, res) => { res.json([]); }); app.get('/api/ebooks/:id', authenticateToken, (req, res) => { res.json({ id: req.params.id, title: 'Stub' }); }); app.listen(PORT, () => { console.log(`Server running on http://localhost:${PORT}`); });