ai-wpa/models/user.ts

126 lines
3.0 KiB
TypeScript

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)