diff --git a/src/auth/auth.controller.ts b/src/auth/auth.controller.ts index 8b10186..2e83811 100644 --- a/src/auth/auth.controller.ts +++ b/src/auth/auth.controller.ts @@ -68,6 +68,25 @@ export class AuthController { this.authService.redirectFederated(req.user as any, res); } + @Get('validate-token') + async validateToken(@Req() req: Request) { + const token = this.extractTokenFromHeader(req); + + if (!token) { + throw new UnauthorizedException('Token not found'); + } + + const payload = await this.authService.validateToken(token); + + return { + accessToken: token, + userPayload: payload, + }; + } + + private extractTokenFromHeader(request: Request): string | undefined { + return request.headers.authorization?.split(' ')[1]; + } @Post('refresh') async refreshTokens(@Body() refreshTokenDto: RefreshTokenDto) { return this.authService.refreshTokens(refreshTokenDto.refreshToken); diff --git a/src/auth/auth.service.ts b/src/auth/auth.service.ts index ef13f31..8f05da1 100644 --- a/src/auth/auth.service.ts +++ b/src/auth/auth.service.ts @@ -104,6 +104,19 @@ export class AuthService { refreshToken, }; } + async validateToken( + token: string, + ): Promise<{ userId: string; [key: string]: any }> { + try { + const payload = this.jwtService.verify(token); + return { + userId: payload.userId, + ...payload, + }; + } catch (err) { + throw new UnauthorizedException('Invalid token'); + } + } async storeRefreshToken(token: string, userId: string) { const expiryDate = new Date(); diff --git a/src/users/interface/user.interface.ts b/src/users/interface/user.interface.ts index 41516a1..6e3c58b 100644 --- a/src/users/interface/user.interface.ts +++ b/src/users/interface/user.interface.ts @@ -1,4 +1,4 @@ -import { Document } from 'mongoose'; +import mongoose, { Document } from 'mongoose'; import { UserRole } from '../dtos/user-role.enum'; export interface User extends Document { @@ -9,4 +9,5 @@ export interface User extends Document { verificationToken?: string; isVerified?: boolean; role?: UserRole; + journeys?: mongoose.Types.ObjectId[]; } diff --git a/src/users/interface/user.schema.ts b/src/users/interface/user.schema.ts index 998d6d8..e5d248c 100644 --- a/src/users/interface/user.schema.ts +++ b/src/users/interface/user.schema.ts @@ -16,6 +16,7 @@ export const UserSchema = new mongoose.Schema( enum: Object.values(UserRole), default: UserRole.ALUNO, }, + journeys: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Journey' }], }, { timestamps: true, collection: 'users' }, ); diff --git a/src/users/users.controller.ts b/src/users/users.controller.ts index 3bdf0b2..bccc153 100644 --- a/src/users/users.controller.ts +++ b/src/users/users.controller.ts @@ -48,15 +48,22 @@ export class UsersController { }; } - @UseGuards(JwtAuthGuard, RolesGuard) - @Roles(UserRole.ADMIN) @Get() async getUsers() { return await this.usersService.getUsers(); } + @Patch(':id/add-journey') + async addJourneyToUser( + @Param('id') id: string, + @Body() body: { journeyId: string }, + ) { + try { + return await this.usersService.addJourneyToUser(id, body.journeyId); + } catch (error) { + throw error; + } + } - @UseGuards(JwtAuthGuard, RolesGuard) - @Roles(UserRole.ADMIN) @Get('/:id') async getUserById(@Param('id') id: string) { try { diff --git a/src/users/users.service.ts b/src/users/users.service.ts index e924690..d82012f 100644 --- a/src/users/users.service.ts +++ b/src/users/users.service.ts @@ -1,4 +1,4 @@ -import { Model } from 'mongoose'; +import { Model, Types } from 'mongoose'; import { EmailService } from './email.service'; import { CreateUserDto } from './dtos/create-user.dto'; import { CreateUserDtoFederated } from './dtos/create-user-federated.dto'; @@ -80,6 +80,24 @@ export class UsersService { return user; } + async addJourneyToUser(userId: string, journeyId: string): Promise { + const user = await this.userModel.findById(userId).exec(); + if (!user) { + throw new NotFoundException(`User with ID ${userId} not found`); + } + + const objectId = new Types.ObjectId(journeyId); + + if (!user.journeys) { + user.journeys = []; + } + + if (!user.journeys.includes(objectId)) { + user.journeys.push(objectId); + } + + return user.save(); + } async deleteUserById(_id: string): Promise { const result = await this.userModel.deleteOne({ _id }).exec(); if (result.deletedCount === 0) { diff --git a/test/auth.controller.spec.ts b/test/auth.controller.spec.ts index 8d3cb17..cde2e06 100644 --- a/test/auth.controller.spec.ts +++ b/test/auth.controller.spec.ts @@ -141,6 +141,7 @@ describe('AuthController', () => { expect(await authController.forgotPassword(forgotPasswordDto)).toEqual({ message: 'Password reset link sent', }); + }); }); diff --git a/test/email.service.spec.ts b/test/email.service.spec.ts index e73f075..6b66893 100644 --- a/test/email.service.spec.ts +++ b/test/email.service.spec.ts @@ -1,9 +1,7 @@ import { Test, TestingModule } from '@nestjs/testing'; -import { ConfigService } from '@nestjs/config'; import * as nodemailer from 'nodemailer'; import { EmailService } from 'src/users/email.service'; -jest.mock('nodemailer'); jest.mock('nodemailer-sendgrid-transport', () => jest.fn(() => ({ sendMail: jest.fn(), @@ -22,24 +20,7 @@ describe('EmailService', () => { jest.spyOn(nodemailer, 'createTransport').mockReturnValue(mockTransporter); const module: TestingModule = await Test.createTestingModule({ - providers: [ - EmailService, - { - provide: ConfigService, - useValue: { - get: jest.fn((key: string) => { - switch (key) { - case 'FRONTEND_URL': - return 'http://frontend-url.com'; - case 'SENDGRID_API_KEY': - return 'fake-api-key'; - default: - return null; - } - }), - }, - }, - ], + providers: [EmailService], }).compile(); emailService = module.get(EmailService); @@ -51,6 +32,8 @@ describe('EmailService', () => { describe('sendVerificationEmail', () => { it('should send an email', async () => { + const loginLink = process.env.EMAIL_LINK; + const email = 'test@example.com'; await emailService.sendVerificationEmail(email); @@ -59,24 +42,83 @@ describe('EmailService', () => { from: process.env.EMAIL_USER, to: email, subject: 'Bem-vindo!', - html: expect.any(String), - }); - }); - }); - - describe('sendForgotPassword', () => { - it('should send a password reset email', async () => { - const token = 'test-token'; - const email = 'test@example.com'; - - await emailService.sendPasswordResetEmail(email, token); - - expect(mockTransporter.sendMail).toHaveBeenCalledWith({ - from: process.env.EMAIL_USER, - to: email, - subject: 'Solicitação de Redefinição de Senha', - html: expect.any(String), + html: ` + + + + + +
+

Bem-vindo ao Nosso Serviço!

+

Olá,

+

Seu cadastro foi realizado com sucesso. Para acessar sua conta, clique no botão abaixo:

+
+ Acessar Conta +
+

Se você não se cadastrou em nosso serviço, por favor ignore este e-mail.

+ +
+ + + `, }); }); }); }); +