Files
payload/src/index.ts

382 lines
11 KiB
TypeScript

import express, { Express, Router } from 'express';
import pino from 'pino';
import crypto from 'crypto';
import {
TypeWithID,
Collection, CollectionModel,
} from './collections/config/types';
import {
SanitizedConfig,
EmailOptions,
InitOptions,
} from './config/types';
import { TypeWithVersion } from './versions/types';
import { PaginatedDocs } from './mongoose/types';
import Logger from './utilities/logger';
import bindOperations, { Operations } from './init/bindOperations';
import bindRequestHandlers, { RequestHandlers } from './init/bindRequestHandlers';
import loadConfig from './config/load';
import authenticate, { PayloadAuthenticate } from './express/middleware/authenticate';
import connectMongoose from './mongoose/connect';
import expressMiddleware from './express/middleware';
import initAdmin from './express/admin';
import initAuth from './auth/init';
import initCollections from './collections/init';
import initPreferences from './preferences/init';
import initGlobals from './globals/init';
import { Globals } from './globals/config/types';
import initGraphQLPlayground from './graphql/initPlayground';
import initStatic from './express/static';
import GraphQL from './graphql';
import bindResolvers, { GraphQLResolvers } from './graphql/bindResolvers';
import buildEmail from './email/build';
import identifyAPI from './express/middleware/identifyAPI';
import errorHandler, { ErrorHandler } from './express/middleware/errorHandler';
import performFieldOperations from './fields/performFieldOperations';
import localOperations from './collections/operations/local';
import localGlobalOperations from './globals/operations/local';
import { encrypt, decrypt } from './auth/crypto';
import { BuildEmailResult, Message } from './email/types';
import { PayloadRequest } from './express/types';
import sendEmail from './email/sendEmail';
import { Preferences } from './preferences/types';
import { Options as CreateOptions } from './collections/operations/local/create';
import { Options as FindOptions } from './collections/operations/local/find';
import { Options as FindByIDOptions } from './collections/operations/local/findByID';
import { Options as UpdateOptions } from './collections/operations/local/update';
import { Options as DeleteOptions } from './collections/operations/local/delete';
import { Options as FindVersionsOptions } from './collections/operations/local/findVersions';
import { Options as FindVersionByIDOptions } from './collections/operations/local/findVersionByID';
import { Options as RestoreVersionOptions } from './collections/operations/local/restoreVersion';
import { Options as FindGlobalVersionsOptions } from './globals/operations/local/findVersions';
import { Options as FindGlobalVersionByIDOptions } from './globals/operations/local/findVersionByID';
import { Options as RestoreGlobalVersionOptions } from './globals/operations/local/restoreVersion';
import { Result } from './auth/operations/login';
require('isomorphic-fetch');
/**
* @description Payload
*/
export class Payload {
config: SanitizedConfig;
collections: {
[slug: string]: Collection;
} = {}
versions: {
[slug: string]: CollectionModel;
} = {}
graphQL: {
resolvers: GraphQLResolvers
};
preferences: Preferences;
globals: Globals;
logger: pino.Logger;
express: Express
router: Router;
emailOptions: EmailOptions;
email: BuildEmailResult;
sendEmail: (message: Message) => Promise<unknown>;
license: string;
secret: string;
mongoURL: string;
local: boolean;
encrypt = encrypt;
decrypt = decrypt;
operations: Operations;
errorHandler: ErrorHandler;
authenticate: PayloadAuthenticate;
performFieldOperations: typeof performFieldOperations;
requestHandlers: RequestHandlers;
/**
* @description Initializes Payload
* @param options
*/
init(options: InitOptions): void {
this.logger = Logger('payload', options.loggerOptions);
this.logger.info('Starting Payload...');
if (!options.secret) {
throw new Error(
'Error: missing secret key. A secret key is needed to secure Payload.',
);
}
if (!options.mongoURL) {
throw new Error('Error: missing MongoDB connection URL.');
}
this.license = options.license;
this.emailOptions = { ...(options.email) };
this.secret = crypto
.createHash('sha256')
.update(options.secret)
.digest('hex')
.slice(0, 32);
this.mongoURL = options.mongoURL;
this.local = options.local;
this.config = loadConfig(this.logger);
bindOperations(this);
bindRequestHandlers(this);
bindResolvers(this);
this.performFieldOperations = performFieldOperations.bind(this);
// If not initializing locally, scaffold router
if (!this.local) {
this.router = express.Router();
this.router.use(...expressMiddleware(this));
initAuth(this);
}
// Configure email service
this.email = buildEmail(this.emailOptions, this.logger);
this.sendEmail = sendEmail.bind(this);
// Initialize collections & globals
initCollections(this);
initGlobals(this);
initPreferences(this);
// Connect to database
connectMongoose(this.mongoURL, options.mongoOptions, options.local, this.logger);
// If not initializing locally, set up HTTP routing
if (!this.local) {
options.express.use((req: PayloadRequest, res, next) => {
req.payload = this;
next();
});
this.express = options.express;
if (this.config.rateLimit.trustProxy) {
this.express.set('trust proxy', 1);
}
initAdmin(this);
this.router.get('/access', this.requestHandlers.collections.auth.access);
const graphQLHandler = new GraphQL(this);
if (!this.config.graphQL.disable) {
this.router.use(
this.config.routes.graphQL,
identifyAPI('GraphQL'),
(req, res) => graphQLHandler.init(req, res)(req, res),
);
initGraphQLPlayground(this);
}
// Bind router to API
this.express.use(this.config.routes.api, this.router);
// Enable static routes for all collections permitting upload
initStatic(this);
this.errorHandler = errorHandler(this.config, this.logger);
this.router.use(this.errorHandler);
this.authenticate = authenticate(this.config);
}
if (typeof options.onInit === 'function') options.onInit(this);
}
getAdminURL = (): string => `${this.config.serverURL}${this.config.routes.admin}`;
getAPIURL = (): string => `${this.config.serverURL}${this.config.routes.api}`;
/**
* @description Performs create operation
* @param options
* @returns created document
*/
create = async <T = any>(options: CreateOptions<T>): Promise<T> => {
let { create } = localOperations;
create = create.bind(this);
return create(options);
}
/**
* @description Find documents with criteria
* @param options
* @returns documents satisfying query
*/
find = async <T extends TypeWithID = any>(options: FindOptions): Promise<PaginatedDocs<T>> => {
let { find } = localOperations;
find = find.bind(this);
return find(options);
}
findGlobal = async <T>(options): Promise<T> => {
let { findOne } = localGlobalOperations;
findOne = findOne.bind(this);
return findOne(options);
}
updateGlobal = async <T>(options): Promise<T> => {
let { update } = localGlobalOperations;
update = update.bind(this);
return update(options);
}
/**
* @description Find global versions with criteria
* @param options
* @returns versions satisfying query
*/
findGlobalVersions = async <T extends TypeWithVersion<T> = any>(options: FindGlobalVersionsOptions): Promise<PaginatedDocs<T>> => {
let { findVersions } = localGlobalOperations;
findVersions = findVersions.bind(this);
return findVersions<T>(options);
}
/**
* @description Find global version by ID
* @param options
* @returns global version with specified ID
*/
findGlobalVersionByID = async <T extends TypeWithVersion<T> = any>(options: FindGlobalVersionByIDOptions): Promise<T> => {
let { findVersionByID } = localGlobalOperations;
findVersionByID = findVersionByID.bind(this);
return findVersionByID(options);
}
/**
* @description Restore global version by ID
* @param options
* @returns version with specified ID
*/
restoreGlobalVersion = async <T extends TypeWithVersion<T> = any>(options: RestoreGlobalVersionOptions): Promise<T> => {
let { restoreVersion } = localGlobalOperations;
restoreVersion = restoreVersion.bind(this);
return restoreVersion(options);
}
/**
* @description Find document by ID
* @param options
* @returns document with specified ID
*/
findByID = async <T extends TypeWithID = any>(options: FindByIDOptions): Promise<T> => {
let { findByID } = localOperations;
findByID = findByID.bind(this);
return findByID<T>(options);
}
/**
* @description Update document
* @param options
* @returns Updated document
*/
update = async <T = any>(options: UpdateOptions<T>): Promise<T> => {
let { update } = localOperations;
update = update.bind(this);
return update<T>(options);
}
delete = async <T extends TypeWithID = any>(options: DeleteOptions): Promise<T> => {
let { localDelete: deleteOperation } = localOperations;
deleteOperation = deleteOperation.bind(this);
return deleteOperation<T>(options);
}
/**
* @description Find versions with criteria
* @param options
* @returns versions satisfying query
*/
findVersions = async <T extends TypeWithVersion<T> = any>(options: FindVersionsOptions): Promise<PaginatedDocs<T>> => {
let { findVersions } = localOperations;
findVersions = findVersions.bind(this);
return findVersions<T>(options);
}
/**
* @description Find version by ID
* @param options
* @returns version with specified ID
*/
findVersionByID = async <T extends TypeWithVersion<T> = any>(options: FindVersionByIDOptions): Promise<T> => {
let { findVersionByID } = localOperations;
findVersionByID = findVersionByID.bind(this);
return findVersionByID(options);
}
/**
* @description Restore version by ID
* @param options
* @returns version with specified ID
*/
restoreVersion = async <T extends TypeWithVersion<T> = any>(options: RestoreVersionOptions): Promise<T> => {
let { restoreVersion } = localOperations;
restoreVersion = restoreVersion.bind(this);
return restoreVersion(options);
}
login = async <T extends TypeWithID = any>(options): Promise<Result & { user: T}> => {
let { login } = localOperations.auth;
login = login.bind(this);
return login(options);
}
forgotPassword = async (options): Promise<any> => {
let { forgotPassword } = localOperations.auth;
forgotPassword = forgotPassword.bind(this);
return forgotPassword(options);
}
resetPassword = async (options): Promise<any> => {
let { resetPassword } = localOperations.auth;
resetPassword = resetPassword.bind(this);
return resetPassword(options);
}
unlock = async (options): Promise<any> => {
let { unlock } = localOperations.auth;
unlock = unlock.bind(this);
return unlock(options);
}
verifyEmail = async (options): Promise<any> => {
let { verifyEmail } = localOperations.auth;
verifyEmail = verifyEmail.bind(this);
return verifyEmail(options);
}
}
const payload = new Payload();
export default payload;
module.exports = payload;