Files
dyolink/backend/src/modules/auth/auth.controller.ts

237 lines
6.0 KiB
TypeScript
Raw Normal View History

// 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,
);
}
2026-04-29 15:55:58 +03:30
// =========================
// 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: '/',
});
}
2026-04-29 15:55:58 +03:30
private clearAuthCookies(res: Response) {
res.clearCookie('accessToken', {
httpOnly: true,
secure: false,
sameSite: 'lax',
path: '/',
});
res.clearCookie('refreshToken', {
httpOnly: true,
secure: false,
sameSite: 'lax',
path: '/',
});
}
}