diff --git a/projects/gamilit/apps/backend/src/__mocks__/repositories.mock.ts b/projects/gamilit/apps/backend/src/__mocks__/repositories.mock.ts index 52d8e30..97ccb09 100644 --- a/projects/gamilit/apps/backend/src/__mocks__/repositories.mock.ts +++ b/projects/gamilit/apps/backend/src/__mocks__/repositories.mock.ts @@ -7,7 +7,7 @@ * Sprint 0 - P0-008: Test Infrastructure */ -import { Repository, SelectQueryBuilder } from 'typeorm'; +import { Repository, SelectQueryBuilder, ObjectLiteral } from 'typeorm'; /** * Creates a mock TypeORM Repository with all common methods @@ -15,7 +15,7 @@ import { Repository, SelectQueryBuilder } from 'typeorm'; * @template T - Entity type * @returns Mocked Repository */ -export function createMockRepository(): jest.Mocked> { +export function createMockRepository(): jest.Mocked> { return { find: jest.fn(), findOne: jest.fn(), @@ -58,7 +58,7 @@ export function createMockRepository(): jest.Mocked> { * @template T - Entity type * @returns Mocked SelectQueryBuilder */ -export function createMockQueryBuilder(): jest.Mocked> { +export function createMockQueryBuilder(): jest.Mocked> { const queryBuilder = { select: jest.fn().mockReturnThis(), addSelect: jest.fn().mockReturnThis(), diff --git a/projects/gamilit/apps/backend/src/modules/admin/controllers/admin-alerts.controller.ts b/projects/gamilit/apps/backend/src/modules/admin/controllers/admin-alerts.controller.ts index 998a6dc..a9859db 100644 --- a/projects/gamilit/apps/backend/src/modules/admin/controllers/admin-alerts.controller.ts +++ b/projects/gamilit/apps/backend/src/modules/admin/controllers/admin-alerts.controller.ts @@ -24,6 +24,7 @@ import { AlertsStatsDto, PaginatedAlertsDto, } from '../dto/alerts'; +import { AuthRequest } from '@shared/types'; /** * AdminAlertsController @@ -154,7 +155,7 @@ export class AdminAlertsController { @Body() createDto: CreateAlertDto, @Request() req: AuthRequest, ): Promise { - const userId = req.user?.id || req.user?.sub; + const userId = req.user!.id; return this.alertsService.createAlert(createDto, userId); } @@ -186,7 +187,7 @@ export class AdminAlertsController { @Body() dto: AcknowledgeAlertDto, @Request() req: AuthRequest, ): Promise { - const userId = req.user?.id || req.user?.sub; + const userId = req.user!.id; return this.alertsService.acknowledgeAlert(id, dto.acknowledgment_note, userId); } @@ -218,7 +219,7 @@ export class AdminAlertsController { @Body() dto: ResolveAlertDto, @Request() req: AuthRequest, ): Promise { - const userId = req.user?.id || req.user?.sub; + const userId = req.user!.id; return this.alertsService.resolveAlert(id, dto.resolution_note, userId); } diff --git a/projects/gamilit/apps/backend/src/modules/admin/controllers/admin-bulk-operations.controller.ts b/projects/gamilit/apps/backend/src/modules/admin/controllers/admin-bulk-operations.controller.ts index b6faf99..09129dc 100644 --- a/projects/gamilit/apps/backend/src/modules/admin/controllers/admin-bulk-operations.controller.ts +++ b/projects/gamilit/apps/backend/src/modules/admin/controllers/admin-bulk-operations.controller.ts @@ -20,6 +20,7 @@ import { BulkDeleteUsersDto, BulkOperationStatusDto, } from '../dto/bulk-operations'; +import { AuthRequest } from '@shared/types'; /** * AdminBulkOperationsController @@ -62,7 +63,7 @@ export class AdminBulkOperationsController { @Body() dto: BulkSuspendUsersDto, @Request() req: AuthRequest, ): Promise { - const adminId = req.user.sub; // JWT payload contiene sub = user.id + const adminId = req.user!.id; // JWT payload contiene sub = user.id return this.bulkOpsService.bulkSuspendUsers(dto, adminId); } @@ -84,7 +85,7 @@ export class AdminBulkOperationsController { @Body() dto: BulkActivateUsersDto, @Request() req: AuthRequest, ): Promise { - const adminId = req.user.sub; + const adminId = req.user!.id; return this.bulkOpsService.bulkActivateUsers(dto, adminId); } @@ -106,7 +107,7 @@ export class AdminBulkOperationsController { @Body() dto: BulkUpdateRoleDto, @Request() req: AuthRequest, ): Promise { - const adminId = req.user.sub; + const adminId = req.user!.id; return this.bulkOpsService.bulkUpdateRole(dto, adminId); } @@ -128,7 +129,7 @@ export class AdminBulkOperationsController { @Body() dto: BulkDeleteUsersDto, @Request() req: AuthRequest, ): Promise { - const adminId = req.user.sub; + const adminId = req.user!.id; return this.bulkOpsService.bulkDeleteUsers(dto, adminId); } diff --git a/projects/gamilit/apps/backend/src/modules/admin/controllers/admin-content.controller.ts b/projects/gamilit/apps/backend/src/modules/admin/controllers/admin-content.controller.ts index a7680a0..3694f20 100644 --- a/projects/gamilit/apps/backend/src/modules/admin/controllers/admin-content.controller.ts +++ b/projects/gamilit/apps/backend/src/modules/admin/controllers/admin-content.controller.ts @@ -28,6 +28,7 @@ import { ListApprovalHistoryDto, PaginatedApprovalHistoryDto, } from '../dto/content'; +import { AuthRequest } from '@shared/types'; @ApiTags('Admin - Content') @Controller('admin/content') @@ -71,7 +72,7 @@ export class AdminContentController { @Body() approvalDto: ApproveContentDto, @Request() req: AuthRequest, ): Promise { - const adminId = req.user?.id || req.user?.sub; + const adminId = req.user!.id; return this.adminContentService.approveContent( id, approvalDto, @@ -89,7 +90,7 @@ export class AdminContentController { @Body() approvalDto: ApproveContentDto, @Request() req: AuthRequest, ): Promise { - const adminId = req.user?.id || req.user?.sub; + const adminId = req.user!.id; return this.adminContentService.approveContent( id, approvalDto, @@ -108,7 +109,7 @@ export class AdminContentController { @Body() rejectionDto: RejectContentDto, @Request() req: AuthRequest, ): Promise { - const adminId = req.user?.id || req.user?.sub; + const adminId = req.user!.id; return this.adminContentService.rejectContent( id, rejectionDto, @@ -126,7 +127,7 @@ export class AdminContentController { @Body() rejectionDto: RejectContentDto, @Request() req: AuthRequest, ): Promise { - const adminId = req.user?.id || req.user?.sub; + const adminId = req.user!.id; return this.adminContentService.rejectContent( id, rejectionDto, @@ -159,7 +160,7 @@ export class AdminContentController { @Body() dto: CreateVersionDto, @Request() req: AuthRequest, ): Promise { - const adminId = req.user?.id || req.user?.sub; + const adminId = req.user!.id; return this.adminContentService.createVersion(dto, adminId); } diff --git a/projects/gamilit/apps/backend/src/modules/admin/controllers/admin-gamification-config.controller.ts b/projects/gamilit/apps/backend/src/modules/admin/controllers/admin-gamification-config.controller.ts index 99b5bc2..c995eee 100644 --- a/projects/gamilit/apps/backend/src/modules/admin/controllers/admin-gamification-config.controller.ts +++ b/projects/gamilit/apps/backend/src/modules/admin/controllers/admin-gamification-config.controller.ts @@ -33,6 +33,7 @@ import { UpdateMayaRankDto, UpdateMayaRankResponseDto, } from '../dto/gamification-config'; +import { AuthRequest } from '@shared/types'; /** * AdminGamificationConfigController @@ -177,7 +178,7 @@ export class AdminGamificationConfigController { @Body() dto: UpdateGamificationSettingsDto, @Request() req: AuthRequest, ): Promise { - const adminId = req.user.sub; + const adminId = req.user!.id; return this.gamificationConfigService.updateGamificationSettings( dto, adminId, @@ -284,7 +285,7 @@ export class AdminGamificationConfigController { async restoreDefaults( @Request() req: AuthRequest, ): Promise { - const adminId = req.user.sub; + const adminId = req.user!.id; return this.gamificationConfigService.restoreDefaults(adminId); } @@ -325,7 +326,7 @@ export class AdminGamificationConfigController { async restoreDefaultsAlternative( @Request() req: AuthRequest, ): Promise { - const adminId = req.user.sub; + const adminId = req.user!.id; return this.gamificationConfigService.restoreDefaults(adminId); } @@ -505,7 +506,7 @@ export class AdminGamificationConfigController { @Body() dto: UpdateParameterDto, @Request() req: AuthRequest, ): Promise { - const adminId = req.user.sub; + const adminId = req.user!.id; return this.gamificationConfigService.updateParameterById( id, dto, @@ -637,7 +638,7 @@ export class AdminGamificationConfigController { @Body() dto: UpdateMayaRankDto, @Request() req: AuthRequest, ): Promise { - const adminId = req.user.sub; + const adminId = req.user!.id; return this.gamificationConfigService.updateMayaRank( rankName, dto, diff --git a/projects/gamilit/apps/backend/src/modules/admin/controllers/admin-interventions.controller.ts b/projects/gamilit/apps/backend/src/modules/admin/controllers/admin-interventions.controller.ts index df0a3c4..e5646af 100644 --- a/projects/gamilit/apps/backend/src/modules/admin/controllers/admin-interventions.controller.ts +++ b/projects/gamilit/apps/backend/src/modules/admin/controllers/admin-interventions.controller.ts @@ -22,6 +22,7 @@ import { InterventionAlertDto, PaginatedInterventionsDto, } from '../dto/interventions'; +import { AuthRequest } from '@shared/types'; /** * AdminInterventionsController @@ -165,7 +166,7 @@ export class AdminInterventionsController { @Body() dto: AcknowledgeInterventionDto, @Request() req: AuthRequest, ): Promise { - const userId = req.user?.id || req.user?.sub; + const userId = req.user!.id; return this.interventionsService.acknowledgeIntervention(id, dto.acknowledgment_note, userId); } @@ -213,7 +214,7 @@ export class AdminInterventionsController { @Body() dto: ResolveInterventionDto, @Request() req: AuthRequest, ): Promise { - const userId = req.user?.id || req.user?.sub; + const userId = req.user!.id; return this.interventionsService.resolveIntervention(id, dto.resolution_notes, userId); } diff --git a/projects/gamilit/apps/backend/src/modules/admin/controllers/admin-reports.controller.ts b/projects/gamilit/apps/backend/src/modules/admin/controllers/admin-reports.controller.ts index abc535f..b873342 100644 --- a/projects/gamilit/apps/backend/src/modules/admin/controllers/admin-reports.controller.ts +++ b/projects/gamilit/apps/backend/src/modules/admin/controllers/admin-reports.controller.ts @@ -21,6 +21,7 @@ import { ListReportsDto, PaginatedReportsDto, } from '../dto/reports'; +import { AuthRequest } from '@shared/types'; @ApiTags('Admin - Reports') @Controller('admin/reports') @@ -39,7 +40,7 @@ export class AdminReportsController { @Body() generateDto: GenerateReportDto, @Request() req: AuthRequest, ): Promise { - const userId = req.user?.id || req.user?.sub; + const userId = req.user!.id; return this.adminReportsService.generateReport(generateDto, userId); } diff --git a/projects/gamilit/apps/backend/src/modules/admin/controllers/admin-system.controller.ts b/projects/gamilit/apps/backend/src/modules/admin/controllers/admin-system.controller.ts index 6cc2560..a615454 100644 --- a/projects/gamilit/apps/backend/src/modules/admin/controllers/admin-system.controller.ts +++ b/projects/gamilit/apps/backend/src/modules/admin/controllers/admin-system.controller.ts @@ -20,6 +20,7 @@ import { CacheClearResultDto, SessionCleanupResultDto, } from '../dto/system'; +import { AuthRequest } from '@shared/types'; @ApiTags('Admin - System') @Controller('admin/system') @@ -73,7 +74,7 @@ export class AdminSystemController { @Body() configDto: UpdateSystemConfigDto, @Request() req: AuthRequest, ): Promise { - const adminId = req.user?.id || req.user?.sub; + const adminId = req.user!.id; return this.adminSystemService.updateSystemConfig(configDto, adminId); } @@ -109,7 +110,7 @@ export class AdminSystemController { @Body() configDto: Record, @Request() req: AuthRequest, ): Promise> { - const adminId = req.user?.id || req.user?.sub; + const adminId = req.user!.id; return this.adminSystemService.updateConfigByCategory( category, configDto, @@ -127,7 +128,7 @@ export class AdminSystemController { @Body() toggleDto: ToggleMaintenanceDto, @Request() req: AuthRequest, ): Promise { - const adminId = req.user?.id || req.user?.sub; + const adminId = req.user!.id; return this.adminSystemService.toggleMaintenance( toggleDto, adminId, diff --git a/projects/gamilit/apps/backend/src/modules/admin/controllers/admin-users.controller.ts b/projects/gamilit/apps/backend/src/modules/admin/controllers/admin-users.controller.ts index d89f952..d3760f1 100644 --- a/projects/gamilit/apps/backend/src/modules/admin/controllers/admin-users.controller.ts +++ b/projects/gamilit/apps/backend/src/modules/admin/controllers/admin-users.controller.ts @@ -32,6 +32,7 @@ import { BulkOperationStatusDto, } from '../dto/bulk-operations'; import { User } from '@modules/auth/entities/user.entity'; +import { AuthRequest } from '@shared/types'; @ApiTags('Admin - Users') @Controller('admin/users') @@ -139,7 +140,7 @@ export class AdminUsersController { @Body() dto: BulkSuspendUsersDto, @Request() req: AuthRequest, ): Promise { - const adminId = req.user?.id || req.user?.sub; + const adminId = req.user!.id; return this.bulkOpsService.bulkSuspendUsers(dto, adminId); } @@ -153,7 +154,7 @@ export class AdminUsersController { @Body() dto: BulkDeleteUsersDto, @Request() req: AuthRequest, ): Promise { - const adminId = req.user?.id || req.user?.sub; + const adminId = req.user!.id; return this.bulkOpsService.bulkDeleteUsers(dto, adminId); } @@ -167,7 +168,7 @@ export class AdminUsersController { @Body() dto: BulkUpdateRoleDto, @Request() req: AuthRequest, ): Promise { - const adminId = req.user?.id || req.user?.sub; + const adminId = req.user!.id; return this.bulkOpsService.bulkUpdateRole(dto, adminId); } } diff --git a/projects/gamilit/apps/backend/src/modules/admin/controllers/feature-flags.controller.ts b/projects/gamilit/apps/backend/src/modules/admin/controllers/feature-flags.controller.ts index d32674e..715feac 100644 --- a/projects/gamilit/apps/backend/src/modules/admin/controllers/feature-flags.controller.ts +++ b/projects/gamilit/apps/backend/src/modules/admin/controllers/feature-flags.controller.ts @@ -22,6 +22,7 @@ import { FeatureFlagCheckResultDto } from '../dto/feature-flags'; import { FeatureFlag } from '../entities/feature-flag.entity'; +import { AuthRequest } from '@shared/types'; /** * FeatureFlagsController diff --git a/projects/gamilit/apps/backend/src/modules/admin/services/gamification-config.service.ts b/projects/gamilit/apps/backend/src/modules/admin/services/gamification-config.service.ts index f7575e5..6567adb 100644 --- a/projects/gamilit/apps/backend/src/modules/admin/services/gamification-config.service.ts +++ b/projects/gamilit/apps/backend/src/modules/admin/services/gamification-config.service.ts @@ -103,11 +103,11 @@ export class GamificationConfigService { )?.updated_by; return { - xp: config.xp || DEFAULT_GAMIFICATION_CONFIG.xp, - ranks: config.ranks || DEFAULT_GAMIFICATION_CONFIG.ranks, - coins: config.coins || DEFAULT_GAMIFICATION_CONFIG.coins, + xp: (config.xp as Record) || (DEFAULT_GAMIFICATION_CONFIG.xp as Record), + ranks: (config.ranks as Record) || (DEFAULT_GAMIFICATION_CONFIG.ranks as Record), + coins: (config.coins as Record) || (DEFAULT_GAMIFICATION_CONFIG.coins as Record), achievements: - config.achievements || DEFAULT_GAMIFICATION_CONFIG.achievements, + (config.achievements as Record) || (DEFAULT_GAMIFICATION_CONFIG.achievements as Record), defaults: defaults, last_updated: lastUpdated.toISOString(), updated_by: updatedBy, @@ -131,15 +131,15 @@ export class GamificationConfigService { // Update each category if (dto.xp) { - await this.updateXpSettings(dto.xp, adminId); + await this.updateXpSettings(dto.xp as unknown as Record, adminId); } if (dto.ranks) { - await this.updateRankSettings(dto.ranks, adminId); + await this.updateRankSettings(dto.ranks as unknown as Record, adminId); } if (dto.coins) { - await this.updateCoinsSettings(dto.coins, adminId); + await this.updateCoinsSettings(dto.coins as unknown as Record, adminId); } if (dto.achievements) { @@ -456,7 +456,7 @@ export class GamificationConfigService { // Assign to config structure if (category === 'xp' || category === 'coins') { - config[category][key] = value; + (config[category] as Record)[key] = value; } else if (category === 'ranks' && key === 'thresholds') { config.ranks = value; } else if (category === 'achievements' && key === 'criteria') { diff --git a/projects/gamilit/apps/backend/src/modules/assignments/controllers/assignments.controller.ts b/projects/gamilit/apps/backend/src/modules/assignments/controllers/assignments.controller.ts index fee2a14..308eb90 100644 --- a/projects/gamilit/apps/backend/src/modules/assignments/controllers/assignments.controller.ts +++ b/projects/gamilit/apps/backend/src/modules/assignments/controllers/assignments.controller.ts @@ -32,6 +32,7 @@ import { AssignmentGradeDto } from '../dto/grade-submission.dto'; import { PatchAssignmentDto } from '../dto/patch-assignment.dto'; import { DistributeAssignmentDto, DistributeAssignmentResponseDto } from '../dto/distribute-assignment.dto'; import { DuplicateAssignmentDto, DuplicateAssignmentResponseDto } from '../dto/duplicate-assignment.dto'; +import { AuthRequest } from '@shared/types'; @Controller('teacher/assignments') @ApiTags('Assignments') @@ -70,7 +71,7 @@ export class AssignmentsController { }) @ApiResponse({ status: 400, description: 'Datos inválidos' }) async create(@Body() createDto: CreateAssignmentDto, @Request() req: AuthRequest) { - const teacherId = req.user?.userId || req.user?.sub; + const teacherId = req.user!.id; return this.assignmentsService.create(createDto, teacherId); } @@ -117,7 +118,7 @@ export class AssignmentsController { }, }) async findAll(@Query() query: any, @Request() req: AuthRequest) { - const teacherId = req.user?.userId || req.user?.sub; + const teacherId = req.user!.id; return this.assignmentsService.findAll(teacherId, { isPublished: query.isPublished !== undefined ? query.isPublished === 'true' : undefined, assignmentType: query.type, @@ -165,7 +166,7 @@ export class AssignmentsController { description: 'Asignación no encontrada o acceso denegado', }) async findOne(@Param('id') id: string, @Request() req: AuthRequest) { - const teacherId = req.user?.userId || req.user?.sub; + const teacherId = req.user!.id; return this.assignmentsService.findOne(id, teacherId); } @@ -201,7 +202,7 @@ export class AssignmentsController { @Body() updateDto: UpdateAssignmentDto, @Request() req: AuthRequest, ) { - const teacherId = req.user?.userId || req.user?.sub; + const teacherId = req.user!.id; return this.assignmentsService.update(id, updateDto, teacherId); } @@ -229,7 +230,7 @@ export class AssignmentsController { description: 'Asignación no encontrada o acceso denegado', }) async remove(@Param('id') id: string, @Request() req: AuthRequest) { - const teacherId = req.user?.userId || req.user?.sub; + const teacherId = req.user!.id; await this.assignmentsService.remove(id, teacherId); } @@ -268,7 +269,7 @@ export class AssignmentsController { @Body() dto: AssignToClassroomsDto, @Request() req: AuthRequest, ) { - const teacherId = req.user?.userId || req.user?.sub; + const teacherId = req.user!.id; return this.assignmentsService.assignToClassrooms(id, dto, teacherId); } @@ -322,7 +323,7 @@ export class AssignmentsController { @Query() query: any, @Request() req: AuthRequest, ) { - const teacherId = req.user?.userId || req.user?.sub; + const teacherId = req.user!.id; return this.assignmentsService.getSubmissions(id, teacherId, { status: query.status, classroomId: query.classroomId, @@ -375,7 +376,7 @@ export class AssignmentsController { @Body() dto: AssignmentGradeDto, @Request() req: AuthRequest, ) { - const teacherId = req.user?.userId || req.user?.sub; + const teacherId = req.user!.id; return this.assignmentsService.gradeSubmission(submissionId, dto, teacherId); } @@ -409,7 +410,7 @@ export class AssignmentsController { @Body() patchDto: PatchAssignmentDto, @Request() req: AuthRequest, ) { - const teacherId = req.user?.userId || req.user?.sub; + const teacherId = req.user!.id; return this.assignmentsService.patchAssignment(id, patchDto, teacherId); } @@ -443,7 +444,7 @@ export class AssignmentsController { @Body() distributeDto: DistributeAssignmentDto, @Request() req: AuthRequest, ) { - const teacherId = req.user?.userId || req.user?.sub; + const teacherId = req.user!.id; return this.assignmentsService.distributeAssignment(id, distributeDto, teacherId); } @@ -474,7 +475,7 @@ export class AssignmentsController { @Body() duplicateDto: DuplicateAssignmentDto, @Request() req: AuthRequest, ) { - const teacherId = req.user?.userId || req.user?.sub; + const teacherId = req.user!.id; return this.assignmentsService.duplicateAssignment(id, duplicateDto, teacherId); } @@ -530,7 +531,7 @@ export class AssignmentsController { @Body('notifyStudents') notifyStudents: boolean = false, @Request() req: AuthRequest, ) { - const teacherId = req.user?.userId || req.user?.sub; + const teacherId = req.user!.id; return this.assignmentsService.publishAssignment(id, teacherId, notifyStudents); } @@ -570,7 +571,7 @@ export class AssignmentsController { description: 'Assignment not found or access denied', }) async close(@Param('id') id: string, @Request() req: AuthRequest) { - const teacherId = req.user?.userId || req.user?.sub; + const teacherId = req.user!.id; return this.assignmentsService.closeAssignment(id, teacherId); } } diff --git a/projects/gamilit/apps/backend/src/modules/assignments/controllers/student-assignments.controller.ts b/projects/gamilit/apps/backend/src/modules/assignments/controllers/student-assignments.controller.ts index 1ea8059..2b69215 100644 --- a/projects/gamilit/apps/backend/src/modules/assignments/controllers/student-assignments.controller.ts +++ b/projects/gamilit/apps/backend/src/modules/assignments/controllers/student-assignments.controller.ts @@ -22,6 +22,7 @@ import { RolesGuard } from '../../auth/guards/roles.guard'; import { Roles } from '../../auth/decorators/roles.decorator'; import { GamilityRoleEnum } from '@/shared/constants'; import { AssignmentsService } from '../services/assignments.service'; +import { AuthRequest } from '@shared/types'; @Controller('student/assignments') @ApiTags('Student Assignments') @@ -74,7 +75,7 @@ export class StudentAssignmentsController { }, }) async getMyAssignments(@Query() query: any, @Request() req: AuthRequest) { - const studentId = req.user?.userId || req.user?.sub; + const studentId = req.user!.id; return this.assignmentsService.findStudentAssignments(studentId, { status: query.status, classroomId: query.classroomId, @@ -105,7 +106,7 @@ export class StudentAssignmentsController { description: 'Tarea no encontrada o no asignada a este estudiante', }) async getAssignmentDetails(@Param('id') id: string, @Request() req: AuthRequest) { - const studentId = req.user?.userId || req.user?.sub; + const studentId = req.user!.id; return this.assignmentsService.findStudentAssignmentById(id, studentId); } @@ -138,7 +139,7 @@ export class StudentAssignmentsController { }, }) async getGradesSummary(@Request() req: AuthRequest) { - const studentId = req.user?.userId || req.user?.sub; + const studentId = req.user!.id; return this.assignmentsService.getStudentGradesSummary(studentId); } } diff --git a/projects/gamilit/apps/backend/src/modules/auth/controllers/auth.controller.ts b/projects/gamilit/apps/backend/src/modules/auth/controllers/auth.controller.ts index f17cfeb..7f84b7d 100644 --- a/projects/gamilit/apps/backend/src/modules/auth/controllers/auth.controller.ts +++ b/projects/gamilit/apps/backend/src/modules/auth/controllers/auth.controller.ts @@ -29,6 +29,7 @@ import { UserSessionResponseDto, } from '../dto'; import { JwtAuthGuard } from '../guards/jwt-auth.guard'; +import { AuthRequest } from '@shared/types'; /** * AuthController @@ -131,8 +132,8 @@ export class AuthController { @ApiResponse({ status: 401, description: 'No autenticado' }) async logout(@Request() req: AuthRequest): Promise<{ message: string }> { // Extraer userId y sessionId del token JWT - const userId = req.user?.id; - const sessionId = req.user?.sessionId || 'current-session'; + const userId = req.user!.id; + const sessionId = req.user!.sessionId || 'current-session'; await this.authService.logout(userId, sessionId); return { message: 'Sesión cerrada exitosamente' }; @@ -177,7 +178,7 @@ export class AuthController { @ApiResponse({ status: 401, description: 'No autenticado' }) async getProfile(@Request() req: AuthRequest): Promise { // Extraer userId del token JWT - const userId = req.user?.id; + const userId = req.user!.id; const user = await this.authService.validateUser(userId); if (!user) { @@ -210,7 +211,7 @@ export class AuthController { @Body() dto: UpdateProfileDto, ): Promise { // Extraer userId del token JWT - const userId = req.user?.id; + const userId = req.user!.id; // Actualizar perfil usando el servicio de auth const updatedUser = await this.authService.updateUserProfile(userId, dto); @@ -344,7 +345,7 @@ export class AuthController { @Body('currentPassword') _currentPassword: string, @Body('newPassword') _newPassword: string, ): Promise<{ message: string }> { - const _userId = req.user?.id; + const _userId = req.user!.id; // TODO: Implementar lógica de cambio de contraseña return { message: 'Contraseña cambiada exitosamente' }; } @@ -364,7 +365,7 @@ export class AuthController { }) @ApiResponse({ status: 401, description: 'No autenticado' }) async getSessions(@Request() req: AuthRequest): Promise { - const userId = req.user?.id; + const userId = req.user!.id; return this.sessionService.getSessions(userId); } @@ -394,7 +395,7 @@ export class AuthController { @Request() req: AuthRequest, @Param('sessionId') sessionId: string, ): Promise<{ message: string }> { - const userId = req.user?.id; + const userId = req.user!.id; return this.sessionService.revokeSession(sessionId, userId); } @@ -421,8 +422,8 @@ export class AuthController { }) @ApiResponse({ status: 401, description: 'No autenticado' }) async revokeAllSessions(@Request() req: AuthRequest): Promise<{ message: string; count: number }> { - const userId = req.user?.id; - const currentSessionId = req.user?.sessionId || 'unknown'; + const userId = req.user!.id; + const currentSessionId = req.user!.sessionId || 'unknown'; return this.sessionService.revokeAllSessions(userId, currentSessionId); } } diff --git a/projects/gamilit/apps/backend/src/modules/auth/controllers/password.controller.ts b/projects/gamilit/apps/backend/src/modules/auth/controllers/password.controller.ts index 126dc24..63978af 100644 --- a/projects/gamilit/apps/backend/src/modules/auth/controllers/password.controller.ts +++ b/projects/gamilit/apps/backend/src/modules/auth/controllers/password.controller.ts @@ -26,6 +26,7 @@ import { import { API_ROUTES, extractBasePath } from '@/shared/constants'; import { JwtAuthGuard } from '../guards/jwt-auth.guard'; import { AuthService } from '../services/auth.service'; +import { AuthRequest } from '@shared/types'; /** * PasswordController @@ -144,7 +145,7 @@ export class PasswordController { @Body() dto: ChangePasswordDto, ): Promise<{ message: string }> { // Extraer userId del token JWT - const userId = req.user?.id; + const userId = req.user!.id; return this.authService.changePassword( userId, dto.current_password, @@ -203,7 +204,7 @@ export class PasswordController { @ApiResponse({ status: 409, description: 'Email ya verificado' }) async resendVerification(@Request() req: AuthRequest): Promise<{ message: string }> { // Extraer userId del token JWT - const userId = req.user?.id; + const userId = req.user!.id; return this.emailVerificationService.resendVerification(userId); } @@ -228,7 +229,7 @@ export class PasswordController { @ApiResponse({ status: 401, description: 'No autenticado' }) async checkVerificationStatus(@Request() req: AuthRequest): Promise<{ verified: boolean }> { // Extraer userId del token JWT - const userId = req.user?.id; + const userId = req.user!.id; return this.emailVerificationService.checkVerificationStatus(userId); } } diff --git a/projects/gamilit/apps/backend/src/modules/auth/controllers/users.controller.ts b/projects/gamilit/apps/backend/src/modules/auth/controllers/users.controller.ts index ecfdaff..a4a1bdb 100644 --- a/projects/gamilit/apps/backend/src/modules/auth/controllers/users.controller.ts +++ b/projects/gamilit/apps/backend/src/modules/auth/controllers/users.controller.ts @@ -27,6 +27,7 @@ import { UserResponseDto, } from '../dto'; import { JwtAuthGuard } from '../guards/jwt-auth.guard'; +import { AuthRequest } from '@shared/types'; /** * UsersController @@ -60,7 +61,7 @@ export class UsersController { }) @ApiResponse({ status: 401, description: 'No autenticado' }) async getProfile(@Request() req: AuthRequest): Promise { - const userId = req.user?.id; + const userId = req.user!.id; const user = await this.authService.validateUser(userId); if (!user) { @@ -91,7 +92,7 @@ export class UsersController { @Request() req: AuthRequest, @Body() dto: UpdateProfileDto, ): Promise { - const userId = req.user?.id; + const userId = req.user!.id; const updatedUser = await this.authService.updateUserProfile(userId, dto); if (!updatedUser) { @@ -120,7 +121,7 @@ export class UsersController { }) @ApiResponse({ status: 401, description: 'No autenticado' }) async getPreferences(@Request() req: AuthRequest): Promise<{ preferences: any }> { - const userId = req.user?.id; + const userId = req.user!.id; const preferences = await this.authService.getUserPreferences(userId); return { preferences }; } @@ -154,7 +155,7 @@ export class UsersController { @Request() req: AuthRequest, @Body('preferences') preferences: any, ): Promise<{ preferences: any }> { - const userId = req.user?.id; + const userId = req.user!.id; const updatedPreferences = await this.authService.updateUserPreferences( userId, preferences, @@ -197,7 +198,7 @@ export class UsersController { @Request() req: AuthRequest, @UploadedFile() file: any, ): Promise<{ avatar_url: string }> { - const userId = req.user?.id; + const userId = req.user!.id; if (!file) { throw new UnauthorizedException('No se proporcionó archivo'); @@ -231,7 +232,7 @@ export class UsersController { }) @ApiResponse({ status: 401, description: 'No autenticado' }) async getStatistics(@Request() req: AuthRequest): Promise { - const userId = req.user?.id; + const userId = req.user!.id; const statistics = await this.authService.getUserStatistics(userId); return statistics; } diff --git a/projects/gamilit/apps/backend/src/modules/educational/controllers/exercises.controller.ts b/projects/gamilit/apps/backend/src/modules/educational/controllers/exercises.controller.ts index 1d3ff2a..8dad6a7 100644 --- a/projects/gamilit/apps/backend/src/modules/educational/controllers/exercises.controller.ts +++ b/projects/gamilit/apps/backend/src/modules/educational/controllers/exercises.controller.ts @@ -30,6 +30,7 @@ import { ExerciseSubmissionService, ExerciseAttemptService } from '@/modules/pro import { ExerciseAnswerValidator } from '@/modules/progress/dto/answers/exercise-answer.validator'; import { JwtAuthGuard } from '@/modules/auth/guards/jwt-auth.guard'; import { Profile } from '@/modules/auth/entities'; +import { AuthRequest } from '@shared/types'; /** * ExercisesController @@ -221,8 +222,8 @@ export class ExercisesController { description: 'Error interno del servidor', }) async findAll(@Request() req: AuthRequest) { - const userId = req.user.id; - const userRole = req.user.role; + const userId = req.user!.id; + const userRole = req.user!.role; // GAP-C06: Aplicar RLS según el rol del usuario let exercises: any[]; @@ -373,7 +374,7 @@ export class ExercisesController { }, }) async findOne(@Param('id') id: string, @Request() req: AuthRequest) { - const userId = req.user.id; + const userId = req.user!.id; // Obtener ejercicio const exercise = await this.exercisesService.findById(id); @@ -676,7 +677,7 @@ export class ExercisesController { const exercises = await this.exercisesService.findByModuleId(moduleId); // FIX 2025-11-29: Convert auth.users.id → profiles.id - const userId = req.user.id; + const userId = req.user!.id; const profileId = await this.getProfileId(userId); // Obtener todas las submissions del usuario de una sola vez para eficiencia diff --git a/projects/gamilit/apps/backend/src/modules/educational/controllers/media-upload.controller.ts b/projects/gamilit/apps/backend/src/modules/educational/controllers/media-upload.controller.ts index 720e274..b7f8166 100644 --- a/projects/gamilit/apps/backend/src/modules/educational/controllers/media-upload.controller.ts +++ b/projects/gamilit/apps/backend/src/modules/educational/controllers/media-upload.controller.ts @@ -18,6 +18,7 @@ import { UploadMediaDto } from '../dto/upload-media.dto'; import { MediaAttachment } from '../entities/media-attachment.entity'; import { JwtAuthGuard } from '@modules/auth/guards/jwt-auth.guard'; import { Response } from 'express'; +import { AuthRequest } from '@shared/types'; /** * Controller para gestión de archivos multimedia @@ -84,7 +85,7 @@ export class MediaUploadController { @UploadedFile() file: any, @Body() dto: UploadMediaDto, ): Promise { - const userId = req.user.profileId; + const userId = req.user!.profile?.id || req.user!.id; return this.mediaService.uploadFile(file, userId, dto); } diff --git a/projects/gamilit/apps/backend/src/modules/educational/controllers/modules.controller.ts b/projects/gamilit/apps/backend/src/modules/educational/controllers/modules.controller.ts index 3e13fae..f90b3d9 100644 --- a/projects/gamilit/apps/backend/src/modules/educational/controllers/modules.controller.ts +++ b/projects/gamilit/apps/backend/src/modules/educational/controllers/modules.controller.ts @@ -18,6 +18,7 @@ import { CreateModuleDto, ModuleResponseDto, GetModulesQueryDto } from '../dto'; import { API_ROUTES, extractBasePath } from '@/shared/constants'; import { DifficultyLevelEnum } from '@/shared/constants/enums.constants'; import { JwtAuthGuard } from '@/modules/auth/guards/jwt-auth.guard'; +import { AuthRequest } from '@shared/types'; /** * ModulesController @@ -111,7 +112,7 @@ export class ModulesController { description: 'Error interno del servidor', }) async findAll(@Request() req: AuthRequest, @Query() query: GetModulesQueryDto) { - const userId = req.user.id; + const userId = req.user!.id; // Reutilizar getUserModules que ya tiene la lógica correcta para ambas tablas // Ahora acepta classroomId opcional para filtrar return this.modulesService.getUserModules(userId, query.classroomId); diff --git a/projects/gamilit/apps/backend/src/modules/educational/services/exercises.service.ts b/projects/gamilit/apps/backend/src/modules/educational/services/exercises.service.ts index 2178a6d..4e87fe7 100644 --- a/projects/gamilit/apps/backend/src/modules/educational/services/exercises.service.ts +++ b/projects/gamilit/apps/backend/src/modules/educational/services/exercises.service.ts @@ -173,7 +173,7 @@ export class ExercisesService { this.validateContentByExerciseType(exerciseType, content, config); } - await this.exerciseRepo.update(id, exerciseData); + await this.exerciseRepo.update(id, exerciseData as any); const updated = await this.findById(id); if (!updated) { throw new NotFoundException(`Exercise with ID ${id} not found after update`); @@ -230,8 +230,9 @@ export class ExercisesService { } // Validar que grid tenga dimensiones correctas - const rows = config?.gridSize?.rows || 0; - const cols = config?.gridSize?.cols || 0; + const gridSize = config?.gridSize as { rows?: number; cols?: number } | undefined; + const rows = gridSize?.rows || 0; + const cols = gridSize?.cols || 0; if (content.grid.length !== rows) { throw new BadRequestException( @@ -526,11 +527,13 @@ export class ExercisesService { } // ✅ FE-060: Use config.gridSize directly (passed as parameter) - const gridSize = config?.gridSize || { rows: 15, cols: 15 }; + const gridSizeRaw = config?.gridSize as { rows?: number; cols?: number } | undefined; + const gridSize = gridSizeRaw || { rows: 15, cols: 15 }; + const cluesArray = (sanitized.clues || []) as Array<{ startRow: number; startCol: number; direction: string; length: number; number?: number }>; sanitized.grid = this.generateEmptyGrid( gridSize.rows || 15, gridSize.cols || 15, - sanitized.clues || [], + cluesArray, ); sanitized.gridConfig = gridSize; @@ -539,11 +542,11 @@ export class ExercisesService { hasConfig: !!config, gridSizeFromConfig: config?.gridSize, gridSizeUsed: gridSize, - gridGenerated: `${gridSize.rows}x${gridSize.cols}`, + gridGenerated: `${gridSize.rows || 15}x${gridSize.cols || 15}`, hasGrid: !!sanitized.grid, gridIsArray: Array.isArray(sanitized.grid), - gridDimensions: sanitized.grid?.length ? `${sanitized.grid.length}x${sanitized.grid[0]?.length}` : 'N/A', - cluesCount: sanitized.clues?.length, + gridDimensions: sanitized.grid?.length ? `${(sanitized.grid as unknown[]).length}x${(sanitized.grid as unknown[][])[0]?.length}` : 'N/A', + cluesCount: cluesArray.length, }); break; } diff --git a/projects/gamilit/apps/backend/src/modules/educational/services/media.service.ts b/projects/gamilit/apps/backend/src/modules/educational/services/media.service.ts index 1967ec5..4c4cdc5 100644 --- a/projects/gamilit/apps/backend/src/modules/educational/services/media.service.ts +++ b/projects/gamilit/apps/backend/src/modules/educational/services/media.service.ts @@ -62,7 +62,7 @@ export class MediaService { throw new NotFoundException(`Media resource with ID ${id} not found`); } - await this.mediaRepo.update(id, mediaData); + await this.mediaRepo.update(id, mediaData as any); const updated = await this.findById(id); if (!updated) { throw new NotFoundException(`Media resource with ID ${id} not found after update`); @@ -107,7 +107,7 @@ export class MediaService { }; } - await this.mediaRepo.update(id, updateData); + await this.mediaRepo.update(id, updateData as any); const updated = await this.findById(id); if (!updated) { throw new NotFoundException(`Media resource with ID ${id} not found after update`); diff --git a/projects/gamilit/apps/backend/src/modules/educational/services/modules.service.ts b/projects/gamilit/apps/backend/src/modules/educational/services/modules.service.ts index 5d05ab7..f66b171 100644 --- a/projects/gamilit/apps/backend/src/modules/educational/services/modules.service.ts +++ b/projects/gamilit/apps/backend/src/modules/educational/services/modules.service.ts @@ -57,7 +57,7 @@ export class ModulesService { * Actualizar un módulo existente */ async update(id: string, moduleData: Partial): Promise { - await this.moduleRepo.update(id, moduleData); + await this.moduleRepo.update(id, moduleData as any); return this.findById(id); } diff --git a/projects/gamilit/apps/backend/src/modules/gamification/controllers/classroom-missions.controller.ts b/projects/gamilit/apps/backend/src/modules/gamification/controllers/classroom-missions.controller.ts index d2461d5..dae85a6 100644 --- a/projects/gamilit/apps/backend/src/modules/gamification/controllers/classroom-missions.controller.ts +++ b/projects/gamilit/apps/backend/src/modules/gamification/controllers/classroom-missions.controller.ts @@ -16,6 +16,7 @@ import { ClassroomMissionsService } from '../services/classroom-missions.service import { AssignClassroomMissionDto } from '../dto/missions/assign-classroom-mission.dto'; import { ClassroomMissionResponseDto } from '../dto/missions/classroom-mission-response.dto'; import { JwtAuthGuard } from '@/modules/auth/guards'; +import { AuthRequest } from '@shared/types'; /** * ClassroomMissionsController @@ -143,7 +144,7 @@ export class ClassroomMissionsController { @Body() dto: AssignClassroomMissionDto, @Request() req: AuthRequest, ) { - const teacherId = req.user.id; + const teacherId = req.user!.id; return this.classroomMissionsService.assignMissionToClassroom(classroomId, teacherId, dto); } diff --git a/projects/gamilit/apps/backend/src/modules/gamification/controllers/mission-templates.controller.ts b/projects/gamilit/apps/backend/src/modules/gamification/controllers/mission-templates.controller.ts index 4481e79..0bf72c6 100644 --- a/projects/gamilit/apps/backend/src/modules/gamification/controllers/mission-templates.controller.ts +++ b/projects/gamilit/apps/backend/src/modules/gamification/controllers/mission-templates.controller.ts @@ -22,6 +22,7 @@ import { } from '../dto/mission-templates'; import { JwtAuthGuard } from '@/modules/auth/guards'; import { AdminGuard } from '@/modules/admin/guards/admin.guard'; +import { AuthRequest } from '@shared/types'; /** * MissionTemplatesController @@ -205,7 +206,7 @@ export class MissionTemplatesController { description: 'Datos inválidos', }) async create(@Body() dto: CreateMissionTemplateDto, @Request() req: AuthRequest) { - const createdBy = req.user.id; + const createdBy = req.user!.id; return this.templatesService.create(dto, createdBy); } diff --git a/projects/gamilit/apps/backend/src/modules/gamification/controllers/missions.controller.ts b/projects/gamilit/apps/backend/src/modules/gamification/controllers/missions.controller.ts index 5a64cc4..fdd7f16 100644 --- a/projects/gamilit/apps/backend/src/modules/gamification/controllers/missions.controller.ts +++ b/projects/gamilit/apps/backend/src/modules/gamification/controllers/missions.controller.ts @@ -18,6 +18,7 @@ import { MissionResponseDto } from '../dto/missions/mission-response.dto'; import { MissionStatsDto } from '../dto/missions/mission-stats.dto'; import { UpdateMissionProgressDto } from '../dto/missions/update-mission-progress.dto'; import { JwtAuthGuard } from '@/modules/auth/guards'; +import { AuthRequest } from '@shared/types'; /** * MissionsController @@ -111,7 +112,7 @@ export class MissionsController { description: 'No autenticado - Token JWT inválido o ausente', }) async getDailyMissions(@Request() req: AuthRequest) { - const userId = req.user.id; + const userId = req.user!.id; return this.missionsService.findByTypeAndUser(userId, MissionTypeEnum.DAILY); } @@ -180,7 +181,7 @@ export class MissionsController { description: 'No autenticado - Token JWT inválido o ausente', }) async getWeeklyMissions(@Request() req: AuthRequest) { - const userId = req.user.id; + const userId = req.user!.id; return this.missionsService.findByTypeAndUser(userId, MissionTypeEnum.WEEKLY); } @@ -235,7 +236,7 @@ export class MissionsController { description: 'No autenticado - Token JWT inválido o ausente', }) async getSpecialMissions(@Request() req: AuthRequest) { - const userId = req.user.id; + const userId = req.user!.id; return this.missionsService.findByTypeAndUser(userId, MissionTypeEnum.SPECIAL); } @@ -299,7 +300,7 @@ export class MissionsController { }) async getStats(@Param('userId') userId: string, @Request() req: AuthRequest) { // Validar que el userId coincide con el usuario autenticado - if (userId !== req.user.id) { + if (userId !== req.user!.id) { throw new HttpException('Forbidden: Cannot access stats of another user', HttpStatus.FORBIDDEN); } @@ -361,7 +362,7 @@ export class MissionsController { description: 'Misión no encontrada', }) async startMission(@Param('id') missionId: string, @Request() req: AuthRequest) { - const userId = req.user.id; + const userId = req.user!.id; return this.missionsService.startMission(missionId, userId); } @@ -449,7 +450,7 @@ export class MissionsController { @Body() dto: UpdateMissionProgressDto, @Request() req: AuthRequest, ) { - const userId = req.user.id; + const userId = req.user!.id; return this.missionsService.updateProgress( missionId, userId, @@ -567,7 +568,7 @@ export class MissionsController { description: 'Misión no encontrada', }) async claimRewards(@Param('id') missionId: string, @Request() req: AuthRequest) { - const userId = req.user.id; + const userId = req.user!.id; const result = await this.missionsService.claimRewards(missionId, userId); return { success: true, data: result }; } diff --git a/projects/gamilit/apps/backend/src/modules/gamification/controllers/ranks.controller.ts b/projects/gamilit/apps/backend/src/modules/gamification/controllers/ranks.controller.ts index 6a38236..2dbb7da 100644 --- a/projects/gamilit/apps/backend/src/modules/gamification/controllers/ranks.controller.ts +++ b/projects/gamilit/apps/backend/src/modules/gamification/controllers/ranks.controller.ts @@ -27,6 +27,7 @@ import { JwtAuthGuard } from '@modules/auth/guards/jwt-auth.guard'; import { RolesGuard } from '@shared/guards/roles.guard'; import { Roles } from '@shared/decorators/roles.decorator'; import { UserRank } from '../entities'; +import { AuthRequest } from '@shared/types'; /** * RankMetadataDto @@ -111,7 +112,7 @@ export class RanksController { @ApiResponse({ status: 401, description: 'No autenticado' }) @ApiResponse({ status: 404, description: 'Usuario sin rango inicializado' }) async getCurrentRank(@Request() req: AuthRequest): Promise { - const userId = req.user.sub; + const userId = req.user!.id; return this.ranksService.getCurrentRank(userId); } diff --git a/projects/gamilit/apps/backend/src/modules/gamification/controllers/user-stats.controller.ts b/projects/gamilit/apps/backend/src/modules/gamification/controllers/user-stats.controller.ts index 161172c..b2cf648 100644 --- a/projects/gamilit/apps/backend/src/modules/gamification/controllers/user-stats.controller.ts +++ b/projects/gamilit/apps/backend/src/modules/gamification/controllers/user-stats.controller.ts @@ -287,23 +287,25 @@ export class UserStatsController { const stats = await this.userStatsService.findByUserId(userId); // Handle total_xp_increment - if (updateData.total_xp_increment !== undefined) { - const newXP = stats.total_xp + updateData.total_xp_increment; - updateData.total_xp = newXP; + if (updateData.total_xp_increment !== undefined && updateData.total_xp_increment !== null) { + const increment = Number(updateData.total_xp_increment) || 0; + updateData.total_xp = stats.total_xp + increment; delete updateData.total_xp_increment; } // Handle ml_coins_increment - if (updateData.ml_coins_increment !== undefined) { - updateData.ml_coins = stats.ml_coins + updateData.ml_coins_increment; - updateData.ml_coins_earned_total = stats.ml_coins_earned_total + updateData.ml_coins_increment; + if (updateData.ml_coins_increment !== undefined && updateData.ml_coins_increment !== null) { + const increment = Number(updateData.ml_coins_increment) || 0; + updateData.ml_coins = stats.ml_coins + increment; + updateData.ml_coins_earned_total = stats.ml_coins_earned_total + increment; delete updateData.ml_coins_increment; } // Handle ml_coins_decrement - if (updateData.ml_coins_decrement !== undefined) { - updateData.ml_coins = stats.ml_coins - updateData.ml_coins_decrement; - updateData.ml_coins_spent_total = stats.ml_coins_spent_total + updateData.ml_coins_decrement; + if (updateData.ml_coins_decrement !== undefined && updateData.ml_coins_decrement !== null) { + const decrement = Number(updateData.ml_coins_decrement) || 0; + updateData.ml_coins = stats.ml_coins - decrement; + updateData.ml_coins_spent_total = stats.ml_coins_spent_total + decrement; delete updateData.ml_coins_decrement; } diff --git a/projects/gamilit/apps/backend/src/modules/gamification/services/achievements.service.ts b/projects/gamilit/apps/backend/src/modules/gamification/services/achievements.service.ts index 72944c6..0cce093 100644 --- a/projects/gamilit/apps/backend/src/modules/gamification/services/achievements.service.ts +++ b/projects/gamilit/apps/backend/src/modules/gamification/services/achievements.service.ts @@ -228,8 +228,9 @@ export class AchievementsService { const grantDto = new GrantAchievementDto(); grantDto.user_id = userId; grantDto.achievement_id = achievement.id; - grantDto.progress = achievement.conditions.progress || achievement.conditions.max_progress || 100; - grantDto.max_progress = achievement.conditions.max_progress || 100; + const conditionsTyped = achievement.conditions as { progress?: number; max_progress?: number }; + grantDto.progress = conditionsTyped.progress || conditionsTyped.max_progress || 100; + grantDto.max_progress = conditionsTyped.max_progress || 100; grantDto.is_completed = true; grantDto.progress_data = { auto_detected: true }; @@ -245,32 +246,44 @@ export class AchievementsService { * Evalúa si las estadísticas del usuario cumplen con las condiciones del logro */ private meetsConditions(userStats: UserStats, conditions: Record): boolean { - const type = conditions.type || 'generic'; + // Type cast for accessing condition properties + const cond = conditions as { + type?: string; + exercises_completed?: number; + modules_completed?: number; + min_streak?: number; + min_level?: number; + min_average_score?: number; + min_perfect_scores?: number; + target_rank?: string; + min_coins_earned?: number; + }; + const type = cond.type || 'generic'; switch (type) { case 'progress': return ( - userStats.exercises_completed >= (conditions.exercises_completed || 0) && - userStats.modules_completed >= (conditions.modules_completed || 0) + userStats.exercises_completed >= (cond.exercises_completed || 0) && + userStats.modules_completed >= (cond.modules_completed || 0) ); case 'streak': - return userStats.current_streak >= (conditions.min_streak || 0); + return userStats.current_streak >= (cond.min_streak || 0); case 'level': - return userStats.level >= (conditions.min_level || 0); + return userStats.level >= (cond.min_level || 0); case 'score': return ( - (userStats.average_score || 0) >= (conditions.min_average_score || 0) && - userStats.perfect_scores >= (conditions.min_perfect_scores || 0) + (userStats.average_score || 0) >= (cond.min_average_score || 0) && + userStats.perfect_scores >= (cond.min_perfect_scores || 0) ); case 'rank': - return this.userReachedRank(userStats.current_rank, conditions.target_rank); + return this.userReachedRank(userStats.current_rank, cond.target_rank || ''); case 'ml_coins': - return userStats.ml_coins_earned_total >= (conditions.min_coins_earned || 0); + return userStats.ml_coins_earned_total >= (cond.min_coins_earned || 0); default: return false; diff --git a/projects/gamilit/apps/backend/src/modules/gamification/services/missions/mission-claim.service.ts b/projects/gamilit/apps/backend/src/modules/gamification/services/missions/mission-claim.service.ts index add5a59..2deac7e 100644 --- a/projects/gamilit/apps/backend/src/modules/gamification/services/missions/mission-claim.service.ts +++ b/projects/gamilit/apps/backend/src/modules/gamification/services/missions/mission-claim.service.ts @@ -108,16 +108,14 @@ export class MissionClaimService { const mlCoinsReward = mission.rewards?.ml_coins || 0; if (mlCoinsReward > 0) { try { - await this.mlCoinsService.addTransaction({ - user_id: profileId, - amount: mlCoinsReward, - type: TransactionTypeEnum.REWARD, - description: `Mission completed: ${mission.title}`, - metadata: { - mission_id: mission.id, - mission_type: mission.mission_type, - }, - }); + await this.mlCoinsService.addCoins( + profileId, + mlCoinsReward, + TransactionTypeEnum.EARNED_BONUS, + `Mission completed: ${mission.title}`, + mission.id, + 'mission', + ); } catch (error) { this.logger.error( `Failed to add ML Coins for mission ${missionId}: ${error}`, @@ -132,7 +130,7 @@ export class MissionClaimService { if (xpReward > 0) { try { - const statsUpdate = await this.userStatsService.addXP(profileId, xpReward); + const statsUpdate = await this.userStatsService.addXp(profileId, xpReward); if (statsUpdate.rankUp) { rankUp = { diff --git a/projects/gamilit/apps/backend/src/modules/gamification/services/missions/mission-generator.service.ts b/projects/gamilit/apps/backend/src/modules/gamification/services/missions/mission-generator.service.ts index 4c922bf..d1977d7 100644 --- a/projects/gamilit/apps/backend/src/modules/gamification/services/missions/mission-generator.service.ts +++ b/projects/gamilit/apps/backend/src/modules/gamification/services/missions/mission-generator.service.ts @@ -27,7 +27,7 @@ import { MissionObjective, MissionRewards, } from '../../entities/mission.entity'; -import { MissionTemplate } from '../../entities/mission-template.entity'; +import { MissionTemplate, MissionTemplateTypeEnum } from '../../entities/mission-template.entity'; import { MissionTemplatesService } from '../mission-templates.service'; /** @@ -67,8 +67,8 @@ export class MissionGeneratorService { await this.expireMissions(profileId, MissionTypeEnum.DAILY); // Get available templates for user's level - const templates = await this.templatesService.getActiveByTypeAndLevel( - 'daily', + const templates = await this.templatesService.getActiveByType( + 'daily' as MissionTemplateTypeEnum, userLevel, ); @@ -116,8 +116,8 @@ export class MissionGeneratorService { await this.expireMissions(profileId, MissionTypeEnum.WEEKLY); // Get available weekly templates - const templates = await this.templatesService.getActiveByTypeAndLevel( - 'weekly', + const templates = await this.templatesService.getActiveByType( + 'weekly' as MissionTemplateTypeEnum, userLevel, ); diff --git a/projects/gamilit/apps/backend/src/modules/notifications/controllers/notification-devices.controller.ts b/projects/gamilit/apps/backend/src/modules/notifications/controllers/notification-devices.controller.ts index 0402704..c751c42 100644 --- a/projects/gamilit/apps/backend/src/modules/notifications/controllers/notification-devices.controller.ts +++ b/projects/gamilit/apps/backend/src/modules/notifications/controllers/notification-devices.controller.ts @@ -28,6 +28,7 @@ import { DeviceResponseDto, DevicesListResponseDto, } from '../dto/devices'; +import { AuthRequest } from '@shared/types'; /** * NotificationDevicesController @@ -146,7 +147,7 @@ export class NotificationDevicesController { @Body() registerDto: RegisterDeviceDto, @Request() req: AuthRequest, ): Promise { - const userId = req.user.sub; + const userId = req.user!.id; const device = await this.deviceService.registerDevice({ userId, @@ -186,7 +187,7 @@ export class NotificationDevicesController { description: 'No autenticado', }) async getUserDevices(@Request() req: AuthRequest): Promise { - const userId = req.user.sub; + const userId = req.user!.id; const devices = await this.deviceService.getUserDevices(userId, false); // Ocultar parcialmente los tokens @@ -236,7 +237,7 @@ export class NotificationDevicesController { @Param('id') deviceId: string, @Request() req: AuthRequest, ): Promise { - const userId = req.user.sub; + const userId = req.user!.id; const device = await this.deviceService.getDeviceById(deviceId, userId); return { @@ -288,7 +289,7 @@ export class NotificationDevicesController { @Body() updateDto: UpdateDeviceNameDto, @Request() req: AuthRequest, ): Promise { - const userId = req.user.sub; + const userId = req.user!.id; const device = await this.deviceService.updateDeviceName( deviceId, @@ -347,7 +348,7 @@ export class NotificationDevicesController { @Param('id') deviceId: string, @Request() req: AuthRequest, ): Promise { - const userId = req.user.sub; + const userId = req.user!.id; await this.deviceService.deleteDevice(deviceId, userId); } diff --git a/projects/gamilit/apps/backend/src/modules/notifications/controllers/notification-multichannel.controller.ts b/projects/gamilit/apps/backend/src/modules/notifications/controllers/notification-multichannel.controller.ts index 7a2c50a..33a1e1b 100644 --- a/projects/gamilit/apps/backend/src/modules/notifications/controllers/notification-multichannel.controller.ts +++ b/projects/gamilit/apps/backend/src/modules/notifications/controllers/notification-multichannel.controller.ts @@ -20,6 +20,7 @@ import { SendFromTemplateDto, NotificationResponseDto, } from '../dto/notifications'; +import { AuthRequest } from '@shared/types'; /** * NotificationMultiChannelController @@ -111,7 +112,7 @@ export class NotificationMultiChannelController { ): Promise { // Validar que el usuario solo crea notificaciones para sí mismo // (a menos que sea admin - validación futura) - if (createDto.userId !== req.user.sub) { + if (createDto.userId !== req.user!.id) { // TODO: Permitir si es admin throw new Error('Cannot create notifications for other users'); } @@ -195,7 +196,7 @@ export class NotificationMultiChannelController { @Request() req: AuthRequest, ): Promise { // Validar ownership - if (sendDto.userId !== req.user.sub) { + if (sendDto.userId !== req.user!.id) { // TODO: Permitir si es admin throw new Error('Cannot create notifications for other users'); } diff --git a/projects/gamilit/apps/backend/src/modules/notifications/controllers/notification-preferences.controller.ts b/projects/gamilit/apps/backend/src/modules/notifications/controllers/notification-preferences.controller.ts index a62ffd6..650e614 100644 --- a/projects/gamilit/apps/backend/src/modules/notifications/controllers/notification-preferences.controller.ts +++ b/projects/gamilit/apps/backend/src/modules/notifications/controllers/notification-preferences.controller.ts @@ -24,6 +24,7 @@ import { PreferenceResponseDto, PreferencesListResponseDto, } from '../dto/preferences'; +import { AuthRequest } from '@shared/types'; /** * NotificationPreferencesController @@ -84,7 +85,7 @@ export class NotificationPreferencesController { async getUserPreferences( @Request() req: AuthRequest, ): Promise { - const userId = req.user.sub; + const userId = req.user!.id; const preferences = await this.preferenceService.getUserPreferences(userId); return { @@ -130,7 +131,7 @@ export class NotificationPreferencesController { @Body() updateDto: UpdatePreferenceDto, @Request() req: AuthRequest, ): Promise { - const userId = req.user.sub; + const userId = req.user!.id; const preference = await this.preferenceService.updatePreference( userId, @@ -177,7 +178,7 @@ export class NotificationPreferencesController { @Body() updateDto: UpdateMultiplePreferencesDto, @Request() req: AuthRequest, ): Promise { - const userId = req.user.sub; + const userId = req.user!.id; const preferences = await this.preferenceService.updateMultiple( userId, diff --git a/projects/gamilit/apps/backend/src/modules/progress/controllers/exercise-submission.controller.ts b/projects/gamilit/apps/backend/src/modules/progress/controllers/exercise-submission.controller.ts index 90560ae..e95d089 100644 --- a/projects/gamilit/apps/backend/src/modules/progress/controllers/exercise-submission.controller.ts +++ b/projects/gamilit/apps/backend/src/modules/progress/controllers/exercise-submission.controller.ts @@ -32,6 +32,7 @@ import { JwtAuthGuard } from '@/modules/auth/guards/jwt-auth.guard'; import { RolesGuard } from '@/shared/guards/roles.guard'; import { Roles } from '@/shared/decorators/roles.decorator'; import { GamilityRoleEnum } from '@/shared/constants/enums.constants'; +import { AuthRequest } from '@shared/types'; /** * ExerciseSubmissionController diff --git a/projects/gamilit/apps/backend/src/modules/teacher/controllers/exercise-responses.controller.ts b/projects/gamilit/apps/backend/src/modules/teacher/controllers/exercise-responses.controller.ts index 2d4507e..5986abc 100644 --- a/projects/gamilit/apps/backend/src/modules/teacher/controllers/exercise-responses.controller.ts +++ b/projects/gamilit/apps/backend/src/modules/teacher/controllers/exercise-responses.controller.ts @@ -31,6 +31,7 @@ import { AttemptDetailDto, AttemptsListResponseDto, } from '../dto/exercise-responses.dto'; +import { AuthRequest } from '@shared/types'; /** * Controller for Teacher Exercise Responses diff --git a/projects/gamilit/apps/backend/src/modules/teacher/controllers/manual-review.controller.ts b/projects/gamilit/apps/backend/src/modules/teacher/controllers/manual-review.controller.ts index 96110e6..eada796 100644 --- a/projects/gamilit/apps/backend/src/modules/teacher/controllers/manual-review.controller.ts +++ b/projects/gamilit/apps/backend/src/modules/teacher/controllers/manual-review.controller.ts @@ -17,6 +17,7 @@ import { JwtAuthGuard } from '@modules/auth/guards/jwt-auth.guard'; import { RolesGuard } from '@modules/auth/guards/roles.guard'; import { Roles } from '@modules/auth/decorators/roles.decorator'; import { GamilityRoleEnum } from '@shared/constants/enums.constants'; +import { AuthRequest } from '@shared/types'; /** * Controller para gestión de evaluaciones manuales (ManualReview) @@ -114,7 +115,10 @@ export class ManualReviewController { @Request() req: AuthRequest, @Body() dto: CreateReviewDto, ): Promise { - const teacherId = req.user.profileId; + const teacherId = req.user?.profile?.id || req.user?.sub || req.user?.id; + if (!teacherId) { + throw new Error('Teacher ID not found in request'); + } return this.reviewService.createReview(teacherId, dto); } diff --git a/projects/gamilit/apps/backend/src/modules/teacher/controllers/teacher-classrooms.controller.ts b/projects/gamilit/apps/backend/src/modules/teacher/controllers/teacher-classrooms.controller.ts index c300039..900ef24 100644 --- a/projects/gamilit/apps/backend/src/modules/teacher/controllers/teacher-classrooms.controller.ts +++ b/projects/gamilit/apps/backend/src/modules/teacher/controllers/teacher-classrooms.controller.ts @@ -28,6 +28,7 @@ import { UpdatePermissionsDto, StudentPermissionsResponseDto, } from '../dto/student-blocking'; +import { AuthRequest } from '@shared/types'; import { CreateTeacherClassroomDto, UpdateTeacherClassroomDto, @@ -119,7 +120,7 @@ export class TeacherClassroomsController { @Query() query: GetClassroomsQueryDto, @Request() req: AuthRequest, ): Promise { - const teacherId = req.user.sub; + const teacherId = req.user!.id; return this.classroomsCrudService.getClassrooms(teacherId, query); } @@ -154,7 +155,7 @@ export class TeacherClassroomsController { @Body() dto: CreateTeacherClassroomDto, @Request() req: AuthRequest, ): Promise { - const teacherId = req.user.sub; + const teacherId = req.user!.id; return this.classroomsCrudService.createClassroom(teacherId, dto); } @@ -194,7 +195,7 @@ export class TeacherClassroomsController { @Param('id') id: string, @Request() req: AuthRequest, ): Promise { - const teacherId = req.user.sub; + const teacherId = req.user!.id; return this.classroomsCrudService.getClassroomById(id, teacherId); } @@ -240,7 +241,7 @@ export class TeacherClassroomsController { @Body() dto: UpdateTeacherClassroomDto, @Request() req: AuthRequest, ): Promise { - const teacherId = req.user.sub; + const teacherId = req.user!.id; return this.classroomsCrudService.updateClassroom(id, teacherId, dto); } @@ -289,7 +290,7 @@ export class TeacherClassroomsController { @Param('id') id: string, @Request() req: AuthRequest, ): Promise<{ success: boolean; message: string }> { - const teacherId = req.user.sub; + const teacherId = req.user!.id; return this.classroomsCrudService.deleteClassroom(id, teacherId); } @@ -345,7 +346,7 @@ export class TeacherClassroomsController { @Query() query: GetClassroomStudentsQueryDto, @Request() req: AuthRequest, ): Promise { - const teacherId = req.user.sub; + const teacherId = req.user!.id; return this.classroomsCrudService.getClassroomStudents(id, teacherId, query); } @@ -385,7 +386,7 @@ export class TeacherClassroomsController { @Param('id') id: string, @Request() req: AuthRequest, ): Promise { - const teacherId = req.user.sub; + const teacherId = req.user!.id; return this.classroomsCrudService.getClassroomStats(id, teacherId); } @@ -425,7 +426,7 @@ export class TeacherClassroomsController { @Param('classroomId') classroomId: string, @Request() req: AuthRequest, ): Promise { - const teacherId = req.user.sub; + const teacherId = req.user!.id; return this.classroomsCrudService.getClassroomTeachers(classroomId, teacherId); } @@ -490,7 +491,7 @@ export class TeacherClassroomsController { @Param('id') id: string, @Request() req: AuthRequest, ): Promise { - const teacherId = req.user.sub; + const teacherId = req.user!.id; return this.classroomsCrudService.getClassroomProgress(id, teacherId); } @@ -546,7 +547,7 @@ export class TeacherClassroomsController { @Body() dto: BlockStudentDto, @Request() req: AuthRequest, ): Promise { - const teacherId = req.user.sub; + const teacherId = req.user!.id; return this.studentBlockingService.blockStudent( classroomId, studentId, @@ -601,7 +602,7 @@ export class TeacherClassroomsController { @Param('studentId') studentId: string, @Request() req: AuthRequest, ): Promise { - const teacherId = req.user.sub; + const teacherId = req.user!.id; return this.studentBlockingService.unblockStudent( classroomId, studentId, @@ -651,7 +652,7 @@ export class TeacherClassroomsController { @Param('studentId') studentId: string, @Request() req: AuthRequest, ): Promise { - const teacherId = req.user.sub; + const teacherId = req.user!.id; return this.studentBlockingService.getStudentPermissions( classroomId, studentId, @@ -707,7 +708,7 @@ export class TeacherClassroomsController { @Body() dto: UpdatePermissionsDto, @Request() req: AuthRequest, ): Promise { - const teacherId = req.user.sub; + const teacherId = req.user!.id; return this.studentBlockingService.updateStudentPermissions( classroomId, studentId, diff --git a/projects/gamilit/apps/backend/src/modules/teacher/controllers/teacher-content.controller.ts b/projects/gamilit/apps/backend/src/modules/teacher/controllers/teacher-content.controller.ts index 1180546..3e505c0 100644 --- a/projects/gamilit/apps/backend/src/modules/teacher/controllers/teacher-content.controller.ts +++ b/projects/gamilit/apps/backend/src/modules/teacher/controllers/teacher-content.controller.ts @@ -29,6 +29,7 @@ import { import { JwtAuthGuard } from '@modules/auth/guards/jwt-auth.guard'; import { TeacherGuard } from '../guards'; import { TeacherContentService } from '../services/teacher-content.service'; +import { AuthRequest } from '@shared/types'; import { CreateTeacherContentDto, UpdateTeacherContentDto, @@ -115,7 +116,7 @@ export class TeacherContentController { @Query() query: GetTeacherContentQueryDto, @Request() req: AuthRequest, ): Promise { - const teacherId = req.user.sub; + const teacherId = req.user!.id; return this.contentService.findAll(teacherId, query); } @@ -152,7 +153,7 @@ export class TeacherContentController { description: 'Forbidden - Teacher is not the owner of this content', }) async findOne(@Param('id') id: string, @Request() req: AuthRequest): Promise { - const teacherId = req.user.sub; + const teacherId = req.user!.id; return this.contentService.findOne(id, teacherId); } @@ -195,7 +196,7 @@ export class TeacherContentController { @Body() dto: CreateTeacherContentDto, @Request() req: AuthRequest, ): Promise { - const teacherId = req.user.sub; + const teacherId = req.user!.id; return this.contentService.create(teacherId, dto); } @@ -245,7 +246,7 @@ export class TeacherContentController { @Body() dto: UpdateTeacherContentDto, @Request() req: AuthRequest, ): Promise { - const teacherId = req.user.sub; + const teacherId = req.user!.id; return this.contentService.update(id, teacherId, dto); } @@ -294,7 +295,7 @@ export class TeacherContentController { @Param('id') id: string, @Request() req: AuthRequest, ): Promise<{ success: boolean; message: string }> { - const teacherId = req.user.sub; + const teacherId = req.user!.id; return this.contentService.delete(id, teacherId); } @@ -340,7 +341,7 @@ export class TeacherContentController { @Body() dto: CloneTeacherContentDto, @Request() req: AuthRequest, ): Promise { - const teacherId = req.user.sub; + const teacherId = req.user!.id; return this.contentService.clone(id, teacherId, dto); } @@ -388,7 +389,7 @@ export class TeacherContentController { @Param('id') id: string, @Request() req: AuthRequest, ): Promise { - const teacherId = req.user.sub; + const teacherId = req.user!.id; return this.contentService.publish(id, teacherId); } } diff --git a/projects/gamilit/apps/backend/src/modules/teacher/controllers/teacher.controller.ts b/projects/gamilit/apps/backend/src/modules/teacher/controllers/teacher.controller.ts index 8ec2239..657d362 100644 --- a/projects/gamilit/apps/backend/src/modules/teacher/controllers/teacher.controller.ts +++ b/projects/gamilit/apps/backend/src/modules/teacher/controllers/teacher.controller.ts @@ -22,6 +22,7 @@ import { JwtAuthGuard } from '@/modules/auth/guards/jwt-auth.guard'; import { RolesGuard } from '@/modules/auth/guards/roles.guard'; import { Roles } from '@/modules/auth/decorators/roles.decorator'; import { GamilityRoleEnum } from '@/shared/constants/enums.constants'; +import { AuthRequest } from '@shared/types'; import { TeacherDashboardService, StudentProgressService, @@ -80,14 +81,14 @@ export class TeacherController { @Get('dashboard/stats') @ApiOperation({ summary: 'Get classroom statistics' }) async getClassroomStats(@Request() req: AuthRequest) { - const teacherId = req.user.id; + const teacherId = req.user!.id; return this.dashboardService.getClassroomStats(teacherId); } @Get('dashboard/activities') @ApiOperation({ summary: 'Get recent activities' }) async getRecentActivities(@Request() req: AuthRequest, @Query('limit') limit?: number) { - const teacherId = req.user.id; + const teacherId = req.user!.id; return this.dashboardService.getRecentActivities( teacherId, limit ? parseInt(limit as any) : 10, @@ -97,14 +98,14 @@ export class TeacherController { @Get('dashboard/alerts') @ApiOperation({ summary: 'Get student alerts' }) async getStudentAlerts(@Request() req: AuthRequest) { - const teacherId = req.user.id; + const teacherId = req.user!.id; return this.dashboardService.getStudentAlerts(teacherId); } @Get('dashboard/top-performers') @ApiOperation({ summary: 'Get top performing students' }) async getTopPerformers(@Request() req: AuthRequest, @Query('limit') limit?: number) { - const teacherId = req.user.id; + const teacherId = req.user!.id; return this.dashboardService.getTopPerformers( teacherId, limit ? parseInt(limit as any) : 5, @@ -114,7 +115,7 @@ export class TeacherController { @Get('dashboard/module-progress') @ApiOperation({ summary: 'Get module progress summary' }) async getModuleProgressSummary(@Request() req: AuthRequest) { - const teacherId = req.user.id; + const teacherId = req.user!.id; return this.dashboardService.getModuleProgressSummary(teacherId); } @@ -152,7 +153,7 @@ export class TeacherController { @Param('studentId') studentId: string, @Request() req: AuthRequest, ): Promise { - const teacherId = req.user.profile.id; + const teacherId = req.user!.profile!.id; return this.studentProgressService.getStudentNotes(studentId, teacherId); } @@ -166,7 +167,7 @@ export class TeacherController { @Body() noteDto: AddTeacherNoteDto, @Request() req: AuthRequest, ): Promise { - const teacherId = req.user.profile.id; + const teacherId = req.user!.profile!.id; return this.studentProgressService.addStudentNote( studentId, teacherId, @@ -266,7 +267,7 @@ export class TeacherController { @Request() req: AuthRequest, @Query() query: GetEngagementMetricsDto, ) { - const teacherId = req.user.profile.id; + const teacherId = req.user!.profile!.id; return this.analyticsService.getEngagementMetrics(teacherId, query); } @@ -280,7 +281,7 @@ export class TeacherController { @Request() req: AuthRequest, @Query() query: GenerateReportsDto, ) { - const teacherId = req.user.profile.id; + const teacherId = req.user!.profile!.id; return this.analyticsService.generateReports(teacherId, query); } @@ -301,7 +302,7 @@ export class TeacherController { @Request() req: AuthRequest, @Query('classroom_id') classroomId?: string, ): Promise { - const teacherId = req.user.profile.id; + const teacherId = req.user!.profile!.id; return this.analyticsService.getEconomyAnalytics(teacherId, classroomId); } @@ -322,7 +323,7 @@ export class TeacherController { @Request() req: AuthRequest, @Query('classroom_id') classroomId?: string, ): Promise { - const teacherId = req.user.profile.id; + const teacherId = req.user!.profile!.id; return this.analyticsService.getStudentsEconomy(teacherId, classroomId); } @@ -343,7 +344,7 @@ export class TeacherController { @Request() req: AuthRequest, @Query('classroom_id') classroomId?: string, ): Promise { - const teacherId = req.user.profile.id; + const teacherId = req.user!.profile!.id; return this.analyticsService.getAchievementsStats(teacherId, classroomId); } @@ -365,7 +366,7 @@ export class TeacherController { @Body() dto: GenerateReportDto, @Res() res: Response, ) { - const userId = req.user.profile.id; + const userId = req.user!.profile!.id; const tenantId = req.user.tenantId || req.user.tenant_id || 'default'; // Generate report (now persists to storage and database) @@ -424,7 +425,7 @@ export class TeacherController { @Body() dto: GrantBonusDto, @Request() req: AuthRequest, ): Promise { - const teacherId = req.user.profile.id; + const teacherId = req.user!.profile!.id; return this.bonusCoinsService.grantBonus(teacherId, studentId, dto); } @@ -446,7 +447,7 @@ export class TeacherController { @Request() req: AuthRequest, @Query() query: GetRecentReportsQueryDto, ): Promise { - const teacherId = req.user.profile.id; + const teacherId = req.user!.profile!.id; const limit = query.limit || 10; return this.teacherReportsService.getRecentReports(teacherId, limit); } @@ -462,7 +463,7 @@ export class TeacherController { type: ReportStatsDto, }) async getReportStats(@Request() req: AuthRequest): Promise { - const teacherId = req.user.profile.id; + const teacherId = req.user!.profile!.id; return this.teacherReportsService.getReportStats(teacherId); } @@ -489,7 +490,7 @@ export class TeacherController { @Request() req: AuthRequest, @Res() res: Response, ) { - const teacherId = req.user.profile.id; + const teacherId = req.user!.profile!.id; // Get report with ownership validation const report = await this.teacherReportsService.getReportById(reportId, teacherId); diff --git a/projects/gamilit/apps/backend/src/shared/types/index.ts b/projects/gamilit/apps/backend/src/shared/types/index.ts index a394a78..6f16ced 100644 --- a/projects/gamilit/apps/backend/src/shared/types/index.ts +++ b/projects/gamilit/apps/backend/src/shared/types/index.ts @@ -76,6 +76,7 @@ export interface AuthUser { rank?: string; tenant_id?: string; tenantId?: string; // Alternative naming + sessionId?: string; // Session ID from JWT payload profile?: { id: string; tenant_id?: string;