// User Management Routes (Admin only) const express = require('express'); const bcrypt = require('bcryptjs'); function createUsersRouter(db, requireAuth, requireRole, logAudit) { const router = express.Router(); // All routes require admin role router.use(requireAuth(db), requireRole('admin')); // Get all users router.get('/', async (req, res) => { try { const users = await new Promise((resolve, reject) => { db.all( `SELECT id, username, email, role, is_active, created_at, last_login FROM users ORDER BY created_at DESC`, (err, rows) => { if (err) reject(err); else resolve(rows); } ); }); res.json(users); } catch (err) { console.error('Get users error:', err); res.status(500).json({ error: 'Failed to fetch users' }); } }); // Get single user router.get('/:id', async (req, res) => { try { const user = await new Promise((resolve, reject) => { db.get( `SELECT id, username, email, role, is_active, created_at, last_login FROM users WHERE id = ?`, [req.params.id], (err, row) => { if (err) reject(err); else resolve(row); } ); }); if (!user) { return res.status(404).json({ error: 'User not found' }); } res.json(user); } catch (err) { console.error('Get user error:', err); res.status(500).json({ error: 'Failed to fetch user' }); } }); // Create new user router.post('/', async (req, res) => { const { username, email, password, role } = req.body; if (!username || !email || !password) { return res.status(400).json({ error: 'Username, email, and password are required' }); } if (role && !['admin', 'editor', 'viewer'].includes(role)) { return res.status(400).json({ error: 'Invalid role. Must be admin, editor, or viewer' }); } try { const passwordHash = await bcrypt.hash(password, 10); const result = await new Promise((resolve, reject) => { db.run( `INSERT INTO users (username, email, password_hash, role) VALUES (?, ?, ?, ?)`, [username, email, passwordHash, role || 'viewer'], function(err) { if (err) reject(err); else resolve({ id: this.lastID }); } ); }); logAudit(db, { userId: req.user.id, username: req.user.username, action: 'user_create', entityType: 'user', entityId: String(result.id), details: { created_username: username, role: role || 'viewer' }, ipAddress: req.ip }); res.status(201).json({ message: 'User created successfully', user: { id: result.id, username, email, role: role || 'viewer' } }); } catch (err) { console.error('Create user error:', err); if (err.message.includes('UNIQUE constraint failed')) { return res.status(409).json({ error: 'Username or email already exists' }); } res.status(500).json({ error: 'Failed to create user' }); } }); // Update user router.patch('/:id', async (req, res) => { const { username, email, password, role, is_active } = req.body; const userId = req.params.id; // Prevent self-demotion from admin if (userId == req.user.id && role && role !== 'admin') { return res.status(400).json({ error: 'Cannot remove your own admin role' }); } // Prevent self-deactivation if (userId == req.user.id && is_active === false) { return res.status(400).json({ error: 'Cannot deactivate your own account' }); } try { const updates = []; const values = []; if (username) { updates.push('username = ?'); values.push(username); } if (email) { updates.push('email = ?'); values.push(email); } if (password) { const passwordHash = await bcrypt.hash(password, 10); updates.push('password_hash = ?'); values.push(passwordHash); } if (role) { if (!['admin', 'editor', 'viewer'].includes(role)) { return res.status(400).json({ error: 'Invalid role' }); } updates.push('role = ?'); values.push(role); } if (typeof is_active === 'boolean') { updates.push('is_active = ?'); values.push(is_active ? 1 : 0); } if (updates.length === 0) { return res.status(400).json({ error: 'No fields to update' }); } values.push(userId); await new Promise((resolve, reject) => { db.run( `UPDATE users SET ${updates.join(', ')} WHERE id = ?`, values, function(err) { if (err) reject(err); else resolve({ changes: this.changes }); } ); }); const updatedFields = {}; if (username) updatedFields.username = username; if (email) updatedFields.email = email; if (role) updatedFields.role = role; if (typeof is_active === 'boolean') updatedFields.is_active = is_active; if (password) updatedFields.password_changed = true; logAudit(db, { userId: req.user.id, username: req.user.username, action: 'user_update', entityType: 'user', entityId: String(userId), details: updatedFields, ipAddress: req.ip }); // If user was deactivated, delete their sessions if (is_active === false) { await new Promise((resolve) => { db.run('DELETE FROM sessions WHERE user_id = ?', [userId], () => resolve()); }); } res.json({ message: 'User updated successfully' }); } catch (err) { console.error('Update user error:', err); if (err.message.includes('UNIQUE constraint failed')) { return res.status(409).json({ error: 'Username or email already exists' }); } res.status(500).json({ error: 'Failed to update user' }); } }); // Delete user router.delete('/:id', async (req, res) => { const userId = req.params.id; // Prevent self-deletion if (userId == req.user.id) { return res.status(400).json({ error: 'Cannot delete your own account' }); } try { // Look up the user before deleting const targetUser = await new Promise((resolve, reject) => { db.get('SELECT username FROM users WHERE id = ?', [userId], (err, row) => { if (err) reject(err); else resolve(row); }); }); // Delete sessions first (foreign key) await new Promise((resolve) => { db.run('DELETE FROM sessions WHERE user_id = ?', [userId], () => resolve()); }); // Delete user const result = await new Promise((resolve, reject) => { db.run('DELETE FROM users WHERE id = ?', [userId], function(err) { if (err) reject(err); else resolve({ changes: this.changes }); }); }); if (result.changes === 0) { return res.status(404).json({ error: 'User not found' }); } logAudit(db, { userId: req.user.id, username: req.user.username, action: 'user_delete', entityType: 'user', entityId: String(userId), details: { deleted_username: targetUser ? targetUser.username : 'unknown' }, ipAddress: req.ip }); res.json({ message: 'User deleted successfully' }); } catch (err) { console.error('Delete user error:', err); res.status(500).json({ error: 'Failed to delete user' }); } }); return router; } module.exports = createUsersRouter;