think/packages/server/src/services/user.service.ts

366 lines
11 KiB
TypeScript
Raw Normal View History

2022-06-28 09:11:26 +00:00
import { RegisterUserDto, ResetPasswordDto } from '@dtos/create-user.dto';
2022-05-16 09:23:59 +00:00
import { LoginUserDto } from '@dtos/login-user.dto';
import { UpdateUserDto } from '@dtos/update-user.dto';
2022-06-28 09:11:26 +00:00
import { SystemEntity } from '@entities/system.entity';
2022-05-16 09:23:59 +00:00
import { UserEntity } from '@entities/user.entity';
import { forwardRef, HttpException, HttpStatus, Inject, Injectable } from '@nestjs/common';
2022-02-20 11:51:55 +00:00
import { ConfigService } from '@nestjs/config';
import { JwtService } from '@nestjs/jwt';
2022-05-16 09:23:59 +00:00
import { InjectRepository } from '@nestjs/typeorm';
import { MessageService } from '@services/message.service';
2022-06-29 16:03:02 +00:00
import { OrganizationService } from '@services/organization.service';
import { StarService } from '@services/star.service';
2022-06-29 16:03:02 +00:00
import { SystemService } from '@services/system.service';
2022-06-28 09:11:26 +00:00
import { VerifyService } from '@services/verify.service';
2022-02-20 11:51:55 +00:00
import { WikiService } from '@services/wiki.service';
2022-06-29 16:03:02 +00:00
import { ORGANIZATION_LOGOS } from '@think/constants';
import { IUser, UserStatus } from '@think/domains';
2022-05-16 09:23:59 +00:00
import { instanceToPlain } from 'class-transformer';
import { Repository } from 'typeorm';
2022-02-20 11:51:55 +00:00
@Injectable()
export class UserService {
constructor(
@InjectRepository(UserEntity)
private readonly userRepo: Repository<UserEntity>,
2022-04-04 13:42:52 +00:00
2022-02-20 11:51:55 +00:00
private readonly confifgService: ConfigService,
2022-04-04 13:42:52 +00:00
2022-02-20 11:51:55 +00:00
@Inject(forwardRef(() => JwtService))
private readonly jwtService: JwtService,
2022-04-04 13:42:52 +00:00
2022-02-20 11:51:55 +00:00
@Inject(forwardRef(() => MessageService))
private readonly messageService: MessageService,
2022-04-04 13:42:52 +00:00
@Inject(forwardRef(() => StarService))
private readonly starService: StarService,
2022-04-04 13:42:52 +00:00
2022-06-29 16:03:02 +00:00
@Inject(forwardRef(() => OrganizationService))
private readonly organizationService: OrganizationService,
2022-02-20 11:51:55 +00:00
@Inject(forwardRef(() => WikiService))
2022-06-28 09:11:26 +00:00
private readonly wikiService: WikiService,
@Inject(forwardRef(() => VerifyService))
private readonly verifyService: VerifyService,
@Inject(forwardRef(() => SystemService))
private readonly systemService: SystemService
) {
this.createDefaultSystemAdminFromConfigFile();
}
/**
*
*/
private async createDefaultSystemAdminFromConfigFile() {
if (await this.userRepo.findOne({ isSystemAdmin: true })) {
return;
}
const config = await this.confifgService.get('server.admin');
if (!config.name || !config.password || !config.email) {
throw new Error(`请指定名称、密码和邮箱`);
}
if (await this.userRepo.findOne({ email: config.email })) {
2022-06-28 09:11:26 +00:00
return;
}
try {
2022-06-29 16:03:02 +00:00
const res = await this.userRepo.create({
...config,
isSystemAdmin: true,
});
const createdUser = (await this.userRepo.save(res)) as unknown as IUser;
await this.organizationService.createOrganization(createdUser, {
name: createdUser.name,
description: `${createdUser.name}的个人组织`,
logo: ORGANIZATION_LOGOS[0],
isPersonal: true,
});
2022-06-28 09:11:26 +00:00
console.log('[think] 已创建默认系统管理员,请尽快登录系统修改密码');
} catch (e) {
console.error(`[think] 创建默认系统管理员失败:`, e.message);
}
}
2022-02-20 11:51:55 +00:00
/**
* id
* @param id
* @returns
*/
2022-06-30 06:20:38 +00:00
async findById(id): Promise<IUser> {
2022-02-20 11:51:55 +00:00
const user = await this.userRepo.findOne(id);
2022-06-30 06:20:38 +00:00
return instanceToPlain(user) as IUser;
2022-02-20 11:51:55 +00:00
}
/**
*
* @param opts
* @returns
*/
2022-06-30 06:20:38 +00:00
async findOne(opts: Partial<UserEntity>): Promise<UserEntity> {
2022-02-20 11:51:55 +00:00
const user = await this.userRepo.findOne(opts);
return user;
}
/**
* ids
* @param id
* @returns
*/
2022-06-30 06:20:38 +00:00
async findByIds(ids): Promise<IUser[]> {
2022-02-20 11:51:55 +00:00
const users = await this.userRepo.findByIds(ids);
2022-06-30 06:20:38 +00:00
return users.map((user) => instanceToPlain(user)) as IUser[];
2022-02-20 11:51:55 +00:00
}
/**
*
* @param user CreateUserDto
* @returns
*/
2022-06-30 06:20:38 +00:00
async createUser(user: RegisterUserDto): Promise<IUser> {
2022-06-28 12:26:04 +00:00
const currentSystemConfig = await this.systemService.getConfigFromDatabase();
if (currentSystemConfig.isSystemLocked) {
throw new HttpException('系统维护中,暂不可注册', HttpStatus.FORBIDDEN);
}
2022-02-20 11:51:55 +00:00
if (await this.userRepo.findOne({ name: user.name })) {
throw new HttpException('该账户已被注册', HttpStatus.BAD_REQUEST);
}
if (await this.userRepo.findOne({ name: user.name })) {
throw new HttpException('该账户已被注册', HttpStatus.BAD_REQUEST);
}
if (user.email && (await this.userRepo.findOne({ email: user.email }))) {
throw new HttpException('该邮箱已被注册', HttpStatus.BAD_REQUEST);
}
2022-06-30 06:20:38 +00:00
if (
currentSystemConfig.enableEmailVerify &&
!(await this.verifyService.checkVerifyCode(user.email, user.verifyCode))
) {
2022-06-28 09:11:26 +00:00
throw new HttpException('验证码不正确,请检查', HttpStatus.BAD_REQUEST);
}
2022-02-20 11:51:55 +00:00
const res = await this.userRepo.create(user);
const createdUser = await this.userRepo.save(res);
2022-06-29 16:03:02 +00:00
await this.organizationService.createOrganization(createdUser, {
2022-02-20 11:51:55 +00:00
name: createdUser.name,
2022-06-29 16:03:02 +00:00
description: `${createdUser.name}的个人组织`,
logo: ORGANIZATION_LOGOS[0],
isPersonal: true,
2022-02-20 11:51:55 +00:00
});
2022-06-29 16:03:02 +00:00
2022-06-30 06:20:38 +00:00
return instanceToPlain(createdUser) as IUser;
2022-02-20 11:51:55 +00:00
}
2022-06-28 09:11:26 +00:00
/**
*
* @param registerUser
*/
public async resetPassword(resetPasswordDto: ResetPasswordDto) {
2022-06-28 12:26:04 +00:00
const currentSystemConfig = await this.systemService.getConfigFromDatabase();
if (currentSystemConfig.isSystemLocked) {
throw new HttpException('系统维护中,暂不可使用', HttpStatus.FORBIDDEN);
}
2022-06-28 09:11:26 +00:00
const { email, password, confirmPassword, verifyCode } = resetPasswordDto;
const inDatabaseUser = await this.userRepo.findOne({ email });
if (!inDatabaseUser) {
throw new HttpException('该邮箱尚未注册', HttpStatus.BAD_REQUEST);
}
if (password !== confirmPassword) {
throw new HttpException('两次密码不一致,请重试', HttpStatus.BAD_REQUEST);
}
if (currentSystemConfig.enableEmailVerify && !(await this.verifyService.checkVerifyCode(email, verifyCode))) {
2022-06-28 09:11:26 +00:00
throw new HttpException('验证码不正确,请检查', HttpStatus.BAD_REQUEST);
}
const user = await this.userRepo.save(
await this.userRepo.merge(inDatabaseUser, { password: UserEntity.encryptPassword(password) })
);
return instanceToPlain(user);
}
2022-02-20 11:51:55 +00:00
/**
*
* @param user
* @returns
*/
2022-06-30 06:20:38 +00:00
async login(user: LoginUserDto): Promise<{ user: IUser; token: string; domain: string; expiresIn: number }> {
2022-06-28 09:11:26 +00:00
const currentSystemConfig = await this.systemService.getConfigFromDatabase();
2022-02-20 11:51:55 +00:00
const { name, password } = user;
2022-06-28 09:11:26 +00:00
let existUser = await this.userRepo.findOne({ where: { name } });
if (!existUser) {
existUser = await this.userRepo.findOne({ where: { email: name } });
}
2022-06-28 12:26:04 +00:00
const isExistUserSystemAdmin = existUser ? existUser.isSystemAdmin : false;
if (currentSystemConfig.isSystemLocked && !isExistUserSystemAdmin) {
2022-06-28 09:11:26 +00:00
throw new HttpException('系统维护中,暂不可登录', HttpStatus.FORBIDDEN);
}
2022-02-20 11:51:55 +00:00
2022-03-12 03:27:56 +00:00
if (!existUser || !(await UserEntity.comparePassword(password, existUser.password))) {
2022-02-20 11:51:55 +00:00
throw new HttpException('用户名或密码错误', HttpStatus.BAD_REQUEST);
}
if (existUser.status === UserStatus.locked) {
throw new HttpException('用户已锁定,无法登录', HttpStatus.BAD_REQUEST);
}
2022-06-30 06:20:38 +00:00
const res = instanceToPlain(existUser) as IUser;
2022-02-20 11:51:55 +00:00
const token = this.jwtService.sign(res);
2022-05-24 09:33:30 +00:00
const domain = this.confifgService.get('client.siteDomain');
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
const expiresIn = this.jwtService.decode(token, { complete: true }).payload.exp;
return { user: res, token, domain, expiresIn };
2022-02-20 11:51:55 +00:00
}
2022-06-17 14:12:19 +00:00
async logout() {
const domain = this.confifgService.get('client.siteDomain');
return { token: '', domain };
}
2022-02-20 11:51:55 +00:00
async validateUser(user: UserEntity) {
return await this.findById(user.id);
}
/**
*
* @param user
* @param dto
* @returns
*/
2022-06-30 06:20:38 +00:00
async updateUser(user: UserEntity, dto: UpdateUserDto): Promise<IUser> {
const currentSystemConfig = await this.systemService.getConfigFromDatabase();
2022-02-20 11:51:55 +00:00
const oldData = await this.userRepo.findOne(user.id);
if (oldData.email !== dto.email) {
if (await this.userRepo.findOne({ where: { email: dto.email } })) {
throw new HttpException('该邮箱已被注册', HttpStatus.BAD_REQUEST);
}
if (
currentSystemConfig.enableEmailVerify &&
!(await this.verifyService.checkVerifyCode(dto.email, dto.verifyCode))
) {
throw new HttpException('验证码不正确,请检查', HttpStatus.BAD_REQUEST);
}
}
2022-02-20 11:51:55 +00:00
const res = await this.userRepo.merge(oldData, dto);
const ret = await this.userRepo.save(res);
2022-06-30 06:20:38 +00:00
return instanceToPlain(ret) as IUser;
2022-02-20 11:51:55 +00:00
}
async decodeToken(token) {
const user = this.jwtService.decode(token) as UserEntity;
return user;
}
2022-04-05 05:33:45 +00:00
/**
*
* @param pagination
* @returns
*/
async getUsers() {
const query = this.userRepo.createQueryBuilder('user');
const [data] = await query.getManyAndCount();
return data;
}
2022-06-28 09:11:26 +00:00
/**
*
* @param user
* @param targetUserId
*/
async toggleLockUser(user: UserEntity, targetUserId) {
const currentUser = await this.userRepo.findOne(user.id);
if (!currentUser.isSystemAdmin) {
throw new HttpException('您无权操作', HttpStatus.FORBIDDEN);
}
const targetUser = await this.userRepo.findOne(targetUserId);
if (!targetUser) {
throw new HttpException('目标用户不存在', HttpStatus.NOT_FOUND);
}
const nextStatus = targetUser.status === UserStatus.normal ? UserStatus.locked : UserStatus.normal;
return await this.userRepo.save(await this.userRepo.merge(targetUser, { status: nextStatus }));
}
/**
*
* @param user
* @returns
*/
async getSystemConfig(user: UserEntity) {
const currentUser = await this.userRepo.findOne(user.id);
if (!currentUser.isSystemAdmin) {
throw new HttpException('您无权操作', HttpStatus.FORBIDDEN);
}
return await this.systemService.getConfigFromDatabase();
}
/**
*
* @param user
*/
async sendTestEmail(user: UserEntity) {
const currentUser = await this.userRepo.findOne(user.id);
if (!currentUser.isSystemAdmin) {
throw new HttpException('您无权操作', HttpStatus.FORBIDDEN);
}
const currentConfig = await this.systemService.getConfigFromDatabase();
try {
await this.systemService.sendEmail({
to: currentConfig.emailServiceUser,
subject: '测试邮件',
html: `<p>测试邮件</p>`,
});
return '测试邮件发送成功';
} catch (err) {
2022-07-25 07:18:02 +00:00
throw new HttpException(err.message || err, HttpStatus.BAD_REQUEST);
2022-06-28 09:11:26 +00:00
}
}
/**
*
* @param user
* @param targetUserId
*/
async updateSystemConfig(user: UserEntity, systemConfig: Partial<SystemEntity>) {
const currentUser = await this.userRepo.findOne(user.id);
if (!currentUser.isSystemAdmin) {
throw new HttpException('您无权操作', HttpStatus.FORBIDDEN);
}
return await this.systemService.updateConfigInDatabase(systemConfig);
}
2022-02-20 11:51:55 +00:00
}