progress to buildConfig, splitting out type organization
This commit is contained in:
@@ -1,3 +1,3 @@
|
||||
const config = require('./src/babel.config');
|
||||
|
||||
module.exports = config;
|
||||
module.exports = (api) => config(api);
|
||||
|
||||
1
config.d.ts
vendored
Normal file
1
config.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
||||
export * from './dist/config';
|
||||
@@ -9,12 +9,4 @@ module.exports = {
|
||||
'dist',
|
||||
],
|
||||
testTimeout: 15000,
|
||||
moduleNameMapper: {
|
||||
'\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': '<rootDir>/tests/mocks/fileMock.js',
|
||||
'\\.(css|scss)$': '<rootDir>/tests/mocks/emptyModule.js',
|
||||
},
|
||||
transform: {
|
||||
'\\.(css|less|scss)$': './tests/stub-transformer.js',
|
||||
'\\.(js|jsx|ts|tsx)$': 'babel-jest',
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import unsanitizedConfig from 'payload/unsanitizedConfig';
|
||||
import sanitizeConfig from '../../../../utilities/sanitizeConfig';
|
||||
import sanitizeConfig from '../../../../config/sanitize';
|
||||
import Context from './context';
|
||||
|
||||
const sanitizedConfig = sanitizeConfig(unsanitizedConfig);
|
||||
|
||||
const ConfigProvider = ({ children }) => (
|
||||
<Context.Provider value={sanitizedConfig}>
|
||||
{children}
|
||||
</Context.Provider>
|
||||
<Context.Provider value={sanitizedConfig}>
|
||||
{children}
|
||||
</Context.Provider>
|
||||
);
|
||||
|
||||
ConfigProvider.propTypes = {
|
||||
children: PropTypes.node.isRequired,
|
||||
children: PropTypes.node.isRequired,
|
||||
};
|
||||
|
||||
export default ConfigProvider;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { MongoClient } from 'mongodb';
|
||||
import getConfig from '../utilities/getConfig';
|
||||
import getConfig from '../config/load';
|
||||
import { email, password, mongo } from '../../tests/api/credentials';
|
||||
|
||||
require('isomorphic-fetch');
|
||||
|
||||
@@ -1,27 +1,34 @@
|
||||
module.exports = {
|
||||
presets: [
|
||||
'@babel/typescript',
|
||||
[
|
||||
require.resolve('@babel/preset-env'),
|
||||
{
|
||||
targets: [
|
||||
'defaults',
|
||||
'not IE 11',
|
||||
'not IE_Mob 11',
|
||||
],
|
||||
},
|
||||
module.exports = (api) => {
|
||||
const config = {
|
||||
presets: [
|
||||
'@babel/typescript',
|
||||
[
|
||||
require.resolve('@babel/preset-env'),
|
||||
{
|
||||
targets: [
|
||||
'defaults',
|
||||
'not IE 11',
|
||||
'not IE_Mob 11',
|
||||
],
|
||||
},
|
||||
],
|
||||
require.resolve('@babel/preset-react'),
|
||||
],
|
||||
require.resolve('@babel/preset-react'),
|
||||
],
|
||||
plugins: [
|
||||
require.resolve('@babel/plugin-transform-runtime'),
|
||||
require.resolve('@babel/plugin-proposal-class-properties'),
|
||||
require.resolve('@babel/plugin-proposal-optional-chaining'),
|
||||
[
|
||||
plugins: [
|
||||
require.resolve('@babel/plugin-transform-runtime'),
|
||||
require.resolve('@babel/plugin-proposal-class-properties'),
|
||||
require.resolve('@babel/plugin-proposal-optional-chaining'),
|
||||
],
|
||||
};
|
||||
|
||||
if (api.env('test')) {
|
||||
config.plugins.push([
|
||||
'babel-plugin-ignore-html-and-css-imports',
|
||||
{
|
||||
removeExtensions: ['.svg', '.css', '.scss', '.png', '.jpg'],
|
||||
},
|
||||
],
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
return config;
|
||||
};
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
|
||||
const webpack = require('webpack');
|
||||
const getWebpackProdConfig = require('../webpack/getWebpackProdConfig');
|
||||
const findConfig = require('../utilities/findConfig');
|
||||
const getConfig = require('../utilities/getConfig');
|
||||
const sanitizeConfig = require('../utilities/sanitizeConfig');
|
||||
const findConfig = require('../config/find');
|
||||
const getConfig = require('../config/load');
|
||||
const sanitizeConfig = require('../config/sanitize');
|
||||
|
||||
const configPath = findConfig();
|
||||
|
||||
|
||||
10
src/collections/config/build.ts
Normal file
10
src/collections/config/build.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { Collection } from './types';
|
||||
import sanitize from './sanitize';
|
||||
|
||||
const buildCollection = (collection: Collection) => {
|
||||
const sanitized = sanitize(collection);
|
||||
|
||||
return sanitized;
|
||||
};
|
||||
|
||||
module.exports = buildCollection;
|
||||
@@ -1,13 +1,13 @@
|
||||
import merge from 'deepmerge';
|
||||
import sanitizeFields from '../fields/sanitize';
|
||||
import toKebabCase from '../utilities/toKebabCase';
|
||||
import baseAuthFields from '../fields/baseFields/baseFields';
|
||||
import baseAPIKeyFields from '../fields/baseFields/baseAPIKeyFields';
|
||||
import baseVerificationFields from '../fields/baseFields/baseVerificationFields';
|
||||
import baseAccountLockFields from '../fields/baseFields/baseAccountLockFields';
|
||||
import baseUploadFields from '../fields/baseFields/baseUploadFields';
|
||||
import baseImageUploadFields from '../fields/baseFields/baseImageUploadFields';
|
||||
import formatLabels from '../utilities/formatLabels';
|
||||
import sanitizeFields from '../../fields/config/sanitize';
|
||||
import toKebabCase from '../../utilities/toKebabCase';
|
||||
import baseAuthFields from '../../fields/baseFields/baseFields';
|
||||
import baseAPIKeyFields from '../../fields/baseFields/baseAPIKeyFields';
|
||||
import baseVerificationFields from '../../fields/baseFields/baseVerificationFields';
|
||||
import baseAccountLockFields from '../../fields/baseFields/baseAccountLockFields';
|
||||
import baseUploadFields from '../../fields/baseFields/baseUploadFields';
|
||||
import baseImageUploadFields from '../../fields/baseFields/baseImageUploadFields';
|
||||
import formatLabels from '../../utilities/formatLabels';
|
||||
|
||||
const mergeBaseFields = (fields, baseFields) => {
|
||||
const mergedFields = [];
|
||||
47
src/collections/config/types.ts
Normal file
47
src/collections/config/types.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
|
||||
export type Collection = {
|
||||
slug: string;
|
||||
labels?: {
|
||||
singular: string;
|
||||
plural: string;
|
||||
};
|
||||
admin?: {
|
||||
useAsTitle?: string;
|
||||
defaultColumns?: string[];
|
||||
components?: any;
|
||||
};
|
||||
hooks?: {
|
||||
beforeOperation?: Hook[];
|
||||
beforeValidate?: Hook[];
|
||||
beforeChange?: Hook[];
|
||||
afterChange?: Hook[];
|
||||
beforeRead?: Hook[];
|
||||
afterRead?: Hook[];
|
||||
beforeDelete?: Hook[];
|
||||
afterDelete?: Hook[];
|
||||
};
|
||||
access?: {
|
||||
create?: Access;
|
||||
read?: Access;
|
||||
update?: Access;
|
||||
delete?: Access;
|
||||
admin?: Access;
|
||||
};
|
||||
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: Field[];
|
||||
};
|
||||
@@ -2,7 +2,7 @@
|
||||
* @jest-environment node
|
||||
*/
|
||||
import { request, GraphQLClient } from 'graphql-request';
|
||||
import getConfig from '../../../utilities/getConfig';
|
||||
import getConfig from '../../../config/load';
|
||||
import { email, password } from '../../../../tests/api/credentials';
|
||||
|
||||
require('isomorphic-fetch');
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { v4 as uuid } from 'uuid';
|
||||
import getConfig from '../../utilities/getConfig';
|
||||
import getConfig from '../../config/load';
|
||||
import { email, password } from '../../../tests/api/credentials';
|
||||
|
||||
require('isomorphic-fetch');
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import getConfig from '../../utilities/getConfig';
|
||||
import getConfig from '../../config/load';
|
||||
import { email, password } from '../../../tests/api/credentials';
|
||||
|
||||
require('isomorphic-fetch');
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import getConfig from '../../utilities/getConfig';
|
||||
import getConfig from '../../config/load';
|
||||
import { email, password } from '../../../tests/api/credentials';
|
||||
|
||||
require('isomorphic-fetch');
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import FormData from 'form-data';
|
||||
import getConfig from '../../utilities/getConfig';
|
||||
import getConfig from '../../config/load';
|
||||
import fileExists from '../../../tests/api/utils/fileExists';
|
||||
import { email, password } from '../../../tests/api/credentials';
|
||||
|
||||
|
||||
12
src/config/build.ts
Normal file
12
src/config/build.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { PayloadConfig } from '../types';
|
||||
import sanitize from './sanitize';
|
||||
import validate from './validate';
|
||||
|
||||
const buildConfig = (config: PayloadConfig) => {
|
||||
const validated = validate(config);
|
||||
const sanitized = sanitize(validated);
|
||||
|
||||
return sanitized;
|
||||
};
|
||||
|
||||
module.exports = buildConfig;
|
||||
@@ -1,10 +1,11 @@
|
||||
/* eslint-disable import/no-dynamic-require */
|
||||
/* eslint-disable global-require */
|
||||
import path from 'path';
|
||||
import findConfig from './findConfig';
|
||||
import { Config } from './types';
|
||||
import findConfig from './find';
|
||||
|
||||
const configPath = findConfig();
|
||||
const getConfig = () => {
|
||||
const getConfig = (): Config => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const publicConfig = require(configPath);
|
||||
return {
|
||||
@@ -1,13 +1,12 @@
|
||||
import { PayloadConfig } from '../types';
|
||||
import { Config } from './types';
|
||||
import defaultUser from '../auth/default';
|
||||
import sanitizeCollection from '../collections/sanitize';
|
||||
import sanitizeCollection from '../collections/config/sanitize';
|
||||
import { InvalidConfiguration } from '../errors';
|
||||
import sanitizeGlobals from '../globals/sanitize';
|
||||
import validateSchema from '../schema/validateSchema';
|
||||
import checkDuplicateCollections from './checkDuplicateCollections';
|
||||
import sanitizeGlobals from '../globals/config/sanitize';
|
||||
import checkDuplicateCollections from '../utilities/checkDuplicateCollections';
|
||||
|
||||
const sanitizeConfig = (config: PayloadConfig) => {
|
||||
const sanitizedConfig = validateSchema({ ...config });
|
||||
const sanitizeConfig = (config: Config): Config => {
|
||||
const sanitizedConfig = { ...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
|
||||
if (sanitizedConfig.publicENV === undefined) sanitizedConfig.publicENV = {};
|
||||
107
src/config/types.ts
Normal file
107
src/config/types.ts
Normal file
@@ -0,0 +1,107 @@
|
||||
import { Express } from 'express';
|
||||
import { CSSProperties } from 'react';
|
||||
import { Transporter } from 'nodemailer';
|
||||
import SMTPConnection from 'nodemailer/lib/smtp-connection';
|
||||
import { Collection } from '../collections/config/types';
|
||||
|
||||
type MockEmailTransport = {
|
||||
transport?: 'mock';
|
||||
fromName?: string;
|
||||
fromAddress?: string;
|
||||
};
|
||||
type ValidEmailTransport = {
|
||||
transport: Transporter;
|
||||
transportOptions?: SMTPConnection.Options;
|
||||
fromName: string;
|
||||
fromAddress: string;
|
||||
};
|
||||
|
||||
export type EmailOptions = ValidEmailTransport | MockEmailTransport;
|
||||
|
||||
export type InitOptions = {
|
||||
express?: Express;
|
||||
mongoURL: string;
|
||||
secret: string;
|
||||
license?: string;
|
||||
email?: EmailOptions;
|
||||
local?: boolean; // I have no idea what this is
|
||||
onInit?: () => void;
|
||||
};
|
||||
|
||||
export type SendEmailOptions = {
|
||||
from: string;
|
||||
to: string;
|
||||
subject: string;
|
||||
html: string;
|
||||
};
|
||||
|
||||
export type MockEmailCredentials = {
|
||||
user: string;
|
||||
pass: string;
|
||||
web: string;
|
||||
};
|
||||
|
||||
export type Hook = (...args: any[]) => any | void;
|
||||
export type Access = (args?: any) => boolean;
|
||||
|
||||
export type Config = {
|
||||
admin?: {
|
||||
user?: string
|
||||
meta?: {
|
||||
titleSuffix?: string
|
||||
ogImage?: string
|
||||
favicon?: string
|
||||
}
|
||||
disable?: boolean
|
||||
};
|
||||
collections?: Collection[];
|
||||
globals?: Global[];
|
||||
serverURL?: string;
|
||||
cookiePrefix?: string;
|
||||
csrf?: string[];
|
||||
cors?: string[];
|
||||
publicENV: { [key: string]: string };
|
||||
routes?: {
|
||||
api?: string;
|
||||
admin?: string;
|
||||
graphQL?: string;
|
||||
graphQLPlayground?: string;
|
||||
};
|
||||
email?: EmailOptions;
|
||||
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?: {
|
||||
[key: string]: unknown
|
||||
},
|
||||
queries?: {
|
||||
[key: string]: unknown
|
||||
},
|
||||
maxComplexity?: number;
|
||||
disablePlaygroundInProduction?: boolean;
|
||||
};
|
||||
components: { [key: string]: JSX.Element | (() => JSX.Element) };
|
||||
paths?: { [key: string]: string };
|
||||
hooks?: {
|
||||
afterError?: () => void;
|
||||
};
|
||||
webpack?: (config: any) => any;
|
||||
serverModules?: string[];
|
||||
};
|
||||
@@ -1,15 +1,15 @@
|
||||
import Ajv from 'ajv';
|
||||
import * as payloadSchema from './payload.schema.json';
|
||||
import * as collectionSchema from './collection.schema.json';
|
||||
import * as configSchema from './schema.json';
|
||||
import * as collectionSchema from '../collections/config/schema.json';
|
||||
|
||||
import InvalidSchema from '../errors/InvalidSchema';
|
||||
import { PayloadConfig } from '../types';
|
||||
import { PayloadConfig } from './types';
|
||||
|
||||
const validateSchema = (config: PayloadConfig) => {
|
||||
const validateSchema = (config: PayloadConfig): PayloadConfig => {
|
||||
const ajv = new Ajv({ useDefaults: true });
|
||||
const validate = ajv.addSchema(collectionSchema, '/collection.schema.json')
|
||||
.compile(payloadSchema);
|
||||
const validate = ajv.addSchema(collectionSchema, '../collections/config/schema.json').compile(configSchema);
|
||||
const valid = validate(config);
|
||||
|
||||
if (!valid) {
|
||||
throw new InvalidSchema(`Invalid payload config provided. Found ${validate.errors.length} errors`, validate.errors);
|
||||
}
|
||||
@@ -1,12 +1,11 @@
|
||||
const babelConfig = require('./babel.config');
|
||||
const babelConfig = require('./babel.config')({
|
||||
env: () => false,
|
||||
});
|
||||
|
||||
if (process.env.NODE_ENV !== 'test') {
|
||||
// eslint-disable-next-line global-require
|
||||
require('@babel/register')({
|
||||
...babelConfig,
|
||||
ignore: [
|
||||
/node_modules[\\/](?!@payloadcms[\\/]payload[\\/]src[\\/]admin|@payloadcms[\\/]payload[\\/]components|@payloadcms[\\/]payload[\\/]hooks).*/,
|
||||
],
|
||||
extensions: ['.js', '.jsx', '.ts', '.tsx'],
|
||||
plugins: [
|
||||
[
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import nodemailer, { Transporter } from 'nodemailer';
|
||||
import { PayloadEmailOptions } from '../types';
|
||||
import { EmailOptions } from '../config/types';
|
||||
import { InvalidConfiguration } from '../errors';
|
||||
import mockHandler from './mockHandler';
|
||||
import Logger from '../utilities/logger';
|
||||
import { BuildEmailResult } from './types';
|
||||
|
||||
const logger = Logger();
|
||||
|
||||
export default async function buildEmail(emailConfig: PayloadEmailOptions) {
|
||||
export default async function buildEmail(emailConfig: EmailOptions): BuildEmailResult {
|
||||
if (!emailConfig.transport || emailConfig.transport === 'mock') {
|
||||
const mockAccount = await mockHandler(emailConfig);
|
||||
// Only log mock credentials if was explicitly set in config
|
||||
@@ -38,8 +40,8 @@ export default async function buildEmail(emailConfig: PayloadEmailOptions) {
|
||||
email.transport = transport;
|
||||
} catch (err) {
|
||||
logger.error(
|
||||
"There is an error with the email configuration you have provided.",
|
||||
err
|
||||
'There is an error with the email configuration you have provided.',
|
||||
err,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,12 @@
|
||||
import { TestAccount, Transporter } from 'nodemailer';
|
||||
import Mail from 'nodemailer/lib/mailer';
|
||||
import SMTPConnection from 'nodemailer/lib/smtp-connection';
|
||||
|
||||
export type BuildEmailResult = Promise<{
|
||||
transport: Mail,
|
||||
transportOptions?: SMTPConnection.Options,
|
||||
fromName: string,
|
||||
fromAddress: string,
|
||||
}>
|
||||
|
||||
export type MockEmailHandler = { account: TestAccount; transport: Transporter };
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import sanitizeFields from './sanitize';
|
||||
import { MissingFieldType, InvalidFieldRelationship } from '../errors';
|
||||
import { MissingFieldType, InvalidFieldRelationship } from '../../errors';
|
||||
|
||||
describe('sanitizeFields', () => {
|
||||
it('should throw on missing type field', () => {
|
||||
@@ -1,5 +1,5 @@
|
||||
import { MissingFieldType, InvalidFieldRelationship } from '../errors';
|
||||
import validations from './validations';
|
||||
import { MissingFieldType, InvalidFieldRelationship } from '../../errors';
|
||||
import validations from '../validations';
|
||||
|
||||
const sanitizeFields = (fields, validRelationships) => {
|
||||
if (!fields) return [];
|
||||
30
src/fields/config/types.ts
Normal file
30
src/fields/config/types.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { CSSProperties } from 'react';
|
||||
|
||||
export type Field = {
|
||||
name: string;
|
||||
label: string;
|
||||
type:
|
||||
| 'number'
|
||||
| 'text'
|
||||
| 'email'
|
||||
| 'textarea'
|
||||
| 'richText'
|
||||
| 'code'
|
||||
| 'radio'
|
||||
| 'checkbox'
|
||||
| 'date'
|
||||
| 'upload'
|
||||
| 'relationship'
|
||||
| 'row'
|
||||
| 'array'
|
||||
| 'group'
|
||||
| 'select'
|
||||
| 'blocks';
|
||||
localized?: boolean;
|
||||
fields?: Field[];
|
||||
admin?: {
|
||||
position?: string;
|
||||
width?: string;
|
||||
style?: CSSProperties;
|
||||
};
|
||||
};
|
||||
0
src/globals/config/build.ts
Normal file
0
src/globals/config/build.ts
Normal file
@@ -1,5 +1,5 @@
|
||||
import { MissingGlobalLabel } from '../errors';
|
||||
import sanitizeFields from '../fields/sanitize';
|
||||
import { MissingGlobalLabel } from '../../errors';
|
||||
import sanitizeFields from '../../fields/config/sanitize';
|
||||
|
||||
const sanitizeGlobals = (collections, globals) => {
|
||||
// /////////////////////////////////
|
||||
0
src/globals/config/types.ts
Normal file
0
src/globals/config/types.ts
Normal file
@@ -2,7 +2,7 @@
|
||||
* @jest-environment node
|
||||
*/
|
||||
|
||||
import getConfig from '../../utilities/getConfig';
|
||||
import getConfig from '../../config/load';
|
||||
import { email, password } from '../../../tests/api/credentials';
|
||||
|
||||
require('isomorphic-fetch');
|
||||
|
||||
105
src/index.ts
105
src/index.ts
@@ -1,11 +1,15 @@
|
||||
import express from 'express';
|
||||
import express, { Express, Router } from 'express';
|
||||
import crypto from 'crypto';
|
||||
import { Router } from 'express';
|
||||
|
||||
import { TestAccount } from 'nodemailer';
|
||||
import {
|
||||
Config,
|
||||
InitOptions,
|
||||
} from './config/types';
|
||||
import {
|
||||
Collection,
|
||||
} from './collections/config/types';
|
||||
import {
|
||||
PayloadConfig,
|
||||
PayloadCollection,
|
||||
PayloadInitOptions,
|
||||
PayloadLogger,
|
||||
CreateOptions,
|
||||
FindOptions,
|
||||
FindGlobalOptions,
|
||||
@@ -18,7 +22,7 @@ import Logger from './utilities/logger';
|
||||
import bindOperations from './init/bindOperations';
|
||||
import bindRequestHandlers from './init/bindRequestHandlers';
|
||||
import bindResolvers from './init/bindResolvers';
|
||||
import getConfig from './utilities/getConfig';
|
||||
import loadConfig from './config/load';
|
||||
import authenticate from './express/middleware/authenticate';
|
||||
import connectMongoose from './mongoose/connect';
|
||||
import expressMiddleware from './express/middleware';
|
||||
@@ -29,7 +33,6 @@ import initGlobals from './globals/init';
|
||||
import initGraphQLPlayground from './graphql/initPlayground';
|
||||
import initStatic from './express/static';
|
||||
import GraphQL from './graphql';
|
||||
import sanitizeConfig from './utilities/sanitizeConfig';
|
||||
import buildEmail from './email/build';
|
||||
import identifyAPI from './express/middleware/identifyAPI';
|
||||
import errorHandler from './express/middleware/errorHandler';
|
||||
@@ -37,26 +40,58 @@ 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';
|
||||
import { MockEmailHandler, BuildEmailResult } from './email/types';
|
||||
|
||||
require('es6-promise').polyfill();
|
||||
require('isomorphic-fetch');
|
||||
|
||||
class Payload {
|
||||
config: PayloadConfig;
|
||||
collections: PayloadCollection[] = [];
|
||||
logger: PayloadLogger;
|
||||
router: Router;
|
||||
email: any;
|
||||
config: Config;
|
||||
|
||||
init(options: PayloadInitOptions) {
|
||||
collections: Collection[] = [];
|
||||
|
||||
logger: typeof Logger;
|
||||
|
||||
express: Express
|
||||
|
||||
router: Router;
|
||||
|
||||
emailOptions: any;
|
||||
|
||||
email: BuildEmailResult;
|
||||
|
||||
license: string;
|
||||
|
||||
secret: string;
|
||||
|
||||
mongoURL: string;
|
||||
|
||||
local: boolean;
|
||||
|
||||
initAuth: typeof initAuth;
|
||||
|
||||
encrypt: typeof encrypt;
|
||||
|
||||
decrypt: typeof decrypt;
|
||||
|
||||
initCollections: typeof initCollections;
|
||||
|
||||
initGlobals: typeof initGlobals;
|
||||
|
||||
initGraphQLPlayground: typeof initGraphQLPlayground;
|
||||
|
||||
initStatic: typeof initStatic;
|
||||
|
||||
initAdmin: typeof initAdmin;
|
||||
|
||||
performFieldOperations: typeof performFieldOperations;
|
||||
|
||||
init(options: InitOptions) {
|
||||
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.'
|
||||
'Error: missing secret key. A secret key is needed to secure Payload.',
|
||||
);
|
||||
}
|
||||
|
||||
@@ -64,21 +99,18 @@ class Payload {
|
||||
throw new Error('Error: missing MongoDB connection URL.');
|
||||
}
|
||||
|
||||
const config = getConfig(options);
|
||||
const email = { ...(config.email || {}), ...(options.email || {}) };
|
||||
this.license = options.license;
|
||||
this.emailOptions = { ...(options.email || {}) };
|
||||
this.secret = crypto
|
||||
.createHash('sha256')
|
||||
.update(options.secret)
|
||||
.digest('hex')
|
||||
.slice(0, 32);
|
||||
|
||||
this.config = sanitizeConfig({
|
||||
...config,
|
||||
email,
|
||||
license: options.license,
|
||||
secret: crypto
|
||||
.createHash('sha256')
|
||||
.update(options.secret)
|
||||
.digest('hex')
|
||||
.slice(0, 32),
|
||||
mongoURL: options.mongoURL,
|
||||
local: options.local,
|
||||
});
|
||||
this.mongoURL = options.mongoURL;
|
||||
this.local = options.local;
|
||||
|
||||
this.config = loadConfig();
|
||||
|
||||
if (typeof this.config.paths === 'undefined') this.config.paths = {};
|
||||
|
||||
@@ -121,14 +153,14 @@ class Payload {
|
||||
}
|
||||
|
||||
// Configure email service
|
||||
this.email = buildEmail(this.config.email);
|
||||
this.email = buildEmail(this.emailOptions);
|
||||
|
||||
// Initialize collections & globals
|
||||
this.initCollections();
|
||||
this.initGlobals();
|
||||
|
||||
// Connect to database
|
||||
connectMongoose(this.config.mongoURL);
|
||||
connectMongoose(this.mongoURL);
|
||||
|
||||
options.express.use((req, res, next) => {
|
||||
req.payload = this;
|
||||
@@ -138,8 +170,7 @@ 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();
|
||||
|
||||
@@ -150,7 +181,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();
|
||||
|
||||
@@ -1,32 +1,3 @@
|
||||
// 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;
|
||||
@@ -84,168 +55,3 @@ export type ForgotPasswordOptions = {
|
||||
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;
|
||||
|
||||
@@ -2,7 +2,6 @@ import path from 'path';
|
||||
import MiniCSSExtractPlugin from 'mini-css-extract-plugin';
|
||||
import TerserJSPlugin from 'terser-webpack-plugin';
|
||||
import OptimizeCSSAssetsPlugin from 'optimize-css-assets-webpack-plugin';
|
||||
import babelConfig from '../babel.config';
|
||||
|
||||
export default {
|
||||
entry: {
|
||||
@@ -31,7 +30,6 @@ export default {
|
||||
exclude: /node_modules/,
|
||||
use: {
|
||||
loader: 'babel-loader',
|
||||
options: babelConfig,
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
export default (cssOptions, preProcessor) => {
|
||||
const loaders = [
|
||||
'isomorphic-style-loader',
|
||||
{
|
||||
loader: require.resolve('css-loader'),
|
||||
options: cssOptions,
|
||||
},
|
||||
{
|
||||
// Options for PostCSS as we reference these options twice
|
||||
// Adds vendor prefixing based on your specified browser support in
|
||||
// package.json
|
||||
loader: require.resolve('postcss-loader'),
|
||||
options: {
|
||||
// Necessary for external CSS imports to work
|
||||
// https://github.com/facebook/create-react-app/issues/2677
|
||||
ident: 'postcss',
|
||||
plugins: () => [
|
||||
require('postcss-flexbugs-fixes'),
|
||||
require('postcss-preset-env'),
|
||||
],
|
||||
},
|
||||
},
|
||||
];
|
||||
if (preProcessor) {
|
||||
loaders.push(require.resolve(preProcessor));
|
||||
}
|
||||
return loaders;
|
||||
};
|
||||
@@ -2,7 +2,6 @@ import HtmlWebpackPlugin from 'html-webpack-plugin';
|
||||
import path from 'path';
|
||||
import webpack from 'webpack';
|
||||
import MiniCSSExtractPlugin from 'mini-css-extract-plugin';
|
||||
import babelConfig from '../babel.config';
|
||||
|
||||
const mockModulePath = path.resolve(__dirname, '../mocks/emptyModule.js');
|
||||
|
||||
@@ -10,19 +9,19 @@ export default (config) => {
|
||||
let webpackConfig = {
|
||||
entry: {
|
||||
main: [
|
||||
"webpack-hot-middleware/client",
|
||||
path.resolve(__dirname, "../admin/index.tsx"),
|
||||
'webpack-hot-middleware/client',
|
||||
path.resolve(__dirname, '../admin/index.tsx'),
|
||||
],
|
||||
},
|
||||
output: {
|
||||
path: "/",
|
||||
path: '/',
|
||||
publicPath: config.routes.admin,
|
||||
filename: "[name].js",
|
||||
filename: '[name].js',
|
||||
},
|
||||
devtool: "inline-cheap-source-map",
|
||||
mode: "development",
|
||||
devtool: 'inline-cheap-source-map',
|
||||
mode: 'development',
|
||||
resolveLoader: {
|
||||
modules: ["node_modules", path.join(__dirname, "../../node_modules")],
|
||||
modules: ['node_modules', path.join(__dirname, '../../node_modules')],
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
@@ -31,8 +30,7 @@ export default (config) => {
|
||||
exclude: /node_modules[\\/](?!(@payloadcms[\\/]payload)[\\/]).*/,
|
||||
use: [
|
||||
{
|
||||
loader: "babel-loader",
|
||||
options: babelConfig,
|
||||
loader: 'babel-loader',
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -40,33 +38,33 @@ export default (config) => {
|
||||
oneOf: [
|
||||
{
|
||||
test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
|
||||
loader: require.resolve("url-loader"),
|
||||
loader: require.resolve('url-loader'),
|
||||
options: {
|
||||
limit: 10000,
|
||||
name: "static/media/[name].[hash:8].[ext]",
|
||||
name: 'static/media/[name].[hash:8].[ext]',
|
||||
},
|
||||
},
|
||||
{
|
||||
test: /\.(sa|sc|c)ss$/,
|
||||
use: [
|
||||
MiniCSSExtractPlugin.loader,
|
||||
"css-loader",
|
||||
'css-loader',
|
||||
{
|
||||
loader: "postcss-loader",
|
||||
loader: 'postcss-loader',
|
||||
options: {
|
||||
postcssOptions: {
|
||||
plugins: ["postcss-preset-env"],
|
||||
plugins: ['postcss-preset-env'],
|
||||
},
|
||||
},
|
||||
},
|
||||
"sass-loader",
|
||||
'sass-loader',
|
||||
],
|
||||
},
|
||||
{
|
||||
exclude: [/\.((t|j)s|(t|j)sx|mjs)$/, /\.html$/, /\.json$/],
|
||||
loader: require.resolve("file-loader"),
|
||||
loader: require.resolve('file-loader'),
|
||||
options: {
|
||||
name: "static/media/[name].[hash:8].[ext]",
|
||||
name: 'static/media/[name].[hash:8].[ext]',
|
||||
},
|
||||
},
|
||||
],
|
||||
@@ -74,12 +72,12 @@ export default (config) => {
|
||||
],
|
||||
},
|
||||
resolve: {
|
||||
modules: ["node_modules", path.resolve(__dirname, "../../node_modules")],
|
||||
modules: ['node_modules', path.resolve(__dirname, '../../node_modules')],
|
||||
alias: {
|
||||
"payload/unsanitizedConfig": config.paths.config,
|
||||
"@payloadcms/payload$": mockModulePath,
|
||||
'payload/unsanitizedConfig': config.paths.config,
|
||||
'@payloadcms/payload$': mockModulePath,
|
||||
},
|
||||
extensions: [".ts", ".tsx", ".js", ".json"],
|
||||
extensions: ['.ts', '.tsx', '.js', '.json'],
|
||||
},
|
||||
plugins: [
|
||||
new webpack.DefinePlugin(
|
||||
@@ -88,15 +86,15 @@ export default (config) => {
|
||||
...values,
|
||||
[`process.env.${key}`]: `'${val}'`,
|
||||
}),
|
||||
{}
|
||||
)
|
||||
{},
|
||||
),
|
||||
),
|
||||
new HtmlWebpackPlugin({
|
||||
template:
|
||||
config.admin && config.admin.indexHTML
|
||||
? path.join(config.paths.configDir, config.admin.indexHTML)
|
||||
: path.resolve(__dirname, "../admin/index.html"),
|
||||
filename: "./index.html",
|
||||
: path.resolve(__dirname, '../admin/index.html'),
|
||||
filename: './index.html',
|
||||
}),
|
||||
new webpack.HotModuleReplacementPlugin(),
|
||||
new MiniCSSExtractPlugin({
|
||||
|
||||
@@ -5,7 +5,6 @@ import OptimizeCSSAssetsPlugin from 'optimize-css-assets-webpack-plugin';
|
||||
import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer';
|
||||
import path from 'path';
|
||||
import webpack from 'webpack';
|
||||
import babelConfig from '../babel.config';
|
||||
|
||||
const mockModulePath = path.resolve(__dirname, '../mocks/emptyModule.js');
|
||||
|
||||
@@ -44,7 +43,6 @@ export default (config) => {
|
||||
exclude: /node_modules[\\/](?!(@payloadcms[\\/]payload)[\\/]).*/,
|
||||
use: {
|
||||
loader: 'babel-loader',
|
||||
options: babelConfig,
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user