Spaces:
Sleeping
Sleeping
Download src/modules/auth/auth.controller.ts from mahrooshishaq/SmartfolioBackend: direct link, hf CLI and curl.
- Browser
- Download file 11.5 kB
-
https://huggingface.co/spaces/mahrooshishaq/SmartfolioBackend/resolve/main/src/modules/auth/auth.controller.ts
- Command line
-
hf download hf://spaces/mahrooshishaq/SmartfolioBackend/src/modules/auth/auth.controller.ts
-
curl -L -o auth.controller.ts https://huggingface.co/spaces/mahrooshishaq/SmartfolioBackend/resolve/main/src/modules/auth/auth.controller.ts
11.5 kB
| import { Controller, Post, Body, Res, BadRequestException, Logger, Req, UseGuards,Get } from '@nestjs/common'; | |
| import { AuthService } from './auth.service'; | |
| import { SignupDto } from '../../common/dto/signup.dto'; | |
| import { LoginDto } from '../../common/dto/login.dto'; | |
| import { ApiTags, ApiOperation, ApiResponse, ApiBearerAuth, ApiBody, ApiExcludeEndpoint, ApiOkResponse } from '@nestjs/swagger'; | |
| import { JwtAuthGuard } from './jwt-auth.guard'; | |
| import { Request } from 'express'; | |
| import { VerifyOtpDto } from '../../common/dto/verify-otp.dto'; | |
| import { AuthGuard } from '@nestjs/passport'; | |
| import { Response } from 'express'; | |
| import { ConfigService } from '@nestjs/config'; | |
| ('Auth') | |
| ('auth') | |
| export class AuthController { | |
| private readonly logger = new Logger(AuthController.name); // declare logger here | |
| constructor( | |
| private readonly authService: AuthService, | |
| private readonly config: ConfigService, | |
| ) {} | |
| ('signup') | |
| ({ summary: 'Create a new user' }) | |
| ({ | |
| description: 'Signup payload', | |
| type: SignupDto, | |
| examples: { | |
| valid: { | |
| summary: 'Valid input', | |
| value: { email: 'test@example.com', password: 'MyPass@123', name: 'John Doe' }, | |
| }, | |
| invalidEmail: { | |
| summary: 'Invalid email format', | |
| value: { email: 'testexample.com', password: 'MyPass@123', name: 'John Doe' }, | |
| }, | |
| invalidPassword: { | |
| summary: 'Invalid password (no uppercase/special char)', | |
| value: { email: 'test@example.com', password: 'mypassword', name: 'John Doe' }, | |
| }, | |
| bothInvalid: { | |
| summary: 'Both email and password invalid', | |
| value: { email: 'testexample.com', password: 'mypassword', name: 'John Doe' }, | |
| }, | |
| }, | |
| }) | |
| ({ | |
| status: 201, | |
| description: 'User created successfully', | |
| schema: { | |
| example: { | |
| message: 'User created successfully', | |
| user: { id: 'uuid', email: 'test@example.com', name: 'John Doe' }, | |
| accessToken: 'jwt-access-token', | |
| refreshToken: 'jwt-refresh-token', | |
| }, | |
| }, | |
| }) | |
| ({ | |
| status: 400, | |
| description: 'Validation failed', | |
| schema: { | |
| example: { | |
| statusCode: 400, | |
| message: [ | |
| 'Email must be valid', | |
| 'Password must contain at least 1 uppercase letter and 1 special character', | |
| ], | |
| error: 'Bad Request', | |
| }, | |
| }, | |
| }) | |
| async signup(() dto: SignupDto) { | |
| try { | |
| const { name, email, password } = dto; | |
| // 1️⃣ Email format validation | |
| const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; | |
| if (!emailRegex.test(email)) { | |
| this.logger.warn(`Signup failed: Invalid email format: ${email}`); | |
| throw new BadRequestException('Invalid email format'); | |
| } | |
| // 2️⃣ Password strength validation | |
| const passwordRegex = /^(?=.*[A-Z])(?=.*[!@#$%^&*()_+{}[\]:;<>,.?~\\/-]).{8,}$/; | |
| if (!passwordRegex.test(password)) { | |
| this.logger.warn( | |
| `Signup failed for email ${email}: Password does not meet strength requirements` | |
| ); | |
| throw new BadRequestException( | |
| 'Password must be at least 8 characters long, contain 1 uppercase letter, and 1 special character', | |
| ); | |
| } | |
| // 3️⃣ Check if user already exists | |
| const existingUser = await this.authService.findUserByEmail(email); | |
| if (existingUser) { | |
| this.logger.warn(`Signup failed: Email already registered: ${email}`); | |
| throw new BadRequestException('Email already registered'); | |
| } | |
| // 4️⃣ Create user | |
| const user = await this.authService.signup(name, email, password); | |
| this.logger.log(`User created successfully: ${email}`); | |
| return { | |
| message: 'User created successfully. Please check your email for the OTP to verify your account.', | |
| userId: user.user.id, | |
| }; | |
| } catch (error: unknown) { | |
| if (error instanceof Error) { | |
| this.logger.error(`Signup failed for email ${dto.email}: ${error.message}`); | |
| throw error; | |
| } else { | |
| this.logger.error(`Signup failed for email ${dto.email}: Unknown error`); | |
| throw new BadRequestException('Signup failed due to unknown error'); | |
| } | |
| } | |
| } | |
| ('login') | |
| ({ summary: 'User Login' }) | |
| ({ | |
| description: 'Login payload', | |
| type: LoginDto, | |
| examples: { | |
| valid: { | |
| summary: 'Valid credentials', | |
| value: { email: 'test@example.com', password: 'MyPass@123' }, | |
| }, | |
| invalidEmail: { | |
| summary: 'Email not registered', | |
| value: { email: 'unknown@example.com', password: 'MyPass@123' }, | |
| }, | |
| invalidPassword: { | |
| summary: 'Wrong password', | |
| value: { email: 'test@example.com', password: 'wrongPass' }, | |
| }, | |
| }, | |
| }) | |
| ({ | |
| status: 200, | |
| description: 'Login successful', | |
| schema: { | |
| example: { | |
| message: 'Login successful', | |
| user: { id: 'uuid', email: 'test@example.com', name: 'John Doe', isLoggedin: true }, | |
| accessToken: 'jwt-access-token', | |
| refreshToken: 'jwt-refresh-token', | |
| }, | |
| }, | |
| }) | |
| ({ | |
| status: 401, | |
| description: 'Invalid credentials', | |
| schema: { | |
| example: { | |
| statusCode: 401, | |
| message: 'Invalid credentials', | |
| error: 'Unauthorized', | |
| }, | |
| }, | |
| }) | |
| async login(() dto: LoginDto) { | |
| try { | |
| const { email, password } = dto; | |
| // Basic checks for empty email/password | |
| if (!email || !password) { | |
| this.logger.warn('Login failed: Email or password missing'); | |
| throw new BadRequestException('Email and password are required'); | |
| } | |
| const result = await this.authService.login(email, password); | |
| this.logger.log(`User logged in successfully: ${email}`); | |
| return result; | |
| } catch (error: unknown) { | |
| if (error instanceof Error) { | |
| this.logger.error(`Login failed for email ${dto.email}: ${error.message}`); | |
| throw error; | |
| } else { | |
| this.logger.error(`Login failed for email ${dto.email}: Unknown error`); | |
| throw new BadRequestException('Login failed due to unknown error'); | |
| } | |
| } | |
| } | |
| ('refresh') | |
| ({ summary: 'Refresh JWT tokens using refresh token' }) | |
| ({ | |
| description: 'Refresh token payload', | |
| schema: { | |
| type: 'object', | |
| properties: { | |
| refreshToken: { type: 'string', example: 'refresh-token-here' }, | |
| userId: { type: 'string', example: 'uuid-of-user (optional; derived from token when omitted)' }, | |
| }, | |
| required: ['refreshToken'], | |
| }, | |
| }) | |
| ({ | |
| status: 200, | |
| description: 'Tokens refreshed successfully', | |
| schema: { | |
| example: { | |
| message: 'Tokens refreshed successfully', | |
| user: { id: 'uuid', email: 'test@example.com', name: 'John Doe' }, | |
| accessToken: 'new-jwt-access-token', | |
| refreshToken: 'new-jwt-refresh-token', | |
| }, | |
| }, | |
| }) | |
| async refresh(() body: { userId?: string; refreshToken: string }) { | |
| const { userId, refreshToken } = body; | |
| return this.authService.refreshTokens(userId, refreshToken); | |
| } | |
| ('logout') | |
| (JwtAuthGuard) | |
| () | |
| ({ summary: 'Logout user and revoke refresh token' }) | |
| async logout(() req: Request) { // ✅ type req as Request | |
| const userId = (req.user as any).id; // user comes from JWT guard | |
| await this.authService.logout(userId); | |
| return { message: 'Logged out successfully' }; | |
| } | |
| ('verify-otp') | |
| ({ summary: 'Verify OTP sent to email' }) | |
| ({ | |
| schema: { | |
| type: 'object', | |
| properties: { | |
| email: { type: 'string', example: 'johndoe@example.com' }, | |
| otp: { type: 'string', example: '123456' }, | |
| }, | |
| required: ['email', 'otp'], | |
| }, | |
| }) | |
| async verifyOtp(() verifyOtpDto: VerifyOtpDto) { | |
| return this.authService.verifyEmailOtp( | |
| verifyOtpDto.email, | |
| verifyOtpDto.otp, | |
| ); | |
| } | |
| ('resend-otp') | |
| ({ summary: 'Resend OTP to user email' }) | |
| ({ | |
| schema: { | |
| type: 'object', | |
| properties: { | |
| email: { type: 'string', example: 'johndoe@example.com' }, | |
| }, | |
| required: ['email'], | |
| }, | |
| }) | |
| ({ summary: 'Resend OTP to user email' }) | |
| async resendOtp(() body: { email: string }) { | |
| return this.authService.resendOtp(body.email); | |
| } | |
| //Initiate Google Login | |
| ('google') | |
| () | |
| (AuthGuard('google')) | |
| async googleLogin(() req: Request) { | |
| // This will redirect user to Google for login | |
| } | |
| // 2. Google callback route | |
| ('google/callback') | |
| () | |
| (AuthGuard('google')) | |
| async googleCallback(() req: Request, () res: Response) { | |
| const user = req.user; | |
| if (!user) throw new BadRequestException('Google login failed'); | |
| const frontendUrl = this.config.get<string>('FRONTEND_URL') || 'http://localhost:3000'; | |
| const accessToken = (user as any).accessToken; | |
| const refreshToken = (user as any).refreshToken; | |
| const name = (user as any).user?.name || ''; | |
| const email = (user as any).user?.email || ''; | |
| const redirectUrl = | |
| `${frontendUrl}/auth/google/callback` + | |
| `?accessToken=${encodeURIComponent(accessToken)}` + | |
| `&refreshToken=${encodeURIComponent(refreshToken)}` + | |
| `&name=${encodeURIComponent(name)}` + | |
| `&email=${encodeURIComponent(email)}`; | |
| res.redirect(redirectUrl); | |
| return; | |
| } | |
| ('me') | |
| (JwtAuthGuard) | |
| () | |
| ({ | |
| summary: 'Get current authenticated user', | |
| description: 'Returns the logged-in user\'s basic info. Call this immediately after login/verify-otp to get user details.', | |
| }) | |
| ({ | |
| description: 'Current user info', | |
| schema: { | |
| example: { | |
| id: 'c80d42ce-dfa3-41be-9676-599b58b93004', | |
| name: 'John Doe', | |
| email: 'john@example.com', | |
| isEmailVerified: true, | |
| }, | |
| }, | |
| }) | |
| async getMe(() req: Request) { | |
| const user = req.user as any; | |
| return { | |
| id: user.id, | |
| name: user.name, | |
| email: user.email, | |
| isEmailVerified: user.isVerified, | |
| // Read live from the DB user the JWT strategy loaded, not from the token. | |
| // The admin UI uses it only to decide what to render; the API is the actual | |
| // gate, so a stale or forged value here grants nothing. | |
| role: user.role ?? 'candidate', | |
| }; | |
| } | |
| ('forgot-password') | |
| ({ summary: 'Send password reset link to user email' }) | |
| ({ | |
| schema: { | |
| type: 'object', | |
| properties: { | |
| email: { type: 'string', example: 'user@example.com' }, | |
| }, | |
| required: ['email'], | |
| }, | |
| }) | |
| async forgotPassword(() body: { email: string }) { | |
| await this.authService.forgotPassword(body.email); | |
| return { message: 'Password reset link sent to email' }; | |
| } | |
| ('reset-password') | |
| ({ summary: 'Reset password using token' }) | |
| ({ | |
| schema: { | |
| type: 'object', | |
| properties: { | |
| email: { type: 'string', example: 'johndoe@example.com' }, | |
| token: { type: 'string', example: 'reset-token-from-email' }, | |
| newPassword: { type: 'string', example: 'NewPass@123' }, | |
| }, | |
| required: ['email', 'token', 'newPassword'], | |
| }, | |
| }) | |
| async resetPassword(() body: { email: string; token: string; newPassword: string }) { | |
| return this.authService.resetPassword(body.email, body.token, body.newPassword); | |
| } | |
| } | |