Initial commit: Full project structure
- Backend: NestJS with Docker - Frontend: Next.js with Docker - Nginx configuration for reverse proxy - PostgreSQL setup - Docker compose for orchestration - Development environment configuration
This commit is contained in:
177
backend/src/modules/auth/auth.controller.ts
Normal file
177
backend/src/modules/auth/auth.controller.ts
Normal file
@@ -0,0 +1,177 @@
|
||||
// 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 { 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,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// =========================
|
||||
// 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);
|
||||
}
|
||||
|
||||
// =========================
|
||||
// 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: '/',
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user