Files
Checkmate/server/src/db/models/User.ts
T
2026-01-18 06:02:07 +00:00

96 lines
2.7 KiB
TypeScript

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<User, "id" | "teamId" | "createdAt" | "updatedAt"> & {
teamId?: Types.ObjectId;
profileImage?: Required<UserProfileImage>;
};
interface UserDocument extends UserDocumentBase {
_id: Types.ObjectId;
teamId?: Types.ObjectId;
createdAt: Date;
updatedAt: Date;
}
const profileImageSchema = new Schema<Required<UserProfileImage>>(
{
data: { type: Buffer },
contentType: { type: String },
},
{ _id: false }
);
const UserSchema = new Schema<UserDocument>(
{
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<UserDocument>("User", UserSchema);
export type { UserDocument };
export { UserModel };
export default UserModel;