add hook types

This commit is contained in:
Elliot DeNolf
2020-11-23 10:23:04 -05:00
parent 7670a23611
commit d10f3f140f
12 changed files with 142 additions and 54 deletions

View File

@@ -1,11 +1,12 @@
import crypto from 'crypto';
import { AfterForgotPasswordHook, BeforeOperationHook } from '../../collections/config/types';
import { APIError } from '../../errors';
async function forgotPassword(incomingArgs) {
const { config, sendEmail: email } = this;
if (!Object.prototype.hasOwnProperty.call(incomingArgs.data, 'email')) {
throw new APIError('Missing email.');
throw new APIError('Missing email.', 400);
}
let args = incomingArgs;
@@ -14,7 +15,7 @@ async function forgotPassword(incomingArgs) {
// beforeOperation - Collection
// /////////////////////////////////////
await args.collection.config.hooks.beforeOperation.reduce(async (priorHook, hook) => {
await args.collection.config.hooks.beforeOperation.reduce(async (priorHook: BeforeOperationHook, hook: BeforeOperationHook) => {
await priorHook;
args = (await hook({
@@ -38,7 +39,7 @@ async function forgotPassword(incomingArgs) {
// Forget password
// /////////////////////////////////////
let token = crypto.randomBytes(20);
let token: string | Buffer = crypto.randomBytes(20);
token = token.toString('hex');
const user = await Model.findOne({ email: data.email.toLowerCase() });
@@ -90,7 +91,7 @@ async function forgotPassword(incomingArgs) {
// afterForgotPassword - Collection
// /////////////////////////////////////
await collectionConfig.hooks.afterForgotPassword.reduce(async (priorHook, hook) => {
await collectionConfig.hooks.afterForgotPassword.reduce(async (priorHook: AfterForgotPasswordHook, hook: AfterForgotPasswordHook) => {
await priorHook;
await hook({ args });
}, Promise.resolve());

View File

@@ -3,6 +3,7 @@ import { AuthenticationError, LockedAuth } from '../../errors';
import getCookieExpiration from '../../utilities/getCookieExpiration';
import isLocked from '../isLocked';
import removeInternalFields from '../../utilities/removeInternalFields';
import { BeforeLoginHook, BeforeOperationHook } from '../../collections/config/types';
async function login(incomingArgs) {
const { config, operations, secret } = this;
@@ -13,7 +14,7 @@ async function login(incomingArgs) {
// beforeOperation - Collection
// /////////////////////////////////////
await args.collection.config.hooks.beforeOperation.reduce(async (priorHook, hook) => {
await args.collection.config.hooks.beforeOperation.reduce(async (priorHook: BeforeOperationHook, hook: BeforeOperationHook) => {
await priorHook;
args = (await hook({

View File

@@ -1,4 +1,5 @@
import jwt from 'jsonwebtoken';
import { BeforeOperationHook } from '../../collections/config/types';
import { Forbidden } from '../../errors';
import getCookieExpiration from '../../utilities/getCookieExpiration';
@@ -9,7 +10,7 @@ async function refresh(incomingArgs) {
// beforeOperation - Collection
// /////////////////////////////////////
await args.collection.config.hooks.beforeOperation.reduce(async (priorHook, hook) => {
await args.collection.config.hooks.beforeOperation.reduce(async (priorHook: BeforeOperationHook, hook: BeforeOperationHook) => {
await priorHook;
args = (await hook({

View File

@@ -1,14 +1,9 @@
import { Access, Hook } from '../../config/types';
/* eslint-disable no-use-before-define */
/* eslint-disable @typescript-eslint/no-explicit-any */
import { Access } from '../../config/types';
import { Field } from '../../fields/config/types';
import { PayloadRequest } from '../../express/types/payloadRequest';
export type ImageSize = {
name: string,
width: number,
height: number,
crop: string, // comes from sharp package
};
export type Collection = {
slug: string;
labels?: {
@@ -22,18 +17,17 @@ export type Collection = {
components?: any;
};
hooks?: {
beforeOperation?: Hook[];
beforeValidate?: Hook[];
beforeChange?: Hook[];
afterChange?: Hook[];
beforeRead?: Hook[];
afterRead?: Hook[];
beforeDelete?: Hook[];
afterDelete?: Hook[];
beforeLogin?: Hook[];
afterLogin?: Hook[];
afterForgotPassword?: Hook[];
forgotPassword?: Hook[];
beforeOperation?: BeforeOperationHook[];
beforeValidate?: BeforeValidateHook[];
beforeChange?: BeforeChangeHook[];
afterChange?: AfterChangeHook[];
beforeRead?: BeforeReadHook[];
afterRead?: AfterReadHook[];
beforeDelete?: BeforeDeleteHook[];
afterDelete?: AfterDeleteHook[];
beforeLogin?: BeforeLoginHook[];
afterLogin?: AfterLoginHook[];
afterForgotPassword?: AfterForgotPasswordHook[];
};
access?: {
create?: Access;
@@ -59,11 +53,11 @@ export type Collection = {
}
| boolean;
forgotPassword?: {
generateEmailHTML?: (args?: {token?: string, email?: string, req?: PayloadRequest}) => string,
generateEmailSubject?: (args?: {req?: PayloadRequest}) => string,
generateEmailHTML?: (args?: { token?: string, email?: string, req?: PayloadRequest }) => string,
generateEmailSubject?: (args?: { req?: PayloadRequest }) => string,
}
};
config: {[key: string]: any};
config: { [key: string]: any };
upload: {
imageSizes: ImageSize[];
staticURL: string;
@@ -71,3 +65,83 @@ export type Collection = {
adminThumbnail?: string;
};
};
export type ImageSize = {
name: string,
width: number,
height: number,
crop: string, // comes from sharp package
};
// Hooks
export type HookOperationType =
| 'create'
| 'read'
| 'update'
| 'delete'
| 'refresh'
| 'login'
| 'forgotPassword';
export type BeforeOperationHook = (args?: {
args?: any;
operation: HookOperationType;
}) => any;
export type BeforeValidateHook = (args?: {
data?: any;
req?: PayloadRequest;
operation: 'create' | 'update';
originalDoc?: any; // undefined on 'create' operation
}) => any;
export type BeforeChangeHook = (args?: {
data: any;
req: PayloadRequest;
operation: 'create' | 'update'
originalDoc?: any; // undefined on 'create' operation
}) => any;
export type AfterChangeHook = (args?: {
doc: any;
req: PayloadRequest;
operation: 'create' | 'update';
}) => any;
export type BeforeReadHook = (args?: {
doc: any;
req: PayloadRequest;
query: { [key: string]: any };
}) => any;
export type AfterReadHook = (args?: {
doc: any;
req: PayloadRequest;
query: { [key: string]: any };
}) => any;
export type BeforeDeleteHook = (args?: {
req: PayloadRequest;
id: string;
}) => any;
export type AfterDeleteHook = (args?: {
req: PayloadRequest;
id: string;
doc: any;
}) => any;
export type BeforeLoginHook = (args?: {
req: PayloadRequest;
}) => any;
export type AfterLoginHook = (args?: {
req: PayloadRequest;
user: any;
token: string;
}) => any;
export type AfterForgotPasswordHook = (args?: {
args?: any;
}) => any;

View File

@@ -2,13 +2,17 @@ import mongoose from 'mongoose';
import express from 'express';
import passport from 'passport';
import passportLocalMongoose from 'passport-local-mongoose';
const LocalStrategy = require('passport-local').Strategy;
import Passport from 'passport-local';
import { UpdateQuery } from 'mongodb';
import apiKeyStrategy from '../auth/strategies/apiKey';
import buildSchema from './buildSchema';
import bindCollectionMiddleware from './bindCollection';
import { Collection } from './config/types';
function registerCollections() {
this.config.collections = this.config.collections.map((collection) => {
const LocalStrategy = Passport.Strategy;
export default function registerCollections(): void {
this.config.collections = this.config.collections.map((collection: Collection) => {
const formattedCollection = collection;
const schema = buildSchema(formattedCollection, this.config);
@@ -32,7 +36,8 @@ function registerCollections() {
}, cb);
}
const updates = { $inc: { loginAttempts: 1 } };
type LoginSchema = { loginAttempts: number; };
const updates: UpdateQuery<LoginSchema> = { $inc: { loginAttempts: 1 } };
// Lock the account if at max attempts and not already locked
if (this.loginAttempts + 1 >= maxLoginAttempts && !this.isLocked) {
updates.$set = { lockUntil: Date.now() + lockTime };
@@ -151,5 +156,3 @@ function registerCollections() {
return formattedCollection;
});
}
export default registerCollections;

View File

@@ -12,6 +12,7 @@ import getImageSize from '../../uploads/getImageSize';
import imageMIMETypes from '../../uploads/imageMIMETypes';
import sendVerificationEmail from '../../auth/sendVerificationEmail';
import { AfterChangeHook, BeforeOperationHook, BeforeValidateHook } from '../config/types';
async function create(incomingArgs) {
const { performFieldOperations, config } = this;
@@ -22,7 +23,7 @@ async function create(incomingArgs) {
// beforeOperation - Collection
// /////////////////////////////////////
await args.collection.config.hooks.beforeOperation.reduce(async (priorHook, hook) => {
await args.collection.config.hooks.beforeOperation.reduce(async (priorHook: BeforeOperationHook, hook: BeforeOperationHook) => {
await priorHook;
args = (await hook({
@@ -73,7 +74,7 @@ async function create(incomingArgs) {
// beforeValidate - Collections
// /////////////////////////////////////
await collectionConfig.hooks.beforeValidate.reduce(async (priorHook, hook) => {
await collectionConfig.hooks.beforeValidate.reduce(async (priorHook: BeforeValidateHook, hook: BeforeValidateHook) => {
await priorHook;
data = (await hook({
@@ -214,7 +215,7 @@ async function create(incomingArgs) {
// afterChange - Collection
// /////////////////////////////////////
await collectionConfig.hooks.afterChange.reduce(async (priorHook, hook) => {
await collectionConfig.hooks.afterChange.reduce(async (priorHook: AfterChangeHook, hook: AfterChangeHook) => {
await priorHook;
result = await hook({

View File

@@ -5,6 +5,7 @@ import removeInternalFields from '../../utilities/removeInternalFields';
import { NotFound, Forbidden, ErrorDeletingFile } from '../../errors';
import executeAccess from '../../auth/executeAccess';
import fileExists from '../../uploads/fileExists';
import { BeforeOperationHook } from '../config/types';
async function deleteQuery(incomingArgs) {
let args = incomingArgs;
@@ -13,7 +14,7 @@ async function deleteQuery(incomingArgs) {
// beforeOperation - Collection
// /////////////////////////////////////
await args.collection.config.hooks.beforeOperation.reduce(async (priorHook, hook) => {
await args.collection.config.hooks.beforeOperation.reduce(async (priorHook: BeforeOperationHook, hook: BeforeOperationHook) => {
await priorHook;
args = (await hook({

View File

@@ -1,5 +1,6 @@
import executeAccess from '../../auth/executeAccess';
import removeInternalFields from '../../utilities/removeInternalFields';
import { BeforeOperationHook, BeforeReadHook } from '../config/types';
async function find(incomingArgs) {
let args = incomingArgs;
@@ -8,7 +9,7 @@ async function find(incomingArgs) {
// beforeOperation - Collection
// /////////////////////////////////////
await args.collection.config.hooks.beforeOperation.reduce(async (priorHook, hook) => {
await args.collection.config.hooks.beforeOperation.reduce(async (priorHook: BeforeOperationHook, hook: BeforeOperationHook) => {
await priorHook;
args = (await hook({

View File

@@ -1,5 +1,6 @@
/* eslint-disable no-underscore-dangle */
import memoize from 'micro-memoize';
import { BeforeOperationHook } from '../config/types';
/* eslint-disable no-underscore-dangle */
import removeInternalFields from '../../utilities/removeInternalFields';
import { Forbidden, NotFound } from '../../errors';
import executeAccess from '../../auth/executeAccess';
@@ -11,7 +12,7 @@ async function findByID(incomingArgs) {
// beforeOperation - Collection
// /////////////////////////////////////
await args.collection.config.hooks.beforeOperation.reduce(async (priorHook, hook) => {
await args.collection.config.hooks.beforeOperation.reduce(async (priorHook: BeforeOperationHook, hook: BeforeOperationHook) => {
await priorHook;
args = (await hook({

View File

@@ -1,6 +1,7 @@
import httpStatus from 'http-status';
import deepmerge from 'deepmerge';
import path from 'path';
import { BeforeOperationHook, BeforeChangeHook, BeforeValidateHook } from '../config/types';
import removeInternalFields from '../../utilities/removeInternalFields';
import overwriteMerge from '../../utilities/overwriteMerge';
@@ -12,6 +13,7 @@ import getSafeFilename from '../../uploads/getSafeFilename';
import resizeAndSave from '../../uploads/imageResizer';
async function update(incomingArgs) {
let args = incomingArgs;
@@ -19,7 +21,7 @@ async function update(incomingArgs) {
// beforeOperation - Collection
// /////////////////////////////////////
await args.collection.config.hooks.beforeOperation.reduce(async (priorHook, hook) => {
await args.collection.config.hooks.beforeOperation.reduce(async (priorHook: BeforeOperationHook, hook: BeforeOperationHook) => {
await priorHook;
args = (await hook({
@@ -111,7 +113,7 @@ async function update(incomingArgs) {
// beforeValidate - Collection
// /////////////////////////////////////
await collectionConfig.hooks.beforeValidate.reduce(async (priorHook, hook) => {
await collectionConfig.hooks.beforeValidate.reduce(async (priorHook: BeforeValidateHook, hook: BeforeValidateHook) => {
await priorHook;
data = (await hook({
@@ -140,7 +142,7 @@ async function update(incomingArgs) {
// beforeChange - Collection
// /////////////////////////////////////
await collectionConfig.hooks.beforeChange.reduce(async (priorHook, hook) => {
await collectionConfig.hooks.beforeChange.reduce(async (priorHook: BeforeChangeHook, hook: BeforeChangeHook) => {
await priorHook;
data = (await hook({

View File

@@ -47,17 +47,18 @@ export type Access = (args?: any) => boolean;
export type Config = {
admin?: {
user?: string
user?: string;
meta?: {
titleSuffix?: string
ogImage?: string
favicon?: string
titleSuffix?: string;
ogImage?: string;
favicon?: string;
}
disable?: boolean
disable?: boolean;
indexHTML?: string;
};
collections?: Collection[];
globals?: Global[];
serverURL?: string;
serverURL: string;
cookiePrefix?: string;
csrf?: string[];
cors?: string[];
@@ -68,7 +69,7 @@ export type Config = {
graphQL?: string;
graphQLPlayground?: string;
};
express: {
express?: {
json: {
limit?: number
}

View File

@@ -2,6 +2,7 @@ import deepmerge from 'deepmerge';
import overwriteMerge from '../../utilities/overwriteMerge';
import executeAccess from '../../auth/executeAccess';
import removeInternalFields from '../../utilities/removeInternalFields';
import { AfterChangeHook, BeforeValidateHook } from '../../collections/config/types';
async function update(args) {
const { globals: { Model } } = this;
@@ -53,7 +54,7 @@ async function update(args) {
// 3. Execute before validate collection hooks
// /////////////////////////////////////
await globalConfig.hooks.beforeValidate.reduce(async (priorHook, hook) => {
await globalConfig.hooks.beforeValidate.reduce(async (priorHook: BeforeValidateHook, hook: BeforeValidateHook) => {
await priorHook;
data = (await hook({
@@ -123,7 +124,7 @@ async function update(args) {
// 9. Execute after global hook
// /////////////////////////////////////
await globalConfig.hooks.afterChange.reduce(async (priorHook, hook) => {
await globalConfig.hooks.afterChange.reduce(async (priorHook: AfterChangeHook, hook: AfterChangeHook) => {
await priorHook;
global = await hook({