import { Body, Controller, Get, HttpCode, Post } from '@nestjs/common'; import { ApiBadRequestResponse, ApiBearerAuth, ApiConflictResponse, ApiCreatedResponse, ApiNoContentResponse, ApiOkResponse, ApiOperation, ApiTags, ApiTooManyRequestsResponse, ApiUnauthorizedResponse, } from '@nestjs/swagger'; import { Throttle } from '@nestjs/throttler'; import type { AuthUser } from '../../common/auth/auth-user'; import { CurrentUser } from '../../common/decorators/current-user.decorator'; import { Public } from '../../common/decorators/public.decorator'; import { BEARER_AUTH_NAME } from '../../common/swagger/setup-swagger'; import { PrivilegesService } from '../privileges/privileges.service'; import { UsersService } from '../users/users.service'; import { AuthService } from './auth.service'; import { LoginDto, MeResponseDto, RefreshTokenDto, RegisterDto, TokenPairDto, } from './dto/auth.dto'; @ApiTags('auth') @Controller('auth') export class AuthController { constructor( private readonly authService: AuthService, private readonly usersService: UsersService, private readonly privilegesService: PrivilegesService, ) {} @Public() @Throttle({ default: { limit: 5, ttl: 60_000 } }) @Post('register') @ApiOperation({ summary: 'Register a new user' }) @ApiCreatedResponse({ type: TokenPairDto }) @ApiBadRequestResponse({ description: 'Validation failed' }) @ApiConflictResponse({ description: 'Username already registered' }) @ApiTooManyRequestsResponse({ description: 'Rate limit exceeded' }) register(@Body() dto: RegisterDto): Promise { return this.authService.register(dto.username, dto.password); } @Public() @Throttle({ default: { limit: 5, ttl: 60_000 } }) @Post('login') @HttpCode(200) @ApiOperation({ summary: 'Log in with username and password' }) @ApiOkResponse({ type: TokenPairDto }) @ApiBadRequestResponse({ description: 'Validation failed' }) @ApiUnauthorizedResponse({ description: 'Invalid credentials' }) @ApiTooManyRequestsResponse({ description: 'Rate limit exceeded' }) login(@Body() dto: LoginDto): Promise { return this.authService.login(dto.username, dto.password); } @Public() @Throttle({ default: { limit: 10, ttl: 60_000 } }) @Post('refresh') @HttpCode(200) @ApiOperation({ summary: 'Rotate refresh token and issue a new access token', }) @ApiOkResponse({ type: TokenPairDto }) @ApiBadRequestResponse({ description: 'Validation failed' }) @ApiUnauthorizedResponse({ description: 'Invalid refresh token' }) @ApiTooManyRequestsResponse({ description: 'Rate limit exceeded' }) refresh(@Body() dto: RefreshTokenDto): Promise { return this.authService.refresh(dto.refreshToken); } @Public() @Throttle({ default: { limit: 10, ttl: 60_000 } }) @Post('revoke') @HttpCode(204) @ApiOperation({ summary: 'Revoke a refresh token session' }) @ApiNoContentResponse({ description: 'Session revoked (or already invalid)' }) @ApiBadRequestResponse({ description: 'Validation failed' }) @ApiTooManyRequestsResponse({ description: 'Rate limit exceeded' }) async revoke(@Body() dto: RefreshTokenDto): Promise { await this.authService.revoke(dto.refreshToken); } @Get('me') @ApiBearerAuth(BEARER_AUTH_NAME) @ApiOperation({ summary: 'Get the current authenticated user' }) @ApiOkResponse({ type: MeResponseDto }) @ApiUnauthorizedResponse({ description: 'Missing or invalid access token' }) async me(@CurrentUser() user: AuthUser): Promise { const full = await this.usersService.findById(user.id); const isSuperadmin = full?.isSuperadmin ?? user.isSuperadmin; if (!full?.privilegeId) { return { id: user.id, username: user.username, isSuperadmin, privilege: null, permissions: {}, }; } const privilege = await this.privilegesService.findPrivilegeSummary( full.privilegeId, ); const permissions = privilege ? await this.privilegesService.getPermissionsMap(privilege.id) : {}; return { id: user.id, username: user.username, isSuperadmin, privilege, permissions, }; } }