const mongoose = require("mongoose"); const bcrypt = require("bcryptjs"); const enrolledCourseSchema = new mongoose.Schema( { course: { type: mongoose.Schema.Types.ObjectId, ref: "Course", required: true }, order: { type: mongoose.Schema.Types.ObjectId, ref: "Order", required: true }, accessGrantedAt: { type: Date, default: Date.now }, }, { _id: false } ); const userSchema = new mongoose.Schema( { name: { type: String, required: true, trim: true, maxlength: 120 }, email: { type: String, required: true, unique: true, lowercase: true, trim: true, maxlength: 254, index: true, }, passwordHash: { type: String, required: true, select: false }, role: { type: String, enum: ["user", "admin"], default: "user" }, // Access-control / session-security bookkeeping tokenVersion: { type: Number, default: 0 }, // bump to invalidate all issued JWTs activeRefreshTokens: { // store only the jti + expiry, never the raw token, so a leaked DB // dump can't be replayed as a session type: [ { jti: String, expiresAt: Date, userAgent: String, ip: String, }, ], default: [], select: false, }, // Brute-force protection failedLoginAttempts: { type: Number, default: 0, select: false }, lockUntil: { type: Date, default: null, select: false }, enrolledCourses: { type: [enrolledCourseSchema], default: [] }, isActive: { type: Boolean, default: true }, }, { timestamps: true } ); userSchema.methods.setPassword = async function setPassword(plainPassword) { const salt = await bcrypt.genSalt(12); this.passwordHash = await bcrypt.hash(plainPassword, salt); }; userSchema.methods.comparePassword = function comparePassword(plainPassword) { return bcrypt.compare(plainPassword, this.passwordHash); }; userSchema.methods.isLocked = function isLocked() { return !!(this.lockUntil && this.lockUntil > Date.now()); }; userSchema.methods.hasAccessToCourse = function hasAccessToCourse(courseId) { return this.enrolledCourses.some((ec) => ec.course.toString() === courseId.toString()); }; userSchema.methods.toSafeJSON = function toSafeJSON() { return { id: this._id, name: this.name, email: this.email, role: this.role, enrolledCourses: this.enrolledCourses.map((ec) => ({ course: ec.course, order: ec.order, accessGrantedAt: ec.accessGrantedAt, })), createdAt: this.createdAt, }; }; module.exports = mongoose.model("User", userSchema);