initial commit
This commit is contained in:
125
models/user.ts
Normal file
125
models/user.ts
Normal file
@@ -0,0 +1,125 @@
|
||||
import mongoose, { Document, Model } from 'mongoose'
|
||||
import { z } from 'zod'
|
||||
import bcrypt from 'bcryptjs'
|
||||
|
||||
// Zod schema for validation
|
||||
export const UserSchema = z.object({
|
||||
email: z.string().email('Invalid email format'),
|
||||
name: z.string().min(1, 'Name is required'),
|
||||
password: z.string().min(6, 'Password must be at least 6 characters'),
|
||||
siliconId: z.string().optional(),
|
||||
role: z.enum(['user', 'admin']).optional().default('user'),
|
||||
isVerified: z.boolean().optional().default(false),
|
||||
provider: z.string().optional(),
|
||||
providerId: z.string().optional(),
|
||||
avatar: z.string().url().optional(),
|
||||
})
|
||||
|
||||
export type UserType = z.infer<typeof UserSchema>
|
||||
|
||||
// Mongoose interface
|
||||
export interface IUser extends Document {
|
||||
email: string
|
||||
name: string
|
||||
password?: string
|
||||
siliconId: string
|
||||
role: 'user' | 'admin'
|
||||
isVerified: boolean
|
||||
provider: 'local' | 'google' | 'github'
|
||||
providerId?: string
|
||||
avatar?: string
|
||||
refreshToken?: string
|
||||
balance?: number
|
||||
lastLogin: Date
|
||||
createdAt: Date
|
||||
updatedAt: Date
|
||||
comparePassword(candidatePassword: string): Promise<boolean>
|
||||
}
|
||||
|
||||
// Mongoose schema
|
||||
const userSchema = new mongoose.Schema<IUser>(
|
||||
{
|
||||
email: {
|
||||
type: String,
|
||||
required: true,
|
||||
lowercase: true,
|
||||
trim: true,
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
required: true,
|
||||
trim: true,
|
||||
},
|
||||
siliconId: {
|
||||
type: String,
|
||||
required: true,
|
||||
trim: true,
|
||||
},
|
||||
password: {
|
||||
type: String,
|
||||
required: function (this: any) {
|
||||
return !this.provider // Password required only if not using OAuth
|
||||
},
|
||||
},
|
||||
role: {
|
||||
type: String,
|
||||
enum: ['user', 'admin'],
|
||||
default: 'user',
|
||||
},
|
||||
balance: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
min: 0,
|
||||
},
|
||||
isVerified: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
provider: {
|
||||
type: String,
|
||||
enum: ['local', 'google', 'github'],
|
||||
default: 'local',
|
||||
},
|
||||
providerId: String,
|
||||
avatar: String,
|
||||
refreshToken: String,
|
||||
lastLogin: {
|
||||
type: Date,
|
||||
default: Date.now,
|
||||
},
|
||||
},
|
||||
{
|
||||
timestamps: true,
|
||||
}
|
||||
)
|
||||
|
||||
// Index for performance
|
||||
userSchema.index({ email: 1 }, { unique: true })
|
||||
userSchema.index({ siliconId: 1 }, { unique: true })
|
||||
userSchema.index({ providerId: 1, provider: 1 })
|
||||
|
||||
// Hash password before saving
|
||||
userSchema.pre('save', async function (next) {
|
||||
if (!this.isModified('password')) return next()
|
||||
|
||||
if (this.password) {
|
||||
this.password = await bcrypt.hash(this.password, 12)
|
||||
}
|
||||
next()
|
||||
})
|
||||
|
||||
// Compare password method
|
||||
userSchema.methods.comparePassword = async function (candidatePassword: string): Promise<boolean> {
|
||||
if (!this.password) return false
|
||||
return bcrypt.compare(candidatePassword, this.password)
|
||||
}
|
||||
|
||||
// Transform method to remove sensitive fields
|
||||
userSchema.methods.toJSON = function () {
|
||||
const user = this.toObject()
|
||||
delete user.password
|
||||
delete user.refreshToken
|
||||
return user
|
||||
}
|
||||
|
||||
export const User: Model<IUser> = mongoose.models.User || mongoose.model<IUser>('User', userSchema)
|
||||
Reference in New Issue
Block a user