const mongoose = require('mongoose') const bcrypt = require('bcryptjs') const { nanoid } = require('nanoid') const userResponse = u => ({ _id: u._id, email: u.email, isVerified: u.isVerified, noteList: u.noteList, theme: u.theme, language: u.language }) const userSchema = new mongoose.Schema({ email: { type: String, trim: true, lowercase: true, unique: true, required: true, minlength: 6, maxlength: 255, validate (value) { if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)) { throw new Error('Wrong email address') } } }, password: { type: String, required: true, minlength: 6, maxlength: 1024 }, isVerified: { type: Boolean, default: false }, verificationKey: { type: String, default: () => nanoid(6) }, noteList: { type: mongoose.Schema.Types.ObjectId, ref: 'NoteList', required: true }, theme: { type: String, default: () => 'green' }, language: { type: String, default: () => 'en' }, created_at: { type: Date, default: Date.now }, updated_at: { type: Date, default: Date.now } }) userSchema.statics.findByCredentials = async function (email, password) { const user = await User.findOne({ email }) if (!user) { throw new Error('Unable to login') } const isMatch = await bcrypt.compare(password, user.password) if (!isMatch) { throw new Error('Unable to login') } return userResponse(user) } userSchema.statics.getVerificationKey = async function (email) { const { verificationKey: key } = await User.findOne({ email }) if (!key) { throw new Error('Could not verify user') } return key } userSchema.statics.verifyUser = async function (_id, key) { const user = await User.findOneAndUpdate({ _id, verificationKey: key }, { isVerified: true, verificationKey: nanoid(10) }, { new: true }) if (!user) { throw new Error('Could not verify user') } return userResponse(user) } userSchema.statics.saveSettings = async function ({ _id, theme, language }) { const user = await User.findOneAndUpdate({ _id }, { theme, language }, { new: true }) if (!user) { throw new Error('Could not save settings') } return userResponse(user) } userSchema.statics.savePassword = async function ({ _id, currentPassword, newPassword }) { const user = await User.findOne({ _id }) if (!user) { throw new Error('Unable to change password') } const isMatch = await bcrypt.compare(currentPassword, user.password) if (!isMatch) { throw new Error('Wrong password') } const password = await bcrypt.hash(newPassword, 8) const newUser = await User.findOneAndUpdate({ _id }, { password }, { new: true }) if (!newUser) { throw new Error('Could not update password') } return userResponse(newUser) } userSchema.statics.state = async function (_id) { const user = await User.findOne({ _id }) if (!user) { return { isLoggedIn: false } } else if (!user.isVerified) { return { isVerified: false } } return {} } userSchema.pre('save', async function (next) { const user = this if (user.isModified('password')) { user.password = await bcrypt.hash(user.password, 8) } user.updated_at = Date.now() next() }) const User = mongoose.models.User || mongoose.model('User', userSchema) export default User