import { Body, Controller, Post, HttpCode, HttpStatus, UseGuards, Get, Patch, Delete, Param, Request, Query, UnauthorizedException } from '@nestjs/common'; import { ApiTags, ApiOperation, ApiResponse, ApiBody, ApiBearerAuth } from '@nestjs/swagger'; import { Throttle, SkipThrottle } from '@nestjs/throttler'; import { IsPublic } from '@tria-plc/api-common/modules/auth/decorators/public.decorator'; import { PassengerAuthService } from './passenger-auth.service'; import { RegisterDto, LoginDto, FaydaRequestPasswordSetupDto, FaydaVerifyAndLoginDto } from './auth.dto'; import { JwtGuard } from '../../common/jwt.guard'; @ApiTags('Passenger Auth') @Controller('auth') @Throttle({ auth: { limit: 5, ttl: 60_000 } }) export class AuthController { constructor(private passengerAuthService: PassengerAuthService) {} @Post('register') @IsPublic() @ApiOperation({ summary: 'Register new passenger account' }) @ApiResponse({ status: 201, description: 'Account created. Returns token + user.' }) @ApiResponse({ status: 409, description: 'Email or phone already registered' }) @ApiBody({ type: RegisterDto }) register(@Request() req: any, @Body() dto: RegisterDto) { return this.passengerAuthService.register(dto, req); } @Post('login') @IsPublic() @HttpCode(HttpStatus.OK) @ApiOperation({ summary: 'Login with email and password' }) @ApiResponse({ status: 200, description: 'Login successful. Returns token + passengerId.' }) @ApiResponse({ status: 401, description: 'Invalid credentials' }) @ApiBody({ type: LoginDto }) login(@Request() req: any, @Body() dto: LoginDto) { return this.passengerAuthService.login(dto, req); } @Post('logout') @HttpCode(HttpStatus.OK) @UseGuards(JwtGuard) @ApiBearerAuth('JWT-auth') @ApiOperation({ summary: 'Logout current user' }) @ApiResponse({ status: 200, description: 'Logout successful' }) @ApiResponse({ status: 401, description: 'Unauthorized' }) logout(@Request() req: any) { if (!req.user?.id) throw new UnauthorizedException('User not authenticated'); return this.passengerAuthService.logout(req.user, req); } @Get('me') @UseGuards(JwtGuard) @ApiBearerAuth('JWT-auth') @ApiOperation({ summary: '[DEV] Inspect raw JWT payload — shows full req.user from JwtGuard' }) @ApiResponse({ status: 200, description: 'Returns the full req.user object set by JwtGuard' }) @ApiResponse({ status: 401, description: 'Unauthorized' }) getMe(@Request() req: any) { return { user: req.user }; } @Get('profile') @UseGuards(JwtGuard) @ApiBearerAuth('JWT-auth') @ApiOperation({ summary: 'Get current user profile' }) @ApiResponse({ status: 200, description: 'User profile retrieved successfully' }) @ApiResponse({ status: 401, description: 'Unauthorized' }) getProfile(@Request() req: any) { const userId = req.user?.id; if (!userId) throw new UnauthorizedException('User not authenticated'); return this.passengerAuthService.getProfile(userId); } // TODO: admin user management endpoints — implement when admin module is ready @Get('users') @UseGuards(JwtGuard) @ApiBearerAuth('JWT-auth') @ApiOperation({ summary: 'List all users (admin)' }) listUsers( @Query('search') search?: string, @Query('role') role?: string, @Query('status') status?: string, @Query('page') page?: string, @Query('pageSize') pageSize?: string, ) { return this.passengerAuthService.listUsers({ search, role, status, page: page ? +page : 1, pageSize: pageSize ? +pageSize : 20, }); } @Post('users') @UseGuards(JwtGuard) @ApiBearerAuth('JWT-auth') @ApiOperation({ summary: 'Create user (admin)' }) createUser(@Body() body: any) { return this.passengerAuthService.createUser(body); } @Patch('users/:id') @UseGuards(JwtGuard) @ApiBearerAuth('JWT-auth') @ApiOperation({ summary: 'Update user (admin)' }) updateUser(@Param('id') id: string, @Body() body: any) { return this.passengerAuthService.updateUser(id, body); } @Delete('users/:id') @UseGuards(JwtGuard) @ApiBearerAuth('JWT-auth') @ApiOperation({ summary: 'Delete user (admin)' }) deleteUser(@Param('id') id: string) { return this.passengerAuthService.deleteUser(id); } @Post('users/:id/reset-password') @UseGuards(JwtGuard) @ApiBearerAuth('JWT-auth') @ApiOperation({ summary: 'Reset user password (admin)' }) resetPassword(@Param('id') id: string, @Body() body: { tempPassword: string }) { return this.passengerAuthService.resetUserPassword(id, body.tempPassword); } @Post('fayda/request-password-setup') @IsPublic() @HttpCode(HttpStatus.OK) @ApiOperation({ summary: 'Send OTP to phone for Fayda-verified account password setup' }) @ApiResponse({ status: 200, description: 'OTP sent to registered phone number' }) @ApiBody({ type: FaydaRequestPasswordSetupDto }) requestFaydaPasswordSetup(@Body() dto: FaydaRequestPasswordSetupDto, @Request() req: any) { return this.passengerAuthService.requestFaydaPasswordSetup(dto.phoneNumber, req); } @Post('fayda/verify-and-login') @IsPublic() @HttpCode(HttpStatus.OK) @ApiOperation({ summary: 'Verify OTP and receive session token for Fayda-verified account' }) @ApiResponse({ status: 200, description: 'Returns token + requiresPassword flag. Use token with POST /v1/auth/set-fayda-password.' }) @ApiBody({ type: FaydaVerifyAndLoginDto }) verifyFaydaAndLogin(@Body() dto: FaydaVerifyAndLoginDto) { return this.passengerAuthService.verifyFaydaAndLogin(dto.phoneNumber, dto.otp); } }