@fdm-monster/server
Version:
FDM Monster is a bulk OctoPrint, Klipper, PrusaLink and BambuLab manager to set up, configure and monitor 3D printers. Our aim is to provide neat overview over your farm.
144 lines (143 loc) • 5.91 kB
JavaScript
import { InternalServerException, NotFoundException } from "../../exceptions/runtime.exceptions.js";
import { BaseService } from "./base.service.js";
import { validateInput } from "../../handlers/validators.js";
import { User } from "../../entities/user.entity.js";
import { ROLES } from "../../constants/authorization.constants.js";
import { UserDto } from "../interfaces/user.dto.js";
import "../../entities/index.js";
import { newPasswordSchema, registerUserSchema } from "../validators/user-service.validation.js";
import { comparePasswordHash, hashPassword } from "../../utils/crypto.utils.js";
import { In } from "typeorm";
//#region src/services/orm/user.service.ts
var UserService = class extends BaseService(User, UserDto) {
constructor(typeormService, userRoleService, roleService) {
super(typeormService);
this.userRoleService = userRoleService;
this.roleService = roleService;
}
toDto(user) {
const roleIds = (user.roles ?? []).map((r) => r.roleId);
return {
id: user.id,
createdAt: user.createdAt,
isVerified: user.isVerified,
isDemoUser: user.isDemoUser,
isRootUser: user.isRootUser,
username: user.username,
needsPasswordChange: user.needsPasswordChange,
roles: this.roleService.roleIdsToRoleNames(roleIds)
};
}
listUsers(limit) {
return this.list({ take: limit });
}
async getUser(userId) {
return this.get(userId);
}
async getUserRoles(userId) {
const roleIds = (await this.userRoleService.listUserRoles(userId)).map((ur) => ur.roleId);
return this.roleService.roleIdsToRoleNames(roleIds);
}
findRootUsers() {
return this.repository.findBy({ isRootUser: true });
}
findVerifiedUsers() {
return this.repository.findBy({ isVerified: true });
}
async isUserRootUser(userId) {
return (await this.get(userId)).isRootUser;
}
async deleteUser(userId) {
const user = await this.getUser(userId);
if (user.isRootUser) throw new InternalServerException("Cannot delete a root user");
if (!user.roles?.length) throw new InternalServerException("User:roles relation not loaded, cannot perform deletion role check");
const role = this.roleService.getRoleByName(ROLES.ADMIN);
if (user.roles.find((r) => r.roleId == role.id)) {
const administrators = await this.findUsersByRoleId(role.id);
if (administrators?.length === 1 && administrators[0].id === userId) throw new InternalServerException("Cannot delete the last user with ADMIN role");
}
await this.delete(userId);
}
findRawByUsername(username) {
return this.repository.findOneBy({ username });
}
async getDemoUserId() {
return (await this.repository.findOneBy({ isDemoUser: true }))?.id;
}
async setIsRootUserById(userId, isRootUser) {
if (!await this.getUser(userId)) throw new NotFoundException("User not found");
if (!isRootUser) {
const rootUsers = await this.findRootUsers();
if (rootUsers.length === 1 && rootUsers[0].id === userId) throw new InternalServerException("Cannot set the last root user to non-root user");
}
await this.update(userId, { isRootUser });
}
async setUserRoles(userId, roles) {
const roleIds = roles.map((roleName) => this.roleService.getRoleByName(roleName).id);
await this.userRoleService.setUserRoles(userId, roleIds);
return await this.get(userId);
}
async setVerifiedById(userId, isVerified) {
const user = await this.getUser(userId);
if (!user) throw new NotFoundException("User not found");
if (!isVerified) {
if (user.isRootUser) throw new InternalServerException("Cannot set a owner (root user) to unverified");
if ((await this.findVerifiedUsers()).length === 1) throw new InternalServerException("Cannot set the last user to unverified");
}
await this.update(userId, { isVerified });
}
async updatePasswordById(userId, oldPassword, newPassword) {
const user = await this.getUser(userId);
if (!user) throw new NotFoundException("User not found");
if (!comparePasswordHash(oldPassword, user.passwordHash)) throw new NotFoundException("User old password incorrect");
const { password } = await validateInput({ password: newPassword }, newPasswordSchema);
const passwordHash = hashPassword(password);
return await this.update(userId, {
passwordHash,
needsPasswordChange: false
});
}
async updatePasswordUnsafeByUsername(username, newPassword) {
const { password } = await validateInput({ password: newPassword }, newPasswordSchema);
const passwordHash = hashPassword(password);
const user = await this.findRawByUsername(username);
if (!user) throw new NotFoundException("User not found");
return await this.update(user.id, {
passwordHash,
needsPasswordChange: false
});
}
async updatePasswordHashUnsafeByUsername(username, passwordHash) {
const user = await this.findRawByUsername(username);
if (!user) throw new NotFoundException("User not found");
return await this.update(user.id, {
passwordHash,
needsPasswordChange: false
});
}
updateUsernameById(userId, newUsername) {
return this.update(userId, { username: newUsername });
}
async register(input) {
const { username, password, roles, isDemoUser, isRootUser, needsPasswordChange, isVerified } = await validateInput(input, registerUserSchema);
const passwordHash = hashPassword(password);
const result = await this.create({
username,
passwordHash,
isVerified: isVerified ?? false,
isDemoUser: isDemoUser ?? false,
isRootUser: isRootUser ?? false,
needsPasswordChange: needsPasswordChange ?? true
});
const roleIds = roles.map((roleName) => this.roleService.getRoleByName(roleName).id);
await this.userRoleService.setUserRoles(result.id, roleIds);
return this.get(result.id);
}
async findUsersByRoleId(roleId) {
const ids = (await this.userRoleService.listByRoleId(roleId)).map((u) => u.id);
return this.repository.findBy({ id: In(ids) });
}
};
//#endregion
export { UserService };
//# sourceMappingURL=user.service.js.map