courseworx/backend/routes/auth.js
Mahmoud M. Abdalla cca90322d8 Release v1.0.0 - Complete Course Management System
Major Features:
- Authentication & Authorization with JWT and role-based access
- Complete User Management System with CRUD operations
- Course Management System with publishing and enrollment
- Modern React UI with Tailwind CSS and responsive design
- Internationalization (i18n) with English and Arabic support
- File Upload System for images and documents
- RESTful API with Express.js and Sequelize ORM
- PostgreSQL database with proper relationships
- Super Admin password change functionality
- CSV import for bulk user creation
- Modal-based user add/edit operations
- Search, filter, and pagination capabilities

Technical Improvements:
- Fixed homepage routing and accessibility issues
- Resolved API endpoint mismatches and data rendering
- Enhanced security with proper validation and hashing
- Optimized performance with React Query and caching
- Improved error handling and user feedback
- Clean code structure with ESLint compliance

Bug Fixes:
- Fixed non-functional Add/Edit/Delete buttons
- Resolved CSV import BOM issues
- Fixed modal rendering and accessibility
- Corrected API base URL configuration
- Enhanced backend stability and error handling

This version represents a complete, production-ready Course Management System.
2025-07-27 23:30:23 +03:00

268 lines
No EOL
7.3 KiB
JavaScript

const express = require('express');
const jwt = require('jsonwebtoken');
const bcrypt = require('bcryptjs');
const { body, validationResult } = require('express-validator');
const { User } = require('../models');
const { auth, requireSuperAdmin } = require('../middleware/auth');
const router = express.Router();
// Generate JWT token
const generateToken = (userId) => {
return jwt.sign({ userId }, process.env.JWT_SECRET, {
expiresIn: process.env.JWT_EXPIRES_IN || '7d'
});
};
// @route POST /api/auth/login
// @desc Login user
// @access Public
router.post('/login', [
body('email').isEmail().normalizeEmail(),
body('password').isLength({ min: 6 })
], async (req, res) => {
try {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
const { email, password } = req.body;
const user = await User.findOne({ where: { email } });
console.log('Login attempt for email:', email, 'User found:', !!user, 'User active:', user?.isActive);
if (!user || !user.isActive) {
return res.status(401).json({ error: 'Invalid credentials or account inactive.' });
}
const isPasswordValid = await user.comparePassword(password);
console.log('Password validation result:', isPasswordValid);
if (!isPasswordValid) {
return res.status(401).json({ error: 'Invalid credentials.' });
}
// Update last login
await user.update({ lastLogin: new Date() });
const token = generateToken(user.id);
res.json({
token,
user: {
id: user.id,
firstName: user.firstName,
lastName: user.lastName,
email: user.email,
role: user.role,
avatar: user.avatar,
phone: user.phone,
requiresPasswordChange: user.requiresPasswordChange
}
});
} catch (error) {
console.error('Login error:', error);
res.status(500).json({ error: 'Server error.' });
}
});
// @route POST /api/auth/register
// @desc Register new user (Super Admin only)
// @access Private (Super Admin)
router.post('/register', [
auth,
requireSuperAdmin,
body('firstName').isLength({ min: 2, max: 50 }),
body('lastName').isLength({ min: 2, max: 50 }),
body('email').isEmail().normalizeEmail(),
body('password').isLength({ min: 6 }),
body('role').isIn(['super_admin', 'trainer', 'trainee'])
], async (req, res) => {
try {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
const { firstName, lastName, email, password, role, phone } = req.body;
// Check if user already exists
const existingUser = await User.findOne({ where: { email } });
if (existingUser) {
return res.status(400).json({ error: 'User with this email already exists.' });
}
const user = await User.create({
firstName,
lastName,
email,
password,
role,
phone
});
console.log('User created successfully:', {
id: user.id,
email: user.email,
role: user.role,
isActive: user.isActive
});
res.status(201).json({
message: 'User created successfully.',
user: {
id: user.id,
firstName: user.firstName,
lastName: user.lastName,
email: user.email,
role: user.role,
phone: user.phone
}
});
} catch (error) {
console.error('Registration error:', error);
res.status(500).json({ error: 'Server error.' });
}
});
// @route GET /api/auth/me
// @desc Get current user
// @access Private
router.get('/me', auth, async (req, res) => {
try {
res.json({
user: {
id: req.user.id,
firstName: req.user.firstName,
lastName: req.user.lastName,
email: req.user.email,
role: req.user.role,
avatar: req.user.avatar,
phone: req.user.phone,
lastLogin: req.user.lastLogin
}
});
} catch (error) {
console.error('Get user error:', error);
res.status(500).json({ error: 'Server error.' });
}
});
// @route PUT /api/auth/profile
// @desc Update user profile
// @access Private
router.put('/profile', [
auth,
body('firstName').optional().isLength({ min: 2, max: 50 }),
body('lastName').optional().isLength({ min: 2, max: 50 }),
body('phone').optional().isMobilePhone()
], async (req, res) => {
try {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
const { firstName, lastName, phone } = req.body;
const updateData = {};
if (firstName) updateData.firstName = firstName;
if (lastName) updateData.lastName = lastName;
if (phone) updateData.phone = phone;
await req.user.update(updateData);
res.json({
message: 'Profile updated successfully.',
user: {
id: req.user.id,
firstName: req.user.firstName,
lastName: req.user.lastName,
email: req.user.email,
role: req.user.role,
avatar: req.user.avatar,
phone: req.user.phone
}
});
} catch (error) {
console.error('Profile update error:', error);
res.status(500).json({ error: 'Server error.' });
}
});
// @route PUT /api/auth/change-password
// @desc Change user password
// @access Private
router.put('/change-password', [
auth,
body('currentPassword').isLength({ min: 6 }),
body('newPassword').isLength({ min: 6 })
], async (req, res) => {
try {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
const { currentPassword, newPassword } = req.body;
const isCurrentPasswordValid = await req.user.comparePassword(currentPassword);
if (!isCurrentPasswordValid) {
return res.status(400).json({ error: 'Current password is incorrect.' });
}
await req.user.update({ password: newPassword });
res.json({ message: 'Password changed successfully.' });
} catch (error) {
console.error('Password change error:', error);
res.status(500).json({ error: 'Server error.' });
}
});
// @route PUT /api/auth/first-password-change
// @desc Change password on first login (for imported users)
// @access Private
router.put('/first-password-change', [
auth,
body('newPassword').isLength({ min: 6 })
], async (req, res) => {
try {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
const { newPassword } = req.body;
// Check if user requires password change
if (!req.user.requiresPasswordChange) {
return res.status(400).json({ error: 'Password change not required.' });
}
await req.user.update({
password: newPassword,
requiresPasswordChange: false
});
res.json({
message: 'Password changed successfully.',
user: {
id: req.user.id,
firstName: req.user.firstName,
lastName: req.user.lastName,
email: req.user.email,
role: req.user.role,
avatar: req.user.avatar,
phone: req.user.phone,
requiresPasswordChange: false
}
});
} catch (error) {
console.error('First password change error:', error);
res.status(500).json({ error: 'Server error.' });
}
});
module.exports = router;