-
Notifications
You must be signed in to change notification settings - Fork 32
Pull Request for Job Listing Retrieval and Pagination #135
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
1927a19
f9978e7
53ae541
6e18d2c
af55828
2e0ea89
81a93b7
751849d
96233ab
fcb27a1
cd1ecda
18e93ad
0e67daf
16f44fe
e7084f0
a3d8984
09f557b
0f3ec7b
6a2a3ad
39e32e6
3531e3e
ba99ca4
06ace4f
f04c5d8
34e80b8
10eda03
cd1ad1a
db35b06
b566e87
a971af5
77a6020
3f4c543
97af55e
e79b011
332b00b
f7e3376
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -20,15 +20,18 @@ import { addHours, addDays, addMinutes } from 'date-fns'; | |||||||||||||||||||||||||
| import * as crypto from 'crypto'; | ||||||||||||||||||||||||||
| import { MailService } from '../mail/mail.service'; | ||||||||||||||||||||||||||
| import { ConfigService } from '@nestjs/config'; | ||||||||||||||||||||||||||
| import type { LogInDto } from './dto/loginDto'; | ||||||||||||||||||||||||||
| import type { LoginDto } from './dto/login-user.dto'; | ||||||||||||||||||||||||||
| import { LogInProvider } from './providers/loginProvider'; | ||||||||||||||||||||||||||
| import { JwtPayload } from './interfaces/jwt-payload.interface'; | ||||||||||||||||||||||||||
| import { TeamService } from './services/team.service'; | ||||||||||||||||||||||||||
| import { SkillVerification, VerificationStatus } from './entities/skills-verification.entity'; | ||||||||||||||||||||||||||
| import type { | ||||||||||||||||||||||||||
| CreateSkillVerificationDto, | ||||||||||||||||||||||||||
| SkillAssessmentDto, | ||||||||||||||||||||||||||
| UpdateSkillVerificationDto | ||||||||||||||||||||||||||
| import { | ||||||||||||||||||||||||||
| SkillVerification, | ||||||||||||||||||||||||||
| VerificationStatus, | ||||||||||||||||||||||||||
| } from './entities/skills-verification.entity'; | ||||||||||||||||||||||||||
| import type { | ||||||||||||||||||||||||||
| CreateSkillVerificationDto, | ||||||||||||||||||||||||||
| SkillAssessmentDto, | ||||||||||||||||||||||||||
| UpdateSkillVerificationDto, | ||||||||||||||||||||||||||
| } from './dto/skills.dto'; | ||||||||||||||||||||||||||
|
Comment on lines
+27
to
35
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove unused imports The following imports are not used in this file and should be removed:
-import {
- SkillVerification,
- VerificationStatus,
-} from './entities/skills-verification.entity';
-import type {
- CreateSkillVerificationDto,
- SkillAssessmentDto,
- UpdateSkillVerificationDto,
-} from './dto/skills.dto';
+import {
+ SkillVerification,
+} from './entities/skills-verification.entity';📝 Committable suggestion
Suggested change
🧰 Tools🪛 ESLint[error] 29-29: 'VerificationStatus' is defined but never used. (@typescript-eslint/no-unused-vars) [error] 32-32: 'CreateSkillVerificationDto' is defined but never used. (@typescript-eslint/no-unused-vars) [error] 33-33: 'SkillAssessmentDto' is defined but never used. (@typescript-eslint/no-unused-vars) [error] 34-34: 'UpdateSkillVerificationDto' is defined but never used. (@typescript-eslint/no-unused-vars) 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| @Injectable() | ||||||||||||||||||||||||||
|
|
@@ -66,8 +69,8 @@ export class AuthService { | |||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| // ... [keep all other methods exactly as they are] ... | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| async login(loginDto: LogInDto): Promise<{ | ||||||||||||||||||||||||||
| accessToken: string; | ||||||||||||||||||||||||||
| async login(loginDto: LoginDto): Promise<{ | ||||||||||||||||||||||||||
| accessToken: string; | ||||||||||||||||||||||||||
| refreshToken: string; | ||||||||||||||||||||||||||
| user: Omit<User, 'password'>; | ||||||||||||||||||||||||||
| }> { | ||||||||||||||||||||||||||
|
|
@@ -87,15 +90,136 @@ export class AuthService { | |||||||||||||||||||||||||
| // Generate tokens using the refresh token flow | ||||||||||||||||||||||||||
| const tokens = await this.getTokens(user.id, user.email); | ||||||||||||||||||||||||||
| await this.updateRefreshToken(user.id, tokens.refreshToken); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| // Remove password from the user object | ||||||||||||||||||||||||||
| const { password: _, ...userWithoutPassword } = user; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| return { | ||||||||||||||||||||||||||
| ...tokens, | ||||||||||||||||||||||||||
| user: userWithoutPassword | ||||||||||||||||||||||||||
| user: userWithoutPassword, | ||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| async getTokens( | ||||||||||||||||||||||||||
| userId: string, | ||||||||||||||||||||||||||
| email: string, | ||||||||||||||||||||||||||
| ): Promise<{ | ||||||||||||||||||||||||||
| accessToken: string; | ||||||||||||||||||||||||||
| refreshToken: string; | ||||||||||||||||||||||||||
| }> { | ||||||||||||||||||||||||||
| const jwtPayload: JwtPayload = { | ||||||||||||||||||||||||||
| sub: userId, | ||||||||||||||||||||||||||
| email: email, | ||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| const [accessToken, refreshToken] = await Promise.all([ | ||||||||||||||||||||||||||
| this.jwtService.signAsync(jwtPayload, { | ||||||||||||||||||||||||||
| secret: this.configService.get<string>('JWT_SECRET'), | ||||||||||||||||||||||||||
| expiresIn: '15m', | ||||||||||||||||||||||||||
| }), | ||||||||||||||||||||||||||
| this.jwtService.signAsync(jwtPayload, { | ||||||||||||||||||||||||||
| secret: this.configService.get<string>('JWT_REFRESH_SECRET'), | ||||||||||||||||||||||||||
| expiresIn: '7d', | ||||||||||||||||||||||||||
| }), | ||||||||||||||||||||||||||
| ]); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| return { | ||||||||||||||||||||||||||
| accessToken, | ||||||||||||||||||||||||||
| refreshToken, | ||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| async updateRefreshToken( | ||||||||||||||||||||||||||
| userId: string, | ||||||||||||||||||||||||||
| refreshToken: string, | ||||||||||||||||||||||||||
| ): Promise<void> { | ||||||||||||||||||||||||||
| const hashedRefreshToken = await bcrypt.hash(refreshToken, 10); | ||||||||||||||||||||||||||
| await this.userRepository.update(userId, { | ||||||||||||||||||||||||||
| refreshToken: hashedRefreshToken, | ||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| async refreshTokens(refreshToken: string): Promise<{ | ||||||||||||||||||||||||||
| accessToken: string; | ||||||||||||||||||||||||||
| refreshToken: string; | ||||||||||||||||||||||||||
| }> { | ||||||||||||||||||||||||||
| const payload = this.jwtService.verify(refreshToken, { | ||||||||||||||||||||||||||
| secret: this.configService.get<string>('JWT_REFRESH_SECRET'), | ||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| const user = await this.userRepository.findOne({ | ||||||||||||||||||||||||||
| where: { id: payload.sub }, | ||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| if (!user || !user.refreshToken) { | ||||||||||||||||||||||||||
| throw new ForbiddenException('Access Denied'); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| const refreshTokenMatches = await bcrypt.compare( | ||||||||||||||||||||||||||
| refreshToken, | ||||||||||||||||||||||||||
| user.refreshToken, | ||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| if (!refreshTokenMatches) { | ||||||||||||||||||||||||||
| throw new ForbiddenException('Access Denied'); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| const tokens = await this.getTokens(user.id, user.email); | ||||||||||||||||||||||||||
| await this.updateRefreshToken(user.id, tokens.refreshToken); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| return tokens; | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| async sendPasswordResetEmail(email: string): Promise<{ message: string }> { | ||||||||||||||||||||||||||
| const user = await this.userRepository.findOne({ where: { email } }); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| if (!user) { | ||||||||||||||||||||||||||
| // Don't reveal if email exists | ||||||||||||||||||||||||||
| return { | ||||||||||||||||||||||||||
| message: | ||||||||||||||||||||||||||
| 'If an account with that email exists, a password reset email has been sent.', | ||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| // Generate reset token | ||||||||||||||||||||||||||
| const resetToken = crypto.randomBytes(32).toString('hex'); | ||||||||||||||||||||||||||
| const expiresAt = addHours(new Date(), 1); // 1 hour expiration | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| // Save reset token | ||||||||||||||||||||||||||
| const passwordReset = this.passwordResetRepository.create({ | ||||||||||||||||||||||||||
| user, | ||||||||||||||||||||||||||
| token: resetToken, | ||||||||||||||||||||||||||
| expiresAt, | ||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| await this.passwordResetRepository.save(passwordReset); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| // Send email with reset link | ||||||||||||||||||||||||||
| const resetUrl = `${this.configService.get<string>('FRONTEND_URL')}/auth/reset-password?token=${resetToken}`; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| await this.mailService.sendEmail({ | ||||||||||||||||||||||||||
| to: email, | ||||||||||||||||||||||||||
| subject: 'Password Reset Request', | ||||||||||||||||||||||||||
| template: 'password-reset', | ||||||||||||||||||||||||||
| context: { | ||||||||||||||||||||||||||
| resetUrl, | ||||||||||||||||||||||||||
| expiresIn: '1 hour', | ||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| return { | ||||||||||||||||||||||||||
| message: | ||||||||||||||||||||||||||
| 'If an account with that email exists, a password reset email has been sent.', | ||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| async getOneByEmail(email: string): Promise<User | null> { | ||||||||||||||||||||||||||
| return this.userRepository.findOne({ where: { email } }); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| async validateUserById(userId: string): Promise<User | null> { | ||||||||||||||||||||||||||
| return this.userRepository.findOne({ where: { id: userId } }); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| // ... [keep all other methods exactly as they are] ... | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Verify if RateLimitGuard should remain commented out.
The global
RateLimitGuardprovider is commented out, which removes rate limiting protection from the entire application. This could be a security concern if intentional for production.Please confirm if this is intentional:
🏁 Script executed:
Length of output: 2478
Global RateLimitGuard is disabled—no rate limiting in effect
The ThrottlerModule is configured (100 requests per minute), but the global
RateLimitGuardprovider is commented out and not used elsewhere. This means no rate-limiting guard is bound, leaving the app unprotected.• File
src/app.module.ts(around lines 110–113):If you intentionally disabled global throttling for development, ensure you apply
@UseGuards(RateLimitGuard)on critical controllers or restore the provider before production.📝 Committable suggestion
🤖 Prompt for AI Agents