merge
This commit is contained in:
@@ -104,8 +104,8 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
graphQL: {
|
graphQL: {
|
||||||
maxComplexity: 1000,
|
maxComplexity: 1000,
|
||||||
mutations: {},
|
mutations: {}, // TODO: needs typing
|
||||||
queries: {},
|
queries: {}, // TODO: needs typing
|
||||||
disablePlaygroundInProduction: true,
|
disablePlaygroundInProduction: true,
|
||||||
},
|
},
|
||||||
rateLimit: {
|
rateLimit: {
|
||||||
|
|||||||
@@ -12,8 +12,7 @@ payload.init({
|
|||||||
mongoURL: 'mongodb://localhost/payload',
|
mongoURL: 'mongodb://localhost/payload',
|
||||||
express: expressApp,
|
express: expressApp,
|
||||||
onInit: () => {
|
onInit: () => {
|
||||||
console.log('Payload is initialized');
|
console.log('Payload Demo Initialized');
|
||||||
// console.log('Payload is initialized');
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -3,11 +3,12 @@
|
|||||||
".git",
|
".git",
|
||||||
"node_modules",
|
"node_modules",
|
||||||
"node_modules/**/node_modules",
|
"node_modules/**/node_modules",
|
||||||
"src/client"
|
"src/client",
|
||||||
|
"src/**/*.spec.ts"
|
||||||
],
|
],
|
||||||
"watch": [
|
"watch": [
|
||||||
"src/",
|
"src/**/*.ts",
|
||||||
"demo/"
|
"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 mockHandler from './mockHandler';
|
||||||
|
import Logger from '../utilities/logger';
|
||||||
|
const logger = Logger();
|
||||||
|
|
||||||
async function buildEmail() {
|
export default async function buildEmail(emailConfig: PayloadEmailOptions) {
|
||||||
if (!this.config.email.transport || this.config.email.transport === 'mock') {
|
if (!emailConfig.transport || emailConfig.transport === 'mock') {
|
||||||
this.logger.info('E-mail configured with mock configuration');
|
const mockAccount = await mockHandler(emailConfig);
|
||||||
const mockAccount = await mockHandler(this.config.email);
|
// Only log mock credentials if was explicitly set in config
|
||||||
if (this.config.email.transport === 'mock') {
|
if (emailConfig.transport === 'mock') {
|
||||||
const { account: { web, user, pass } } = mockAccount;
|
const { account: { web, user, pass } } = mockAccount;
|
||||||
this.logger.info(`Log into mock email provider at ${web}`);
|
logger.info('E-mail configured with mock configuration');
|
||||||
this.logger.info(`Mock email account username: ${user}`);
|
logger.info(`Log into mock email provider at ${web}`);
|
||||||
this.logger.info(`Mock email account password: ${pass}`);
|
logger.info(`Mock email account username: ${user}`);
|
||||||
|
logger.info(`Mock email account password: ${pass}`);
|
||||||
}
|
}
|
||||||
return mockAccount;
|
return mockAccount;
|
||||||
}
|
}
|
||||||
|
|
||||||
const email = { ...this.config.email };
|
const email = { ...emailConfig };
|
||||||
|
|
||||||
if (this.config.email.transport) {
|
if (!email.fromName || !email.fromAddress) {
|
||||||
email.transport = this.config.email.transport;
|
throw new InvalidConfiguration('Email fromName and fromAddress must be configured when transport is configured');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.config.email.transportOptions) {
|
let transport: Transporter;
|
||||||
email.transport = nodemailer.createTransport(this.config.email.transportOptions);
|
// 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 {
|
try {
|
||||||
await email.transport.verify();
|
await transport.verify();
|
||||||
|
email.transport = transport;
|
||||||
} catch (err) {
|
} 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;
|
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 testAccount = await nodemailer.createTestAccount();
|
||||||
|
|
||||||
const smtpOptions = {
|
const smtpOptions = {
|
||||||
...emailConfig,
|
...emailConfig,
|
||||||
host: 'smtp.ethereal.email',
|
host: "smtp.ethereal.email",
|
||||||
port: 587,
|
port: 587,
|
||||||
secure: false,
|
secure: false,
|
||||||
fromName: emailConfig.fromName || 'Payload CMS',
|
fromName: emailConfig.fromName || "Payload CMS",
|
||||||
fromAddress: emailConfig.fromAddress || 'info@payloadcms.com',
|
fromAddress: emailConfig.fromAddress || "info@payloadcms.com",
|
||||||
auth: {
|
auth: {
|
||||||
user: testAccount.user,
|
user: testAccount.user,
|
||||||
pass: testAccount.pass,
|
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
|
* @extends Error
|
||||||
*/
|
*/
|
||||||
class ExtendableError extends Error {
|
class ExtendableError extends Error {
|
||||||
constructor(message, status, data, isPublic) {
|
constructor(message: string, status: number, data: any, isPublic: boolean) {
|
||||||
super(message);
|
super(message);
|
||||||
this.name = this.constructor.name;
|
this.name = this.constructor.name;
|
||||||
this.message = message;
|
this.message = message;
|
||||||
@@ -29,7 +29,7 @@ class APIError extends ExtendableError {
|
|||||||
* @param {object} data - response data to be returned.
|
* @param {object} data - response data to be returned.
|
||||||
* @param {boolean} isPublic - Whether the message should be visible to user or not.
|
* @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);
|
super(message, status, data, isPublic);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import httpStatus from 'http-status';
|
|||||||
import APIError from './APIError';
|
import APIError from './APIError';
|
||||||
|
|
||||||
class InvalidConfiguration extends APIError {
|
class InvalidConfiguration extends APIError {
|
||||||
constructor(message, results) {
|
constructor(message, results?) {
|
||||||
super(message, httpStatus.INTERNAL_SERVER_ERROR, 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 express from 'express';
|
||||||
import crypto from 'crypto';
|
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 Logger from './utilities/logger';
|
||||||
import bindOperations from './init/bindOperations';
|
import bindOperations from './init/bindOperations';
|
||||||
import bindRequestHandlers from './init/bindRequestHandlers';
|
import bindRequestHandlers from './init/bindRequestHandlers';
|
||||||
@@ -23,21 +37,27 @@ import performFieldOperations from './fields/performFieldOperations';
|
|||||||
import localOperations from './collections/operations/local';
|
import localOperations from './collections/operations/local';
|
||||||
import localGlobalOperations from './globals/operations/local';
|
import localGlobalOperations from './globals/operations/local';
|
||||||
import { encrypt, decrypt } from './auth/crypto';
|
import { encrypt, decrypt } from './auth/crypto';
|
||||||
|
import { TestAccount } from 'nodemailer';
|
||||||
|
import { MockEmailHandler } from './email/types';
|
||||||
|
|
||||||
require('es6-promise').polyfill();
|
require('es6-promise').polyfill();
|
||||||
require('isomorphic-fetch');
|
require('isomorphic-fetch');
|
||||||
|
|
||||||
const logger = Logger();
|
|
||||||
|
|
||||||
class Payload {
|
class Payload {
|
||||||
logger: any;
|
config: PayloadConfig;
|
||||||
|
collections: PayloadCollection[] = [];
|
||||||
|
logger: PayloadLogger;
|
||||||
|
router: Router;
|
||||||
|
email: any;
|
||||||
|
|
||||||
init(options) {
|
init(options: PayloadInitOptions) {
|
||||||
this.logger = logger;
|
this.logger = Logger();
|
||||||
this.logger.info('Starting Payload...');
|
this.logger.info('Starting Payload...');
|
||||||
|
|
||||||
if (!options.secret) {
|
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) {
|
if (!options.mongoURL) {
|
||||||
@@ -51,14 +71,18 @@ class Payload {
|
|||||||
...config,
|
...config,
|
||||||
email,
|
email,
|
||||||
license: options.license,
|
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,
|
mongoURL: options.mongoURL,
|
||||||
local: options.local,
|
local: options.local,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (typeof this.config.paths === 'undefined') this.config.paths = {};
|
if (typeof this.config.paths === 'undefined') this.config.paths = {};
|
||||||
|
|
||||||
this.collections = {};
|
// this.collections = {};
|
||||||
|
|
||||||
bindOperations(this);
|
bindOperations(this);
|
||||||
bindRequestHandlers(this);
|
bindRequestHandlers(this);
|
||||||
@@ -70,7 +94,7 @@ class Payload {
|
|||||||
this.initCollections = initCollections.bind(this);
|
this.initCollections = initCollections.bind(this);
|
||||||
this.initGlobals = initGlobals.bind(this);
|
this.initGlobals = initGlobals.bind(this);
|
||||||
this.initGraphQLPlayground = initGraphQLPlayground.bind(this);
|
this.initGraphQLPlayground = initGraphQLPlayground.bind(this);
|
||||||
this.buildEmail = buildEmail.bind(this);
|
// this.buildEmail = buildEmail.bind(this);
|
||||||
this.sendEmail = this.sendEmail.bind(this);
|
this.sendEmail = this.sendEmail.bind(this);
|
||||||
this.getMockEmailCredentials = this.getMockEmailCredentials.bind(this);
|
this.getMockEmailCredentials = this.getMockEmailCredentials.bind(this);
|
||||||
this.initStatic = initStatic.bind(this);
|
this.initStatic = initStatic.bind(this);
|
||||||
@@ -97,7 +121,7 @@ class Payload {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Configure email service
|
// Configure email service
|
||||||
this.email = this.buildEmail();
|
this.email = buildEmail(this.config.email);
|
||||||
|
|
||||||
// Initialize collections & globals
|
// Initialize collections & globals
|
||||||
this.initCollections();
|
this.initCollections();
|
||||||
@@ -114,7 +138,8 @@ class Payload {
|
|||||||
// If not initializing locally, set up HTTP routing
|
// If not initializing locally, set up HTTP routing
|
||||||
if (!this.config.local) {
|
if (!this.config.local) {
|
||||||
this.express = options.express;
|
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();
|
this.initAdmin();
|
||||||
|
|
||||||
@@ -125,7 +150,7 @@ class Payload {
|
|||||||
this.router.use(
|
this.router.use(
|
||||||
this.config.routes.graphQL,
|
this.config.routes.graphQL,
|
||||||
identifyAPI('GraphQL'),
|
identifyAPI('GraphQL'),
|
||||||
(req, res) => graphQLHandler.init(req, res)(req, res),
|
(req, res) => graphQLHandler.init(req, res)(req, res)
|
||||||
);
|
);
|
||||||
|
|
||||||
this.initGraphQLPlayground();
|
this.initGraphQLPlayground();
|
||||||
@@ -145,54 +170,54 @@ class Payload {
|
|||||||
if (typeof options.onInit === 'function') options.onInit();
|
if (typeof options.onInit === 'function') options.onInit();
|
||||||
}
|
}
|
||||||
|
|
||||||
async sendEmail(message) {
|
async sendEmail(message: string) {
|
||||||
const email = await this.email;
|
const email = await this.email;
|
||||||
const result = email.transport.sendMail(message);
|
const result = email.transport.sendMail(message);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getMockEmailCredentials() {
|
async getMockEmailCredentials(): Promise<TestAccount> {
|
||||||
const email = await this.email;
|
const email = await this.email as MockEmailHandler;
|
||||||
return email.account;
|
return email.account;
|
||||||
}
|
}
|
||||||
|
|
||||||
async create(options) {
|
async create(options: CreateOptions) {
|
||||||
let { create } = localOperations;
|
let { create } = localOperations;
|
||||||
create = create.bind(this);
|
create = create.bind(this);
|
||||||
return create(options);
|
return create(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
async find(options) {
|
async find(options: FindOptions) {
|
||||||
let { find } = localOperations;
|
let { find } = localOperations;
|
||||||
find = find.bind(this);
|
find = find.bind(this);
|
||||||
return find(options);
|
return find(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
async findGlobal(options) {
|
async findGlobal(options: FindGlobalOptions) {
|
||||||
let { findOne } = localGlobalOperations;
|
let { findOne } = localGlobalOperations;
|
||||||
findOne = findOne.bind(this);
|
findOne = findOne.bind(this);
|
||||||
return findOne(options);
|
return findOne(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateGlobal(options) {
|
async updateGlobal(options: UpdateGlobalOptions) {
|
||||||
let { update } = localGlobalOperations;
|
let { update } = localGlobalOperations;
|
||||||
update = update.bind(this);
|
update = update.bind(this);
|
||||||
return update(options);
|
return update(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
async findByID(options) {
|
async findByID(options: FindByIDOptions) {
|
||||||
let { findByID } = localOperations;
|
let { findByID } = localOperations;
|
||||||
findByID = findByID.bind(this);
|
findByID = findByID.bind(this);
|
||||||
return findByID(options);
|
return findByID(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
async update(options) {
|
async update(options: UpdateOptions) {
|
||||||
let { update } = localOperations;
|
let { update } = localOperations;
|
||||||
update = update.bind(this);
|
update = update.bind(this);
|
||||||
return update(options);
|
return update(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
async delete(options) {
|
async delete(options: DeleteOptions) {
|
||||||
let { delete: deleteOperation } = localOperations;
|
let { delete: deleteOperation } = localOperations;
|
||||||
deleteOperation = deleteOperation.bind(this);
|
deleteOperation = deleteOperation.bind(this);
|
||||||
return deleteOperation(options);
|
return deleteOperation(options);
|
||||||
|
|||||||
@@ -3,8 +3,9 @@ import * as payloadSchema from './payload.schema.json';
|
|||||||
import * as collectionSchema from './collection.schema.json';
|
import * as collectionSchema from './collection.schema.json';
|
||||||
|
|
||||||
import InvalidSchema from '../errors/InvalidSchema';
|
import InvalidSchema from '../errors/InvalidSchema';
|
||||||
|
import { PayloadConfig } from '../types';
|
||||||
|
|
||||||
const validateSchema = (config) => {
|
const validateSchema = (config: PayloadConfig) => {
|
||||||
const ajv = new Ajv({ useDefaults: true });
|
const ajv = new Ajv({ useDefaults: true });
|
||||||
const validate = ajv.addSchema(collectionSchema, '/collection.schema.json')
|
const validate = ajv.addSchema(collectionSchema, '/collection.schema.json')
|
||||||
.compile(payloadSchema);
|
.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 falsey from 'falsey';
|
||||||
import pino from 'pino';
|
import pino from 'pino';
|
||||||
import memoize from 'micro-memoize';
|
import memoize from 'micro-memoize';
|
||||||
|
import { PayloadLogger } from '../types';
|
||||||
|
|
||||||
export default memoize((name = 'payload') => pino({
|
export default memoize((name = 'payload') => pino({
|
||||||
name,
|
name,
|
||||||
@@ -9,4 +10,4 @@ export default memoize((name = 'payload') => pino({
|
|||||||
ignore: 'pid,hostname',
|
ignore: 'pid,hostname',
|
||||||
translateTime: 'HH:MM:ss',
|
translateTime: 'HH:MM:ss',
|
||||||
},
|
},
|
||||||
}));
|
}) as PayloadLogger);
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { PayloadConfig } from '../types';
|
||||||
import defaultUser from '../auth/default';
|
import defaultUser from '../auth/default';
|
||||||
import sanitizeCollection from '../collections/sanitize';
|
import sanitizeCollection from '../collections/sanitize';
|
||||||
import { InvalidConfiguration } from '../errors';
|
import { InvalidConfiguration } from '../errors';
|
||||||
@@ -5,7 +6,7 @@ import sanitizeGlobals from '../globals/sanitize';
|
|||||||
import validateSchema from '../schema/validateSchema';
|
import validateSchema from '../schema/validateSchema';
|
||||||
import checkDuplicateCollections from './checkDuplicateCollections';
|
import checkDuplicateCollections from './checkDuplicateCollections';
|
||||||
|
|
||||||
const sanitizeConfig = (config) => {
|
const sanitizeConfig = (config: PayloadConfig) => {
|
||||||
const sanitizedConfig = validateSchema({ ...config });
|
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
|
// 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 || {};
|
sanitizedConfig.email = config.email || {};
|
||||||
// TODO: This should likely be moved to the payload.schema.json
|
// if (!sanitizedConfig.email.transport) sanitizedConfig.email.transport = 'mock';
|
||||||
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');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sanitizedConfig.graphQL = config.graphQL || {};
|
sanitizedConfig.graphQL = config.graphQL || {};
|
||||||
sanitizedConfig.graphQL.maxComplexity = (sanitizedConfig.graphQL && sanitizedConfig.graphQL.maxComplexity) ? sanitizedConfig.graphQL.maxComplexity : 1000;
|
sanitizedConfig.graphQL.maxComplexity = (sanitizedConfig.graphQL && sanitizedConfig.graphQL.maxComplexity) ? sanitizedConfig.graphQL.maxComplexity : 1000;
|
||||||
|
|||||||
Reference in New Issue
Block a user