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 { IsPublic } from "@tria-plc/api-common/modules/auth/decorators/public.decorator"; import { PassengerAuthService } from "./passenger-auth.service"; import { RegisterDto, LoginDto, ResendRegistrationCodeDto, 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 (sends SMS verification code)", }) @ApiResponse({ status: 201, description: "Account created as pending. A verification code is sent via SMS — complete signup via PATCH /v1/auth/set-password.", }) @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("register/resend-code") @IsPublic() @HttpCode(HttpStatus.OK) @ApiOperation({ summary: "Resend the registration verification code for a pending account", }) @ApiResponse({ status: 200, description: "Verification code re-sent if the account is pending.", }) @ApiBody({ type: ResendRegistrationCodeDto }) resendRegistrationCode( @Request() req: any, @Body() dto: ResendRegistrationCodeDto, ) { return this.passengerAuthService.resendRegistrationCode(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, ); } }