samoulla-backend / models /userModel.js
Samoulla Sync Bot
Auto-deploy Samoulla Backend: b68e45770de26ed39feb4b1c0925e5345eb3a61d
634b9bb
const mongoose = require('mongoose');
const bcrypt = require('bcryptjs');
const addressSchema = new mongoose.Schema({
governorate: { type: String, required: true },
city: { type: String, required: true },
district: { type: String, required: true },
street: { type: String, required: true },
details: { type: String },
});
const userSchema = new mongoose.Schema(
{
name: {
type: String,
required: [true, 'Please provide your name'],
trim: true,
},
email: {
type: String,
required: [true, 'Please provide your email'],
unique: true,
lowercase: true,
},
phone: {
type: String,
required: [true, 'Please provide your phone number'],
unique: true,
},
password: {
type: String,
required: [true, 'Please provide a password'],
minlength: 6,
select: false, // hide password by default
},
role: {
type: String,
enum: ['customer', 'admin', 'employee', 'vendor'],
default: 'customer',
},
permissions: {
type: [String],
default: [],
// Only relevant for employees - will contain permission strings like 'manage_products', 'view_orders', etc.
},
provider: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Provider',
// Only relevant for users with role 'vendor' - links to their Provider document
},
isActive: {
type: Boolean,
default: true,
},
addresses: [addressSchema],
passwordResetToken: {
type: String,
select: false, // Don't include in queries by default
},
passwordResetExpires: {
type: Date,
select: false,
},
passwordChangedAt: Date,
notificationPreferences: {
orderUpdates: {
email: { type: Boolean, default: true },
app: { type: Boolean, default: true },
},
newsletters: {
email: { type: Boolean, default: true },
app: { type: Boolean, default: true },
},
promotions: {
email: { type: Boolean, default: true },
app: { type: Boolean, default: true },
},
productUpdates: {
email: { type: Boolean, default: true },
app: { type: Boolean, default: true },
},
vendorOrderVisibility: {
email: { type: Boolean, default: true },
app: { type: Boolean, default: true },
disabledAt: { type: Date },
blackoutPeriods: [
{
start: { type: Date },
end: { type: Date },
},
],
},
},
recentSearches: {
type: [String],
default: [],
},
},
{
timestamps: true, // Adds createdAt and updatedAt fields automatically
},
);
// Encrypt password before saving user
userSchema.pre('save', async function (next) {
// Only run this function if password was actually modified
if (!this.isModified('password')) return next();
// Hash the password with cost of 12
this.password = await bcrypt.hash(this.password, 12);
// Update passwordChangedAt field
if (!this.isNew) {
this.passwordChangedAt = Date.now() - 1000;
}
next();
});
// Instance method to check password validity
userSchema.methods.correctPassword = async function (
candidatePassword,
userPassword,
) {
return await bcrypt.compare(candidatePassword, userPassword);
};
// Instance method to create password reset token
userSchema.methods.createPasswordResetToken = function () {
const crypto = require('crypto');
// Generate token
const resetToken = crypto.randomBytes(32).toString('hex');
// Hash token and save to database
this.passwordResetToken = crypto
.createHash('sha256')
.update(resetToken)
.digest('hex');
// Set expiration to 10 minutes from now
this.passwordResetExpires = Date.now() + 10 * 60 * 1000; // 10 minutes
// Return unhashed token to send to user
return resetToken;
};
// Instance method to check if password was changed after JWT was issued
userSchema.methods.changedPasswordAfter = function (JWTTimestamp) {
if (this.passwordChangedAt) {
const changedTimestamp = parseInt(
this.passwordChangedAt.getTime() / 1000,
10,
);
return JWTTimestamp < changedTimestamp;
}
return false;
};
userSchema.index({ role: 1 });
const User = mongoose.model('User', userSchema);
module.exports = User;