193 lines
5.3 KiB
TypeScript
193 lines
5.3 KiB
TypeScript
import { Inject, Injectable } from '@nestjs/common';
|
|
import { Repository } from 'typeorm';
|
|
import { UserTokenType } from '../user-token/user-token.entity';
|
|
import { User } from './user.entity';
|
|
import { TokenService } from 'src/modules/utility/services/token.service';
|
|
import * as bcrypt from 'bcrypt';
|
|
import { EmailService } from '../email/email.service';
|
|
import { RegistrationEmail } from './email/registration.email';
|
|
import { ForgotPasswordEmail } from './email/forgot-password.email';
|
|
import { UserTokenService } from '../user-token/user-token.service';
|
|
import { ConfigurationService } from 'src/modules/config/config.service';
|
|
import { Upload } from '../upload/upload.entity';
|
|
import { UploadService } from '../upload/upload.service';
|
|
|
|
@Injectable()
|
|
export class UserService {
|
|
constructor(
|
|
@Inject('USER_REPOSITORY')
|
|
private userRepository: Repository<User>,
|
|
private userToken: UserTokenService,
|
|
private token: TokenService,
|
|
private email: EmailService,
|
|
private config: ConfigurationService,
|
|
private upload: UploadService,
|
|
) {}
|
|
|
|
public async getById(id: number, relations?: string[]): Promise<User> {
|
|
return this.userRepository.findOne({ id }, { relations });
|
|
}
|
|
|
|
public async getByUUID(uuid: string, relations?: string[]): Promise<User> {
|
|
return this.userRepository.findOne({ uuid }, { relations });
|
|
}
|
|
|
|
public async getByEmail(email: string, relations?: string[]): Promise<User> {
|
|
return this.userRepository.findOne({ email }, { relations });
|
|
}
|
|
|
|
public async getByUsername(
|
|
username: string,
|
|
relations?: string[],
|
|
): Promise<User> {
|
|
return this.userRepository.findOne({ username }, { relations });
|
|
}
|
|
|
|
public async get(
|
|
input: string | number,
|
|
relations?: string[],
|
|
): Promise<User> {
|
|
if (typeof input === 'number') {
|
|
return this.getById(input, relations);
|
|
}
|
|
|
|
if (input.includes('@')) {
|
|
return this.getByEmail(input, relations);
|
|
}
|
|
|
|
if (input.length === 36 && input.includes('-')) {
|
|
return this.getByUUID(input, relations);
|
|
}
|
|
|
|
return this.getByUsername(input, relations);
|
|
}
|
|
|
|
public async updateUser(user: User): Promise<User> {
|
|
await this.userRepository.save(user);
|
|
return user;
|
|
}
|
|
|
|
public async updateAvatar(user: User, upload: Upload): Promise<User> {
|
|
if (user.picture) {
|
|
await this.upload.delete(user.picture);
|
|
}
|
|
|
|
user.picture = upload;
|
|
await this.updateUser(user);
|
|
return user;
|
|
}
|
|
|
|
public async deleteAvatar(user: User): Promise<void> {
|
|
if (user.picture) {
|
|
await this.upload.delete(user.picture);
|
|
}
|
|
}
|
|
|
|
public async comparePasswords(
|
|
hash: string,
|
|
password: string,
|
|
): Promise<boolean> {
|
|
return bcrypt.compare(password, hash);
|
|
}
|
|
|
|
public async hashPassword(password: string): Promise<string> {
|
|
const salt = await bcrypt.genSalt(10);
|
|
return bcrypt.hash(password, salt);
|
|
}
|
|
|
|
public async sendActivationEmail(user: User): Promise<void> {
|
|
const activationToken = await this.userToken.create(
|
|
user,
|
|
UserTokenType.ACTIVATION,
|
|
new Date(Date.now() + 3600 * 1000),
|
|
);
|
|
|
|
try {
|
|
const content = RegistrationEmail(
|
|
user.username,
|
|
`${this.config.get<string>('app.base_url')}/login/activate?token=${
|
|
activationToken.token
|
|
}`,
|
|
);
|
|
await this.email.sendEmailTemplate(
|
|
user.email,
|
|
'Activate your account on Icy Network',
|
|
content,
|
|
);
|
|
} catch (e) {
|
|
await this.userToken.delete(activationToken);
|
|
throw e;
|
|
}
|
|
}
|
|
|
|
public async sendPasswordEmail(user: User): Promise<void> {
|
|
const passwordToken = await this.userToken.create(
|
|
user,
|
|
UserTokenType.PASSWORD,
|
|
new Date(Date.now() + 3600 * 1000),
|
|
);
|
|
|
|
try {
|
|
const content = ForgotPasswordEmail(
|
|
user.username,
|
|
`${this.config.get<string>('app.base_url')}/login/password?token=${
|
|
passwordToken.token
|
|
}`,
|
|
);
|
|
await this.email.sendEmailTemplate(
|
|
user.email,
|
|
'Reset your password on Icy Network',
|
|
content,
|
|
);
|
|
} catch (e) {
|
|
await this.userToken.delete(passwordToken);
|
|
// silently fail
|
|
}
|
|
}
|
|
|
|
public async userPassword(email: string): Promise<void> {
|
|
const user = await this.getByEmail(email);
|
|
if (!user || !user.activated) {
|
|
return;
|
|
}
|
|
|
|
await this.sendPasswordEmail(user);
|
|
}
|
|
|
|
public async userRegistration(newUserInfo: {
|
|
username: string;
|
|
display_name: string;
|
|
email: string;
|
|
password: string;
|
|
}): Promise<User> {
|
|
if (!!(await this.getByEmail(newUserInfo.email))) {
|
|
throw new Error('Email is already in use!');
|
|
}
|
|
|
|
if (!!(await this.getByUsername(newUserInfo.username))) {
|
|
throw new Error('Username is already in use!');
|
|
}
|
|
|
|
const hashword = await this.hashPassword(newUserInfo.password);
|
|
const user = new User();
|
|
user.email = newUserInfo.email;
|
|
user.uuid = this.token.createUUID();
|
|
user.username = newUserInfo.username;
|
|
user.display_name = newUserInfo.display_name;
|
|
user.password = hashword;
|
|
|
|
await this.userRepository.insert(user);
|
|
|
|
try {
|
|
await this.sendActivationEmail(user);
|
|
} catch (e) {
|
|
await this.userRepository.remove(user);
|
|
throw new Error(
|
|
'Failed to send activation email! Please check your email address and try again!',
|
|
);
|
|
}
|
|
|
|
return user;
|
|
}
|
|
}
|