import { Controller, Post, Get, Body, UseGuards, Req, HttpCode, HttpStatus, } from '@nestjs/common'; import { ApiTags, ApiOperation, ApiResponse, ApiBearerAuth, ApiBody, } from '@nestjs/swagger'; import type { Request } from 'express'; import { AuthService } from './auth.service'; import { RegisterDto, LoginDto, SocialAuthDto, ForgotPasswordDto, ResetPasswordDto, RefreshTokenDto, VerifyEmailDto, } from './dto'; import { JwtAuthGuard } from './guards'; import { Public, CurrentUser } from './decorators'; @ApiTags('Auth') @Controller('auth') export class AuthController { constructor(private readonly authService: AuthService) {} // ========================================== // REGISTER // ========================================== @Public() @Post('register') @HttpCode(HttpStatus.CREATED) @ApiOperation({ summary: 'Register a new user' }) @ApiBody({ type: RegisterDto }) @ApiResponse({ status: 201, description: 'User registered successfully', schema: { example: { success: true, data: { user: { id: 'uuid', email: 'john@example.com', role: 'USER', firstName: 'John', lastName: 'Doe', }, accessToken: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...', refreshToken: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...', }, }, }, }) @ApiResponse({ status: 400, description: 'Validation error' }) @ApiResponse({ status: 409, description: 'Email already registered' }) async register(@Body() dto: RegisterDto) { return this.authService.register(dto); } // ========================================== // LOGIN // ========================================== @Public() @Post('login') @HttpCode(HttpStatus.OK) @ApiOperation({ summary: 'Login with email and password' }) @ApiBody({ type: LoginDto }) @ApiResponse({ status: 200, description: 'Login successful', schema: { example: { success: true, data: { user: { id: 'uuid', email: 'john@example.com', role: 'USER', firstName: 'John', lastName: 'Doe', avatar: null, }, accessToken: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...', refreshToken: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...', }, }, }, }) @ApiResponse({ status: 401, description: 'Invalid credentials' }) async login(@Body() dto: LoginDto, @Req() req: Request) { const userAgent = req.headers['user-agent']; const ipAddress = req.ip || req.headers['x-forwarded-for']?.toString() || 'unknown'; return this.authService.login(dto, userAgent, ipAddress); } // ========================================== // SOCIAL AUTH // ========================================== @Public() @Post('social') @HttpCode(HttpStatus.OK) @ApiOperation({ summary: 'Login or register with social provider' }) @ApiBody({ type: SocialAuthDto }) @ApiResponse({ status: 200, description: 'Social auth successful', schema: { example: { success: true, data: { user: { id: 'uuid', email: 'john@example.com', role: 'USER', firstName: 'John', lastName: 'Doe', avatar: 'https://example.com/avatar.jpg', }, accessToken: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...', refreshToken: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...', }, }, }, }) @ApiResponse({ status: 400, description: 'Invalid provider data' }) async socialAuth(@Body() dto: SocialAuthDto) { return this.authService.socialAuth(dto); } // ========================================== // FORGOT PASSWORD // ========================================== @Public() @Post('forgot-password') @HttpCode(HttpStatus.OK) @ApiOperation({ summary: 'Request password reset email' }) @ApiBody({ type: ForgotPasswordDto }) @ApiResponse({ status: 200, description: 'Password reset email sent (if email exists)', schema: { example: { success: true, data: { message: 'If the email exists, a password reset link will be sent', }, }, }, }) async forgotPassword(@Body() dto: ForgotPasswordDto) { return this.authService.forgotPassword(dto); } // ========================================== // RESET PASSWORD // ========================================== @Public() @Post('reset-password') @HttpCode(HttpStatus.OK) @ApiOperation({ summary: 'Reset password with token' }) @ApiBody({ type: ResetPasswordDto }) @ApiResponse({ status: 200, description: 'Password reset successful', schema: { example: { success: true, data: { message: 'Password reset successful. Please login with your new password.', }, }, }, }) @ApiResponse({ status: 400, description: 'Invalid or expired token' }) async resetPassword(@Body() dto: ResetPasswordDto) { return this.authService.resetPassword(dto); } // ========================================== // VERIFY EMAIL // ========================================== @Public() @Post('verify-email') @HttpCode(HttpStatus.OK) @ApiOperation({ summary: 'Verify email with token' }) @ApiBody({ type: VerifyEmailDto }) @ApiResponse({ status: 200, description: 'Email verified successfully', schema: { example: { success: true, data: { message: 'Email verified successfully', }, }, }, }) @ApiResponse({ status: 400, description: 'Invalid or expired token' }) async verifyEmail(@Body() dto: VerifyEmailDto) { return this.authService.verifyEmail(dto); } // ========================================== // RESEND VERIFICATION EMAIL // ========================================== @Post('resend-verification') @UseGuards(JwtAuthGuard) @HttpCode(HttpStatus.OK) @ApiBearerAuth('JWT-auth') @ApiOperation({ summary: 'Resend email verification' }) @ApiResponse({ status: 200, description: 'Verification email sent', schema: { example: { success: true, data: { message: 'Verification email sent', }, }, }, }) @ApiResponse({ status: 401, description: 'Unauthorized' }) async resendVerification(@CurrentUser('id') userId: string) { return this.authService.resendVerificationEmail(userId); } // ========================================== // REFRESH TOKEN // ========================================== @Public() @Post('refresh') @HttpCode(HttpStatus.OK) @ApiOperation({ summary: 'Refresh access token' }) @ApiBody({ type: RefreshTokenDto }) @ApiResponse({ status: 200, description: 'Tokens refreshed successfully', schema: { example: { success: true, data: { accessToken: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...', refreshToken: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...', }, }, }, }) @ApiResponse({ status: 401, description: 'Invalid refresh token' }) async refreshToken(@Body() dto: RefreshTokenDto) { return this.authService.refreshToken(dto); } // ========================================== // GET CURRENT USER // ========================================== @Get('me') @UseGuards(JwtAuthGuard) @ApiBearerAuth('JWT-auth') @ApiOperation({ summary: 'Get current user profile' }) @ApiResponse({ status: 200, description: 'Current user profile', schema: { example: { success: true, data: { id: 'uuid', email: 'john@example.com', role: 'USER', status: 'ACTIVE', emailVerified: true, authProvider: 'LOCAL', firstName: 'John', lastName: 'Doe', avatar: null, phone: null, city: null, state: null, country: null, createdAt: '2024-01-01T00:00:00.000Z', lastLoginAt: '2024-01-01T00:00:00.000Z', }, }, }, }) @ApiResponse({ status: 401, description: 'Unauthorized' }) async getCurrentUser(@CurrentUser('id') userId: string) { return this.authService.getCurrentUser(userId); } // ========================================== // LOGOUT // ========================================== @Post('logout') @UseGuards(JwtAuthGuard) @HttpCode(HttpStatus.OK) @ApiBearerAuth('JWT-auth') @ApiOperation({ summary: 'Logout current session' }) @ApiResponse({ status: 200, description: 'Logged out successfully', schema: { example: { success: true, data: { message: 'Logged out successfully', }, }, }, }) @ApiResponse({ status: 401, description: 'Unauthorized' }) async logout(@CurrentUser('id') userId: string, @Req() req: Request) { const authHeader = req.headers.authorization; const accessToken = authHeader?.replace('Bearer ', ''); return this.authService.logout(userId, accessToken); } // ========================================== // LOGOUT ALL SESSIONS // ========================================== @Post('logout-all') @UseGuards(JwtAuthGuard) @HttpCode(HttpStatus.OK) @ApiBearerAuth('JWT-auth') @ApiOperation({ summary: 'Logout all sessions' }) @ApiResponse({ status: 200, description: 'All sessions logged out', schema: { example: { success: true, data: { message: 'Logged out successfully', }, }, }, }) @ApiResponse({ status: 401, description: 'Unauthorized' }) async logoutAll(@CurrentUser('id') userId: string) { return this.authService.logout(userId); } }