diff --git a/server/src/db/models/AppSettings.js b/server/src/db/models/AppSettings.js deleted file mode 100755 index a1a9964f2..000000000 --- a/server/src/db/models/AppSettings.js +++ /dev/null @@ -1,84 +0,0 @@ -import mongoose from "mongoose"; - -const AppSettingsSchema = mongoose.Schema( - { - checkTTL: { - type: Number, - default: 30, - }, - language: { - type: String, - default: "gb", - }, - pagespeedApiKey: { - type: String, - }, - systemEmailHost: { - type: String, - }, - systemEmailPort: { - type: Number, - }, - systemEmailAddress: { - type: String, - }, - systemEmailPassword: { - type: String, - }, - systemEmailUser: { - type: String, - }, - systemEmailConnectionHost: { - type: String, - default: "localhost", - }, - systemEmailTLSServername: { - type: String, - }, - systemEmailSecure: { - type: Boolean, - default: false, - }, - systemEmailPool: { - type: Boolean, - default: false, - }, - systemEmailIgnoreTLS: { - type: Boolean, - default: false, - }, - systemEmailRequireTLS: { - type: Boolean, - default: false, - }, - systemEmailRejectUnauthorized: { - type: Boolean, - default: true, - }, - showURL: { - type: Boolean, - default: false, - }, - singleton: { - type: Boolean, - required: true, - unique: true, - default: true, - }, - version: { - type: Number, - default: 1, - }, - globalThresholds: { - cpu: { type: Number }, - memory: { type: Number }, - disk: { type: Number }, - temperature: { type: Number }, - }, - }, - { - timestamps: true, - } -); - -export default mongoose.model("AppSettings", AppSettingsSchema); diff --git a/server/src/db/models/AppSettings.ts b/server/src/db/models/AppSettings.ts new file mode 100644 index 000000000..22d1369e5 --- /dev/null +++ b/server/src/db/models/AppSettings.ts @@ -0,0 +1,49 @@ +import { Schema, model, type Types } from "mongoose"; +import type { Settings, SettingsThresholds } from "@/types/settings.js"; + +interface AppSettingsDocument extends Omit { + _id: Types.ObjectId; + createdAt: Date; + updatedAt: Date; +} + +const thresholdsSchema = new Schema( + { + cpu: { type: Number }, + memory: { type: Number }, + disk: { type: Number }, + temperature: { type: Number }, + }, + { _id: false } +); + +const AppSettingsSchema = new Schema( + { + checkTTL: { type: Number, default: 30 }, + language: { type: String, default: "gb" }, + pagespeedApiKey: { type: String }, + systemEmailHost: { type: String }, + systemEmailPort: { type: Number }, + systemEmailAddress: { type: String }, + systemEmailPassword: { type: String }, + systemEmailUser: { type: String }, + systemEmailConnectionHost: { type: String, default: "localhost" }, + systemEmailTLSServername: { type: String }, + systemEmailSecure: { type: Boolean, default: false }, + systemEmailPool: { type: Boolean, default: false }, + systemEmailIgnoreTLS: { type: Boolean, default: false }, + systemEmailRequireTLS: { type: Boolean, default: false }, + systemEmailRejectUnauthorized: { type: Boolean, default: true }, + showURL: { type: Boolean, default: false }, + singleton: { type: Boolean, required: true, unique: true, default: true }, + version: { type: Number, default: 1 }, + globalThresholds: { type: thresholdsSchema }, + }, + { timestamps: true } +); + +const AppSettingsModel = model("AppSettings", AppSettingsSchema); + +export type { AppSettingsDocument }; +export { AppSettingsModel }; +export default AppSettingsModel; diff --git a/server/src/db/models/index.ts b/server/src/db/models/index.ts index 1e5024b1c..59ed8125d 100644 --- a/server/src/db/models/index.ts +++ b/server/src/db/models/index.ts @@ -16,5 +16,8 @@ export { default as UserModel } from "@/db/models/User.js"; export * from "@/db/models/Invite.js"; export { default as InviteModel } from "@/db/models/Invite.js"; +export * from "@/db/models/AppSettings.js"; +export { default as AppSettingsModel } from "@/db/models/AppSettings.js"; + export * from "@/db/models/RecoveryToken.js"; export { default as RecoveryTokenModel } from "@/db/models/RecoveryToken.js"; diff --git a/server/src/repositories/index.ts b/server/src/repositories/index.ts index 7a3c08c81..09a686595 100644 --- a/server/src/repositories/index.ts +++ b/server/src/repositories/index.ts @@ -18,3 +18,6 @@ export { default as MongoInvitesRepository } from "@/repositories/invites/MongoI export * from "@/repositories/recovery-tokens/IRecoveryTokensRepository.js"; export { default as MongoRecoveryTokensRepository } from "@/repositories/recovery-tokens/MongoRecoveryTokensRepository.js"; + +export * from "@/repositories/settings/ISettingsRepository.js"; +export { default as MongoSettingsRepository } from "@/repositories/settings/MongoSettingsRepository.js"; diff --git a/server/src/repositories/settings/ISettingsRepository.ts b/server/src/repositories/settings/ISettingsRepository.ts new file mode 100644 index 000000000..111a92a55 --- /dev/null +++ b/server/src/repositories/settings/ISettingsRepository.ts @@ -0,0 +1,8 @@ +import type { Settings } from "@/types/index.js"; +export interface ISettingsRepository { + // create + // fetch + // update + update(settings: Partial): Promise; + // delete +} diff --git a/server/src/repositories/settings/MongoSettingsRepository.ts b/server/src/repositories/settings/MongoSettingsRepository.ts new file mode 100644 index 000000000..f4d71d56e --- /dev/null +++ b/server/src/repositories/settings/MongoSettingsRepository.ts @@ -0,0 +1,74 @@ +import mongoose from "mongoose"; +import { ISettingsRepository } from "@/repositories/settings/ISettingsRepository.js"; +import type { Settings } from "@/types/index.js"; +import { AppSettingsModel, type AppSettingsDocument } from "@/db/models/AppSettings.js"; +import { AppError } from "@/utils/AppError.js"; + +class MongoSettingsRepository implements ISettingsRepository { + private toStringId = (value?: mongoose.Types.ObjectId | string | null): string => { + if (!value) { + return ""; + } + return value instanceof mongoose.Types.ObjectId ? value.toString() : String(value); + }; + + private toDateString = (value?: Date | string | null): string => { + if (!value) { + return new Date(0).toISOString(); + } + return value instanceof Date ? value.toISOString() : new Date(value).toISOString(); + }; + + protected toEntity = (doc: AppSettingsDocument): Settings => { + return { + id: this.toStringId(doc._id), + checkTTL: doc.checkTTL, + language: doc.language, + pagespeedApiKey: doc.pagespeedApiKey ?? undefined, + systemEmailHost: doc.systemEmailHost ?? undefined, + systemEmailPort: doc.systemEmailPort ?? undefined, + systemEmailAddress: doc.systemEmailAddress ?? undefined, + systemEmailPassword: doc.systemEmailPassword ?? undefined, + systemEmailUser: doc.systemEmailUser ?? undefined, + systemEmailConnectionHost: doc.systemEmailConnectionHost ?? undefined, + systemEmailTLSServername: doc.systemEmailTLSServername ?? undefined, + systemEmailSecure: doc.systemEmailSecure ?? false, + systemEmailPool: doc.systemEmailPool ?? false, + systemEmailIgnoreTLS: doc.systemEmailIgnoreTLS ?? false, + systemEmailRequireTLS: doc.systemEmailRequireTLS ?? false, + systemEmailRejectUnauthorized: doc.systemEmailRejectUnauthorized ?? true, + showURL: doc.showURL ?? false, + singleton: doc.singleton, + version: doc.version ?? 1, + globalThresholds: doc.globalThresholds ?? undefined, + createdAt: this.toDateString(doc.createdAt), + updatedAt: this.toDateString(doc.updatedAt), + }; + }; + + update = async (settings: Partial) => { + const update: Record = { $set: { ...settings } }; + + if (settings.pagespeedApiKey === "") { + update.$unset = { pagespeedApiKey: "" }; + delete update.$set.pagespeedApiKey; + } + + if (settings.systemEmailPassword === "") { + update.$unset = { systemEmailPassword: "" }; + delete update.$set.systemEmailPassword; + } + + await AppSettingsModel.findOneAndUpdate({}, update, { + upsert: true, + }); + + const currentSettings = await AppSettingsModel.findOne().select("-__v -_id -createdAt -updatedAt -singleton").lean(); + if (!currentSettings) { + throw new AppError({ message: "Settings not found after update", status: 500 }); + } + return this.toEntity(currentSettings); + }; +} + +export default MongoSettingsRepository; diff --git a/server/src/types/index.ts b/server/src/types/index.ts index 1a43d4849..fcfb6fc37 100644 --- a/server/src/types/index.ts +++ b/server/src/types/index.ts @@ -6,3 +6,4 @@ export * from "@/types/network.js"; export * from "@/types/user.js"; export * from "@/types/invite.js"; export * from "@/types/recoveryToken.js"; +export * from "@/types/settings.js"; diff --git a/server/src/types/settings.ts b/server/src/types/settings.ts new file mode 100644 index 000000000..78284a451 --- /dev/null +++ b/server/src/types/settings.ts @@ -0,0 +1,31 @@ +export interface SettingsThresholds { + cpu?: number; + memory?: number; + disk?: number; + temperature?: number; +} + +export interface Settings { + id: string; + checkTTL: number; + language: string; + pagespeedApiKey?: string; + systemEmailHost?: string; + systemEmailPort?: number; + systemEmailAddress?: string; + systemEmailPassword?: string; + systemEmailUser?: string; + systemEmailConnectionHost?: string; + systemEmailTLSServername?: string; + systemEmailSecure: boolean; + systemEmailPool: boolean; + systemEmailIgnoreTLS: boolean; + systemEmailRequireTLS: boolean; + systemEmailRejectUnauthorized: boolean; + showURL: boolean; + singleton: boolean; + version: number; + globalThresholds?: SettingsThresholds; + createdAt: string; + updatedAt: string; +}