Skip to content

Commit

Permalink
Merge pull request #14 from Adoptaunpeludo/resend_validation_email
Browse files Browse the repository at this point in the history
Resend validation email
  • Loading branch information
JoseAlbDR authored Feb 19, 2024
2 parents e1af61b + 104b408 commit c3c31a0
Show file tree
Hide file tree
Showing 5 changed files with 125 additions and 14 deletions.
13 changes: 13 additions & 0 deletions src/presentation/auth/controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,19 @@ export class AuthController {
});
};

resendVerificationToken = async (req: Request, res: Response) => {
const { email } = req.params;

const verificationToken = await this.authService.resendValidationEmail(
email
);

res.status(HttpCodes.OK).json({
message: 'Success!, Please check your email to verify your account',
token: verificationToken,
});
};

login = async (req: Request, res: Response) => {
const userAgent = req.headers['user-agent'] || '';
const ip = req.ip || '';
Expand Down
5 changes: 5 additions & 0 deletions src/presentation/auth/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ export class AuthRoutes {
authController.register
);

router.post(
'/resend-validation-email/:email',
authController.resendVerificationToken
);

router.post(
'/forgot-password',
ValidationMiddleware.validate(ForgotPasswordDto),
Expand Down
43 changes: 42 additions & 1 deletion src/presentation/services/auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@ import {
UnauthorizedError,
} from '../../domain';
import { JWTAdapter } from '../../config';
import { InternalServerError, UnauthenticatedError } from '../../domain/errors';
import {
InternalServerError,
NotFoundError,
UnauthenticatedError,
} from '../../domain/errors';
import { CryptoAdapter } from '../../config/crypto.adapter';
import { ProducerService } from './producer.service';
import { PartialUserResponse } from '../../domain/interfaces';
Expand Down Expand Up @@ -103,6 +107,43 @@ export class AuthService {
return createdUser;
}

public async resendValidationEmail(email: string) {
const user = await prisma.user.findUnique({
where: { email },
});

if (!user) throw new NotFoundError('User not found');

if (user.emailValidated)
throw new BadRequestError('Email already validated');

const verificationToken = this.jwt.generateToken(
{ user: { email } },
'15m'
);

if (!verificationToken)
throw new InternalServerError('JWT token error, check server logs');

await prisma.user.update({
where: { email },
data: {
verificationToken,
},
});

await this.emailService.addMessageToQueue(
{
email,
verificationToken,
type: 'email',
},
'verify-email'
);

return verificationToken;
}

private async validateCredentials(loginUserDto: LoginUserDto) {
const user = await prisma.user.findUnique({
where: { email: loginUserDto.email },
Expand Down
70 changes: 59 additions & 11 deletions src/presentation/services/user.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,23 @@ import { prismaWithPasswordExtension as prisma } from '../../data/postgres';
import {
AnimalFilterDto,
BadRequestError,
FileUploadDto,
NotFoundError,
PaginationDto,
UpdateUserDto,
UserEntity,
} from '../../domain';
import { UpdateSocialMediaDto } from '../../domain/dtos/users/update-social-media.dto';
import { AnimalEntity } from '../../domain/entities/animals.entity';
import { PayloadUser, UserRoles } from '../../domain/interfaces';
import { AnimalResponse, PayloadUser } from '../../domain/interfaces';
import { CheckPermissions } from '../../utils';
import { ProducerService } from './producer.service';
import { S3Service } from './s3.service';

export class UserService {
constructor(private readonly s3Service: S3Service) {}
constructor(
private readonly s3Service: S3Service,
private readonly notificationService: ProducerService
) {}

public async getAllUsers() {
const users = await prisma.user.findMany({
Expand Down Expand Up @@ -91,29 +94,74 @@ export class UserService {
return userEntity;
}

public async deleteUser(user: PayloadUser) {
const userToDelete = await prisma.user.findUnique({
where: { email: user.email },
include: {
shelter: {
select: {
images: true,
private async notifyDeletedAnimalsToUsers(animalsCreated: AnimalResponse[]) {
const deletedAnimalsIds = animalsCreated.map((animal) => animal.id);

const usersWithFavs = await prisma.user.findMany({
where: {
userFav: {
some: {
id: {
in: deletedAnimalsIds,
},
},
},
},
include: {
userFav: true,
},
});

usersWithFavs.forEach((user) => {
user.userFav.forEach((animal) => {
if (deletedAnimalsIds.includes(animal.id))
this.notificationService.addMessageToQueue(
{
message: `Animal with id: ${animal.id} and name: ${animal.name} was deleted`,
userId: user.id,
},
'animal-changed-push-notification'
);
});
});
}

public async deleteUser(user: PayloadUser) {
const [userToDelete, animalsCreated] = await prisma.$transaction([
prisma.user.findUnique({
where: { email: user.email },
include: {
shelter: {
select: {
images: true,
},
},
},
}),
prisma.animal.findMany({
where: {
createdBy: user.id,
},
include: {
cat: true,
dog: true,
},
}),
]);

if (!userToDelete) throw new NotFoundError('User not found');

CheckPermissions.check(user, userToDelete.id);

await prisma.user.delete({ where: { email: userToDelete.email } });
await this.notifyDeletedAnimalsToUsers(animalsCreated);

const imagesToDelete =
userToDelete.shelter?.images.map((image: string) => image) || [];

if (imagesToDelete.length > 0)
await this.s3Service.deleteFiles(imagesToDelete);

await prisma.user.delete({ where: { email: userToDelete.email } });
}

public async changePassword(
Expand Down
8 changes: 6 additions & 2 deletions src/presentation/users/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
UpdatePasswordDto,
UpdateSocialMediaDto,
} from '../../domain';
import { UserService, S3Service } from '../services';
import { UserService, S3Service, ProducerService } from '../services';

export class UserRoutes {
static get routes() {
Expand All @@ -27,7 +27,11 @@ export class UserRoutes {
envs.AWS_SECRET_ACCESS_KEY,
envs.AWS_BUCKET
);
const userService = new UserService(s3Service);
const notificationService = new ProducerService(
envs.RABBITMQ_URL,
'notification-request'
);
const userService = new UserService(s3Service, notificationService);
const userController = new UserController(userService);
const fileUploadMiddleware = new FileUploadMiddleware(s3Service);

Expand Down

0 comments on commit c3c31a0

Please sign in to comment.