import { Response } from 'express'; import { AuthenticatedRequest } from '../middleware/auth.middleware'; import * as userService from '../services/user.service'; import { CreateUserInput, UpdateUserInput, ChangePasswordInput, } from '../validators/user.validator'; /** * GET /users * List all users (admin only) * Supports filtering by role_id, is_active, and search * Supports pagination with page, limit, sortBy, sortOrder */ export async function getAllUsers( req: AuthenticatedRequest, res: Response ): Promise { try { // Parse query parameters for filters const filters: userService.UserFilter = {}; if (req.query.role_id) { filters.role_id = parseInt(req.query.role_id as string, 10); } if (req.query.is_active !== undefined) { filters.is_active = req.query.is_active === 'true'; } if (req.query.search) { filters.search = req.query.search as string; } // Parse pagination parameters const pagination = { page: parseInt(req.query.page as string, 10) || 1, limit: parseInt(req.query.limit as string, 10) || 10, sortBy: (req.query.sortBy as string) || 'created_at', sortOrder: (req.query.sortOrder as 'asc' | 'desc') || 'desc', }; const result = await userService.getAll(filters, pagination); res.status(200).json({ success: true, message: 'Users retrieved successfully', data: result.users, pagination: result.pagination, }); } catch (error) { const message = error instanceof Error ? error.message : 'Failed to retrieve users'; res.status(500).json({ success: false, error: message, }); } } /** * GET /users/:id * Get a single user by ID (admin or self) */ export async function getUserById( req: AuthenticatedRequest, res: Response ): Promise { try { const userId = req.params.id; if (!userId) { res.status(400).json({ success: false, error: 'Invalid user ID', }); return; } // Check if user is admin or requesting their own data const requestingUser = req.user; const isAdmin = requestingUser?.roleName === 'ADMIN'; const isSelf = requestingUser?.userId === userId; if (!isAdmin && !isSelf) { res.status(403).json({ success: false, error: 'Insufficient permissions', }); return; } const user = await userService.getById(userId); if (!user) { res.status(404).json({ success: false, error: 'User not found', }); return; } res.status(200).json({ success: true, message: 'User retrieved successfully', data: user, }); } catch (error) { const message = error instanceof Error ? error.message : 'Failed to retrieve user'; res.status(500).json({ success: false, error: message, }); } } /** * POST /users * Create a new user (admin only) */ export async function createUser( req: AuthenticatedRequest, res: Response ): Promise { try { const data = req.body as CreateUserInput; const user = await userService.create({ email: data.email, password: data.password, name: data.name, avatar_url: data.avatar_url, role_id: data.role_id, is_active: data.is_active, }); res.status(201).json({ success: true, message: 'User created successfully', data: user, }); } catch (error) { const message = error instanceof Error ? error.message : 'Failed to create user'; if (message === 'Email already in use') { res.status(409).json({ success: false, error: message, }); return; } res.status(500).json({ success: false, error: message, }); } } /** * PUT /users/:id * Update a user (admin can update all fields, regular users can only update limited fields on self) */ export async function updateUser( req: AuthenticatedRequest, res: Response ): Promise { try { const userId = req.params.id; if (!userId) { res.status(400).json({ success: false, error: 'Invalid user ID', }); return; } const requestingUser = req.user; const isAdmin = requestingUser?.roleName === 'ADMIN'; const isSelf = requestingUser?.userId === userId; if (!isAdmin && !isSelf) { res.status(403).json({ success: false, error: 'Insufficient permissions', }); return; } const data = req.body as UpdateUserInput; // Non-admin users can only update their own profile fields (not role_id or is_active) if (!isAdmin) { if (data.role_id !== undefined || data.is_active !== undefined) { res.status(403).json({ success: false, error: 'You can only update your profile information', }); return; } } const user = await userService.update(userId, data); if (!user) { res.status(404).json({ success: false, error: 'User not found', }); return; } res.status(200).json({ success: true, message: 'User updated successfully', data: user, }); } catch (error) { const message = error instanceof Error ? error.message : 'Failed to update user'; if (message === 'Email already in use') { res.status(409).json({ success: false, error: message, }); return; } res.status(500).json({ success: false, error: message, }); } } /** * DELETE /users/:id * Deactivate a user (soft delete, admin only) */ export async function deleteUser( req: AuthenticatedRequest, res: Response ): Promise { try { const userId = req.params.id; if (!userId) { res.status(400).json({ success: false, error: 'Invalid user ID', }); return; } // Prevent admin from deleting themselves if (req.user?.userId === userId) { res.status(400).json({ success: false, error: 'Cannot deactivate your own account', }); return; } const deleted = await userService.deleteUser(userId); if (!deleted) { res.status(404).json({ success: false, error: 'User not found', }); return; } res.status(200).json({ success: true, message: 'User deactivated successfully', }); } catch (error) { const message = error instanceof Error ? error.message : 'Failed to deactivate user'; res.status(500).json({ success: false, error: message, }); } } /** * PUT /users/:id/password * Change user password (self only) */ export async function changePassword( req: AuthenticatedRequest, res: Response ): Promise { try { const userId = req.params.id; if (!userId) { res.status(400).json({ success: false, error: 'Invalid user ID', }); return; } // Only allow users to change their own password if (req.user?.userId !== userId) { res.status(403).json({ success: false, error: 'You can only change your own password', }); return; } const data = req.body as ChangePasswordInput; await userService.changePassword( userId, data.current_password, data.new_password ); res.status(200).json({ success: true, message: 'Password changed successfully', }); } catch (error) { const message = error instanceof Error ? error.message : 'Failed to change password'; if (message === 'Current password is incorrect') { res.status(401).json({ success: false, error: message, }); return; } if (message === 'User not found') { res.status(404).json({ success: false, error: message, }); return; } res.status(500).json({ success: false, error: message, }); } }