merge
This commit is contained in:
@@ -104,8 +104,8 @@ module.exports = {
|
||||
},
|
||||
graphQL: {
|
||||
maxComplexity: 1000,
|
||||
mutations: {},
|
||||
queries: {},
|
||||
mutations: {}, // TODO: needs typing
|
||||
queries: {}, // TODO: needs typing
|
||||
disablePlaygroundInProduction: true,
|
||||
},
|
||||
rateLimit: {
|
||||
|
||||
@@ -12,8 +12,7 @@ payload.init({
|
||||
mongoURL: 'mongodb://localhost/payload',
|
||||
express: expressApp,
|
||||
onInit: () => {
|
||||
console.log('Payload is initialized');
|
||||
// console.log('Payload is initialized');
|
||||
console.log('Payload Demo Initialized');
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -3,11 +3,12 @@
|
||||
".git",
|
||||
"node_modules",
|
||||
"node_modules/**/node_modules",
|
||||
"src/client"
|
||||
"src/client",
|
||||
"src/**/*.spec.ts"
|
||||
],
|
||||
"watch": [
|
||||
"src/",
|
||||
"src/**/*.ts",
|
||||
"demo/"
|
||||
],
|
||||
"ext": "js,json"
|
||||
"ext": "ts,js,json"
|
||||
}
|
||||
|
||||
@@ -1,37 +1,47 @@
|
||||
import nodemailer from 'nodemailer';
|
||||
import nodemailer, { Transporter } from 'nodemailer';
|
||||
import { PayloadEmailOptions } from '../types';
|
||||
import { InvalidConfiguration } from '../errors';
|
||||
import mockHandler from './mockHandler';
|
||||
import Logger from '../utilities/logger';
|
||||
const logger = Logger();
|
||||
|
||||
async function buildEmail() {
|
||||
if (!this.config.email.transport || this.config.email.transport === 'mock') {
|
||||
this.logger.info('E-mail configured with mock configuration');
|
||||
const mockAccount = await mockHandler(this.config.email);
|
||||
if (this.config.email.transport === 'mock') {
|
||||
export default async function buildEmail(emailConfig: PayloadEmailOptions) {
|
||||
if (!emailConfig.transport || emailConfig.transport === 'mock') {
|
||||
const mockAccount = await mockHandler(emailConfig);
|
||||
// Only log mock credentials if was explicitly set in config
|
||||
if (emailConfig.transport === 'mock') {
|
||||
const { account: { web, user, pass } } = mockAccount;
|
||||
this.logger.info(`Log into mock email provider at ${web}`);
|
||||
this.logger.info(`Mock email account username: ${user}`);
|
||||
this.logger.info(`Mock email account password: ${pass}`);
|
||||
logger.info('E-mail configured with mock configuration');
|
||||
logger.info(`Log into mock email provider at ${web}`);
|
||||
logger.info(`Mock email account username: ${user}`);
|
||||
logger.info(`Mock email account password: ${pass}`);
|
||||
}
|
||||
return mockAccount;
|
||||
}
|
||||
|
||||
const email = { ...this.config.email };
|
||||
const email = { ...emailConfig };
|
||||
|
||||
if (this.config.email.transport) {
|
||||
email.transport = this.config.email.transport;
|
||||
if (!email.fromName || !email.fromAddress) {
|
||||
throw new InvalidConfiguration('Email fromName and fromAddress must be configured when transport is configured');
|
||||
}
|
||||
|
||||
if (this.config.email.transportOptions) {
|
||||
email.transport = nodemailer.createTransport(this.config.email.transportOptions);
|
||||
let transport: Transporter;
|
||||
// TODO: Is this ever populated when not using 'mock'?
|
||||
if (emailConfig.transport) {
|
||||
transport = emailConfig.transport;
|
||||
} else if (emailConfig.transportOptions) {
|
||||
transport = nodemailer.createTransport(emailConfig.transportOptions);
|
||||
}
|
||||
|
||||
try {
|
||||
await email.transport.verify();
|
||||
await transport.verify();
|
||||
email.transport = transport;
|
||||
} catch (err) {
|
||||
this.logger.error('There is an error with the email configuration you have provided.', err);
|
||||
logger.error(
|
||||
"There is an error with the email configuration you have provided.",
|
||||
err
|
||||
);
|
||||
}
|
||||
|
||||
return email;
|
||||
}
|
||||
|
||||
|
||||
export default buildEmail;
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
import nodemailer from 'nodemailer';
|
||||
import nodemailer, { TestAccount, Transporter } from 'nodemailer';
|
||||
import { PayloadEmailOptions } from '../types';
|
||||
import { MockEmailHandler } from './types';
|
||||
|
||||
const mockEmailHandler = async (emailConfig) => {
|
||||
const mockEmailHandler = async (emailConfig: PayloadEmailOptions): Promise<MockEmailHandler> => {
|
||||
const testAccount = await nodemailer.createTestAccount();
|
||||
|
||||
const smtpOptions = {
|
||||
...emailConfig,
|
||||
host: 'smtp.ethereal.email',
|
||||
host: "smtp.ethereal.email",
|
||||
port: 587,
|
||||
secure: false,
|
||||
fromName: emailConfig.fromName || 'Payload CMS',
|
||||
fromAddress: emailConfig.fromAddress || 'info@payloadcms.com',
|
||||
fromName: emailConfig.fromName || "Payload CMS",
|
||||
fromAddress: emailConfig.fromAddress || "info@payloadcms.com",
|
||||
auth: {
|
||||
user: testAccount.user,
|
||||
pass: testAccount.pass,
|
||||
|
||||
3
src/email/types.ts
Normal file
3
src/email/types.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import { TestAccount, Transporter } from 'nodemailer';
|
||||
|
||||
export type MockEmailHandler = { account: TestAccount; transport: Transporter };
|
||||
@@ -5,7 +5,7 @@ import httpStatus from 'http-status';
|
||||
* @extends Error
|
||||
*/
|
||||
class ExtendableError extends Error {
|
||||
constructor(message, status, data, isPublic) {
|
||||
constructor(message: string, status: number, data: any, isPublic: boolean) {
|
||||
super(message);
|
||||
this.name = this.constructor.name;
|
||||
this.message = message;
|
||||
@@ -29,7 +29,7 @@ class APIError extends ExtendableError {
|
||||
* @param {object} data - response data to be returned.
|
||||
* @param {boolean} isPublic - Whether the message should be visible to user or not.
|
||||
*/
|
||||
constructor(message, status = httpStatus.INTERNAL_SERVER_ERROR, data, isPublic = false) {
|
||||
constructor(message: string, status: number = httpStatus.INTERNAL_SERVER_ERROR, data: any, isPublic = false) {
|
||||
super(message, status, data, isPublic);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import httpStatus from 'http-status';
|
||||
import APIError from './APIError';
|
||||
|
||||
class InvalidConfiguration extends APIError {
|
||||
constructor(message, results) {
|
||||
constructor(message, results?) {
|
||||
super(message, httpStatus.INTERNAL_SERVER_ERROR, results);
|
||||
}
|
||||
}
|
||||
|
||||
69
src/index.ts
69
src/index.ts
@@ -1,5 +1,19 @@
|
||||
import express from 'express';
|
||||
import crypto from 'crypto';
|
||||
import { Router } from 'express';
|
||||
import {
|
||||
PayloadConfig,
|
||||
PayloadCollection,
|
||||
PayloadInitOptions,
|
||||
PayloadLogger,
|
||||
CreateOptions,
|
||||
FindOptions,
|
||||
FindGlobalOptions,
|
||||
UpdateGlobalOptions,
|
||||
FindByIDOptions,
|
||||
UpdateOptions,
|
||||
DeleteOptions,
|
||||
} from './types';
|
||||
import Logger from './utilities/logger';
|
||||
import bindOperations from './init/bindOperations';
|
||||
import bindRequestHandlers from './init/bindRequestHandlers';
|
||||
@@ -23,21 +37,27 @@ import performFieldOperations from './fields/performFieldOperations';
|
||||
import localOperations from './collections/operations/local';
|
||||
import localGlobalOperations from './globals/operations/local';
|
||||
import { encrypt, decrypt } from './auth/crypto';
|
||||
import { TestAccount } from 'nodemailer';
|
||||
import { MockEmailHandler } from './email/types';
|
||||
|
||||
require('es6-promise').polyfill();
|
||||
require('isomorphic-fetch');
|
||||
|
||||
const logger = Logger();
|
||||
|
||||
class Payload {
|
||||
logger: any;
|
||||
config: PayloadConfig;
|
||||
collections: PayloadCollection[] = [];
|
||||
logger: PayloadLogger;
|
||||
router: Router;
|
||||
email: any;
|
||||
|
||||
init(options) {
|
||||
this.logger = logger;
|
||||
init(options: PayloadInitOptions) {
|
||||
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.');
|
||||
throw new Error(
|
||||
'Error: missing secret key. A secret key is needed to secure Payload.'
|
||||
);
|
||||
}
|
||||
|
||||
if (!options.mongoURL) {
|
||||
@@ -51,14 +71,18 @@ class Payload {
|
||||
...config,
|
||||
email,
|
||||
license: options.license,
|
||||
secret: crypto.createHash('sha256').update(options.secret).digest('hex').slice(0, 32),
|
||||
secret: crypto
|
||||
.createHash('sha256')
|
||||
.update(options.secret)
|
||||
.digest('hex')
|
||||
.slice(0, 32),
|
||||
mongoURL: options.mongoURL,
|
||||
local: options.local,
|
||||
});
|
||||
|
||||
if (typeof this.config.paths === 'undefined') this.config.paths = {};
|
||||
|
||||
this.collections = {};
|
||||
// this.collections = {};
|
||||
|
||||
bindOperations(this);
|
||||
bindRequestHandlers(this);
|
||||
@@ -70,7 +94,7 @@ class Payload {
|
||||
this.initCollections = initCollections.bind(this);
|
||||
this.initGlobals = initGlobals.bind(this);
|
||||
this.initGraphQLPlayground = initGraphQLPlayground.bind(this);
|
||||
this.buildEmail = buildEmail.bind(this);
|
||||
// this.buildEmail = buildEmail.bind(this);
|
||||
this.sendEmail = this.sendEmail.bind(this);
|
||||
this.getMockEmailCredentials = this.getMockEmailCredentials.bind(this);
|
||||
this.initStatic = initStatic.bind(this);
|
||||
@@ -97,7 +121,7 @@ class Payload {
|
||||
}
|
||||
|
||||
// Configure email service
|
||||
this.email = this.buildEmail();
|
||||
this.email = buildEmail(this.config.email);
|
||||
|
||||
// Initialize collections & globals
|
||||
this.initCollections();
|
||||
@@ -114,7 +138,8 @@ 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 && this.config.rateLimit.trustProxy)
|
||||
this.express.set('trust proxy', 1);
|
||||
|
||||
this.initAdmin();
|
||||
|
||||
@@ -125,7 +150,7 @@ class Payload {
|
||||
this.router.use(
|
||||
this.config.routes.graphQL,
|
||||
identifyAPI('GraphQL'),
|
||||
(req, res) => graphQLHandler.init(req, res)(req, res),
|
||||
(req, res) => graphQLHandler.init(req, res)(req, res)
|
||||
);
|
||||
|
||||
this.initGraphQLPlayground();
|
||||
@@ -145,54 +170,54 @@ class Payload {
|
||||
if (typeof options.onInit === 'function') options.onInit();
|
||||
}
|
||||
|
||||
async sendEmail(message) {
|
||||
async sendEmail(message: string) {
|
||||
const email = await this.email;
|
||||
const result = email.transport.sendMail(message);
|
||||
return result;
|
||||
}
|
||||
|
||||
async getMockEmailCredentials() {
|
||||
const email = await this.email;
|
||||
async getMockEmailCredentials(): Promise<TestAccount> {
|
||||
const email = await this.email as MockEmailHandler;
|
||||
return email.account;
|
||||
}
|
||||
|
||||
async create(options) {
|
||||
async create(options: CreateOptions) {
|
||||
let { create } = localOperations;
|
||||
create = create.bind(this);
|
||||
return create(options);
|
||||
}
|
||||
|
||||
async find(options) {
|
||||
async find(options: FindOptions) {
|
||||
let { find } = localOperations;
|
||||
find = find.bind(this);
|
||||
return find(options);
|
||||
}
|
||||
|
||||
async findGlobal(options) {
|
||||
async findGlobal(options: FindGlobalOptions) {
|
||||
let { findOne } = localGlobalOperations;
|
||||
findOne = findOne.bind(this);
|
||||
return findOne(options);
|
||||
}
|
||||
|
||||
async updateGlobal(options) {
|
||||
async updateGlobal(options: UpdateGlobalOptions) {
|
||||
let { update } = localGlobalOperations;
|
||||
update = update.bind(this);
|
||||
return update(options);
|
||||
}
|
||||
|
||||
async findByID(options) {
|
||||
async findByID(options: FindByIDOptions) {
|
||||
let { findByID } = localOperations;
|
||||
findByID = findByID.bind(this);
|
||||
return findByID(options);
|
||||
}
|
||||
|
||||
async update(options) {
|
||||
async update(options: UpdateOptions) {
|
||||
let { update } = localOperations;
|
||||
update = update.bind(this);
|
||||
return update(options);
|
||||
}
|
||||
|
||||
async delete(options) {
|
||||
async delete(options: DeleteOptions) {
|
||||
let { delete: deleteOperation } = localOperations;
|
||||
deleteOperation = deleteOperation.bind(this);
|
||||
return deleteOperation(options);
|
||||
|
||||
@@ -3,8 +3,9 @@ import * as payloadSchema from './payload.schema.json';
|
||||
import * as collectionSchema from './collection.schema.json';
|
||||
|
||||
import InvalidSchema from '../errors/InvalidSchema';
|
||||
import { PayloadConfig } from '../types';
|
||||
|
||||
const validateSchema = (config) => {
|
||||
const validateSchema = (config: PayloadConfig) => {
|
||||
const ajv = new Ajv({ useDefaults: true });
|
||||
const validate = ajv.addSchema(collectionSchema, '/collection.schema.json')
|
||||
.compile(payloadSchema);
|
||||
|
||||
251
src/types/index.ts
Normal file
251
src/types/index.ts
Normal file
@@ -0,0 +1,251 @@
|
||||
// TODO: Split out init options from config types into own file
|
||||
|
||||
import { Express, Request } from 'express';
|
||||
import { Transporter } from 'nodemailer';
|
||||
import SMTPConnection from 'nodemailer/lib/smtp-connection';
|
||||
import { Logger } from 'pino';
|
||||
|
||||
type MockEmailTransport = {
|
||||
transport?: 'mock';
|
||||
fromName?: string;
|
||||
fromAddress?: string;
|
||||
};
|
||||
type ValidEmailTransport = {
|
||||
transport: Transporter;
|
||||
transportOptions?: SMTPConnection.Options;
|
||||
fromName: string;
|
||||
fromAddress: string;
|
||||
};
|
||||
export type PayloadEmailOptions = ValidEmailTransport | MockEmailTransport;
|
||||
|
||||
export type PayloadInitOptions = {
|
||||
express?: Express;
|
||||
mongoURL: string;
|
||||
secret: string;
|
||||
license?: string;
|
||||
email?: PayloadEmailOptions;
|
||||
local?: boolean; // I have no idea what this is
|
||||
onInit?: () => void;
|
||||
};
|
||||
|
||||
export type Document = {
|
||||
id: string;
|
||||
};
|
||||
|
||||
export type CreateOptions = {
|
||||
collection: string;
|
||||
data: any;
|
||||
};
|
||||
|
||||
export type FindOptions = {
|
||||
collection: string;
|
||||
where?: { [key: string]: any };
|
||||
};
|
||||
|
||||
export type FindResponse = {
|
||||
docs: Document[];
|
||||
totalDocs: number;
|
||||
limit: number;
|
||||
totalPages: number;
|
||||
page: number;
|
||||
pagingCounter: number;
|
||||
hasPrevPage: boolean;
|
||||
hasNextPage: boolean;
|
||||
prevPage: number | null;
|
||||
nextPage: number | null;
|
||||
};
|
||||
|
||||
export type FindGlobalOptions = {
|
||||
global: string;
|
||||
};
|
||||
export type UpdateGlobalOptions = {
|
||||
global: string;
|
||||
data: any;
|
||||
};
|
||||
|
||||
export type FindByIDOptions = {
|
||||
collection: string;
|
||||
id: string;
|
||||
};
|
||||
export type UpdateOptions = {
|
||||
collection: string;
|
||||
id: string;
|
||||
data: any;
|
||||
};
|
||||
|
||||
export type DeleteOptions = {
|
||||
collection: string;
|
||||
id: string;
|
||||
};
|
||||
|
||||
export type ForgotPasswordOptions = {
|
||||
collection: string;
|
||||
generateEmailHTML?: (token: string) => string;
|
||||
expiration: Date;
|
||||
data: any;
|
||||
};
|
||||
|
||||
export type SendEmailOptions = {
|
||||
from: string;
|
||||
to: string;
|
||||
subject: string;
|
||||
html: string;
|
||||
};
|
||||
|
||||
export type MockEmailCredentials = {
|
||||
user: string;
|
||||
pass: string;
|
||||
web: string;
|
||||
};
|
||||
|
||||
export type PayloadField = {
|
||||
name: string;
|
||||
label: string;
|
||||
type:
|
||||
| 'number'
|
||||
| 'text'
|
||||
| 'email'
|
||||
| 'textarea'
|
||||
| 'richText'
|
||||
| 'code'
|
||||
| 'radio'
|
||||
| 'checkbox'
|
||||
| 'date'
|
||||
| 'upload'
|
||||
| 'relationship'
|
||||
| 'row'
|
||||
| 'array'
|
||||
| 'group'
|
||||
| 'select'
|
||||
| 'blocks';
|
||||
localized?: boolean;
|
||||
fields?: PayloadField[];
|
||||
admin?: {
|
||||
position?: string;
|
||||
width?: string;
|
||||
style?: Object;
|
||||
};
|
||||
};
|
||||
|
||||
export type PayloadCollectionHook = (...args: any[]) => any | void;
|
||||
export type PayloadAccess = (args?: any) => boolean;
|
||||
|
||||
export type PayloadCollection = {
|
||||
slug: string;
|
||||
labels?: {
|
||||
singular: string;
|
||||
plural: string;
|
||||
};
|
||||
admin?: {
|
||||
useAsTitle?: string;
|
||||
defaultColumns?: string[];
|
||||
components?: any;
|
||||
};
|
||||
hooks?: {
|
||||
beforeOperation?: PayloadCollectionHook[];
|
||||
beforeValidate?: PayloadCollectionHook[];
|
||||
beforeChange?: PayloadCollectionHook[];
|
||||
afterChange?: PayloadCollectionHook[];
|
||||
beforeRead?: PayloadCollectionHook[];
|
||||
afterRead?: PayloadCollectionHook[];
|
||||
beforeDelete?: PayloadCollectionHook[];
|
||||
afterDelete?: PayloadCollectionHook[];
|
||||
};
|
||||
access?: {
|
||||
create?: PayloadAccess;
|
||||
read?: PayloadAccess;
|
||||
update?: PayloadAccess;
|
||||
delete?: PayloadAccess;
|
||||
admin?: PayloadAccess;
|
||||
};
|
||||
auth?: {
|
||||
tokenExpiration?: number;
|
||||
verify?:
|
||||
| boolean
|
||||
| { generateEmailHTML: string; generateEmailSubject: string };
|
||||
maxLoginAttempts?: number;
|
||||
lockTime?: number;
|
||||
useAPIKey?: boolean;
|
||||
cookies?:
|
||||
| {
|
||||
secure?: boolean;
|
||||
sameSite?: string;
|
||||
domain?: string | undefined;
|
||||
}
|
||||
| boolean;
|
||||
};
|
||||
fields: PayloadField[];
|
||||
};
|
||||
|
||||
export type PayloadGlobal = {
|
||||
slug: string;
|
||||
label: string;
|
||||
access?: {
|
||||
create?: PayloadAccess;
|
||||
read?: PayloadAccess;
|
||||
update?: PayloadAccess;
|
||||
delete?: PayloadAccess;
|
||||
admin?: PayloadAccess;
|
||||
};
|
||||
fields: PayloadField[];
|
||||
};
|
||||
|
||||
export type PayloadConfig = {
|
||||
admin?: {
|
||||
user?: string;
|
||||
meta?: {
|
||||
titleSuffix?: string;
|
||||
ogImage?: string;
|
||||
favicon?: string;
|
||||
};
|
||||
disable?: boolean;
|
||||
};
|
||||
collections?: PayloadCollection[];
|
||||
globals?: PayloadGlobal[];
|
||||
serverURL?: string;
|
||||
cookiePrefix?: string;
|
||||
csrf?: string[];
|
||||
cors?: string[];
|
||||
publicENV: { [key: string]: string };
|
||||
routes?: {
|
||||
api?: string;
|
||||
admin?: string;
|
||||
graphQL?: string;
|
||||
graphQLPlayground?: string;
|
||||
};
|
||||
email?: PayloadEmailOptions;
|
||||
local?: boolean;
|
||||
defaultDepth?: number;
|
||||
maxDepth?: number;
|
||||
rateLimit?: {
|
||||
window?: number;
|
||||
max?: number;
|
||||
trustProxy?: boolean;
|
||||
skip?: (req: Request) => boolean; // TODO: Type join Request w/ PayloadRequest
|
||||
};
|
||||
upload?: {
|
||||
limits?: {
|
||||
fileSize?: number;
|
||||
};
|
||||
};
|
||||
localization?: {
|
||||
locales: string[];
|
||||
};
|
||||
defaultLocale?: string;
|
||||
fallback?: boolean;
|
||||
graphQL?: {
|
||||
mutations?: Object;
|
||||
queries?: Object;
|
||||
maxComplexity?: number;
|
||||
disablePlaygroundInProduction?: boolean;
|
||||
};
|
||||
components: { [key: string]: JSX.Element | (() => JSX.Element) };
|
||||
paths?: { [key: string]: string };
|
||||
hooks?: {
|
||||
afterError?: () => void;
|
||||
};
|
||||
webpack?: (config: any) => any;
|
||||
serverModules?: string[];
|
||||
};
|
||||
|
||||
export type PayloadLogger = Logger;
|
||||
@@ -1,6 +1,7 @@
|
||||
import falsey from 'falsey';
|
||||
import pino from 'pino';
|
||||
import memoize from 'micro-memoize';
|
||||
import { PayloadLogger } from '../types';
|
||||
|
||||
export default memoize((name = 'payload') => pino({
|
||||
name,
|
||||
@@ -9,4 +10,4 @@ export default memoize((name = 'payload') => pino({
|
||||
ignore: 'pid,hostname',
|
||||
translateTime: 'HH:MM:ss',
|
||||
},
|
||||
}));
|
||||
}) as PayloadLogger);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { PayloadConfig } from '../types';
|
||||
import defaultUser from '../auth/default';
|
||||
import sanitizeCollection from '../collections/sanitize';
|
||||
import { InvalidConfiguration } from '../errors';
|
||||
@@ -5,7 +6,7 @@ import sanitizeGlobals from '../globals/sanitize';
|
||||
import validateSchema from '../schema/validateSchema';
|
||||
import checkDuplicateCollections from './checkDuplicateCollections';
|
||||
|
||||
const sanitizeConfig = (config) => {
|
||||
const sanitizeConfig = (config: PayloadConfig) => {
|
||||
const sanitizedConfig = validateSchema({ ...config });
|
||||
|
||||
// TODO: remove default values from sanitize in favor of assigning in the schema within validateSchema and use https://www.npmjs.com/package/ajv#coercing-data-types where needed
|
||||
@@ -42,12 +43,7 @@ const sanitizeConfig = (config) => {
|
||||
}
|
||||
|
||||
sanitizedConfig.email = config.email || {};
|
||||
// TODO: This should likely be moved to the payload.schema.json
|
||||
if (sanitizedConfig.email.transports) {
|
||||
if (!sanitizedConfig.email.email.fromName || !sanitizedConfig.email.email.fromAddress) {
|
||||
throw new InvalidConfiguration('Email fromName and fromAddress must be configured when transport is configured');
|
||||
}
|
||||
}
|
||||
// if (!sanitizedConfig.email.transport) sanitizedConfig.email.transport = 'mock';
|
||||
|
||||
sanitizedConfig.graphQL = config.graphQL || {};
|
||||
sanitizedConfig.graphQL.maxComplexity = (sanitizedConfig.graphQL && sanitizedConfig.graphQL.maxComplexity) ? sanitizedConfig.graphQL.maxComplexity : 1000;
|
||||
|
||||
Reference in New Issue
Block a user