Spaces:
Sleeping
Sleeping
| import mongoose from 'mongoose'; | |
| import bcrypt from 'bcryptjs'; | |
| const UserSchema = new mongoose.Schema({ | |
| email: { | |
| type: String, | |
| required: [true, 'Email is required'], | |
| unique: true, | |
| trim: true, | |
| lowercase: true, | |
| match: [/^\w+([.-]?\w+)*@\w+([.-]?\w+)*(\.\w{2,3})+$/, 'Please provide a valid email'] | |
| }, | |
| name: { | |
| type: String, | |
| required: [true, 'Name is required'], | |
| trim: true, | |
| minlength: [2, 'Name must be at least 2 characters'] | |
| }, | |
| hashed_password: { | |
| type: String, | |
| required: [true, 'Password is required'] | |
| }, | |
| role: { | |
| type: String, | |
| enum: { | |
| values: ['admin', 'doctor', 'patient'], | |
| message: '{VALUE} is not a valid role' | |
| }, | |
| default: 'doctor' | |
| }, | |
| is_active: { | |
| type: Boolean, | |
| default: true | |
| }, | |
| specialization: { | |
| type: String, | |
| trim: true | |
| }, | |
| phone: { | |
| type: String, | |
| trim: true | |
| }, | |
| address: { | |
| type: String, | |
| trim: true | |
| } | |
| }, { | |
| timestamps: { | |
| createdAt: 'created_at', | |
| updatedAt: 'updated_at' | |
| } | |
| }); | |
| // Index for faster queries | |
| UserSchema.index({ role: 1 }); | |
| UserSchema.index({ is_active: 1 }); | |
| // Hash password before saving | |
| UserSchema.pre('save', async function() { | |
| // Only hash the password if it has been modified (or is new) | |
| if (!this.isModified('hashed_password')) return; | |
| // Check if the password is already hashed (bcrypt hashes start with $2) | |
| if (this.hashed_password.startsWith('$2')) return; | |
| const salt = await bcrypt.genSalt(10); | |
| this.hashed_password = await bcrypt.hash(this.hashed_password, salt); | |
| }); | |
| // Method to check password | |
| UserSchema.methods.comparePassword = async function(candidatePassword) { | |
| return bcrypt.compare(candidatePassword, this.hashed_password); | |
| }; | |
| // Method to convert to response object (exclude sensitive data) | |
| UserSchema.methods.toResponse = function() { | |
| return { | |
| id: this._id.toString(), | |
| email: this.email, | |
| name: this.name, | |
| role: this.role, | |
| is_active: this.is_active, | |
| specialization: this.specialization, | |
| phone: this.phone, | |
| created_at: this.created_at | |
| }; | |
| }; | |
| // Static method to find by email | |
| UserSchema.statics.findByEmail = function(email) { | |
| return this.findOne({ email: email.toLowerCase() }); | |
| }; | |
| // Static method to find active doctors | |
| UserSchema.statics.findActiveDoctors = function() { | |
| return this.find({ role: 'doctor', is_active: true }); | |
| }; | |
| // Static method to find active admins | |
| UserSchema.statics.findActiveAdmins = function() { | |
| return this.find({ role: 'admin', is_active: true }); | |
| }; | |
| const User = mongoose.model('User', UserSchema); | |
| export default User; | |