import { model, Schema, Document, DocumentQuery } from 'mongoose'; import { accessibleRecordsPlugin } from '@casl/mongoose'; import * as bcrypt from 'bcrypt'; import { Recipe } from '../recipe'; import { RecipeCollection } from '../collection'; // // Type Definitions // export interface UserProps { email: string; firstName: string; lastName: string; password: string; } export interface User extends UserProps, Document { recipes: []; getRecipes: () => DocumentQuery; getRecipeCollections: () => DocumentQuery< RecipeCollection[] | null, RecipeCollection >; isValidPassword: (password: string) => Promise; } // // Schema // export const userSchema = new Schema( { email: { type: String, required: true, unique: true }, password: { type: String, required: true }, firstName: { type: String }, lastName: { type: String }, }, { timestamps: true, } ); userSchema.plugin(accessibleRecordsPlugin); // ** Virutals userSchema.virtual('recipes', { ref: 'Recipe', localField: '_id', foreignField: 'ownerId', }); // ** Methods userSchema.methods = { /** * Returns all recipes that the user owns * * @return {Array} */ getRecipes() { return this.model('Recipe').find({ ownerId: this._id }); }, /** * Returns all recipes that the user owns * * @return {Array} */ getRecipeCollections() { return this.model('RecipeCollection').find({ ownerId: this._id }); }, /** * Hashes the given password and checks if the hashed password stored in the * database matches the one sent. * * @param {string} password * @return {boolean} */ isValidPassword: async function (password: UserProps['password']) { const user: User = this; return await bcrypt.compare(password, user.password); }, }; // ** Hooks /** * When A user is deleted, also delete all recipes owned by the user. */ userSchema.pre('remove', async function (next) { // delete users recipes await Recipe.deleteMany({ ownerId: this._id }, function (err) { // TODO: Throw / report error if (err) { console.log(err); } }); // delete users recipe collections await RecipeCollection.deleteMany({ ownerId: this._id }, function (err) { // TODO: Throw / report error if (err) { console.log(err); } }); }); /** * Before the user information is saved, encrypt the plain text password. */ userSchema.pre('save', async function (next) { const hash = await bcrypt.hash(this.password, 10); this.password = hash; next(); }); // // Model // export const User = model('User', userSchema);