diff --git a/src/app.controller.ts b/src/app.controller.ts index 00a2915..c140868 100644 --- a/src/app.controller.ts +++ b/src/app.controller.ts @@ -1,5 +1,5 @@ import { Controller, Get } from '@nestjs/common' -import { ApiTags } from '@nestjs/swagger' +import { ApiTags, ApiOperation, ApiOkResponse } from '@nestjs/swagger' import { AppService } from './app.service' import { Public } from './auth/decorator' @@ -9,6 +9,8 @@ export class AppController { constructor(private readonly appService: AppService) {} @Public() + @ApiOperation({ summary: 'Hello world endpoint' }) + @ApiOkResponse({ description: 'Returns a greeting message' }) @Get() getHello(): string { return this.appService.getHello() diff --git a/src/audit-log/audit-log.controller.ts b/src/audit-log/audit-log.controller.ts index d55454d..949b74d 100644 --- a/src/audit-log/audit-log.controller.ts +++ b/src/audit-log/audit-log.controller.ts @@ -1,14 +1,24 @@ import { Controller, Get, Query } from '@nestjs/common' +import { + ApiBearerAuth, + ApiOkResponse, + ApiOperation, + ApiTags, +} from '@nestjs/swagger' import { Role } from 'generated/prisma/enums' import { Roles, User } from 'src/auth/decorator' import { AuditLogService } from './audit-log.service' import { GetAuditLogsQueryDto } from './dto' +@ApiTags('Audit Logs') +@ApiBearerAuth() @Controller('audit-logs') export class AuditLogController { constructor(private readonly auditLogService: AuditLogService) {} @Roles(Role.SUPER_ADMIN, Role.ADMIN) + @ApiOperation({ summary: 'Get all audit logs' }) + @ApiOkResponse({ description: 'List of audit logs retrieved successfully' }) @Get() getAuditLogs( @User('role') userRole: string, diff --git a/src/audit-log/dto/getAuditLogs.dto.ts b/src/audit-log/dto/getAuditLogs.dto.ts index 28f6c66..b0847ab 100644 --- a/src/audit-log/dto/getAuditLogs.dto.ts +++ b/src/audit-log/dto/getAuditLogs.dto.ts @@ -1,33 +1,57 @@ +import { ApiPropertyOptional } from '@nestjs/swagger' import { Type } from 'class-transformer' import { IsDateString, IsEmail, IsIn, IsInt, IsOptional } from 'class-validator' import { PaginationQueryDto } from 'src/common/pagination/dto' export class GetAuditLogsQueryDto extends PaginationQueryDto { + @ApiPropertyOptional({ + example: 'createdAt', + description: 'Field to order by', + }) @IsOptional() @IsIn(['createdAt', 'type', 'requestUrl', 'requestMethod']) orderBy?: string + @ApiPropertyOptional({ + example: 'desc', + enum: ['asc', 'desc'], + description: 'Order direction', + }) @IsOptional() @IsIn(['asc', 'desc']) order?: 'asc' | 'desc' + @ApiPropertyOptional({ + example: '2024-01-01', + description: 'From date', + }) @IsOptional() @IsDateString() from?: string + @ApiPropertyOptional({ + example: '2024-12-31', + description: 'To date', + }) @IsOptional() @IsDateString() to?: string + @ApiPropertyOptional({ example: 'CREATE', description: 'Audit log type' }) @IsOptional() @IsIn(['CREATE', 'UPDATE', 'DELETE', 'ERROR']) type?: string + @ApiPropertyOptional({ example: 1, description: 'User ID' }) @Type(() => Number) @IsOptional() @IsInt() userId?: string + @ApiPropertyOptional({ + example: 'user@example.com', + description: 'User email', + }) @IsOptional() @IsEmail() userEmail?: string diff --git a/src/auth/auth.controller.ts b/src/auth/auth.controller.ts index c331c98..4f06080 100644 --- a/src/auth/auth.controller.ts +++ b/src/auth/auth.controller.ts @@ -7,7 +7,13 @@ import { Post, UseGuards, } from '@nestjs/common' -import { ApiBearerAuth, ApiTags } from '@nestjs/swagger' +import { + ApiBearerAuth, + ApiCreatedResponse, + ApiOkResponse, + ApiOperation, + ApiTags, +} from '@nestjs/swagger' import { OtpService } from 'src/otp/otp.service' import { TokenService } from 'src/token/token.service' import { CreateUserDto } from 'src/user/dto' @@ -33,6 +39,8 @@ export class AuthController { ) {} @Public() + @ApiOperation({ summary: 'Sign up new user' }) + @ApiCreatedResponse({ description: 'User signed up successfully' }) @Post('signup') signup(@Body() createUserDto: CreateUserDto) { return this.authService.signup(createUserDto) @@ -40,6 +48,8 @@ export class AuthController { @Public() @HttpCode(HttpStatus.OK) + @ApiOperation({ summary: 'Login user' }) + @ApiOkResponse({ description: 'User logged in successfully' }) @Post('login') login(@Body() loginDto: LoginDto) { return this.authService.login(loginDto) @@ -47,6 +57,10 @@ export class AuthController { @ApiBearerAuth() @HttpCode(HttpStatus.OK) + @ApiOperation({ summary: 'Logout from all devices' }) + @ApiOkResponse({ + description: 'User logged out successfully from all devices', + }) @Post('logout-all') logoutAll(@User('sub') userId: number) { return this.authService.logoutFromAllDevices(userId) @@ -56,6 +70,8 @@ export class AuthController { @ApiBearerAuth() @UseGuards(RefreshTokenGuard) @HttpCode(HttpStatus.OK) + @ApiOperation({ summary: 'Refresh access token' }) + @ApiOkResponse({ description: 'Access token refreshed successfully' }) @Post('refresh-token') refreshToken( @User('sub') userId: number, @@ -68,12 +84,16 @@ export class AuthController { @ApiBearerAuth() @UseGuards(RefreshTokenGuard) @HttpCode(HttpStatus.OK) + @ApiOperation({ summary: 'Revoke refresh token' }) + @ApiOkResponse({ description: 'Refresh token revoked successfully' }) @Post('revoke-refresh-token') revokeRefreshToken(@User('rtid') refreshTokenId: string) { return this.tokenService.revokeRefreshToken(refreshTokenId) } @ApiBearerAuth() + @ApiOperation({ summary: 'Change user email' }) + @ApiOkResponse({ description: 'Email changed successfully' }) @Patch('change-email') changeEmail( @User('email') oldEmail: string, @@ -83,6 +103,8 @@ export class AuthController { } @ApiBearerAuth() + @ApiOperation({ summary: 'Change user password' }) + @ApiOkResponse({ description: 'Password changed successfully' }) @Patch('change-password') changePassword( @User('email') email: string, @@ -92,6 +114,8 @@ export class AuthController { } @Public() + @ApiOperation({ summary: 'Forgot password' }) + @ApiOkResponse({ description: 'Password reset code sent to email' }) @Patch('forgot-password') forgotPassword(@Body() forgotPasswordDto: ForgotPasswordDto) { return this.authService.forgotPassword(forgotPasswordDto) @@ -99,6 +123,8 @@ export class AuthController { @Public() @HttpCode(HttpStatus.OK) + @ApiOperation({ summary: 'Send OTP to email' }) + @ApiOkResponse({ description: 'OTP sent to email successfully' }) @Post('email-otp') emailOtp(@Body() emailOtpDto: EmailOtpDto) { return this.otpService.emailOtp(emailOtpDto.email) @@ -106,6 +132,8 @@ export class AuthController { @ApiBearerAuth() @HttpCode(HttpStatus.OK) + @ApiOperation({ summary: 'Send OTP to authenticated user email' }) + @ApiOkResponse({ description: 'OTP sent successfully' }) @Post('guarded-email-otp') guardedEmailOtp(@User('email') email: string) { return this.otpService.emailOtp(email, true) @@ -113,6 +141,8 @@ export class AuthController { @Public() @HttpCode(HttpStatus.OK) + @ApiOperation({ summary: 'Verify OTP' }) + @ApiOkResponse({ description: 'OTP verified successfully' }) @Post('verify-otp') verifyOtp(@Body() verifyOtpDto: VerifyOtpDto) { return this.otpService.verifyOtp(verifyOtpDto) diff --git a/src/common/pagination/dto/pagination-query.dto.ts b/src/common/pagination/dto/pagination-query.dto.ts index 8426ff9..bb5c5bc 100644 --- a/src/common/pagination/dto/pagination-query.dto.ts +++ b/src/common/pagination/dto/pagination-query.dto.ts @@ -1,12 +1,15 @@ +import { ApiPropertyOptional } from '@nestjs/swagger' import { Type } from 'class-transformer' import { IsOptional, IsPositive } from 'class-validator' export class PaginationQueryDto { + @ApiPropertyOptional({ example: 10, description: 'Number of items per page' }) @Type(() => Number) @IsOptional() @IsPositive() limit?: number + @ApiPropertyOptional({ example: 1, description: 'Page number' }) @Type(() => Number) @IsOptional() @IsPositive() diff --git a/src/user/dto/get-users.dto.ts b/src/user/dto/get-users.dto.ts index bc2c1d3..d71ae27 100644 --- a/src/user/dto/get-users.dto.ts +++ b/src/user/dto/get-users.dto.ts @@ -1,11 +1,21 @@ +import { ApiPropertyOptional } from '@nestjs/swagger' import { IsIn, IsOptional } from 'class-validator' import { PaginationQueryDto } from 'src/common/pagination/dto' export class GetUsersQueryDto extends PaginationQueryDto { + @ApiPropertyOptional({ + example: 'firstName', + description: 'Field to order by', + }) @IsOptional() @IsIn(['firstName', 'lastName']) orderBy?: string + @ApiPropertyOptional({ + example: 'asc', + enum: ['asc', 'desc'], + description: 'Order direction', + }) @IsOptional() @IsIn(['asc', 'desc']) order?: 'asc' | 'desc' diff --git a/src/user/user.controller.ts b/src/user/user.controller.ts index 52b91ca..dd1b9d9 100644 --- a/src/user/user.controller.ts +++ b/src/user/user.controller.ts @@ -11,7 +11,12 @@ import { Post, Query, } from '@nestjs/common' -import { ApiBearerAuth, ApiTags } from '@nestjs/swagger' +import { + ApiBearerAuth, + ApiOkResponse, + ApiOperation, + ApiTags, +} from '@nestjs/swagger' import { Role } from 'generated/prisma/enums' import { Public, Roles, User } from 'src/auth/decorator' import { @@ -30,21 +35,29 @@ export class UserController { constructor(private readonly userService: UserService) {} @Roles(Role.MODERATOR, Role.ADMIN, Role.SUPER_ADMIN) + @ApiOperation({ summary: 'Get all users' }) + @ApiOkResponse({ description: 'List of users retrieved successfully' }) @Get() getAllUsers(@Query() getUsersQueryDto: GetUsersQueryDto) { return this.userService.findAllUsers(getUsersQueryDto) } + @ApiOperation({ summary: 'Get current user profile' }) + @ApiOkResponse({ description: 'User profile retrieved successfully' }) @Get('me') getMe(@User('sub') userId: number) { return this.userService.findUserByUserId(userId) } + @ApiOperation({ summary: 'Get user by ID' }) + @ApiOkResponse({ description: 'User retrieved successfully' }) @Get(':id') getUser(@Param('id', ParseIntPipe) id: number) { return this.userService.findUserByUserId(id) } + @ApiOperation({ summary: 'Update user profile' }) + @ApiOkResponse({ description: 'User updated successfully' }) @Patch() updateUser( @User('sub') userId: number, @@ -53,6 +66,8 @@ export class UserController { return this.userService.updateUser(userId, updateUserDto) } + @ApiOperation({ summary: 'Soft delete user' }) + @ApiOkResponse({ description: 'User soft deleted successfully' }) @Delete() deleteUser( @User('email') email: string, @@ -63,12 +78,16 @@ export class UserController { @Public() @HttpCode(HttpStatus.OK) + @ApiOperation({ summary: 'Reactivate user account' }) + @ApiOkResponse({ description: 'User reactivated successfully' }) @Post('reactivate-user') reactivateUser(@Body() reactivateUserDto: ReactivateUserDto) { return this.userService.reactivateUser(reactivateUserDto) } // #remove this route (hard-delete), instead create user block and unblock routes (allow only super_admin/admin) + @ApiOperation({ summary: 'Hard delete user' }) + @ApiOkResponse({ description: 'User permanently deleted successfully' }) @Delete('hard-delete') hardDeleteUser( @User('email') email: string, @@ -78,6 +97,8 @@ export class UserController { } @Roles(Role.SUPER_ADMIN) + @ApiOperation({ summary: 'Change user role' }) + @ApiOkResponse({ description: 'User role changed successfully' }) @Patch(':id/change-role') changeUserRole( @Param('id', ParseIntPipe) id: number,