/** * User Controller * * HTTP request handlers for user profile endpoints */ import { Request, Response } from 'express'; import { userService } from './user.service'; import { ValidationError, NotFoundError, AuthenticationError, ConflictError } from '../../shared/types'; import { logger } from '../../shared/utils/logger'; import type { UserRole } from '@prisma/client'; // ============================================ // CONTROLLER // ============================================ class UserController { /** * GET /api/users/me * Get current user profile */ async getProfile(req: Request, res: Response): Promise { try { const userId = req.user?.userId; if (!userId) { throw new AuthenticationError('User authentication required'); } const user = await userService.getUserProfile(userId); res.json({ success: true, data: user, }); } catch (error) { logger.error({ error, req }, 'Error getting user profile'); if (error instanceof AuthenticationError) { res.status(401).json({ success: false, error: { code: error.code, message: error.message }, }); return; } if (error instanceof NotFoundError) { res.status(404).json({ success: false, error: { code: error.code, message: error.message }, }); return; } res.status(500).json({ success: false, error: { code: 'INTERNAL_ERROR', message: 'Failed to get profile' }, }); } } /** * PUT /api/users/me * Update current user profile */ async updateProfile(req: Request, res: Response): Promise { try { const userId = req.user?.userId; if (!userId) { throw new AuthenticationError('User authentication required'); } const { username, telegramChatId } = req.body; const updatedUser = await userService.updateProfile(userId, { username, telegramChatId: telegramChatId || null, }); res.json({ success: true, data: updatedUser, }); } catch (error) { logger.error({ error, req }, 'Error updating user profile'); if (error instanceof AuthenticationError) { res.status(401).json({ success: false, error: { code: error.code, message: error.message }, }); return; } if (error instanceof ValidationError) { res.status(400).json({ success: false, error: { code: error.code, message: error.message }, }); return; } if (error instanceof ConflictError) { res.status(409).json({ success: false, error: { code: error.code, message: error.message }, }); return; } res.status(500).json({ success: false, error: { code: 'INTERNAL_ERROR', message: 'Failed to update profile' }, }); } } /** * GET /api/users/me/stats * Get current user statistics */ async getStats(req: Request, res: Response): Promise { try { const userId = req.user?.userId; if (!userId) { throw new AuthenticationError('User authentication required'); } const stats = await userService.getUserStats(userId); res.json({ success: true, data: stats, }); } catch (error) { logger.error({ error, req }, 'Error getting user stats'); if (error instanceof AuthenticationError) { res.status(401).json({ success: false, error: { code: error.code, message: error.message }, }); return; } res.status(500).json({ success: false, error: { code: 'INTERNAL_ERROR', message: 'Failed to get stats' }, }); } } /** * PUT /api/users/me/password * Change current user password */ async changePassword(req: Request, res: Response): Promise { try { const userId = req.user?.userId; if (!userId) { throw new AuthenticationError('User authentication required'); } const { currentPassword, newPassword } = req.body; await userService.changePassword(userId, currentPassword, newPassword); res.json({ success: true, message: 'Password changed successfully', }); } catch (error) { logger.error({ error, req }, 'Error changing password'); if (error instanceof AuthenticationError) { res.status(401).json({ success: false, error: { code: error.code, message: error.message }, }); return; } if (error instanceof ValidationError) { res.status(400).json({ success: false, error: { code: error.code, message: error.message }, }); return; } res.status(500).json({ success: false, error: { code: 'INTERNAL_ERROR', message: 'Failed to change password' }, }); } } /** * GET /api/users (admin only) * List all users */ async listUsers(req: Request, res: Response): Promise { try { const { role, isActive, page = '1', limit = '50' } = req.query; const result = await userService.listUsers({ role: role as UserRole, isActive: isActive === 'true' ? true : isActive === 'false' ? false : undefined, page: parseInt(page as string, 10) || 1, limit: parseInt(limit as string, 10) || 50, }); res.json({ success: true, data: result.users, meta: { pagination: { page: parseInt(page as string, 10) || 1, limit: parseInt(limit as string, 10) || 50, total: result.total, totalPages: Math.ceil(result.total / (parseInt(limit as string, 10) || 50)), }, }, }); } catch (error) { logger.error({ error, req }, 'Error listing users'); res.status(500).json({ success: false, error: { code: 'INTERNAL_ERROR', message: 'Failed to list users' }, }); } } /** * PUT /api/users/:id/role (admin only) * Update user role */ async updateUserRole(req: Request, res: Response): Promise { try { const { id } = req.params; const { role } = req.body; if (!role || !['STUDENT', 'TEACHER', 'ADMIN'].includes(role)) { throw new ValidationError('Valid role is required (STUDENT, TEACHER, ADMIN)'); } const updatedUser = await userService.updateUserRole(id, role as UserRole); res.json({ success: true, data: updatedUser, }); } catch (error) { logger.error({ error, req }, 'Error updating user role'); if (error instanceof ValidationError) { res.status(400).json({ success: false, error: { code: error.code, message: error.message }, }); return; } if (error instanceof NotFoundError) { res.status(404).json({ success: false, error: { code: error.code, message: error.message }, }); return; } res.status(500).json({ success: false, error: { code: 'INTERNAL_ERROR', message: 'Failed to update user role' }, }); } } /** * DELETE /api/users/:id (admin only) * Deactivate user */ async deactivateUser(req: Request, res: Response): Promise { try { const { id } = req.params; const deactivatedUser = await userService.deactivateUser(id); res.json({ success: true, data: deactivatedUser, message: 'User deactivated successfully', }); } catch (error) { logger.error({ error, req }, 'Error deactivating user'); if (error instanceof NotFoundError) { res.status(404).json({ success: false, error: { code: error.code, message: error.message }, }); return; } res.status(500).json({ success: false, error: { code: 'INTERNAL_ERROR', message: 'Failed to deactivate user' }, }); } } } // ============================================ // EXPORT // ============================================ export const userController = new UserController(); export default userController;