import { logger } from "@config/logger";
import mongoose, { Schema } from "mongoose";
import { PasswordUtils } from "../utils/password.utils.js";
/**
* Mongoose schema describing application users.
* @category Models
*/
const UserSchema = new Schema({
email: {
type: String,
required: true,
unique: true,
lowercase: true,
trim: true,
match: [/^\w+([.-]?\w+)*@\w+([.-]?\w+)*(\.\w{2,3})+$/, "Please enter a valid email"],
},
name: {
type: String,
required: true,
trim: true,
minlength: 2,
maxlength: 50,
},
avatar: {
type: String,
default: null,
},
passwordHash: {
type: String,
required: true,
minlength: 60, // bcrypt hash length
},
role: {
type: String,
required: true,
enum: ["admin", "sme"],
default: "sme",
},
}, {
timestamps: true,
toJSON: {
transform: function (doc, ret) {
return ret;
},
},
});
// Index for performance
UserSchema.index({ role: 1 });
/**
* Compare a candidate password to the stored bcrypt hash.
* @category Models
* @param {string} candidatePassword Plain text password to validate.
* @returns {Promise<boolean>} Resolves true when the password matches.
*/
UserSchema.methods.comparePassword = async function (candidatePassword) {
try {
return await PasswordUtils.comparePassword(candidatePassword, this.passwordHash);
}
catch (error) {
logger.error({
error,
message: "Password comparison failed",
});
throw new Error("Password comparison failed");
}
};
/**
* Hash a password using the configured password utility.
* @category Models
* @param {string} password Plain text password to hash.
* @returns {Promise<string>} Bcrypt hash.
*/
UserSchema.statics.hashPassword = async function (password) {
return await PasswordUtils.hashPassword(password);
};
/**
* Pre-save hook responsible for password hashing flow.
* @category Models
*/
UserSchema.pre("save", function (next) {
// Only hash the password if it has been modified (or is new)
if (!this.isModified("passwordHash")) {
return next();
}
// If passwordHash is being set directly (not through password field), skip hashing
if (this.isNew && this.passwordHash) {
return next();
}
next();
});
/**
* User model exposing persistence helpers for platform accounts.
* @category Models
*/
export const User = mongoose.model("User", UserSchema);
Source