All files / src/modules/users users.controller.ts

100% Statements 35/35
75% Branches 6/8
100% Functions 12/12
100% Lines 30/30

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 9712x 12x 12x 12x 12x 12x 12x 12x 12x         12x 12x                 12x 1x           12x 3x           12x 7x           12x 1x               12x 2x                 12x 5x           12x 8x             12x         12x         12x         12x 5x    
import { Controller, Get, UseGuards, Req, Patch, Body, Post, HttpCode, MethodNotAllowedException, Param, ParseIntPipe, UploadedFile, UseInterceptors } from '@nestjs/common';
import { ApiBearerAuth, ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger';
import { UsersService } from '@app/modules/users/users.service';
import { JwtAuthGuard } from '@app/common/guards/jwt-auth.guard';
import { RolesGuard } from '@app/common/guards/roles.guard';
import { Roles } from '@app/common/decorators/roles.decorator';
import { UpdateUserDto } from '@app/modules/users/dto/update-user.dto';
import { Throttle } from '@nestjs/throttler';
import { FileInterceptor } from '@nestjs/platform-express';
 
@ApiTags('Users')
@ApiBearerAuth()
@Controller('users')
export class UsersController {
  constructor(private readonly usersService: UsersService) {}
 
  /** REST GET /​users */
  @Get()
  @UseGuards(JwtAuthGuard, RolesGuard)
  @Roles('ADMIN')
  @Throttle({ general: { limit: 100, ttl: 60000 } })
  @ApiOperation({ summary: 'REST GET /​users', description: 'List all users. Requires role: ADMIN.' })
  @ApiResponse({ status: 200, description: 'Users list returned.' })
  findAll() {
    return this.usersService.findAll();
  }
 
  @Get('me')
  @UseGuards(JwtAuthGuard)
  @ApiOperation({ summary: 'Get current user profile', description: 'Requires authentication.' })
  getProfile(@Req() req: any) {
    return this.usersService.findOneById(req.user.id);
  }
 
  @Patch('me')
  @UseGuards(JwtAuthGuard)
  @ApiOperation({ summary: 'Update current user profile', description: 'Requires authentication.' })
  updateProfile(@Req() req: any, @Body() updateUserDto: UpdateUserDto) {
    return this.usersService.update(req.user.id, updateUserDto);
  }
 
  @Post('me')
  @HttpCode(405)
  @ApiOperation({ summary: 'Method not allowed', description: 'Use GET or PATCH on /users/me' })
  handleMePost() {
    throw new MethodNotAllowedException('Use GET (fetch profile) or PATCH (update profile) instead');
  }
 
  // Avatar upload (multipart) - MUST come before :id route
  @Post('me/avatar')
  @UseGuards(JwtAuthGuard)
  @ApiOperation({ summary: 'Upload avatar', description: 'Multipart endpoint. Requires authentication.' })
  @UseInterceptors(FileInterceptor('file'))
  uploadAvatar(@Req() req: any, @UploadedFile() file: any) {
    return this.usersService.uploadAvatar(req.user.id, file);
  }
 
  // Legacy avatar upload endpoint (kept for backward compatibility)
  @Post('avatar')
  @UseGuards(JwtAuthGuard)
  @ApiOperation({ summary: 'Upload avatar (legacy)', description: 'Multipart endpoint. Requires authentication.' })
  @UseInterceptors(FileInterceptor('file'))
  @HttpCode(200)
  uploadAvatarLegacy(@Req() req: any, @UploadedFile() file: any) {
    return this.usersService.uploadAvatar(req.user.id, file);
  }
 
  // Public profile (redacted)
  @Get(':id/public')
  @ApiOperation({ summary: 'Get public user profile' })
  getPublic(@Param('id', ParseIntPipe) id: number) {
    return this.usersService.getPublicProfile(id);
  }
 
  // Notifications
  @Get('me/notifications')
  @UseGuards(JwtAuthGuard)
  @ApiOperation({ summary: 'List my notifications' })
  myNotifications(@Req() req: any) { return this.usersService.listNotifications(req.user.id); }
 
  @Patch('me/notifications/:id/read')
  @UseGuards(JwtAuthGuard)
  @ApiOperation({ summary: 'Mark notification as read' })
  markRead(@Req() req: any, @Param('id', ParseIntPipe) id: number) { return this.usersService.markNotificationRead(req.user.id, id); }
 
  @Post('me/notification-preferences')
  @UseGuards(JwtAuthGuard)
  @ApiOperation({ summary: 'Set notification preferences' })
  setPrefs(@Req() req: any, @Body() body: any) { return this.usersService.setNotificationPrefs(req.user.id, body); }
 
  @Get('me/stats')
  @UseGuards(JwtAuthGuard)
  @ApiOperation({ summary: 'Get user profile stats and completeness' })
  getStats(@Req() req: any) {
    return this.usersService.getMyStats(req.user.id);
  }
}