File size: 5,390 Bytes
c904036
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
113eaa7
c904036
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
const mongoose = require('mongoose');

const hostEligibilityStatusSchema = new mongoose.Schema(
  {
    status: {
      type: String,
      enum: ['none', 'pending', 'approved', 'rejected'],
      default: 'none',
    },
    requestedAt: Date,
    approvedBy: { type: mongoose.Schema.Types.ObjectId, ref: 'User' },
    approvedAt: Date,
    remarks: String,
  },
  { _id: false },
);

const eventHistorySchema = new mongoose.Schema(
  {
    hosted: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Event' }],
    attended: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Event' }],
    saved: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Event' }],
    waitlisted: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Event' }],
  },
  { _id: false },
);

const referralStatsSchema = new mongoose.Schema(
  {
    sharedLinks: { type: Number, default: 0 },
    successfulSignups: { type: Number, default: 0 },
  },
  { _id: false },
);

const verifierEligibilityStatusSchema = new mongoose.Schema(
  {
    approvedBy: { type: mongoose.Schema.Types.ObjectId, ref: 'User' },
    approvedAt: Date,
    remarks: String,
  },
  { _id: false },
);

const userSchema = new mongoose.Schema({
  name: { type: String, required: true },
  email: {
    type: String,
    required: true,
    unique: true,
    match: /@([\w.-]+\.(ac|edu)\.in|[\w.-]+\.edu|[\w.-]+\.edu\.co\.in|[\w.-]*cuchd\.in)$/i,
  },
  phone: { type: String, required: false },
  location: { type: String },
  bio: { type: String },
  gender: { type: String }, // Changed from Gender to gender
  dateOfBirth: { type: Date }, // Changed from DOB to dateOfBirth
  passwordHash: { type: String, required: true },
  // Indicates whether the user has explicitly set a password (Google-only users start as false)
  passwordSetup: { type: Boolean, default: true },
  profilePhoto: { type: String },
  institutionId: { type: mongoose.Schema.Types.ObjectId, ref: 'Institution' },
  institutionVerificationStatus: {
    type: String,
    enum: ['pending', 'verified', 'rejected'],
    default: 'pending',
  },
  collegeIdNumber: { type: String },
  roles: { type: [String], default: ['student'] },
  isVerified: { type: Boolean, default: false },
  canHost: { type: Boolean, default: false },
  hostEligibilityStatus: {
    type: hostEligibilityStatusSchema,
    default: () => ({ status: 'none' })
  },
  hostRequestIdCardPhoto: { type: String }, // URL to uploaded ID card photo
  hostRequestEventPermission: { type: String }, // URL to uploaded event permission document (optional)
  verifierEligibilityStatus: verifierEligibilityStatusSchema,
  interests: [String],
  skills: [String],
  learningGoals: [String],
  badges: [String],
  referralStats: referralStatsSchema,
  eventHistory: eventHistorySchema,
  lastLogin: Date,
  createdAt: { type: Date, default: Date.now },
  updatedAt: { type: Date, default: Date.now },
  deletionRequestedAt: { type: Date },
  deletionScheduledFor: { type: Date },
  notificationPreferences: {
    email: {
      rsvp: { type: Boolean, default: false },
      certificate: { type: Boolean, default: false },
      cohost: { type: Boolean, default: false },
      cohost_request: { type: Boolean, default: false },
      event_verification: { type: Boolean, default: false },
      host_request: { type: Boolean, default: false },
      registration: { type: Boolean, default: false }, // Host notifications about registrations
    },
    inApp: {
      rsvp: { type: Boolean, default: true },
      certificate: { type: Boolean, default: true },
      cohost: { type: Boolean, default: true },
      event_verification: { type: Boolean, default: true },
      host_request: { type: Boolean, default: true },
      registration: { type: Boolean, default: false }, // Disable in-app notifications for host about registrations
    },
  },
  // Frontend gate: mark onboarding as completed to avoid showing modal repeatedly
  onboardingCompleted: { type: Boolean, default: false },
  // Track if user has linked Google account for dual login capability
  googleLinked: { type: Boolean, default: false },
  // New: track supported auth methods and primary method
  authMethods: { type: [String], enum: ['password', 'google'], default: [] },
  primaryAuthMethod: {
    type: String,
    enum: ['password', 'google'],
    default: 'password',
  },
  googleId: { type: String },
});

userSchema.pre('save', function (next) {
  this.updatedAt = Date.now();
  next();
});

// Performance indexes for common queries
userSchema.index({ institutionId: 1 });
userSchema.index({ roles: 1 });
userSchema.index({ isVerified: 1 });
userSchema.index({ institutionVerificationStatus: 1 });
userSchema.index({ canHost: 1 });
userSchema.index({ createdAt: -1 });
userSchema.index({ lastLogin: -1 });

// Compound indexes for complex queries
userSchema.index({ institutionId: 1, roles: 1 });
userSchema.index({ institutionId: 1, isVerified: 1 });
userSchema.index({ roles: 1, canHost: 1 });
userSchema.index({ email: 1, isVerified: 1 });

// Text search index for user search
userSchema.index({ 
  name: 'text', 
  email: 'text', 
  bio: 'text' 
}, {
  weights: { name: 10, email: 5, bio: 1 },
  name: 'user_text_search'
});

// Sparse indexes for optional fields
userSchema.index({ googleId: 1 }, { sparse: true });
userSchema.index({ deletionScheduledFor: 1 }, { sparse: true });

module.exports = mongoose.model('User', userSchema);