Reactive-Resume/server/src/users/users.service.ts

128 lines
3.6 KiB
TypeScript

import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
import { SchedulerRegistry } from '@nestjs/schedule';
import { InjectRepository } from '@nestjs/typeorm';
import { randomBytes } from 'crypto';
import { Connection, Repository } from 'typeorm';
import { MailService } from '@/mail/mail.service';
import { CreateGoogleUserDto } from './dto/create-google-user.dto';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
import { User } from './entities/user.entity';
export const DELETION_TIME = 30 * 60 * 1000; // 30 minutes
@Injectable()
export class UsersService {
constructor(
@InjectRepository(User) private userRepository: Repository<User>,
private schedulerRegistry: SchedulerRegistry,
private mailService: MailService,
private connection: Connection
) {}
async findById(id: number): Promise<User> {
const user = await this.userRepository.findOne({ id });
if (user) {
return user;
}
throw new HttpException('A user with this username/email does not exist.', HttpStatus.NOT_FOUND);
}
async findByEmail(email: string): Promise<User> {
const user = await this.userRepository.findOne({ email });
if (user) {
return user;
}
throw new HttpException('A user with this email does not exist.', HttpStatus.NOT_FOUND);
}
async findByIdentifier(identifier: string): Promise<User> {
const user = await this.userRepository.findOne({
where: [{ username: identifier }, { email: identifier }],
});
if (user) {
return user;
}
throw new HttpException('A user with this username/email does not exist.', HttpStatus.NOT_FOUND);
}
async findByResetToken(resetToken: string): Promise<User> {
const user = await this.userRepository.findOne({ resetToken });
if (user) {
return user;
}
throw new HttpException('The reset token provided may be invalid or expired.', HttpStatus.NOT_FOUND);
}
async create(createUserDto: CreateUserDto | CreateGoogleUserDto): Promise<User> {
const user = this.userRepository.create(createUserDto);
await this.userRepository.save(user);
return user;
}
async update(id: number, updateUserDto: UpdateUserDto) {
const user = await this.findById(id);
const updatedUser = {
...user,
...updateUserDto,
};
await this.userRepository.save(updatedUser);
return updatedUser;
}
async remove(id: number): Promise<void> {
await this.userRepository.delete(id);
}
async generateResetToken(email: string): Promise<void> {
try {
const user = await this.findByEmail(email);
const resetToken = randomBytes(32).toString('hex');
const queryRunner = this.connection.createQueryRunner();
const timeout = setTimeout(async () => {
await this.userRepository.update(user.id, { resetToken: null });
}, DELETION_TIME);
await queryRunner.connect();
await queryRunner.startTransaction();
try {
await queryRunner.manager.update(User, user.id, { resetToken });
this.schedulerRegistry.addTimeout(`clear-resetToken-${user.id}`, timeout);
await this.mailService.sendForgotPasswordEmail(user, resetToken);
await queryRunner.commitTransaction();
} catch {
await queryRunner.rollbackTransaction();
throw new HttpException(
'Please wait at least 30 minutes before resetting your password again.',
HttpStatus.TOO_MANY_REQUESTS
);
} finally {
await queryRunner.release();
}
} catch {
// pass through
}
}
}