merges and WIP - field types

This commit is contained in:
James
2020-11-27 14:40:16 -05:00
26 changed files with 365 additions and 177 deletions

View File

@@ -45,6 +45,7 @@ module.exports = {
},
{
name: 'apiKey',
type: 'text',
access: {
read: ({ req: { user } }) => {
if (checkRole(['admin'], user)) {

View File

@@ -35,7 +35,6 @@ module.exports = {
verify: true,
maxLoginAttempts: 5,
lockTime: 600 * 1000, // lock time in ms
generateVerificationUrl: (req, token) => `http://localhost:3000/api/verify?token=${token}`,
cookies: {
secure: process.env.NODE_ENV === 'production',
sameSite: 'Lax',

View File

@@ -1,10 +1,11 @@
import passport from 'passport';
import AnonymousStrategy from 'passport-anonymous';
import { Payload } from '../index';
import jwtStrategy from './strategies/jwt';
function initAuth(): void {
function initAuth(ctx: Payload): void {
passport.use(new AnonymousStrategy.Strategy());
passport.use('jwt', jwtStrategy(this));
passport.use('jwt', jwtStrategy(ctx));
}
export default initAuth;

View File

@@ -1,19 +1,89 @@
import joi from 'joi';
import fieldSchema from '../../fields/config/schema';
const schema = joi.object().keys({
const collectionSchema = joi.object().keys({
slug: joi.string().required(),
labels: joi.object().keys({
singular: joi.string(),
plural: joi.string(),
}),
preview: joi.func(),
access: joi.object().keys({
create: joi.func(),
read: joi.func(),
update: joi.func(),
delete: joi.func(),
unlock: joi.func(),
admin: joi.func(),
}),
timestamps: joi.boolean()
.default(true),
}).unknown();
admin: joi.object()
.keys({
useAsTitle: joi.string().default('id'),
defaultColumns: joi.array().items(joi.string()),
enableRichTextRelationship: joi.boolean().default(false),
components: joi.object()
.keys({
List: joi.func(),
Edit: joi.func(),
}),
}),
fields: joi.array()
.items(fieldSchema)
.default([]),
hooks: joi.object()
.keys({
beforeOperation: joi.array().items(joi.func()).default([]),
beforeValidate: joi.array().items(joi.func()).default([]),
beforeChange: joi.array().items(joi.func()).default([]),
afterChange: joi.array().items(joi.func()).default([]),
beforeRead: joi.array().items(joi.func()).default([]),
afterRead: joi.array().items(joi.func()).default([]),
beforeDelete: joi.array().items(joi.func()).default([]),
afterDelete: joi.array().items(joi.func()).default([]),
beforeLogin: joi.array().items(joi.func()).default([]),
afterLogin: joi.array().items(joi.func()).default([]),
afterForgotPassword: joi.array().items(joi.func()).default([]),
}).default(),
auth: joi.object()
.keys({
tokenExpiration: joi.number(),
depth: joi.number().default(0),
verify: joi.alternatives().try(
joi.boolean(),
joi.object().keys({
generateEmailHTML: joi.func(),
generateEmailSubject: joi.func(),
}),
),
lockTime: joi.number(),
useAPIKey: joi.boolean(),
cookies: joi.object().keys({
secure: joi.boolean(),
sameSite: joi.string(), // TODO: add further specificity with joi.xor
domain: joi.string(),
}),
forgotPassword: joi.object().keys({
generateEmailHTML: joi.func(),
generateEmailSubject: joi.func(),
}),
maxLoginAttempts: joi.number(),
}).default(),
upload: joi.object()
.keys({
staticURL: joi.string(),
staticDir: joi.string(),
adminThumbnail: joi.string(),
imageSizes: joi.array().items(
joi.object().keys({
name: joi.string(),
width: joi.number(),
height: joi.number(),
crop: joi.string(), // TODO: add further specificity with joi.xor
}),
),
}).default(),
});
export default schema;
export default collectionSchema;

View File

@@ -1,69 +1,48 @@
/* eslint-disable no-use-before-define */
/* eslint-disable @typescript-eslint/no-explicit-any */
import joi from 'joi';
import 'joi-extract-type';
import { PaginateModel, Document, PassportLocalModel } from 'mongoose';
import { DeepRequired } from 'ts-essentials';
import { Access } from '../../config/types';
import { Field } from '../../fields/config/types';
import { PayloadRequest } from '../../express/types/payloadRequest';
import schema from './schema';
interface PayloadModel extends PaginateModel<Document>, PassportLocalModel<Document>{}
export type Collection = {
slug: string;
labels: {
singular: string;
plural: string;
};
fields: Field[];
admin: {
useAsTitle: string;
defaultColumns: string[];
components: any;
};
Model: PayloadModel;
config: CollectionConfig;
};
type PayloadCollectionConfigFromSchema = joi.extractType<typeof schema>
interface PayloadCollectionConfig extends PayloadCollectionConfigFromSchema {
hooks: {
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;
read: Access;
update: Access;
delete: Access;
admin: Access;
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;
read?: Access;
update?: Access;
delete?: Access;
admin?: Access;
unlock: Access;
};
auth?: {
tokenExpiration: number;
verify:
| boolean
| { generateEmailHTML: string; generateEmailSubject: string };
maxLoginAttempts: number;
lockTime: number;
useAPIKey: boolean;
cookies:
| {
secure: boolean;
sameSite: string;
domain?: string;
}
| boolean;
forgotPassword?: {
generateEmailHTML?: (args?: { token?: string, email?: string, req?: PayloadRequest }) => string,
generateEmailSubject?: (args?: { req?: PayloadRequest }) => string,
}
};
upload: {
imageSizes: ImageSize[];
staticURL: string;
staticDir: string;
adminThumbnail?: string;
};
};
}
export type CollectionConfig = DeepRequired<PayloadCollectionConfig>
export type ImageSize = {
name: string,

View File

@@ -7,15 +7,16 @@ import { UpdateQuery } from 'mongodb';
import apiKeyStrategy from '../auth/strategies/apiKey';
import buildSchema from './buildSchema';
import bindCollectionMiddleware from './bindCollection';
import { Collection } from './config/types';
import { CollectionConfig } from './config/types';
import { Payload } from '../index';
const LocalStrategy = Passport.Strategy;
export default function registerCollections(): void {
this.config.collections = this.config.collections.map((collection: Collection) => {
export default function registerCollections(ctx: Payload): void {
ctx.config.collections = ctx.config.collections.map((collection: CollectionConfig) => {
const formattedCollection = collection;
const schema = buildSchema(formattedCollection, this.config);
const schema = buildSchema(formattedCollection, ctx.config);
if (collection.auth) {
schema.plugin(passportLocalMongoose, {
@@ -55,17 +56,17 @@ export default function registerCollections(): void {
}
}
this.collections[formattedCollection.slug] = {
ctx.collections[formattedCollection.slug] = {
Model: mongoose.model(formattedCollection.slug, schema),
config: formattedCollection,
};
// If not local, open routes
if (!this.config.local) {
if (!ctx.config.local) {
const router = express.Router();
const { slug } = collection;
router.all(`/${slug}*`, bindCollectionMiddleware(this.collections[formattedCollection.slug]));
router.all(`/${slug}*`, bindCollectionMiddleware(ctx.collections[formattedCollection.slug]));
const {
create,
@@ -73,14 +74,14 @@ export default function registerCollections(): void {
update,
findByID,
delete: deleteHandler,
} = this.requestHandlers.collections;
} = ctx.requestHandlers.collections;
if (collection.auth) {
const AuthCollection = this.collections[formattedCollection.slug];
const AuthCollection = ctx.collections[formattedCollection.slug];
passport.use(new LocalStrategy(AuthCollection.Model.authenticate()));
if (collection.auth.useAPIKey) {
passport.use(`${AuthCollection.config.slug}-api-key`, apiKeyStrategy(this, AuthCollection));
passport.use(`${AuthCollection.config.slug}-api-key`, apiKeyStrategy(ctx, AuthCollection));
}
const {
@@ -94,7 +95,7 @@ export default function registerCollections(): void {
resetPassword,
verifyEmail,
unlock,
} = this.requestHandlers.collections.auth;
} = ctx.requestHandlers.collections.auth;
if (collection.auth.verify) {
router
@@ -150,7 +151,7 @@ export default function registerCollections(): void {
.get(findByID)
.delete(deleteHandler);
this.router.use(router);
ctx.router.use(router);
}
return formattedCollection;

View File

@@ -1,11 +1,11 @@
/* eslint-disable import/no-dynamic-require */
/* eslint-disable global-require */
import path from 'path';
import { Config } from './types';
import { PayloadConfig } from './types';
import findConfig from './find';
const configPath = findConfig();
const loadConfig = (): Config => {
const loadConfig = (): PayloadConfig => {
// eslint-disable-next-line @typescript-eslint/no-var-requires
let publicConfig = require(configPath);

View File

@@ -89,6 +89,8 @@ const schema = joi.object().keys({
.keys({
window: joi.number().default(15 * 60 * 100),
max: joi.number().default(500),
trustProxy: joi.boolean().default(false),
skip: joi.func(),
}).default(),
graphQL: joi.object()
.keys({
@@ -97,6 +99,16 @@ const schema = joi.object().keys({
maxComplexity: joi.number().default(1000),
disablePlaygroundInProduction: joi.boolean().default(true),
}).default(),
compression: joi.object().unknown(),
localization: joi.alternatives()
.try(
joi.object().keys({
locales: joi.array().items(joi.string()),
defaultLocale: joi.string(),
fallback: joi.boolean(),
}),
joi.boolean(),
).default(false),
email: joi.alternatives()
.try(
joi.object()
@@ -122,6 +134,6 @@ const schema = joi.object().keys({
config: joi.string(),
scss: joi.string().default(path.resolve(__dirname, '../admin/scss/overrides.scss')),
}).default(),
}).unknown();
});
export default schema;

View File

@@ -1,4 +1,4 @@
import { Express } from 'express';
import { Express, Response } from 'express';
import joi from 'joi';
import 'joi-extract-type';
import { DeepRequired } from 'ts-essentials';
@@ -31,7 +31,7 @@ export type InitOptions = {
license?: string;
email?: EmailOptions;
local?: boolean;
onInit?: () => void;
onInit?: (Payload) => void;
};
export type SendEmailOptions = {
@@ -50,9 +50,11 @@ export type MockEmailCredentials = {
export type Access = (args?: any) => boolean;
// Create type out of Joi schema
// Extend the type with a bit more specificity
// Extend the type with a bit more TypeScript specificity
export type PayloadConfig = joi.extractType<typeof schema> & {
type PayloadConfigFromSchema = joi.extractType<typeof schema>
export interface PayloadConfig extends PayloadConfigFromSchema {
graphQL: {
mutations: {
[key: string]: unknown
@@ -64,6 +66,9 @@ export type PayloadConfig = joi.extractType<typeof schema> & {
disablePlaygroundInProduction: boolean;
},
email: EmailOptions,
};
hooks: {
afterError: (err: Error, res: Response) => void,
}
}
export type Config = DeepRequired<PayloadConfig>

View File

@@ -13,6 +13,7 @@ const validateSchema = (config: PayloadConfig): Config => {
logger.error(`There were ${result.error.details.length} errors validating your Payload config`);
result.error.details.forEach(({ message }, i) => {
console.log(JSON.stringify(result.error.details[i]));
logger.error(`${i + 1}: ${message}`);
});

View File

@@ -2,8 +2,8 @@ import httpStatus from 'http-status';
import APIError from './APIError';
class InvalidConfiguration extends APIError {
constructor(message: string, results?: any) {
super(message, httpStatus.INTERNAL_SERVER_ERROR, results);
constructor(message: string) {
super(message, httpStatus.INTERNAL_SERVER_ERROR);
}
}

View File

@@ -3,13 +3,12 @@ import compression from 'compression';
import history from 'connect-history-api-fallback';
import path from 'path';
import initWebpack from '../webpack/init';
import { Payload } from '../index';
const router = express.Router();
function initAdmin(): void {
if (!this.config.admin.disable && process.env.NODE_ENV !== 'test') {
this.initWebpack = initWebpack.bind(this);
function initAdmin(ctx: Payload): void {
if (!ctx.config.admin.disable && process.env.NODE_ENV !== 'test') {
router.use(history());
if (process.env.NODE_ENV === 'production') {
@@ -22,13 +21,13 @@ function initAdmin(): void {
}
});
router.use(compression(this.config.compression));
router.use(compression(ctx.config.compression));
router.use(express.static(path.resolve(process.cwd(), 'build'), { redirect: false }));
this.express.use(this.config.routes.admin, router);
ctx.express.use(ctx.config.routes.admin, router);
} else {
this.express.use(this.config.routes.admin, history());
this.express.use(this.initWebpack());
ctx.express.use(ctx.config.routes.admin, history());
ctx.express.use(initWebpack(ctx.config));
}
}
}

View File

@@ -1,6 +1,7 @@
import passport from 'passport';
import { PayloadConfig } from '../../config/types';
export default (config) => {
export default (config: PayloadConfig) => {
const methods = config.collections.reduce((enabledMethods, collection) => {
if (collection.auth && collection.auth.useAPIKey) {
const collectionMethods = [...enabledMethods];

View File

@@ -3,24 +3,25 @@ import passport from 'passport';
import path from 'path';
import getExecuteStaticAccess from '../auth/getExecuteStaticAccess';
import authenticate from './middleware/authenticate';
import { Payload } from '../index';
function initStatic() {
Object.entries(this.collections).forEach(([_, collection]) => {
function initStatic(ctx: Payload) {
Object.entries(ctx.collections).forEach(([_, collection]) => {
const { config } = collection;
if (config.upload) {
const router = express.Router();
router.use(passport.initialize());
router.use(authenticate(this.config));
router.use(authenticate(ctx.config));
router.use(getExecuteStaticAccess(collection));
const staticPath = path.resolve(this.config.paths.configDir, config.upload.staticDir);
const staticPath = path.resolve(ctx.config.paths.configDir, config.upload.staticDir);
router.use(express.static(staticPath));
this.express.use(`${config.upload.staticURL}`, router);
ctx.express.use(`${config.upload.staticURL}`, router);
}
});
}

View File

@@ -0,0 +1,137 @@
import joi from 'joi';
const baseField = joi.object().keys({
label: joi.string(),
required: joi.boolean().default(false),
saveToJWT: joi.boolean().default(false),
unique: joi.boolean().default(false),
localized: joi.boolean().default(false),
index: joi.boolean().default(false),
hidden: joi.boolean().default(false),
access: joi.object().keys({
create: joi.func(),
read: joi.func(),
update: joi.func(),
delete: joi.func(),
unlock: joi.func(),
}),
hooks: joi.object()
.keys({
beforeValidate: joi.array().items(joi.func()).default([]),
beforeChange: joi.array().items(joi.func()).default([]),
afterChange: joi.array().items(joi.func()).default([]),
afterRead: joi.array().items(joi.func()).default([]),
}).default(),
admin: joi.object().keys({
position: joi.string().valid('sidebar'),
width: joi.string(),
style: joi.object().unknown(),
readOnly: joi.boolean().default(false),
disabled: joi.boolean().default(false),
condition: joi.func(),
components: joi.object().keys({
Cell: joi.func(),
Field: joi.func(),
Filter: joi.func(),
}).default({}),
}).default({}),
});
// Joi.object({
// type: Joi.string().required().only(['pizza', 'salad'])
// })
// .when(Joi.object({ type: 'pizza' }).unknown(), {
// then: Joi.object({ pepperoni: Joi.boolean() })
// })
// .when(Joi.object({ type: 'salad' }).unknown(), {
// then: Joi.object({ croutons: Joi.boolean() })
// })
const types = {
text: baseField.keys({
name: joi.string().required(),
defaultValue: joi.string(),
}),
number: baseField.keys({
name: joi.string().required(),
defaultValue: joi.string(),
}),
email: baseField.keys({
name: joi.string().required(),
defaultValue: joi.string(),
}),
row: baseField.keys({
defaultValue: joi.object().unknown(),
fields: joi.array().items(joi.link('#field')),
}),
};
const allTypes = Object.keys(types);
const fieldSchema = allTypes.reduce((prev, type) => prev.when(joi.object({ type }).unknown(), {
then: types[type],
}),
joi.object({
type: joi.string().valid(...allTypes).required(),
})).id('field');
// const fieldSchema = joi.object({
// type: joi.string()
// .required()
// .valid(
// 'text',
// 'number',
// 'email',
// 'textarea',
// 'code',
// 'select',
// 'row',
// ).when(joi.object({ type: 'text' }).unknown(), {
// then: ,
// })
// .when(joi.object({ type: 'number' }).unknown(), {
// then: ,
// })
// .when(joi.object({ type: 'email' }).unknown(), {
// then:
// .when(joi.object({ type: 'row' }).unknown(), {
// then: baseField.keys({
// defaultValue: joi.object().unknown(),
// fields: joi.array().items(joi.link('#field')),
// }),
// }),
// }).id('field');
// const fieldSchema = joi.alternatives()
// .try(
// ,
// baseField.keys({
// type: joi.string().valid('number').required(),
// name: joi.string().required(),
// defaultValue: joi.number(),
// }),
// baseField.keys({
// type: joi.string().valid('email').required(),
// name: joi.string().required(),
// }),
// baseField.keys({
// type: joi.string().valid('textarea').required(),
// name: joi.string().required(),
// }),
// baseField.keys({
// type: joi.string().valid('code').required(),
// name: joi.string().required(),
// }),
// baseField.keys({
// type: joi.string().valid('select').required(),
// name: joi.string().required(),
// options: joi.array().items(joi.string()).required(),
// hasMany: joi.boolean().default(false),
// }).default(),
// baseField.keys({
// type: joi.string().valid('row').required(),
// fields: joi.array().items(joi.link('#field')),
// }),
// ).id('field');
export default fieldSchema;

View File

@@ -1,9 +1,9 @@
import mongoose from 'mongoose';
import buildSchema from '../mongoose/buildSchema';
import localizationPlugin from '../localization/plugin';
import { Config } from '../config/types';
import { PayloadConfig } from '../config/types';
const buildModel = (config: Config): mongoose.PaginateModel<any> | null => {
const buildModel = (config: PayloadConfig): mongoose.PaginateModel<any> | null => {
if (config.globals && config.globals.length > 0) {
const globalsSchema = new mongoose.Schema({}, { discriminatorKey: 'globalType', timestamps: true });

View File

@@ -1,25 +1,26 @@
import express from 'express';
import buildModel from './buildModel';
import { Payload } from '../index';
export default function initGlobals(): void {
if (this.config.globals) {
this.globals = {
Model: buildModel(this.config),
config: this.config.globals,
export default function initGlobals(ctx: Payload): void {
if (ctx.config.globals) {
ctx.globals = {
Model: buildModel(ctx.config),
config: ctx.config.globals,
};
// If not local, open routes
if (!this.config.local) {
if (!ctx.config.local) {
const router = express.Router();
this.config.globals.forEach((global) => {
ctx.config.globals.forEach((global) => {
router
.route(`/globals/${global.slug}`)
.get(this.requestHandlers.globals.findOne(global))
.post(this.requestHandlers.globals.update(global));
.get(ctx.requestHandlers.globals.findOne(global))
.post(ctx.requestHandlers.globals.update(global));
});
this.router.use(router);
ctx.router.use(router);
}
}
}

View File

@@ -1,9 +1,10 @@
import graphQLPlayground from 'graphql-playground-middleware-express';
import { Payload } from '../index';
function initPlayground(): void {
if ((!this.config.graphQL.disablePlaygroundInProduction && process.env.NODE_ENV === 'production') || process.env.NODE_ENV !== 'production') {
this.router.get(this.config.routes.graphQLPlayground, graphQLPlayground({
endpoint: `${this.config.routes.api}${this.config.routes.graphQL}`,
function initPlayground(ctx: Payload): void {
if ((!ctx.config.graphQL.disablePlaygroundInProduction && process.env.NODE_ENV === 'production') || process.env.NODE_ENV !== 'production') {
ctx.router.get(ctx.config.routes.graphQLPlayground, graphQLPlayground({
endpoint: `${ctx.config.routes.api}${ctx.config.routes.graphQL}`,
settings: {
'request.credentials': 'include',
},

View File

@@ -1,11 +1,11 @@
import express, { Express, Request, Router } from 'express';
import express, { Express, Router } from 'express';
import crypto from 'crypto';
import { TestAccount } from 'nodemailer';
import { AuthenticateOptions } from 'passport';
import {
Config,
EmailOptions,
InitOptions,
PayloadConfig,
} from './config/types';
import {
Collection,
@@ -20,7 +20,7 @@ import {
DeleteOptions,
FindResponse,
} from './types';
import Logger, { PayloadLogger } from './utilities/logger';
import Logger from './utilities/logger';
import bindOperations from './init/bindOperations';
import bindRequestHandlers from './init/bindRequestHandlers';
import bindResolvers from './init/bindResolvers';
@@ -51,11 +51,15 @@ require('isomorphic-fetch');
* @description Payload
*/
export class Payload {
config: Config;
config: PayloadConfig = loadConfig();
collections: Collection[] = [];
logger: PayloadLogger;
graphQL: any;
globals: any;
logger = Logger();
express: Express
@@ -73,35 +77,26 @@ export class Payload {
local: boolean;
initAuth: typeof initAuth;
encrypt = encrypt;
encrypt: typeof encrypt;
decrypt: typeof decrypt;
initCollections: typeof initCollections;
initGlobals: typeof initGlobals;
initGraphQLPlayground: typeof initGraphQLPlayground;
initStatic: typeof initStatic;
initAdmin: typeof initAdmin;
decrypt = decrypt;
operations: { [key: string]: any };
errorHandler: any;
authenticate: (strategy: string | string[], options: AuthenticateOptions, callback?: (...args: any[]) => any) => any;
performFieldOperations: typeof performFieldOperations;
// requestHandlers: { collections: { create: any; find: any; findByID: any; update: any; delete: any; auth: { access: any; forgotPassword: any; init: any; login: any; logout: any; me: any; refresh: any; registerFirstUser: any; resetPassword: any; verifyEmail: any; unlock: any; }; }; globals: { ...; }; };
requestHandlers: { [key: string]: any };
/**
* @description Initializes Payload
* @param options
*/
init(options: InitOptions): void {
this.logger = Logger();
this.logger.info('Starting Payload...');
if (!options.secret) {
throw new Error(
'Error: missing secret key. A secret key is needed to secure Payload.',
@@ -129,44 +124,21 @@ export class Payload {
bindRequestHandlers(this);
bindResolvers(this);
this.initAuth = initAuth.bind(this);
this.encrypt = encrypt.bind(this);
this.decrypt = decrypt.bind(this);
this.initCollections = initCollections.bind(this);
this.initGlobals = initGlobals.bind(this);
this.initGraphQLPlayground = initGraphQLPlayground.bind(this);
// this.buildEmail = buildEmail.bind(this);
this.sendEmail = this.sendEmail.bind(this);
this.getMockEmailCredentials = this.getMockEmailCredentials.bind(this);
this.initStatic = initStatic.bind(this);
this.initAdmin = initAdmin.bind(this);
this.performFieldOperations = performFieldOperations.bind(this);
this.create = this.create.bind(this);
this.find = this.find.bind(this);
this.findGlobal = this.findGlobal.bind(this);
this.updateGlobal = this.updateGlobal.bind(this);
this.findByID = this.findByID.bind(this);
this.update = this.update.bind(this);
this.login = this.login.bind(this);
this.forgotPassword = this.forgotPassword.bind(this);
this.resetPassword = this.resetPassword.bind(this);
this.unlock = this.unlock.bind(this);
this.verifyEmail = this.verifyEmail.bind(this);
// If not initializing locally, scaffold router
if (!this.config.local) {
this.router = express.Router();
this.router.use(...expressMiddleware(this));
this.initAuth();
initAuth(this);
}
// Configure email service
this.email = buildEmail(this.config.email);
// Initialize collections & globals
this.initCollections();
this.initGlobals();
initCollections(this);
initGlobals(this);
// Connect to database
connectMongoose(this.mongoURL);
@@ -179,9 +151,11 @@ export class Payload {
// If not initializing locally, set up HTTP routing
if (!this.config.local) {
this.express = options.express;
if (this.config.rateLimit && this.config.rateLimit.trustProxy) { this.express.set('trust proxy', 1); }
if (this.config.rateLimit.trustProxy) {
this.express.set('trust proxy', 1);
}
this.initAdmin();
initAdmin(this);
this.router.get('/access', this.requestHandlers.collections.auth.access);
@@ -193,13 +167,13 @@ export class Payload {
(req, res) => graphQLHandler.init(req, res)(req, res),
);
this.initGraphQLPlayground();
initGraphQLPlayground(this);
// Bind router to API
this.express.use(this.config.routes.api, this.router);
// Enable static routes for all collections permitting upload
this.initStatic();
initStatic(this);
this.errorHandler = errorHandler(this.config, this.logger);
this.router.use(this.errorHandler);
@@ -210,13 +184,13 @@ export class Payload {
if (typeof options.onInit === 'function') options.onInit(this);
}
async sendEmail(message: Message): Promise<any> {
sendEmail = async (message: Message): Promise<any> => {
const email = await this.email;
const result = email.transport.sendMail(message);
return result;
}
async getMockEmailCredentials(): Promise<TestAccount> {
getMockEmailCredentials = async (): Promise<TestAccount> => {
const email = await this.email as MockEmailHandler;
return email.account;
}

View File

@@ -1,3 +1,4 @@
import { Payload } from '../index';
import access from '../auth/operations/access';
import forgotPassword from '../auth/operations/forgotPassword';
import init from '../auth/operations/init';
@@ -19,7 +20,7 @@ import deleteHandler from '../collections/operations/delete';
import findOne from '../globals/operations/findOne';
import globalUpdate from '../globals/operations/update';
function bindOperations(ctx): void {
function bindOperations(ctx: Payload): void {
ctx.operations = {
collections: {
create: create.bind(ctx),

View File

@@ -18,8 +18,9 @@ import deleteHandler from '../collections/requestHandlers/delete';
import findOne from '../globals/requestHandlers/findOne';
import globalUpdate from '../globals/requestHandlers/update';
import { Payload } from '../index';
function bindRequestHandlers(ctx): void {
function bindRequestHandlers(ctx: Payload): void {
ctx.requestHandlers = {
collections: {
create: create.bind(ctx),

View File

@@ -17,8 +17,9 @@ import deleteResolver from '../collections/graphql/resolvers/delete';
import findOne from '../globals/graphql/resolvers/findOne';
import globalUpdate from '../globals/graphql/resolvers/update';
import { Payload } from '../index';
function bindResolvers(ctx): void {
function bindResolvers(ctx: Payload): void {
ctx.graphQL = {
resolvers: {
collections: {

View File

@@ -1,6 +1,7 @@
/* eslint-disable no-console */
import mongoose from 'mongoose';
import Logger from '../utilities/logger';
const logger = Logger();
const connectMongoose = async (url: string) => {

View File

@@ -1,12 +1,12 @@
import HtmlWebpackPlugin from 'html-webpack-plugin';
import path from 'path';
import webpack, { Configuration } from 'webpack';
import { Config } from '../config/types';
import babelConfig from '../babel.config';
import { PayloadConfig } from '../config/types';
const mockModulePath = path.resolve(__dirname, './mocks/emptyModule.js');
export default (config: Config): Configuration => {
export default (config: PayloadConfig): Configuration => {
let webpackConfig: Configuration = {
entry: {
main: [

View File

@@ -3,11 +3,12 @@ import express, { Router } from 'express';
import webpackDevMiddleware from 'webpack-dev-middleware';
import webpackHotMiddleware from 'webpack-hot-middleware';
import getWebpackDevConfig from './getWebpackDevConfig';
import { PayloadConfig } from '../config/types';
const router = express.Router();
function initWebpack(): Router {
const webpackDevConfig = getWebpackDevConfig(this.config);
function initWebpack(config: PayloadConfig): Router {
const webpackDevConfig = getWebpackDevConfig(config);
const compiler = webpack(webpackDevConfig);
router.use(webpackDevMiddleware(compiler, {

View File

@@ -1957,7 +1957,7 @@
"@types/passport-local-mongoose@^4.0.13":
version "4.0.13"
resolved "https://registry.yarnpkg.com/@types/passport-local-mongoose/-/passport-local-mongoose-4.0.13.tgz#0edc3aedcc82a70b7e461efc2dc85f42ccb80aa3"
resolved "https://registry.npmjs.org/@types/passport-local-mongoose/-/passport-local-mongoose-4.0.13.tgz#0edc3aedcc82a70b7e461efc2dc85f42ccb80aa3"
integrity sha512-tjVfcyFXO+EIzjTg6DHm+Wq9LwftqDv0kQ/IlLqFHBtZ6gKMy5pLKdr4g62VGEJsgPX8F9UGb9inDREQ2tOpEw==
dependencies:
"@types/passport-local" "*"