sanket3280/code-execution
0
1const mongoose = require('mongoose');2const Schema = mongoose.Schema;3const bcrypt = require('bcryptjs');4 5const ChallengeSchema = new Schema({6 title: {7 type: String,8 required: true,9 trim: true10 },11 description: {12 type: String,13 required: true14 },15 startDate: {16 type: Date,17 required: true18 },19 endDate: {20 type: Date,21 required: true22 },23 problems: [{24 type: Schema.Types.ObjectId,25 ref: 'Problem'26 }],27 participants: [{28 user: {29 type: Schema.Types.ObjectId,30 ref: 'User'31 },32 joinedAt: {33 type: Date,34 default: Date.now35 },36 score: {37 type: Number,38 default: 039 },40 problemsSolved: {41 type: Number,42 default: 043 },44 lastSubmission: {45 type: Date46 },47 hasSpectatorAccess: {48 type: Boolean,49 default: false50 },51 spectatorAccessGrantedAt: {52 type: Date53 },54 spectatorAccessGrantedBy: {55 type: Schema.Types.ObjectId,56 ref: 'User'57 }58 }],59 isActive: {60 type: Boolean,61 default: true62 },63 maxParticipants: {64 type: Number,65 default: 0 // 0 means unlimited66 },67 difficulty: {68 type: String,69 enum: ['easy', 'medium', 'hard', 'mixed'],70 default: 'mixed'71 },72 tags: [{73 type: String,74 trim: true75 }],76 createdBy: {77 type: Schema.Types.ObjectId,78 ref: 'User',79 required: true80 },81 createdAt: {82 type: Date,83 default: Date.now84 },85 updatedAt: {86 type: Date,87 default: Date.now88 },89 visibility: {90 type: String,91 enum: ['public', 'private'],92 default: 'public'93 },94 password: {95 type: String,96 required: function() { return this.visibility === 'private'; }97 },98 invites: [{99 user: {100 type: Schema.Types.ObjectId,101 ref: 'User'102 },103 email: String,104 status: {105 type: String,106 enum: ['pending', 'accepted', 'revoked'],107 default: 'pending'108 },109 sentAt: {110 type: Date,111 default: Date.now112 }113 }]114});115 116// Update the updatedAt field on save117ChallengeSchema.pre('save', function(next) {118 this.updatedAt = Date.now();119 next();120});121 122// Virtual for challenge status123ChallengeSchema.virtual('status').get(function() {124 const now = new Date();125 if (now < this.startDate) {126 return 'upcoming';127 } else if (now >= this.startDate && now <= this.endDate) {128 return 'active';129 } else {130 return 'completed';131 }132});133 134// Virtual for remaining time135ChallengeSchema.virtual('remainingTime').get(function() {136 const now = new Date();137 if (now > this.endDate) {138 return 0;139 }140 return Math.max(0, this.endDate - now);141});142 143// Virtual for participation count144ChallengeSchema.virtual('participantCount').get(function() {145 return this.participants.length;146});147 148// Add method to compare password149ChallengeSchema.methods.comparePassword = async function(candidatePassword) {150 if (!this.password || !candidatePassword) {151 return false;152 }153 154 try {155 return await bcrypt.compare(candidatePassword, this.password);156 } catch (error) {157 console.error('Error in comparePassword:', error);158 return false;159 }160};161 162// Indexes for better query performance163ChallengeSchema.index({ createdBy: 1 });164ChallengeSchema.index({ startDate: 1, endDate: 1 });165ChallengeSchema.index({ visibility: 1 });166ChallengeSchema.index({ isActive: 1 });167ChallengeSchema.index({ 'participants.user': 1 });168ChallengeSchema.index({ createdAt: -1 });169 170// Compound index for common queries171ChallengeSchema.index({ visibility: 1, isActive: 1, startDate: 1 });172 173module.exports = mongoose.model('Challenge', ChallengeSchema);