import { Schema, model, type Types } from "mongoose"; import bcrypt from "bcryptjs"; import type { User, UserProfileImage, UserRole } from "@/types/index.js"; import { MonitorModel } from "@/db/models/index.js"; import Team from "./Team.js"; import NotificationModel from "./Notification.js"; type UserDocumentBase = Omit & { teamId?: Types.ObjectId; profileImage?: Required; }; interface UserDocument extends UserDocumentBase { _id: Types.ObjectId; teamId?: Types.ObjectId; createdAt: Date; updatedAt: Date; } const profileImageSchema = new Schema>( { data: { type: Buffer }, contentType: { type: String }, }, { _id: false } ); const UserSchema = new Schema( { firstName: { type: String, required: true }, lastName: { type: String, required: true }, email: { type: String, required: true, unique: true }, password: { type: String, required: true }, avatarImage: { type: String }, profileImage: { type: profileImageSchema }, isActive: { type: Boolean, default: true }, isVerified: { type: Boolean, default: false }, role: { type: [String], enum: ["user", "admin", "superadmin", "demo" satisfies UserRole], default: ["user"], }, teamId: { type: Schema.Types.ObjectId, ref: "Team", immutable: true, }, checkTTL: { type: Number }, }, { timestamps: true } ); UserSchema.pre("save", function (next) { if (!this.isModified("password")) { return next(); } const salt = bcrypt.genSaltSync(10); this.password = bcrypt.hashSync(this.password, salt); next(); }); UserSchema.pre("findOneAndUpdate", function (next) { const update = this.getUpdate(); if (update && "password" in update) { const salt = bcrypt.genSaltSync(10); (update as any).password = bcrypt.hashSync((update as any).password, salt); } next(); }); UserSchema.pre("findOneAndDelete", async function (next) { try { const userToDelete = await this.model.findOne(this.getFilter()); if (!userToDelete) return next(); if (userToDelete.role.includes("superadmin")) { await Team.deleteOne({ _id: userToDelete.teamId }); await MonitorModel.deleteMany({ userId: userToDelete._id }); await this.model.deleteMany({ teamId: userToDelete.teamId, _id: { $ne: userToDelete._id } }); await NotificationModel.deleteMany({ teamId: userToDelete.teamId }); } next(); } catch (error) { next(error as Error); } }); UserSchema.methods.comparePassword = async function (submittedPassword: string) { return bcrypt.compare(submittedPassword, this.password); }; const UserModel = model("User", UserSchema); export type { UserDocument }; export { UserModel }; export default UserModel;