UNPKG

create-bodhi-node-boilerplate

Version:

Create a Node.js project with basic folder structure and server setup

338 lines (307 loc) 9.48 kB
const User = require('../models/userModel'); const jwt = require('jsonwebtoken'); const config = require('../config/config'); /** * @desc Register a new user * @route POST /api/v1/auth/register * @access Public */ exports.register = async (req, res) => { try { const { username, email, password } = req.body; // Input validation if (!username || !email || !password) { return res.status(400).json({ success: false, message: 'Missing required fields', error: { code: 'VALIDATION_ERROR', details: 'Username, email, and password are required' } }); } // Check if user already exists const existingUser = await User.findOne({ $or: [ { email: email.toLowerCase() }, { username: username.toLowerCase() } ] }); if (existingUser) { return res.status(409).json({ success: false, message: 'User already exists', error: { code: 'USER_EXISTS', details: existingUser.email === email.toLowerCase() ? 'Email already registered' : 'Username already taken' } }); } // Create user const user = await User.create({ username: username.toLowerCase(), email: email.toLowerCase(), password }); // Generate tokens const accessToken = user.getSignedJwtToken(); const refreshToken = user.getRefreshToken(); res.status(201).json({ success: true, message: 'User registered successfully', data: { user: { id: user._id, username: user.username, email: user.email }, tokens: { accessToken, refreshToken } } }); } catch (error) { res.status(400).json({ success: false, message: 'Registration failed', error: { code: 'REGISTRATION_FAILED', details: error.message } }); } }; /** * @desc Login user * @route POST /api/v1/auth/login * @access Public */ exports.login = async (req, res) => { try { const { email, password } = req.body; // Input validation if (!email || !password) { return res.status(400).json({ success: false, message: 'Missing required fields', error: { code: 'VALIDATION_ERROR', details: 'Email and password are required' } }); } // Check for user and include password for comparison const user = await User.findOne({ email: email.toLowerCase() }).select('+password'); if (!user) { return res.status(401).json({ success: false, message: 'Invalid credentials', error: { code: 'AUTH_INVALID_CREDENTIALS', details: 'Invalid email or password' } }); } // Verify password const isMatch = await user.matchPassword(password); if (!isMatch) { return res.status(401).json({ success: false, message: 'Invalid credentials', error: { code: 'AUTH_INVALID_CREDENTIALS', details: 'Invalid email or password' } }); } // Generate tokens const accessToken = user.getSignedJwtToken(); const refreshToken = user.getRefreshToken(); // Save refresh token hash user.refreshTokenHash = await user.hashToken(refreshToken); await user.save(); res.status(200).json({ success: true, message: 'Login successful', data: { user: { id: user._id, username: user.username, email: user.email }, tokens: { accessToken, refreshToken } } }); } catch (error) { res.status(400).json({ success: false, message: 'Login failed', error: { code: 'LOGIN_FAILED', details: error.message } }); } }; /** * @desc Get current user profile * @route GET /api/v1/auth/me * @access Private */ exports.getProfile = async (req, res) => { try { const user = await User.findById(req.user.id); if (!user) { return res.status(404).json({ success: false, message: 'User not found', error: { code: 'USER_NOT_FOUND', details: 'User no longer exists' } }); } res.status(200).json({ success: true, data: { user: { id: user._id, username: user.username, email: user.email } } }); } catch (error) { res.status(500).json({ success: false, message: 'Error retrieving profile', error: { code: 'PROFILE_RETRIEVAL_ERROR', details: error.message } }); } }; /** * @desc Refresh access token * @route POST /api/v1/auth/refresh-token * @access Public */ exports.refreshToken = async (req, res) => { try { const { refreshToken } = req.body; if (!refreshToken) { return res.status(400).json({ success: false, message: 'Refresh token is required', error: { code: 'REFRESH_TOKEN_REQUIRED', details: 'Please provide a refresh token' } }); } // Verify refresh token const decoded = jwt.verify(refreshToken, config.jwt.refreshSecret); // Get user const user = await User.findById(decoded.id); if (!user) { return res.status(401).json({ success: false, message: 'Invalid refresh token', error: { code: 'INVALID_REFRESH_TOKEN', details: 'User not found' } }); } // Verify refresh token hash const isValidToken = await user.verifyRefreshToken(refreshToken); if (!isValidToken) { return res.status(401).json({ success: false, message: 'Invalid refresh token', error: { code: 'INVALID_REFRESH_TOKEN', details: 'Token has been revoked or is invalid' } }); } // Generate new tokens const accessToken = user.getSignedJwtToken(); const newRefreshToken = user.getRefreshToken(); // Update refresh token hash user.refreshTokenHash = await user.hashToken(newRefreshToken); await user.save(); res.status(200).json({ success: true, message: 'Token refreshed successfully', data: { tokens: { accessToken, refreshToken: newRefreshToken } } }); } catch (error) { if (error.name === 'TokenExpiredError') { return res.status(401).json({ success: false, message: 'Refresh token expired', error: { code: 'REFRESH_TOKEN_EXPIRED', details: 'Please login again' } }); } res.status(500).json({ success: false, message: 'Token refresh failed', error: { code: 'TOKEN_REFRESH_ERROR', details: error.message } }); } }; /** * @desc Logout user * @route POST /api/v1/auth/logout * @access Private */ exports.logout = async (req, res) => { try { const user = await User.findById(req.user.id); if (!user) { return res.status(404).json({ success: false, message: 'User not found', error: { code: 'USER_NOT_FOUND', details: 'User no longer exists' } }); } // Clear refresh token hash user.refreshTokenHash = null; await user.save(); res.status(200).json({ success: true, message: 'Logged out successfully' }); } catch (error) { res.status(500).json({ success: false, message: 'Logout failed', error: { code: 'LOGOUT_ERROR', details: error.message } }); } };