// backend/src/modules/auth/auth.controller.ts import { Controller, Post, Body, UseGuards, Req, Res, HttpCode, HttpStatus, Get } from '@nestjs/common'; import type { Response } from 'express'; import { ApiTags, ApiOperation, ApiResponse, ApiBearerAuth, ApiBody, ApiUnauthorizedResponse, ApiBadRequestResponse } from '@nestjs/swagger'; import { AuthService } from './auth.service'; import { LoginDto } from './dto/login.dto'; import { RegisterDto } from './dto/register.dto'; import { CreateOrganizationDto } from './dto/create-organization.dto'; import { JwtAuthGuard } from './guards/jwt-auth.guard'; import { LocalAuthGuard } from './guards/local-auth.guard'; @ApiTags('auth') @Controller('auth') export class AuthController { constructor(private readonly authService: AuthService) {} // ========================= // LOGIN // ========================= @Post('login') @HttpCode(HttpStatus.OK) @UseGuards(LocalAuthGuard) @ApiOperation({ summary: 'Login with email and password' }) @ApiBody({ type: LoginDto }) @ApiResponse({ status: 200, description: 'Login successful' }) @ApiUnauthorizedResponse({ description: 'Invalid credentials' }) @ApiBadRequestResponse({ description: 'Invalid input data' }) async login( @Body() loginDto: LoginDto, @Req() req, @Res({ passthrough: true }) res: Response ) { console.log('Login endpoint hit'); const result = await this.authService.login(loginDto, req.user); // ✅ SET COOKIES HERE this.setAuthCookies(res, result.data.accessToken, result.data.refreshToken); return { success: true, data: { user: result.data.user, organizations: result.data.organizations, }, }; } // ========================= // REGISTER // ========================= @Post('register') @ApiOperation({ summary: 'Register a new user' }) @ApiBody({ type: RegisterDto }) @ApiResponse({ status: 201, description: 'User registered successfully' }) @ApiBadRequestResponse({ description: 'Invalid input data' }) async register( @Body() registerDto: RegisterDto, @Res({ passthrough: true }) res: Response ) { console.log('Register endpoint hit'); const result = await this.authService.register(registerDto); // ✅ SET COOKIES HERE this.setAuthCookies(res, result.data.accessToken, result.data.refreshToken); return { success: true, data: { user: result.data.user, organizations: result.data.organizations, }, }; } // ========================= // SELECT ORGANIZATION // ========================= @Post('select-organization') @UseGuards(JwtAuthGuard) @ApiBearerAuth('JWT-auth') async selectOrganization( @Req() req, @Body('organizationId') organizationId: string, @Res({ passthrough: true }) res: Response ) { const result = await this.authService.selectOrganization( req.user.id, organizationId ); // 🔥 Replace access token with org-scoped token this.setAccessToken(res, result.data.accessToken); return { success: true, data: { organization: result.data.organization, }, }; } @Post('organizations') @UseGuards(JwtAuthGuard) @ApiBearerAuth('JWT-auth') @ApiOperation({ summary: 'Create organization for current user' }) async createOrganization(@Req() req, @Body() dto: CreateOrganizationDto) { return this.authService.createOrganization(req.user.id, dto); } // ========================= // PROFILE // ========================= @Get('profile') @UseGuards(JwtAuthGuard) @ApiBearerAuth('JWT-auth') @ApiOperation({ summary: 'Get user profile' }) @ApiResponse({ status: 200, description: 'Profile retrieved successfully' }) @ApiUnauthorizedResponse({ description: 'Invalid or missing JWT token' }) async getProfile(@Req() req) { console.log('Profile endpoint hit'); console.log('USER FROM JWT:', req.user); return this.authService.getProfile(req.user.id); } @Get('subscription-alert') @UseGuards(JwtAuthGuard) @ApiBearerAuth('JWT-auth') @ApiOperation({ summary: 'Owner-only: seat / trial status for warning indicator (current org from JWT)', }) async getSubscriptionAlert(@Req() req) { return this.authService.getOwnerSubscriptionAlert( req.user.id, req.user.organizationId, ); } // ========================= // LOGOUT // ========================= @Post('logout') @HttpCode(HttpStatus.OK) @ApiOperation({ summary: 'Logout current user' }) @ApiResponse({ status: 200, description: 'Logout successful' }) async logout(@Req() req, @Res({ passthrough: true }) res: Response) { const accessToken = req?.cookies?.accessToken; if (accessToken) { await this.authService.logout(accessToken); } this.clearAuthCookies(res); return { success: true, message: 'Logged out successfully', }; } // ========================= // TEST // ========================= @Get('test') @ApiOperation({ summary: 'Test endpoint' }) test() { return { message: 'Auth controller is working!' }; } // ========================= // 🔥 COOKIE HELPERS // ========================= private setAuthCookies( res: Response, accessToken: string, refreshToken: string ) { this.setAccessToken(res, accessToken); this.setRefreshToken(res, refreshToken); } private setAccessToken(res: Response, token: string) { res.cookie('accessToken', token, { httpOnly: true, secure: false, // ⚠️ true in production (HTTPS) sameSite: 'lax', path: '/', }); } private setRefreshToken(res: Response, token: string) { res.cookie('refreshToken', token, { httpOnly: true, secure: false, sameSite: 'lax', path: '/', }); } private clearAuthCookies(res: Response) { res.clearCookie('accessToken', { httpOnly: true, secure: false, sameSite: 'lax', path: '/', }); res.clearCookie('refreshToken', { httpOnly: true, secure: false, sameSite: 'lax', path: '/', }); } }