Files
math2-platform/backend/src/modules/user/user.controller.ts
Renato bc43c9e772
Some checks failed
Test Suite / test-backend (push) Has been cancelled
Test Suite / test-frontend (push) Has been cancelled
Test Suite / e2e-tests (push) Has been cancelled
Test Suite / coverage-check (push) Has been cancelled
🎓 Initial commit: Math2 Platform - Plataforma de Álgebra Lineal PRO
 Características:
- 45 ejercicios universitarios (Basic → Advanced)
- Renderizado LaTeX profesional
- IA generativa (Z.ai/DashScope)
- Docker 9 servicios
- Tests 123/123 pasando
- Seguridad enterprise (JWT, XSS, Rate limiting)

🐳 Infraestructura:
- Next.js 14 + Node.js 20
- PostgreSQL 15 + Redis 7
- Docker Compose completo
- Nginx + SSL ready

📚 Documentación:
- 5 informes técnicos completos
- README profesional
- Scripts de deployment automatizados

Estado: Producción lista 
2026-03-31 11:27:11 -03:00

322 lines
8.2 KiB
TypeScript

/**
* 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<void> {
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<void> {
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<void> {
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<void> {
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<void> {
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<void> {
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<void> {
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;