helpyourneighbour/backend/routes/auth.js
BibaBot Jarvis e278ee3da5
Some checks are pending
Docker Test / test (push) Waiting to run
feat: implement role-based access control and auth routes
This commit implements the role-based access control middleware and authentication routes as per the project's requirements. It includes:
2026-03-15 20:07:14 +00:00

140 lines
No EOL
4 KiB
JavaScript

const express = require('express');
const router = express.Router();
const { body, validationResult } = require('express-validator');
const bcrypt = require('bcrypt');
const jwt = require('jsonwebtoken');
const db = require('../db');
const requireRole = require('../middleware/requireRole');
// Register a new user
router.post('/register', [
body('email').isEmail().normalizeEmail(),
body('password').isLength({ min: 8 }),
body('name').notEmpty()
], async (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
const { email, password, name } = req.body;
try {
// Check if user already exists
const existingUser = await db.query('SELECT id FROM users WHERE email = $1', [email]);
if (existingUser.rows.length > 0) {
return res.status(409).json({ error: 'User already exists' });
}
// Hash password
const hashedPassword = await bcrypt.hash(password, 12);
// Insert new user with default role 'user'
const newUser = await db.query(
'INSERT INTO users (email, password_hash, name, role) VALUES ($1, $2, $3, $4) RETURNING id, email, name, role',
[email, hashedPassword, name, 'user']
);
res.status(201).json({ user: newUser.rows[0] });
} catch (err) {
console.error(err);
res.status(500).json({ error: 'Internal server error' });
}
});
// Login user
router.post('/login', [
body('email').isEmail().normalizeEmail(),
body('password').notEmpty()
], async (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
const { email, password } = req.body;
try {
// Find user by email
const user = await db.query('SELECT id, email, password_hash, name, role FROM users WHERE email = $1', [email]);
if (user.rows.length === 0) {
return res.status(401).json({ error: 'Invalid credentials' });
}
// Compare passwords
const isValidPassword = await bcrypt.compare(password, user.rows[0].password_hash);
if (!isValidPassword) {
return res.status(401).json({ error: 'Invalid credentials' });
}
// Generate JWT token with role
const token = jwt.sign(
{ userId: user.rows[0].id, email: user.rows[0].email, role: user.rows[0].role },
process.env.JWT_SECRET || 'default_secret',
{ expiresIn: '24h' }
);
res.json({
token,
user: {
id: user.rows[0].id,
email: user.rows[0].email,
name: user.rows[0].name,
role: user.rows[0].role
}
});
} catch (err) {
console.error(err);
res.status(500).json({ error: 'Internal server error' });
}
});
// Get current user profile (requires authentication)
router.get('/profile', requireRole(['user', 'moderator', 'admin']), async (req, res) => {
try {
const user = await db.query('SELECT id, email, name, role FROM users WHERE id = $1', [req.user.userId]);
if (user.rows.length === 0) {
return res.status(404).json({ error: 'User not found' });
}
res.json({ user: user.rows[0] });
} catch (err) {
console.error(err);
res.status(500).json({ error: 'Internal server error' });
}
});
// Update current user profile (requires authentication)
router.put('/profile', requireRole(['user', 'moderator', 'admin']), [
body('name').optional().notEmpty()
], async (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
try {
const { name } = req.body;
const userId = req.user.userId;
// Update user profile
const updatedUser = await db.query(
'UPDATE users SET name = $1 WHERE id = $2 RETURNING id, email, name, role',
[name, userId]
);
if (updatedUser.rows.length === 0) {
return res.status(404).json({ error: 'User not found' });
}
res.json({ user: updatedUser.rows[0] });
} catch (err) {
console.error(err);
res.status(500).json({ error: 'Internal server error' });
}
});
module.exports = router;