import { BadRequestException, Inject, Injectable, NotFoundException, } from "@nestjs/common"; import { Config } from "@prisma/client"; import { EventEmitter } from "events"; import { PrismaService } from "src/prisma/prisma.service"; /** * ConfigService extends EventEmitter to allow listening for config updates, * now only `update` event will be emitted. */ @Injectable() export class ConfigService extends EventEmitter { constructor( @Inject("CONFIG_VARIABLES") private configVariables: Config[], private prisma: PrismaService, ) { super(); } get(key: `${string}.${string}`): any { const configVariable = this.configVariables.filter( (variable) => `${variable.category}.${variable.name}` == key, )[0]; if (!configVariable) throw new Error(`Config variable ${key} not found`); const value = configVariable.value ?? configVariable.defaultValue; if (configVariable.type == "number") return parseInt(value); if (configVariable.type == "boolean") return value == "true"; if (configVariable.type == "string" || configVariable.type == "text") return value; } async getByCategory(category: string) { const configVariables = await this.prisma.config.findMany({ orderBy: { order: "asc" }, where: { category, locked: { equals: false } }, }); return configVariables.map((variable) => { return { ...variable, key: `${variable.category}.${variable.name}`, value: variable.value ?? variable.defaultValue, }; }); } async list() { const configVariables = await this.prisma.config.findMany({ where: { secret: { equals: false } }, }); return configVariables.map((variable) => { return { ...variable, key: `${variable.category}.${variable.name}`, value: variable.value ?? variable.defaultValue, }; }); } async updateMany(data: { key: string; value: string | number | boolean }[]) { const response: Config[] = []; for (const variable of data) { response.push(await this.update(variable.key, variable.value)); } return response; } async update(key: string, value: string | number | boolean) { const configVariable = await this.prisma.config.findUnique({ where: { name_category: { category: key.split(".")[0], name: key.split(".")[1], }, }, }); if (!configVariable || configVariable.locked) throw new NotFoundException("Config variable not found"); if (value === "") { value = null; } else if ( typeof value != configVariable.type && typeof value == "string" && configVariable.type != "text" ) { throw new BadRequestException( `Config variable must be of type ${configVariable.type}`, ); } this.validateConfigVariable(key, value); const updatedVariable = await this.prisma.config.update({ where: { name_category: { category: key.split(".")[0], name: key.split(".")[1], }, }, data: { value: value === null ? null : value.toString() }, }); this.configVariables = await this.prisma.config.findMany(); this.emit("update", key, value); return updatedVariable; } validateConfigVariable(key: string, value: string | number | boolean) { const validations = [ { key: "share.shareIdLength", condition: (value: number) => value >= 2 && value <= 50, message: "Share ID length must be between 2 and 50", }, { key: "share.zipCompressionLevel", condition: (value: number) => value >= 0 && value <= 9, message: "Zip compression level must be between 0 and 9", }, ]; const validation = validations.find((validation) => validation.key == key); if (validation && !validation.condition(value as any)) { throw new BadRequestException(validation.message); } } }