chore: initial next poc
This commit is contained in:
2
.vscode/launch.json
vendored
2
.vscode/launch.json
vendored
@@ -27,7 +27,7 @@
|
|||||||
"type": "node-terminal"
|
"type": "node-terminal"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"command": "pnpm run dev:postgres fields",
|
"command": "pnpm run dev:postgres versions",
|
||||||
"cwd": "${workspaceFolder}",
|
"cwd": "${workspaceFolder}",
|
||||||
"name": "Run Dev Postgres",
|
"name": "Run Dev Postgres",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
|
|||||||
@@ -11,6 +11,13 @@
|
|||||||
"url": "https://payloadcms.com"
|
"url": "https://payloadcms.com"
|
||||||
},
|
},
|
||||||
"main": "./dist/index.js",
|
"main": "./dist/index.js",
|
||||||
|
"exports": {
|
||||||
|
".": {
|
||||||
|
"import": "./src/index.ts",
|
||||||
|
"require": "./src/index.ts",
|
||||||
|
"types": "./src/index.ts"
|
||||||
|
}
|
||||||
|
},
|
||||||
"types": "./dist/index.d.ts",
|
"types": "./dist/index.d.ts",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "pnpm build:swc && pnpm build:types",
|
"build": "pnpm build:swc && pnpm build:types",
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/* eslint-disable no-restricted-syntax, no-await-in-loop */
|
/* eslint-disable no-restricted-syntax, no-await-in-loop */
|
||||||
import type { CreateMigration } from 'payload/database'
|
// import type { CreateMigration } from 'payload/database'
|
||||||
|
|
||||||
import fs from 'fs'
|
import fs from 'fs'
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
@@ -18,11 +18,7 @@ ${downSQL ?? ` // Migration code`}
|
|||||||
};
|
};
|
||||||
`
|
`
|
||||||
|
|
||||||
export const createMigration: CreateMigration = async function createMigration({
|
export const createMigration = async function createMigration({ file, migrationName, payload }) {
|
||||||
file,
|
|
||||||
migrationName,
|
|
||||||
payload,
|
|
||||||
}) {
|
|
||||||
const dir = payload.db.migrationDir
|
const dir = payload.db.migrationDir
|
||||||
if (!fs.existsSync(dir)) {
|
if (!fs.existsSync(dir)) {
|
||||||
fs.mkdirSync(dir)
|
fs.mkdirSync(dir)
|
||||||
@@ -38,8 +34,8 @@ export const createMigration: CreateMigration = async function createMigration({
|
|||||||
|
|
||||||
// Check if predefined migration exists
|
// Check if predefined migration exists
|
||||||
if (fs.existsSync(cleanPath)) {
|
if (fs.existsSync(cleanPath)) {
|
||||||
const { down, up } = require(cleanPath)
|
// const { down, up } = require(cleanPath)
|
||||||
migrationFileContent = migrationTemplate(up, down)
|
// migrationFileContent = migrationTemplate(up, down)
|
||||||
} else {
|
} else {
|
||||||
payload.logger.error({
|
payload.logger.error({
|
||||||
msg: `Canned migration ${predefinedMigrationName} not found.`,
|
msg: `Canned migration ${predefinedMigrationName} not found.`,
|
||||||
|
|||||||
5
packages/dev/next-env.d.ts
vendored
Normal file
5
packages/dev/next-env.d.ts
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
/// <reference types="next" />
|
||||||
|
/// <reference types="next/image-types/global" />
|
||||||
|
|
||||||
|
// NOTE: This file should not be edited
|
||||||
|
// see https://nextjs.org/docs/basic-features/typescript for more information.
|
||||||
15
packages/dev/next.config.js
Normal file
15
packages/dev/next.config.js
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
/** @type {import('next').NextConfig} */
|
||||||
|
const nextConfig = {
|
||||||
|
// experimental: {
|
||||||
|
// serverComponentsExternalPackages: ['@payloadcms/db-mongodb'],
|
||||||
|
// },
|
||||||
|
// transpilePackages: ['@payloadcms/db-mongodb'],
|
||||||
|
webpack: (config) => {
|
||||||
|
return {
|
||||||
|
...config,
|
||||||
|
externals: [...config.externals, 'mongoose'],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = nextConfig
|
||||||
25
packages/dev/package.json
Normal file
25
packages/dev/package.json
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"name": "dev",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"dev": "next dev",
|
||||||
|
"build": "next build",
|
||||||
|
"start": "next start",
|
||||||
|
"lint": "next lint"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"next": "14.0.2-canary.18",
|
||||||
|
"react": "^18",
|
||||||
|
"react-dom": "^18"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@payloadcms/db-mongodb": "workspace:*",
|
||||||
|
"@payloadcms/richtext-lexical": "workspace:*",
|
||||||
|
"@types/node": "^20",
|
||||||
|
"@types/react": "^18",
|
||||||
|
"@types/react-dom": "^18",
|
||||||
|
"payload": "workspace:*",
|
||||||
|
"typescript": "^5"
|
||||||
|
}
|
||||||
|
}
|
||||||
4
packages/dev/src/app/(payload)/admin/page.tsx
Normal file
4
packages/dev/src/app/(payload)/admin/page.tsx
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
import { dashboard } from 'payload/pages'
|
||||||
|
import config from '../../../payload.config'
|
||||||
|
|
||||||
|
export default dashboard({ config })
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
import { initHandler } from 'payload/handlers'
|
||||||
|
import config from 'payload-config'
|
||||||
|
|
||||||
|
export const GET = initHandler({ config })
|
||||||
12
packages/dev/src/app/(payload)/layout.tsx
Normal file
12
packages/dev/src/app/(payload)/layout.tsx
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
export const metadata = {
|
||||||
|
title: 'Next.js',
|
||||||
|
description: 'Generated by Next.js',
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
||||||
|
return (
|
||||||
|
<html lang="en">
|
||||||
|
<body>{children}</body>
|
||||||
|
</html>
|
||||||
|
)
|
||||||
|
}
|
||||||
5
packages/dev/src/app/my-route/route.ts
Normal file
5
packages/dev/src/app/my-route/route.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
export const GET = (request) => {
|
||||||
|
return Response.json({
|
||||||
|
hello: 'elliot',
|
||||||
|
})
|
||||||
|
}
|
||||||
11
packages/dev/src/payload.config.ts
Normal file
11
packages/dev/src/payload.config.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import { mongooseAdapter } from '@payloadcms/db-mongodb'
|
||||||
|
// import { lexicalEditor } from '@payloadcms/richtext-lexical'
|
||||||
|
import { buildConfig } from 'payload/config'
|
||||||
|
|
||||||
|
export default buildConfig({
|
||||||
|
db: mongooseAdapter({
|
||||||
|
url: process.env.DATABASE_URI,
|
||||||
|
}),
|
||||||
|
// editor: lexicalEditor({}),
|
||||||
|
secret: process.env.PAYLOAD_SECRET,
|
||||||
|
})
|
||||||
27
packages/dev/tsconfig.json
Normal file
27
packages/dev/tsconfig.json
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"lib": ["dom", "dom.iterable", "esnext"],
|
||||||
|
"allowJs": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"strict": false,
|
||||||
|
"noEmit": true,
|
||||||
|
"incremental": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"module": "esnext",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"jsx": "preserve",
|
||||||
|
"plugins": [
|
||||||
|
{
|
||||||
|
"name": "next"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"paths": {
|
||||||
|
"payload-config": ["./src/payload.config.ts"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"include": ["next-env.d.ts", ".next/types/**/*.ts", "**/*.ts", "**/*.tsx"],
|
||||||
|
"exclude": ["node_modules"],
|
||||||
|
"references": [{ "path": "../payload" }]
|
||||||
|
}
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "https://json.schemastore.org/nodemon.json",
|
|
||||||
"exec": "ts-node ../../test/dev.ts -- -I",
|
|
||||||
"ext": "ts,js,json",
|
|
||||||
"ignore": [
|
|
||||||
".git",
|
|
||||||
"node_modules",
|
|
||||||
"node_modules/**/node_modules",
|
|
||||||
"src/admin",
|
|
||||||
"src/**/*.spec.ts",
|
|
||||||
"test/**/payload-types.ts"
|
|
||||||
],
|
|
||||||
"watch": ["src/**/*.ts", "../../test/", "../../packages/**/*.ts"],
|
|
||||||
"stdin": false
|
|
||||||
}
|
|
||||||
@@ -54,59 +54,38 @@
|
|||||||
"@faceless-ui/scroll-info": "1.3.0",
|
"@faceless-ui/scroll-info": "1.3.0",
|
||||||
"@faceless-ui/window-info": "2.1.1",
|
"@faceless-ui/window-info": "2.1.1",
|
||||||
"@monaco-editor/react": "4.5.1",
|
"@monaco-editor/react": "4.5.1",
|
||||||
"@swc/core": "1.3.76",
|
|
||||||
"@swc/register": "0.1.10",
|
|
||||||
"body-parser": "1.20.2",
|
|
||||||
"body-scroll-lock": "4.0.0-beta.0",
|
"body-scroll-lock": "4.0.0-beta.0",
|
||||||
"bson-objectid": "2.0.4",
|
"bson-objectid": "2.0.4",
|
||||||
"compression": "1.7.4",
|
|
||||||
"conf": "10.2.0",
|
"conf": "10.2.0",
|
||||||
"connect-history-api-fallback": "1.6.0",
|
|
||||||
"console-table-printer": "2.11.2",
|
"console-table-printer": "2.11.2",
|
||||||
"dataloader": "2.2.2",
|
"dataloader": "2.2.2",
|
||||||
"date-fns": "2.30.0",
|
"date-fns": "2.30.0",
|
||||||
"deep-equal": "2.2.2",
|
"deep-equal": "2.2.2",
|
||||||
"deepmerge": "4.3.1",
|
"deepmerge": "4.3.1",
|
||||||
"dotenv": "8.6.0",
|
|
||||||
"express": "4.18.2",
|
|
||||||
"express-fileupload": "1.4.0",
|
|
||||||
"express-rate-limit": "5.5.1",
|
|
||||||
"file-type": "16.5.4",
|
"file-type": "16.5.4",
|
||||||
"find-up": "4.1.0",
|
"find-up": "4.1.0",
|
||||||
"flatley": "5.2.0",
|
"flatley": "5.2.0",
|
||||||
"fs-extra": "10.1.0",
|
|
||||||
"get-tsconfig": "4.6.2",
|
|
||||||
"graphql": "16.8.1",
|
"graphql": "16.8.1",
|
||||||
"graphql-http": "1.21.0",
|
"graphql-http": "1.21.0",
|
||||||
"graphql-playground-middleware-express": "1.7.23",
|
|
||||||
"graphql-query-complexity": "0.12.0",
|
"graphql-query-complexity": "0.12.0",
|
||||||
"graphql-scalars": "1.22.2",
|
"graphql-scalars": "1.22.2",
|
||||||
"graphql-type-json": "0.3.2",
|
"graphql-type-json": "0.3.2",
|
||||||
"html-webpack-plugin": "5.5.3",
|
|
||||||
"http-status": "1.6.2",
|
"http-status": "1.6.2",
|
||||||
"i18next": "22.5.1",
|
"i18next": "22.5.1",
|
||||||
"i18next-browser-languagedetector": "6.1.8",
|
"i18next-browser-languagedetector": "6.1.8",
|
||||||
"i18next-http-middleware": "3.3.2",
|
"i18next-http-middleware": "3.3.2",
|
||||||
"is-hotkey": "0.2.0",
|
"is-hotkey": "0.2.0",
|
||||||
"is-plain-object": "5.0.0",
|
"is-plain-object": "5.0.0",
|
||||||
"isomorphic-fetch": "3.0.0",
|
|
||||||
"joi": "17.9.2",
|
|
||||||
"json-schema-to-typescript": "11.0.3",
|
"json-schema-to-typescript": "11.0.3",
|
||||||
"jsonwebtoken": "9.0.1",
|
"jsonwebtoken": "9.0.1",
|
||||||
"jwt-decode": "3.1.2",
|
"jwt-decode": "3.1.2",
|
||||||
"md5": "2.3.0",
|
"md5": "2.3.0",
|
||||||
"method-override": "3.0.0",
|
|
||||||
"micro-memoize": "4.1.2",
|
"micro-memoize": "4.1.2",
|
||||||
"minimist": "1.2.8",
|
"minimist": "1.2.8",
|
||||||
"mkdirp": "1.0.4",
|
"mkdirp": "1.0.4",
|
||||||
"monaco-editor": "0.38.0",
|
"monaco-editor": "0.38.0",
|
||||||
"nodemailer": "6.9.4",
|
"nodemailer": "6.9.4",
|
||||||
"object-to-formdata": "4.5.1",
|
"object-to-formdata": "4.5.1",
|
||||||
"passport": "0.6.0",
|
|
||||||
"passport-anonymous": "1.0.1",
|
|
||||||
"passport-headerapikey": "1.2.2",
|
|
||||||
"passport-jwt": "4.0.1",
|
|
||||||
"passport-local": "1.0.0",
|
|
||||||
"pino": "8.15.0",
|
"pino": "8.15.0",
|
||||||
"pino-pretty": "10.2.0",
|
"pino-pretty": "10.2.0",
|
||||||
"pluralize": "8.0.0",
|
"pluralize": "8.0.0",
|
||||||
@@ -114,25 +93,17 @@
|
|||||||
"process": "0.11.10",
|
"process": "0.11.10",
|
||||||
"qs": "6.11.2",
|
"qs": "6.11.2",
|
||||||
"qs-middleware": "1.0.3",
|
"qs-middleware": "1.0.3",
|
||||||
"react": "18.2.0",
|
|
||||||
"react-animate-height": "2.1.2",
|
"react-animate-height": "2.1.2",
|
||||||
"react-datepicker": "4.16.0",
|
"react-datepicker": "4.16.0",
|
||||||
"react-diff-viewer-continued": "3.2.6",
|
"react-diff-viewer-continued": "3.2.6",
|
||||||
"react-dom": "18.2.0",
|
|
||||||
"react-helmet": "6.1.0",
|
|
||||||
"react-i18next": "11.18.6",
|
"react-i18next": "11.18.6",
|
||||||
"react-image-crop": "10.1.8",
|
"react-image-crop": "10.1.8",
|
||||||
"react-router-dom": "5.3.4",
|
|
||||||
"react-router-navigation-prompt": "1.9.6",
|
|
||||||
"react-select": "5.7.4",
|
"react-select": "5.7.4",
|
||||||
"react-toastify": "8.2.0",
|
"react-toastify": "8.2.0",
|
||||||
"sanitize-filename": "1.6.3",
|
"sanitize-filename": "1.6.3",
|
||||||
"sass": "1.69.4",
|
|
||||||
"scheduler": "0.23.0",
|
"scheduler": "0.23.0",
|
||||||
"scmp": "2.1.0",
|
"scmp": "2.1.0",
|
||||||
"sharp": "0.32.6",
|
"sharp": "0.32.6",
|
||||||
"swc-loader": "0.2.3",
|
|
||||||
"terser-webpack-plugin": "5.3.9",
|
|
||||||
"ts-essentials": "7.0.3",
|
"ts-essentials": "7.0.3",
|
||||||
"use-context-selector": "1.4.1",
|
"use-context-selector": "1.4.1",
|
||||||
"uuid": "8.3.2"
|
"uuid": "8.3.2"
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
'use client'
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
import type { ClientConfig } from '../../../exports/config'
|
||||||
|
|
||||||
|
export const ClientComponent: React.FC<{ config: ClientConfig }> = ({ config }) => {
|
||||||
|
return (
|
||||||
|
<p>
|
||||||
|
This is a test client component and I have received the Payload config via props. No Webpack
|
||||||
|
aliases required!
|
||||||
|
</p>
|
||||||
|
)
|
||||||
|
}
|
||||||
18
packages/payload/src/admin/pages/dashboard/index.tsx
Normal file
18
packages/payload/src/admin/pages/dashboard/index.tsx
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
import type { SanitizedConfig } from '../../../config/types'
|
||||||
|
|
||||||
|
import { createClientConfig } from '../../../config/createClientConfig'
|
||||||
|
import { ClientComponent } from './TestClientComponent'
|
||||||
|
|
||||||
|
export const dashboard = ({ config: configPromise }: { config: Promise<SanitizedConfig> }) =>
|
||||||
|
async function () {
|
||||||
|
const config = await createClientConfig(configPromise)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
<h1>Dashboard (rendered on server)</h1>
|
||||||
|
<ClientComponent config={config} />
|
||||||
|
</React.Fragment>
|
||||||
|
)
|
||||||
|
}
|
||||||
17
packages/payload/src/auth/handlers/init.ts
Normal file
17
packages/payload/src/auth/handlers/init.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import type { SanitizedConfig } from '../../exports/config'
|
||||||
|
|
||||||
|
import { getPayload } from '../..'
|
||||||
|
import init from '../operations/init'
|
||||||
|
|
||||||
|
export const initHandler = ({ config }: { config: Promise<SanitizedConfig> }) =>
|
||||||
|
async function (request: Request, { params }: { params: { collection: string } }) {
|
||||||
|
const payload = await getPayload({ config })
|
||||||
|
request.payload = payload
|
||||||
|
|
||||||
|
const initialized = await init({
|
||||||
|
collection: params.collection,
|
||||||
|
req: request,
|
||||||
|
})
|
||||||
|
|
||||||
|
return Response.json({ initialized })
|
||||||
|
}
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
import type { NextFunction, Response } from 'express'
|
|
||||||
|
|
||||||
import type { PayloadRequest } from '../../express/types'
|
|
||||||
|
|
||||||
import init from '../operations/init'
|
|
||||||
|
|
||||||
export default async function initHandler(
|
|
||||||
req: PayloadRequest,
|
|
||||||
res: Response,
|
|
||||||
next: NextFunction,
|
|
||||||
): Promise<any> {
|
|
||||||
try {
|
|
||||||
const initialized = await init({
|
|
||||||
collection: req.collection.config.slug,
|
|
||||||
req,
|
|
||||||
})
|
|
||||||
return res.status(200).json({ initialized })
|
|
||||||
} catch (error) {
|
|
||||||
return next(error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
import type { MarkOptional } from 'ts-essentials'
|
import type { MarkOptional } from 'ts-essentials'
|
||||||
|
|
||||||
import crypto from 'crypto'
|
import crypto from 'crypto'
|
||||||
import fs from 'fs'
|
// import fs from 'fs'
|
||||||
import { promisify } from 'util'
|
// import { promisify } from 'util'
|
||||||
|
|
||||||
import type { GeneratedTypes } from '../../'
|
import type { GeneratedTypes } from '../../'
|
||||||
import type { PayloadRequest } from '../../express/types'
|
import type { PayloadRequest } from '../../express/types'
|
||||||
@@ -22,9 +22,9 @@ import { afterChange } from '../../fields/hooks/afterChange'
|
|||||||
import { afterRead } from '../../fields/hooks/afterRead'
|
import { afterRead } from '../../fields/hooks/afterRead'
|
||||||
import { beforeChange } from '../../fields/hooks/beforeChange'
|
import { beforeChange } from '../../fields/hooks/beforeChange'
|
||||||
import { beforeValidate } from '../../fields/hooks/beforeValidate'
|
import { beforeValidate } from '../../fields/hooks/beforeValidate'
|
||||||
import { generateFileData } from '../../uploads/generateFileData'
|
// import { generateFileData } from '../../uploads/generateFileData'
|
||||||
import { unlinkTempFiles } from '../../uploads/unlinkTempFiles'
|
import { unlinkTempFiles } from '../../uploads/unlinkTempFiles'
|
||||||
import { uploadFiles } from '../../uploads/uploadFiles'
|
// import { uploadFiles } from '../../uploads/uploadFiles'
|
||||||
import { commitTransaction } from '../../utilities/commitTransaction'
|
import { commitTransaction } from '../../utilities/commitTransaction'
|
||||||
import { initTransaction } from '../../utilities/initTransaction'
|
import { initTransaction } from '../../utilities/initTransaction'
|
||||||
import { killTransaction } from '../../utilities/killTransaction'
|
import { killTransaction } from '../../utilities/killTransaction'
|
||||||
@@ -32,7 +32,7 @@ import sanitizeInternalFields from '../../utilities/sanitizeInternalFields'
|
|||||||
import { saveVersion } from '../../versions/saveVersion'
|
import { saveVersion } from '../../versions/saveVersion'
|
||||||
import { buildAfterOperation } from './utils'
|
import { buildAfterOperation } from './utils'
|
||||||
|
|
||||||
const unlinkFile = promisify(fs.unlink)
|
// const unlinkFile = promisify(fs.unlink)
|
||||||
|
|
||||||
export type CreateUpdateType = { [field: number | string | symbol]: unknown }
|
export type CreateUpdateType = { [field: number | string | symbol]: unknown }
|
||||||
|
|
||||||
@@ -123,17 +123,17 @@ async function create<TSlug extends keyof GeneratedTypes['collections']>(
|
|||||||
// Generate data for all files and sizes
|
// Generate data for all files and sizes
|
||||||
// /////////////////////////////////////
|
// /////////////////////////////////////
|
||||||
|
|
||||||
const { data: newFileData, files: filesToUpload } = await generateFileData({
|
// const { data: newFileData, files: filesToUpload } = await generateFileData({
|
||||||
collection,
|
// collection,
|
||||||
config,
|
// config,
|
||||||
data,
|
// data,
|
||||||
overwriteExistingFiles,
|
// overwriteExistingFiles,
|
||||||
req,
|
// req,
|
||||||
throwOnMissingFile:
|
// throwOnMissingFile:
|
||||||
!shouldSaveDraft && collection.config.upload.filesRequiredOnCreate !== false,
|
// !shouldSaveDraft && collection.config.upload.filesRequiredOnCreate !== false,
|
||||||
})
|
// })
|
||||||
|
|
||||||
data = newFileData
|
// data = newFileData
|
||||||
|
|
||||||
// /////////////////////////////////////
|
// /////////////////////////////////////
|
||||||
// beforeValidate - Fields
|
// beforeValidate - Fields
|
||||||
@@ -174,9 +174,9 @@ async function create<TSlug extends keyof GeneratedTypes['collections']>(
|
|||||||
// Write files to local storage
|
// Write files to local storage
|
||||||
// /////////////////////////////////////
|
// /////////////////////////////////////
|
||||||
|
|
||||||
if (!collectionConfig.upload.disableLocalStorage) {
|
// if (!collectionConfig.upload.disableLocalStorage) {
|
||||||
await uploadFiles(payload, filesToUpload, req.t)
|
// await uploadFiles(payload, filesToUpload, req.t)
|
||||||
}
|
// }
|
||||||
|
|
||||||
// /////////////////////////////////////
|
// /////////////////////////////////////
|
||||||
// beforeChange - Collection
|
// beforeChange - Collection
|
||||||
|
|||||||
@@ -17,10 +17,10 @@ import { afterChange } from '../../fields/hooks/afterChange'
|
|||||||
import { afterRead } from '../../fields/hooks/afterRead'
|
import { afterRead } from '../../fields/hooks/afterRead'
|
||||||
import { beforeChange } from '../../fields/hooks/beforeChange'
|
import { beforeChange } from '../../fields/hooks/beforeChange'
|
||||||
import { beforeValidate } from '../../fields/hooks/beforeValidate'
|
import { beforeValidate } from '../../fields/hooks/beforeValidate'
|
||||||
import { deleteAssociatedFiles } from '../../uploads/deleteAssociatedFiles'
|
// import { deleteAssociatedFiles } from '../../uploads/deleteAssociatedFiles'
|
||||||
import { generateFileData } from '../../uploads/generateFileData'
|
// import { generateFileData } from '../../uploads/generateFileData'
|
||||||
import { unlinkTempFiles } from '../../uploads/unlinkTempFiles'
|
import { unlinkTempFiles } from '../../uploads/unlinkTempFiles'
|
||||||
import { uploadFiles } from '../../uploads/uploadFiles'
|
// import { uploadFiles } from '../../uploads/uploadFiles'
|
||||||
import { commitTransaction } from '../../utilities/commitTransaction'
|
import { commitTransaction } from '../../utilities/commitTransaction'
|
||||||
import { initTransaction } from '../../utilities/initTransaction'
|
import { initTransaction } from '../../utilities/initTransaction'
|
||||||
import { killTransaction } from '../../utilities/killTransaction'
|
import { killTransaction } from '../../utilities/killTransaction'
|
||||||
@@ -151,21 +151,20 @@ async function update<TSlug extends keyof GeneratedTypes['collections']>(
|
|||||||
// Generate data for all files and sizes
|
// Generate data for all files and sizes
|
||||||
// /////////////////////////////////////
|
// /////////////////////////////////////
|
||||||
|
|
||||||
const { data: newFileData, files: filesToUpload } = await generateFileData({
|
// const { data: newFileData, files: filesToUpload } = await generateFileData({
|
||||||
collection,
|
// collection,
|
||||||
config,
|
// config,
|
||||||
data: bulkUpdateData,
|
// data: bulkUpdateData,
|
||||||
overwriteExistingFiles,
|
// overwriteExistingFiles,
|
||||||
req,
|
// req,
|
||||||
throwOnMissingFile: false,
|
// throwOnMissingFile: false,
|
||||||
})
|
// })
|
||||||
|
|
||||||
const errors = []
|
const errors = []
|
||||||
|
|
||||||
const promises = docs.map(async (doc) => {
|
const promises = docs.map(async (doc) => {
|
||||||
const { id } = doc
|
const { id } = doc
|
||||||
let data = {
|
let data = {
|
||||||
...newFileData,
|
|
||||||
...bulkUpdateData,
|
...bulkUpdateData,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -181,14 +180,14 @@ async function update<TSlug extends keyof GeneratedTypes['collections']>(
|
|||||||
showHiddenFields: true,
|
showHiddenFields: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
await deleteAssociatedFiles({
|
// await deleteAssociatedFiles({
|
||||||
collectionConfig,
|
// collectionConfig,
|
||||||
config,
|
// config,
|
||||||
doc,
|
// doc,
|
||||||
files: filesToUpload,
|
// // files: filesToUpload,
|
||||||
overrideDelete: false,
|
// overrideDelete: false,
|
||||||
t,
|
// t,
|
||||||
})
|
// })
|
||||||
|
|
||||||
// /////////////////////////////////////
|
// /////////////////////////////////////
|
||||||
// beforeValidate - Fields
|
// beforeValidate - Fields
|
||||||
@@ -228,9 +227,9 @@ async function update<TSlug extends keyof GeneratedTypes['collections']>(
|
|||||||
// Write files to local storage
|
// Write files to local storage
|
||||||
// /////////////////////////////////////
|
// /////////////////////////////////////
|
||||||
|
|
||||||
if (!collectionConfig.upload.disableLocalStorage) {
|
// if (!collectionConfig.upload.disableLocalStorage) {
|
||||||
await uploadFiles(payload, filesToUpload, t)
|
// await uploadFiles(payload, filesToUpload, t)
|
||||||
}
|
// }
|
||||||
|
|
||||||
// /////////////////////////////////////
|
// /////////////////////////////////////
|
||||||
// beforeChange - Collection
|
// beforeChange - Collection
|
||||||
|
|||||||
@@ -16,10 +16,10 @@ import { afterChange } from '../../fields/hooks/afterChange'
|
|||||||
import { afterRead } from '../../fields/hooks/afterRead'
|
import { afterRead } from '../../fields/hooks/afterRead'
|
||||||
import { beforeChange } from '../../fields/hooks/beforeChange'
|
import { beforeChange } from '../../fields/hooks/beforeChange'
|
||||||
import { beforeValidate } from '../../fields/hooks/beforeValidate'
|
import { beforeValidate } from '../../fields/hooks/beforeValidate'
|
||||||
import { deleteAssociatedFiles } from '../../uploads/deleteAssociatedFiles'
|
// import { deleteAssociatedFiles } from '../../uploads/deleteAssociatedFiles'
|
||||||
import { generateFileData } from '../../uploads/generateFileData'
|
// import { generateFileData } from '../../uploads/generateFileData'
|
||||||
import { unlinkTempFiles } from '../../uploads/unlinkTempFiles'
|
import { unlinkTempFiles } from '../../uploads/unlinkTempFiles'
|
||||||
import { uploadFiles } from '../../uploads/uploadFiles'
|
// import { uploadFiles } from '../../uploads/uploadFiles'
|
||||||
import { commitTransaction } from '../../utilities/commitTransaction'
|
import { commitTransaction } from '../../utilities/commitTransaction'
|
||||||
import { initTransaction } from '../../utilities/initTransaction'
|
import { initTransaction } from '../../utilities/initTransaction'
|
||||||
import { killTransaction } from '../../utilities/killTransaction'
|
import { killTransaction } from '../../utilities/killTransaction'
|
||||||
@@ -139,29 +139,29 @@ async function updateByID<TSlug extends keyof GeneratedTypes['collections']>(
|
|||||||
// Generate data for all files and sizes
|
// Generate data for all files and sizes
|
||||||
// /////////////////////////////////////
|
// /////////////////////////////////////
|
||||||
|
|
||||||
const { data: newFileData, files: filesToUpload } = await generateFileData({
|
// const { data: newFileData, files: filesToUpload } = await generateFileData({
|
||||||
collection,
|
// collection,
|
||||||
config,
|
// config,
|
||||||
data,
|
// data,
|
||||||
overwriteExistingFiles,
|
// overwriteExistingFiles,
|
||||||
req,
|
// req,
|
||||||
throwOnMissingFile: false,
|
// throwOnMissingFile: false,
|
||||||
})
|
// })
|
||||||
|
|
||||||
data = newFileData
|
// data = newFileData
|
||||||
|
|
||||||
// /////////////////////////////////////
|
// /////////////////////////////////////
|
||||||
// Delete any associated files
|
// Delete any associated files
|
||||||
// /////////////////////////////////////
|
// /////////////////////////////////////
|
||||||
|
|
||||||
await deleteAssociatedFiles({
|
// await deleteAssociatedFiles({
|
||||||
collectionConfig,
|
// collectionConfig,
|
||||||
config,
|
// config,
|
||||||
doc: docWithLocales,
|
// doc: docWithLocales,
|
||||||
files: filesToUpload,
|
// files: filesToUpload,
|
||||||
overrideDelete: false,
|
// overrideDelete: false,
|
||||||
t,
|
// t,
|
||||||
})
|
// })
|
||||||
|
|
||||||
// /////////////////////////////////////
|
// /////////////////////////////////////
|
||||||
// beforeValidate - Fields
|
// beforeValidate - Fields
|
||||||
@@ -201,9 +201,9 @@ async function updateByID<TSlug extends keyof GeneratedTypes['collections']>(
|
|||||||
// Write files to local storage
|
// Write files to local storage
|
||||||
// /////////////////////////////////////
|
// /////////////////////////////////////
|
||||||
|
|
||||||
if (!collectionConfig.upload.disableLocalStorage) {
|
// if (!collectionConfig.upload.disableLocalStorage) {
|
||||||
await uploadFiles(payload, filesToUpload, t)
|
// await uploadFiles(payload, filesToUpload, t)
|
||||||
}
|
// }
|
||||||
|
|
||||||
// /////////////////////////////////////
|
// /////////////////////////////////////
|
||||||
// beforeChange - Collection
|
// beforeChange - Collection
|
||||||
|
|||||||
54
packages/payload/src/config/createClientConfig.ts
Normal file
54
packages/payload/src/config/createClientConfig.ts
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
import type { Field } from '../fields/config/types'
|
||||||
|
import type { ClientConfig, SanitizedConfig } from './types'
|
||||||
|
|
||||||
|
const sanitizeFields = (fields: Field[]): Field[] =>
|
||||||
|
fields.map((field) => {
|
||||||
|
const sanitized = { ...field }
|
||||||
|
|
||||||
|
if ('access' in field) delete field.access
|
||||||
|
if ('hooks' in field) delete field.hooks
|
||||||
|
if ('validate' in field) delete field.validate
|
||||||
|
if ('defaultValue' in field) delete field.defaultValue
|
||||||
|
|
||||||
|
if ('fields' in field) {
|
||||||
|
field.fields = sanitizeFields(field.fields)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ('admin' in field) {
|
||||||
|
if ('components' in field.admin) {
|
||||||
|
delete field.admin.components
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sanitized
|
||||||
|
})
|
||||||
|
|
||||||
|
export const createClientConfig = async (
|
||||||
|
configPromise: Promise<SanitizedConfig>,
|
||||||
|
): Promise<ClientConfig> => {
|
||||||
|
const config = await configPromise
|
||||||
|
const clientConfig = { ...config }
|
||||||
|
|
||||||
|
delete clientConfig.endpoints
|
||||||
|
delete clientConfig.db
|
||||||
|
|
||||||
|
clientConfig.collections = config.collections.map((collection) => {
|
||||||
|
const sanitized = { ...collection }
|
||||||
|
sanitized.fields = sanitizeFields(sanitized.fields)
|
||||||
|
delete sanitized.hooks
|
||||||
|
delete sanitized.access
|
||||||
|
delete sanitized.endpoints
|
||||||
|
return sanitized
|
||||||
|
})
|
||||||
|
|
||||||
|
clientConfig.globals = config.globals.map((global) => {
|
||||||
|
const sanitized = { ...global }
|
||||||
|
sanitized.fields = sanitizeFields(sanitized.fields)
|
||||||
|
delete sanitized.hooks
|
||||||
|
delete sanitized.access
|
||||||
|
delete sanitized.endpoints
|
||||||
|
return sanitized
|
||||||
|
})
|
||||||
|
|
||||||
|
return clientConfig
|
||||||
|
}
|
||||||
@@ -27,6 +27,7 @@ import type {
|
|||||||
} from '../collections/config/types'
|
} from '../collections/config/types'
|
||||||
import type { BaseDatabaseAdapter } from '../database/types'
|
import type { BaseDatabaseAdapter } from '../database/types'
|
||||||
import type { PayloadRequest } from '../express/types'
|
import type { PayloadRequest } from '../express/types'
|
||||||
|
import type { ClientConfigField, Field } from '../fields/config/types'
|
||||||
import type { GlobalConfig, SanitizedGlobalConfig } from '../globals/config/types'
|
import type { GlobalConfig, SanitizedGlobalConfig } from '../globals/config/types'
|
||||||
import type { Payload } from '../payload'
|
import type { Payload } from '../payload'
|
||||||
import type { Where } from '../types'
|
import type { Where } from '../types'
|
||||||
@@ -118,8 +119,7 @@ export type InitOptions = {
|
|||||||
* The passed config should match the config file, and if it doesn't, there could be mismatches between the admin UI
|
* The passed config should match the config file, and if it doesn't, there could be mismatches between the admin UI
|
||||||
* and the backend functionality
|
* and the backend functionality
|
||||||
*/
|
*/
|
||||||
config?: Promise<SanitizedConfig>
|
config: Promise<SanitizedConfig>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Disable connect to the database on init
|
* Disable connect to the database on init
|
||||||
*/
|
*/
|
||||||
@@ -149,20 +149,17 @@ export type InitOptions = {
|
|||||||
local?: boolean
|
local?: boolean
|
||||||
|
|
||||||
loggerDestination?: DestinationStream
|
loggerDestination?: DestinationStream
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specify options for the built-in Pino logger that Payload uses for internal logging.
|
* Specify options for the built-in Pino logger that Payload uses for internal logging.
|
||||||
*
|
*
|
||||||
* See Pino Docs for options: https://getpino.io/#/docs/api?id=options
|
* See Pino Docs for options: https://getpino.io/#/docs/api?id=options
|
||||||
*/
|
*/
|
||||||
loggerOptions?: LoggerOptions
|
loggerOptions?: LoggerOptions
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A function that is called immediately following startup that receives the Payload instance as it's only argument.
|
* A function that is called immediately following startup that receives the Payload instance as it's only argument.
|
||||||
*/
|
*/
|
||||||
onInit?: (payload: Payload) => Promise<void> | void
|
onInit?: (payload: Payload) => Promise<void> | void
|
||||||
|
|
||||||
/** Secure string that Payload will use for any encryption workflows */
|
|
||||||
secret: string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -509,12 +506,12 @@ export type Config = {
|
|||||||
cookiePrefix?: string
|
cookiePrefix?: string
|
||||||
/** Either a whitelist array of URLS to allow CORS requests from, or a wildcard string ('*') to accept incoming requests from any domain. */
|
/** Either a whitelist array of URLS to allow CORS requests from, or a wildcard string ('*') to accept incoming requests from any domain. */
|
||||||
cors?: '*' | string[]
|
cors?: '*' | string[]
|
||||||
|
|
||||||
/** A whitelist array of URLs to allow Payload cookies to be accepted from as a form of CSRF protection. */
|
/** A whitelist array of URLs to allow Payload cookies to be accepted from as a form of CSRF protection. */
|
||||||
csrf?: string[]
|
csrf?: string[]
|
||||||
|
|
||||||
/** Extension point to add your custom data. */
|
/** Extension point to add your custom data. */
|
||||||
custom?: Record<string, any>
|
custom?: Record<string, any>
|
||||||
|
|
||||||
/** Pass in a database adapter for use on this project. */
|
/** Pass in a database adapter for use on this project. */
|
||||||
db: (args: { payload: Payload }) => BaseDatabaseAdapter
|
db: (args: { payload: Payload }) => BaseDatabaseAdapter
|
||||||
/** Enable to expose more detailed error information. */
|
/** Enable to expose more detailed error information. */
|
||||||
@@ -666,6 +663,8 @@ export type Config = {
|
|||||||
/** @default "/playground" */
|
/** @default "/playground" */
|
||||||
graphQLPlayground?: string
|
graphQLPlayground?: string
|
||||||
}
|
}
|
||||||
|
/** Secure string that Payload will use for any encryption workflows */
|
||||||
|
secret: string
|
||||||
/**
|
/**
|
||||||
* Define the absolute URL of your app including the protocol, for example `https://example.org`.
|
* Define the absolute URL of your app including the protocol, for example `https://example.org`.
|
||||||
* No paths allowed, only protocol, domain and (optionally) port.
|
* No paths allowed, only protocol, domain and (optionally) port.
|
||||||
@@ -703,6 +702,15 @@ export type SanitizedConfig = Omit<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type ClientConfig = Omit<SanitizedConfig, 'db' | 'endpoints' | 'webpack'> & {
|
||||||
|
collections: (Omit<SanitizedCollectionConfig, 'access' | 'endpoints' | 'fields' | 'hooks'> & {
|
||||||
|
fields: ClientConfigField[]
|
||||||
|
})[]
|
||||||
|
globals: (Omit<SanitizedGlobalConfig, 'access' | 'endpoints' | 'fields' | 'hooks'> & {
|
||||||
|
fields: ClientConfigField[]
|
||||||
|
})[]
|
||||||
|
}
|
||||||
|
|
||||||
export type EntityDescription =
|
export type EntityDescription =
|
||||||
| (() => string)
|
| (() => string)
|
||||||
| React.ComponentType<any>
|
| React.ComponentType<any>
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import fs from 'fs'
|
import fs from 'fs'
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
|
|
||||||
import type { Payload } from '../../'
|
// import type { Payload } from '../../'
|
||||||
import type { Migration } from '../types'
|
// import type { Migration } from '../types'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read the migration files from disk
|
* Read the migration files from disk
|
||||||
@@ -27,10 +27,12 @@ export const readMigrationFiles = async ({
|
|||||||
return path.resolve(payload.db.migrationDir, file)
|
return path.resolve(payload.db.migrationDir, file)
|
||||||
})
|
})
|
||||||
|
|
||||||
return files.map((filePath) => {
|
return files
|
||||||
// eslint-disable-next-line @typescript-eslint/no-var-requires,import/no-dynamic-require
|
|
||||||
const migration = require(filePath) as Migration
|
// return files.map((filePath) => {
|
||||||
migration.name = path.basename(filePath).split('.')?.[0]
|
// // eslint-disable-next-line @typescript-eslint/no-var-requires,import/no-dynamic-require
|
||||||
return migration
|
// const migration = require(filePath) as Migration
|
||||||
})
|
// migration.name = path.basename(filePath).split('.')?.[0]
|
||||||
|
// return migration
|
||||||
|
// })
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
export {
|
export type {
|
||||||
BaseDatabaseAdapter,
|
BaseDatabaseAdapter,
|
||||||
BeginTransaction,
|
BeginTransaction,
|
||||||
CommitTransaction,
|
CommitTransaction,
|
||||||
@@ -78,7 +78,7 @@ export { migrationsCollection } from '../database/migrations/migrationsCollectio
|
|||||||
|
|
||||||
export { readMigrationFiles } from '../database/migrations/readMigrationFiles'
|
export { readMigrationFiles } from '../database/migrations/readMigrationFiles'
|
||||||
|
|
||||||
export { EntityPolicies, PathToQuery } from '../database/queryValidation/types'
|
export type { EntityPolicies, PathToQuery } from '../database/queryValidation/types'
|
||||||
|
|
||||||
export { validateQueryPaths } from '../database/queryValidation/validateQueryPaths'
|
export { validateQueryPaths } from '../database/queryValidation/validateQueryPaths'
|
||||||
|
|
||||||
|
|||||||
1
packages/payload/src/exports/handlers.ts
Normal file
1
packages/payload/src/exports/handlers.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export { initHandler } from '../auth/handlers/init'
|
||||||
1
packages/payload/src/exports/pages.ts
Normal file
1
packages/payload/src/exports/pages.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export { dashboard } from '../admin/pages/dashboard'
|
||||||
@@ -15,7 +15,7 @@ export type {
|
|||||||
|
|
||||||
export type { CellComponentProps } from '../admin/components/views/collections/List/Cell/types'
|
export type { CellComponentProps } from '../admin/components/views/collections/List/Cell/types'
|
||||||
|
|
||||||
export { FileData, ImageSize, IncomingUploadType } from '../uploads/types'
|
export type { FileData, ImageSize, IncomingUploadType } from '../uploads/types'
|
||||||
|
|
||||||
export type {
|
export type {
|
||||||
CustomPublishButtonProps,
|
CustomPublishButtonProps,
|
||||||
|
|||||||
@@ -503,6 +503,8 @@ export type Field =
|
|||||||
| UIField
|
| UIField
|
||||||
| UploadField
|
| UploadField
|
||||||
|
|
||||||
|
export type ClientConfigField = Omit<Field, 'access' | 'defaultValue' | 'hooks' | 'validate'>
|
||||||
|
|
||||||
export type FieldAffectingData =
|
export type FieldAffectingData =
|
||||||
| ArrayField
|
| ArrayField
|
||||||
| BlockField
|
| BlockField
|
||||||
|
|||||||
@@ -1,35 +1,421 @@
|
|||||||
import type { TypeWithID } from './collections/config/types'
|
import type { Express, Router } from 'express'
|
||||||
import type { InitOptions } from './config/types'
|
import type { ExecutionResult, GraphQLSchema, ValidationRule } from 'graphql'
|
||||||
import type { BaseDatabaseAdapter } from './database/types'
|
import type { OperationArgs, Request as graphQLRequest } from 'graphql-http/lib/handler'
|
||||||
|
import type { SendMailOptions } from 'nodemailer'
|
||||||
|
import type pino from 'pino'
|
||||||
|
|
||||||
|
import crypto from 'crypto'
|
||||||
|
|
||||||
|
import type { Result as ForgotPasswordResult } from './auth/operations/forgotPassword'
|
||||||
|
import type { Options as ForgotPasswordOptions } from './auth/operations/local/forgotPassword'
|
||||||
|
import type { Options as LoginOptions } from './auth/operations/local/login'
|
||||||
|
import type { Options as ResetPasswordOptions } from './auth/operations/local/resetPassword'
|
||||||
|
import type { Options as UnlockOptions } from './auth/operations/local/unlock'
|
||||||
|
import type { Options as VerifyEmailOptions } from './auth/operations/local/verifyEmail'
|
||||||
|
import type { Result as LoginResult } from './auth/operations/login'
|
||||||
|
import type { Result as ResetPasswordResult } from './auth/operations/resetPassword'
|
||||||
|
import type { BulkOperationResult, Collection, TypeWithID } from './collections/config/types'
|
||||||
|
import type { Options as CreateOptions } from './collections/operations/local/create'
|
||||||
|
import type {
|
||||||
|
ByIDOptions as DeleteByIDOptions,
|
||||||
|
ManyOptions as DeleteManyOptions,
|
||||||
|
Options as DeleteOptions,
|
||||||
|
} from './collections/operations/local/delete'
|
||||||
|
import type { Options as FindOptions } from './collections/operations/local/find'
|
||||||
|
import type { Options as FindByIDOptions } from './collections/operations/local/findByID'
|
||||||
|
import type { Options as FindVersionByIDOptions } from './collections/operations/local/findVersionByID'
|
||||||
|
import type { Options as FindVersionsOptions } from './collections/operations/local/findVersions'
|
||||||
|
import type { Options as RestoreVersionOptions } from './collections/operations/local/restoreVersion'
|
||||||
|
import type {
|
||||||
|
ByIDOptions as UpdateByIDOptions,
|
||||||
|
ManyOptions as UpdateManyOptions,
|
||||||
|
Options as UpdateOptions,
|
||||||
|
} from './collections/operations/local/update'
|
||||||
|
import type { EmailOptions, InitOptions, SanitizedConfig } from './config/types'
|
||||||
|
import type { PaginatedDocs } from './database/types'
|
||||||
|
import type { BuildEmailResult } from './email/types'
|
||||||
|
import type { ErrorHandler } from './express/middleware/errorHandler'
|
||||||
import type { RequestContext } from './express/types'
|
import type { RequestContext } from './express/types'
|
||||||
import type { TypeWithID as GlobalTypeWithID } from './globals/config/types'
|
import type { TypeWithID as GlobalTypeWithID, Globals } from './globals/config/types'
|
||||||
import type { Payload as LocalPayload } from './payload'
|
import type { Options as FindGlobalOptions } from './globals/operations/local/findOne'
|
||||||
|
import type { Options as FindGlobalVersionByIDOptions } from './globals/operations/local/findVersionByID'
|
||||||
|
import type { Options as FindGlobalVersionsOptions } from './globals/operations/local/findVersions'
|
||||||
|
import type { Options as RestoreGlobalVersionOptions } from './globals/operations/local/restoreVersion'
|
||||||
|
import type { Options as UpdateGlobalOptions } from './globals/operations/local/update'
|
||||||
|
import type { TypeWithVersion } from './versions/types'
|
||||||
|
|
||||||
import { initHTTP } from './initHTTP'
|
import { decrypt, encrypt } from './auth/crypto'
|
||||||
import { BasePayload } from './payload'
|
import localOperations from './collections/operations/local'
|
||||||
|
import buildEmail from './email/build'
|
||||||
|
import { defaults as emailDefaults } from './email/defaults'
|
||||||
|
import sendEmail from './email/sendEmail'
|
||||||
|
import localGlobalOperations from './globals/operations/local'
|
||||||
|
import registerGraphQLSchema from './graphql/registerSchema'
|
||||||
|
import Logger from './utilities/logger'
|
||||||
|
import { serverInit as serverInitTelemetry } from './utilities/telemetry/events/serverInit'
|
||||||
|
|
||||||
export { getPayload } from './payload'
|
/**
|
||||||
|
* @description Payload
|
||||||
|
*/
|
||||||
|
export class Payload<TGeneratedTypes extends GeneratedTypes> {
|
||||||
|
Mutation: { fields: { [key: string]: any }; name: string } = { name: 'Mutation', fields: {} }
|
||||||
|
|
||||||
require('isomorphic-fetch')
|
Query: { fields: { [key: string]: any }; name: string } = { name: 'Query', fields: {} }
|
||||||
|
|
||||||
export class Payload extends BasePayload<GeneratedTypes> {
|
collections: {
|
||||||
async init(options: InitOptions): Promise<LocalPayload> {
|
[slug: number | string | symbol]: Collection
|
||||||
const payload = await initHTTP(options)
|
} = {}
|
||||||
Object.assign(this, payload)
|
|
||||||
|
config: SanitizedConfig
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description Performs create operation
|
||||||
|
* @param options
|
||||||
|
* @returns created document
|
||||||
|
*/
|
||||||
|
create = async <T extends keyof TGeneratedTypes['collections']>(
|
||||||
|
options: CreateOptions<T>,
|
||||||
|
): Promise<TGeneratedTypes['collections'][T]> => {
|
||||||
|
const { create } = localOperations
|
||||||
|
return create<T>(this, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
db: DatabaseAdapter
|
||||||
|
|
||||||
|
decrypt = decrypt
|
||||||
|
|
||||||
|
email: BuildEmailResult
|
||||||
|
|
||||||
|
emailOptions: EmailOptions
|
||||||
|
|
||||||
|
encrypt = encrypt
|
||||||
|
|
||||||
|
errorHandler: ErrorHandler
|
||||||
|
|
||||||
|
express?: Express
|
||||||
|
|
||||||
|
extensions: (args: {
|
||||||
|
args: OperationArgs<any>
|
||||||
|
req: graphQLRequest<unknown, unknown>
|
||||||
|
result: ExecutionResult
|
||||||
|
}) => Promise<any>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description Find documents with criteria
|
||||||
|
* @param options
|
||||||
|
* @returns documents satisfying query
|
||||||
|
*/
|
||||||
|
find = async <T extends keyof TGeneratedTypes['collections']>(
|
||||||
|
options: FindOptions<T>,
|
||||||
|
): Promise<PaginatedDocs<TGeneratedTypes['collections'][T]>> => {
|
||||||
|
const { find } = localOperations
|
||||||
|
return find<T>(this, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
findByID = async <T extends keyof TGeneratedTypes['collections']>(
|
||||||
|
options: FindByIDOptions<T>,
|
||||||
|
): Promise<TGeneratedTypes['collections'][T]> => {
|
||||||
|
const { findByID } = localOperations
|
||||||
|
return findByID<T>(this, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
findGlobal = async <T extends keyof TGeneratedTypes['globals']>(
|
||||||
|
options: FindGlobalOptions<T>,
|
||||||
|
): Promise<TGeneratedTypes['globals'][T]> => {
|
||||||
|
const { findOne } = localGlobalOperations
|
||||||
|
return findOne<T>(this, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description Find global version by ID
|
||||||
|
* @param options
|
||||||
|
* @returns global version with specified ID
|
||||||
|
*/
|
||||||
|
findGlobalVersionByID = async <T extends keyof TGeneratedTypes['globals']>(
|
||||||
|
options: FindGlobalVersionByIDOptions<T>,
|
||||||
|
): Promise<TypeWithVersion<TGeneratedTypes['globals'][T]>> => {
|
||||||
|
const { findVersionByID } = localGlobalOperations
|
||||||
|
return findVersionByID<T>(this, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description Find global versions with criteria
|
||||||
|
* @param options
|
||||||
|
* @returns versions satisfying query
|
||||||
|
*/
|
||||||
|
findGlobalVersions = async <T extends keyof TGeneratedTypes['globals']>(
|
||||||
|
options: FindGlobalVersionsOptions<T>,
|
||||||
|
): Promise<PaginatedDocs<TypeWithVersion<TGeneratedTypes['globals'][T]>>> => {
|
||||||
|
const { findVersions } = localGlobalOperations
|
||||||
|
return findVersions<T>(this, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description Find version by ID
|
||||||
|
* @param options
|
||||||
|
* @returns version with specified ID
|
||||||
|
*/
|
||||||
|
findVersionByID = async <T extends keyof TGeneratedTypes['collections']>(
|
||||||
|
options: FindVersionByIDOptions<T>,
|
||||||
|
): Promise<TypeWithVersion<TGeneratedTypes['collections'][T]>> => {
|
||||||
|
const { findVersionByID } = localOperations
|
||||||
|
return findVersionByID<T>(this, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description Find versions with criteria
|
||||||
|
* @param options
|
||||||
|
* @returns versions satisfying query
|
||||||
|
*/
|
||||||
|
findVersions = async <T extends keyof TGeneratedTypes['collections']>(
|
||||||
|
options: FindVersionsOptions<T>,
|
||||||
|
): Promise<PaginatedDocs<TypeWithVersion<TGeneratedTypes['collections'][T]>>> => {
|
||||||
|
const { findVersions } = localOperations
|
||||||
|
return findVersions<T>(this, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
forgotPassword = async <T extends keyof TGeneratedTypes['collections']>(
|
||||||
|
options: ForgotPasswordOptions<T>,
|
||||||
|
): Promise<ForgotPasswordResult> => {
|
||||||
|
const { forgotPassword } = localOperations.auth
|
||||||
|
return forgotPassword<T>(this, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
getAPIURL = (): string => `${this.config.serverURL}${this.config.routes.api}`
|
||||||
|
|
||||||
|
getAdminURL = (): string => `${this.config.serverURL}${this.config.routes.admin}`
|
||||||
|
|
||||||
|
globals: Globals
|
||||||
|
|
||||||
|
local: boolean
|
||||||
|
|
||||||
|
logger: pino.Logger
|
||||||
|
|
||||||
|
login = async <T extends keyof TGeneratedTypes['collections']>(
|
||||||
|
options: LoginOptions<T>,
|
||||||
|
): Promise<LoginResult & { user: TGeneratedTypes['collections'][T] }> => {
|
||||||
|
const { login } = localOperations.auth
|
||||||
|
return login<T>(this, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description Find document by ID
|
||||||
|
* @param options
|
||||||
|
* @returns document with specified ID
|
||||||
|
*/
|
||||||
|
|
||||||
|
resetPassword = async <T extends keyof TGeneratedTypes['collections']>(
|
||||||
|
options: ResetPasswordOptions<T>,
|
||||||
|
): Promise<ResetPasswordResult> => {
|
||||||
|
const { resetPassword } = localOperations.auth
|
||||||
|
return resetPassword<T>(this, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description Restore global version by ID
|
||||||
|
* @param options
|
||||||
|
* @returns version with specified ID
|
||||||
|
*/
|
||||||
|
restoreGlobalVersion = async <T extends keyof TGeneratedTypes['globals']>(
|
||||||
|
options: RestoreGlobalVersionOptions<T>,
|
||||||
|
): Promise<TGeneratedTypes['globals'][T]> => {
|
||||||
|
const { restoreVersion } = localGlobalOperations
|
||||||
|
return restoreVersion<T>(this, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description Restore version by ID
|
||||||
|
* @param options
|
||||||
|
* @returns version with specified ID
|
||||||
|
*/
|
||||||
|
restoreVersion = async <T extends keyof TGeneratedTypes['collections']>(
|
||||||
|
options: RestoreVersionOptions<T>,
|
||||||
|
): Promise<TGeneratedTypes['collections'][T]> => {
|
||||||
|
const { restoreVersion } = localOperations
|
||||||
|
return restoreVersion<T>(this, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
router?: Router
|
||||||
|
|
||||||
|
schema: GraphQLSchema
|
||||||
|
|
||||||
|
secret: string
|
||||||
|
|
||||||
|
sendEmail: (message: SendMailOptions) => Promise<unknown>
|
||||||
|
|
||||||
|
types: {
|
||||||
|
arrayTypes: any
|
||||||
|
blockInputTypes: any
|
||||||
|
blockTypes: any
|
||||||
|
fallbackLocaleInputType?: any
|
||||||
|
groupTypes: any
|
||||||
|
localeInputType?: any
|
||||||
|
tabTypes: any
|
||||||
|
}
|
||||||
|
|
||||||
|
unlock = async <T extends keyof TGeneratedTypes['collections']>(
|
||||||
|
options: UnlockOptions<T>,
|
||||||
|
): Promise<boolean> => {
|
||||||
|
const { unlock } = localOperations.auth
|
||||||
|
return unlock(this, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
updateGlobal = async <T extends keyof TGeneratedTypes['globals']>(
|
||||||
|
options: UpdateGlobalOptions<T>,
|
||||||
|
): Promise<TGeneratedTypes['globals'][T]> => {
|
||||||
|
const { update } = localGlobalOperations
|
||||||
|
return update<T>(this, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
validationRules: (args: OperationArgs<any>) => ValidationRule[]
|
||||||
|
|
||||||
|
verifyEmail = async <T extends keyof TGeneratedTypes['collections']>(
|
||||||
|
options: VerifyEmailOptions<T>,
|
||||||
|
): Promise<boolean> => {
|
||||||
|
const { verifyEmail } = localOperations.auth
|
||||||
|
return verifyEmail(this, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
versions: {
|
||||||
|
[slug: string]: any // TODO: Type this
|
||||||
|
} = {}
|
||||||
|
|
||||||
|
delete<T extends keyof TGeneratedTypes['collections']>(
|
||||||
|
options: DeleteOptions<T>,
|
||||||
|
): Promise<BulkOperationResult<T> | TGeneratedTypes['collections'][T]> {
|
||||||
|
const { deleteLocal } = localOperations
|
||||||
|
return deleteLocal<T>(this, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description delete one or more documents
|
||||||
|
* @param options
|
||||||
|
* @returns Updated document(s)
|
||||||
|
*/
|
||||||
|
delete<T extends keyof TGeneratedTypes['collections']>(
|
||||||
|
options: DeleteByIDOptions<T>,
|
||||||
|
): Promise<TGeneratedTypes['collections'][T]>
|
||||||
|
|
||||||
|
delete<T extends keyof TGeneratedTypes['collections']>(
|
||||||
|
options: DeleteManyOptions<T>,
|
||||||
|
): Promise<BulkOperationResult<T>>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description Initializes Payload
|
||||||
|
* @param options
|
||||||
|
*/
|
||||||
|
// @ts-expect-error // TODO: TypeScript hallucinating again. fix later
|
||||||
|
async init(options: InitOptions): Promise<Payload> {
|
||||||
|
this.logger = Logger('payload', options.loggerOptions, options.loggerDestination)
|
||||||
|
|
||||||
|
this.config = await options.config
|
||||||
|
|
||||||
|
if (!this.config.secret) {
|
||||||
|
throw new Error('Error: missing secret key. A secret key is needed to secure Payload.')
|
||||||
|
}
|
||||||
|
|
||||||
|
this.secret = crypto.createHash('sha256').update(this.config.secret).digest('hex').slice(0, 32)
|
||||||
|
|
||||||
|
this.local = options.local
|
||||||
|
|
||||||
|
this.globals = {
|
||||||
|
config: this.config.globals,
|
||||||
|
}
|
||||||
|
this.config.collections.forEach((collection) => {
|
||||||
|
this.collections[collection.slug] = {
|
||||||
|
config: collection,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
this.db = this.config.db({ payload: this })
|
||||||
|
this.db.payload = this
|
||||||
|
|
||||||
|
if (this.db?.init) {
|
||||||
|
await this.db.init(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!options.disableDBConnect && this.db.connect) {
|
||||||
|
await this.db.connect(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.logger.info('Starting Payload...')
|
||||||
|
|
||||||
|
// Configure email service
|
||||||
|
const emailOptions = options.email ? { ...options.email } : this.config.email
|
||||||
|
if (options.email && this.config.email) {
|
||||||
|
this.logger.warn(
|
||||||
|
'Email options provided in both init options and config. Using init options.',
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.emailOptions = emailOptions ?? emailDefaults
|
||||||
|
this.email = buildEmail(this.emailOptions, this.logger)
|
||||||
|
this.sendEmail = sendEmail.bind(this)
|
||||||
|
|
||||||
|
if (!this.config.graphQL.disable) {
|
||||||
|
registerGraphQLSchema(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
serverInitTelemetry(this)
|
||||||
|
|
||||||
if (!options.disableOnInit) {
|
if (!options.disableOnInit) {
|
||||||
if (typeof options.onInit === 'function') await options.onInit(this)
|
if (typeof options.onInit === 'function') await options.onInit(this)
|
||||||
if (typeof this.config.onInit === 'function') await this.config.onInit(this)
|
if (typeof this.config.onInit === 'function') await this.config.onInit(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
return payload
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
update<T extends keyof TGeneratedTypes['collections']>(
|
||||||
|
options: UpdateManyOptions<T>,
|
||||||
|
): Promise<BulkOperationResult<T>>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description Update one or more documents
|
||||||
|
* @param options
|
||||||
|
* @returns Updated document(s)
|
||||||
|
*/
|
||||||
|
update<T extends keyof TGeneratedTypes['collections']>(
|
||||||
|
options: UpdateByIDOptions<T>,
|
||||||
|
): Promise<TGeneratedTypes['collections'][T]>
|
||||||
|
|
||||||
|
update<T extends keyof TGeneratedTypes['collections']>(
|
||||||
|
options: UpdateOptions<T>,
|
||||||
|
): Promise<BulkOperationResult<T> | TGeneratedTypes['collections'][T]> {
|
||||||
|
const { update } = localOperations
|
||||||
|
return update<T>(this, options)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const payload = new Payload()
|
const initialized = new Payload()
|
||||||
|
|
||||||
export default payload
|
export default initialized
|
||||||
module.exports = payload
|
|
||||||
|
let cached = global._payload
|
||||||
|
|
||||||
|
if (!cached) {
|
||||||
|
// eslint-disable-next-line no-multi-assign
|
||||||
|
cached = global._payload = { payload: null, promise: null }
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getPayload = async (options?: InitOptions): Promise<Payload<GeneratedTypes>> => {
|
||||||
|
if (cached.payload) {
|
||||||
|
return cached.payload
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!cached.promise) {
|
||||||
|
cached.promise = new Payload<GeneratedTypes>().init(options)
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
cached.payload = await cached.promise
|
||||||
|
} catch (e) {
|
||||||
|
cached.promise = null
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
|
||||||
|
return cached.payload
|
||||||
|
}
|
||||||
|
|
||||||
type GeneratedTypes = {
|
type GeneratedTypes = {
|
||||||
collections: {
|
collections: {
|
||||||
@@ -40,6 +426,6 @@ type GeneratedTypes = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type DatabaseAdapter = BaseDatabaseAdapter
|
// type DatabaseAdapter = BaseDatabaseAdapter
|
||||||
|
|
||||||
export type { DatabaseAdapter, GeneratedTypes, RequestContext }
|
export type { GeneratedTypes, RequestContext }
|
||||||
|
|||||||
@@ -1,94 +0,0 @@
|
|||||||
/* eslint-disable no-param-reassign */
|
|
||||||
import type { NextFunction, Response } from 'express'
|
|
||||||
|
|
||||||
import express from 'express'
|
|
||||||
|
|
||||||
import type { InitOptions } from './config/types'
|
|
||||||
import type { PayloadRequest } from './express/types'
|
|
||||||
import type { Payload } from './payload'
|
|
||||||
|
|
||||||
import initAuth from './auth/init'
|
|
||||||
import access from './auth/requestHandlers/access'
|
|
||||||
import { getDataLoader } from './collections/dataloader'
|
|
||||||
import initCollectionsHTTP from './collections/initHTTP'
|
|
||||||
import initAdmin from './express/admin'
|
|
||||||
import expressMiddleware from './express/middleware'
|
|
||||||
import authenticate from './express/middleware/authenticate'
|
|
||||||
import errorHandler from './express/middleware/errorHandler'
|
|
||||||
import identifyAPI from './express/middleware/identifyAPI'
|
|
||||||
import mountEndpoints from './express/mountEndpoints'
|
|
||||||
import initStatic from './express/static'
|
|
||||||
import initGlobalsHTTP from './globals/initHTTP'
|
|
||||||
import graphQLHandler from './graphql/graphQLHandler'
|
|
||||||
import initGraphQLPlayground from './graphql/initPlayground'
|
|
||||||
import { getPayload } from './payload'
|
|
||||||
|
|
||||||
export const initHTTP = async (incomingOptions: InitOptions): Promise<Payload> => {
|
|
||||||
const options = { ...incomingOptions }
|
|
||||||
if (typeof options.local === 'undefined') options.local = false
|
|
||||||
|
|
||||||
// Disable onInit because it will be called in top-level Payload
|
|
||||||
options.disableOnInit = true
|
|
||||||
|
|
||||||
const payload = await getPayload(options)
|
|
||||||
|
|
||||||
if (!options.local) {
|
|
||||||
payload.router = express.Router()
|
|
||||||
payload.router.use(...expressMiddleware(payload))
|
|
||||||
initAuth(payload)
|
|
||||||
|
|
||||||
initCollectionsHTTP(payload)
|
|
||||||
initGlobalsHTTP(payload)
|
|
||||||
|
|
||||||
options.express.use((req: PayloadRequest, res, next) => {
|
|
||||||
req.payload = payload
|
|
||||||
next()
|
|
||||||
})
|
|
||||||
|
|
||||||
options.express.use((req: PayloadRequest, res: Response, next: NextFunction): void => {
|
|
||||||
req.payloadDataLoader = getDataLoader(req)
|
|
||||||
return next()
|
|
||||||
})
|
|
||||||
|
|
||||||
payload.express = options.express
|
|
||||||
|
|
||||||
if (payload.config.rateLimit.trustProxy) {
|
|
||||||
payload.express.set('trust proxy', 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
await initAdmin(payload)
|
|
||||||
|
|
||||||
payload.router.get('/access', access)
|
|
||||||
|
|
||||||
if (!payload.config.graphQL.disable) {
|
|
||||||
payload.router.use(
|
|
||||||
payload.config.routes.graphQL,
|
|
||||||
(req, res, next): void => {
|
|
||||||
if (req.method === 'OPTIONS') {
|
|
||||||
res.sendStatus(204)
|
|
||||||
} else {
|
|
||||||
next()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
identifyAPI('GraphQL'),
|
|
||||||
(req: PayloadRequest, res: Response, next) => graphQLHandler(req, res)(req, res, next),
|
|
||||||
)
|
|
||||||
initGraphQLPlayground(payload)
|
|
||||||
}
|
|
||||||
|
|
||||||
mountEndpoints(options.express, payload.router, payload.config.endpoints)
|
|
||||||
|
|
||||||
// Bind router to API
|
|
||||||
payload.express.use(payload.config.routes.api, payload.router)
|
|
||||||
|
|
||||||
// Enable static routes for all collections permitting upload
|
|
||||||
initStatic(payload)
|
|
||||||
|
|
||||||
payload.errorHandler = errorHandler(payload.config, payload.logger)
|
|
||||||
payload.router.use(payload.errorHandler)
|
|
||||||
|
|
||||||
payload.authenticate = authenticate(payload.config)
|
|
||||||
}
|
|
||||||
|
|
||||||
return payload
|
|
||||||
}
|
|
||||||
@@ -1,438 +0,0 @@
|
|||||||
import type { Express, Router } from 'express'
|
|
||||||
import type { ExecutionResult, GraphQLSchema, ValidationRule } from 'graphql'
|
|
||||||
// @ts-expect-error // TODO Fix this - moduleResolution 16 breaks this import
|
|
||||||
import type { OperationArgs, Request as graphQLRequest } from 'graphql-http/lib/handler'
|
|
||||||
import type { SendMailOptions } from 'nodemailer'
|
|
||||||
import type pino from 'pino'
|
|
||||||
|
|
||||||
import crypto from 'crypto'
|
|
||||||
import path from 'path'
|
|
||||||
|
|
||||||
import type { DatabaseAdapter, GeneratedTypes } from './' // Must import from Payload to support declare module
|
|
||||||
import type { Result as ForgotPasswordResult } from './auth/operations/forgotPassword'
|
|
||||||
import type { Options as ForgotPasswordOptions } from './auth/operations/local/forgotPassword'
|
|
||||||
import type { Options as LoginOptions } from './auth/operations/local/login'
|
|
||||||
import type { Options as ResetPasswordOptions } from './auth/operations/local/resetPassword'
|
|
||||||
import type { Options as UnlockOptions } from './auth/operations/local/unlock'
|
|
||||||
import type { Options as VerifyEmailOptions } from './auth/operations/local/verifyEmail'
|
|
||||||
import type { Result as LoginResult } from './auth/operations/login'
|
|
||||||
import type { Result as ResetPasswordResult } from './auth/operations/resetPassword'
|
|
||||||
import type { BulkOperationResult, Collection } from './collections/config/types'
|
|
||||||
import type { Options as CreateOptions } from './collections/operations/local/create'
|
|
||||||
import type {
|
|
||||||
ByIDOptions as DeleteByIDOptions,
|
|
||||||
ManyOptions as DeleteManyOptions,
|
|
||||||
Options as DeleteOptions,
|
|
||||||
} from './collections/operations/local/delete'
|
|
||||||
import type { Options as FindOptions } from './collections/operations/local/find'
|
|
||||||
import type { Options as FindByIDOptions } from './collections/operations/local/findByID'
|
|
||||||
import type { Options as FindVersionByIDOptions } from './collections/operations/local/findVersionByID'
|
|
||||||
import type { Options as FindVersionsOptions } from './collections/operations/local/findVersions'
|
|
||||||
import type { Options as RestoreVersionOptions } from './collections/operations/local/restoreVersion'
|
|
||||||
import type {
|
|
||||||
ByIDOptions as UpdateByIDOptions,
|
|
||||||
ManyOptions as UpdateManyOptions,
|
|
||||||
Options as UpdateOptions,
|
|
||||||
} from './collections/operations/local/update'
|
|
||||||
import type { EmailOptions, InitOptions, SanitizedConfig } from './config/types'
|
|
||||||
import type { PaginatedDocs } from './database/types'
|
|
||||||
import type { BuildEmailResult } from './email/types'
|
|
||||||
import type { PayloadAuthenticate } from './express/middleware/authenticate'
|
|
||||||
import type { ErrorHandler } from './express/middleware/errorHandler'
|
|
||||||
import type { Globals } from './globals/config/types'
|
|
||||||
import type { Options as FindGlobalOptions } from './globals/operations/local/findOne'
|
|
||||||
import type { Options as FindGlobalVersionByIDOptions } from './globals/operations/local/findVersionByID'
|
|
||||||
import type { Options as FindGlobalVersionsOptions } from './globals/operations/local/findVersions'
|
|
||||||
import type { Options as RestoreGlobalVersionOptions } from './globals/operations/local/restoreVersion'
|
|
||||||
import type { Options as UpdateGlobalOptions } from './globals/operations/local/update'
|
|
||||||
import type { TypeWithVersion } from './versions/types'
|
|
||||||
|
|
||||||
import { decrypt, encrypt } from './auth/crypto'
|
|
||||||
import localOperations from './collections/operations/local'
|
|
||||||
import findConfig from './config/find'
|
|
||||||
import buildEmail from './email/build'
|
|
||||||
import { defaults as emailDefaults } from './email/defaults'
|
|
||||||
import sendEmail from './email/sendEmail'
|
|
||||||
import localGlobalOperations from './globals/operations/local'
|
|
||||||
import registerGraphQLSchema from './graphql/registerSchema'
|
|
||||||
import Logger from './utilities/logger'
|
|
||||||
import { serverInit as serverInitTelemetry } from './utilities/telemetry/events/serverInit'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @description Payload
|
|
||||||
*/
|
|
||||||
export class BasePayload<TGeneratedTypes extends GeneratedTypes> {
|
|
||||||
Mutation: { fields: { [key: string]: any }; name: string } = { name: 'Mutation', fields: {} }
|
|
||||||
|
|
||||||
Query: { fields: { [key: string]: any }; name: string } = { name: 'Query', fields: {} }
|
|
||||||
|
|
||||||
authenticate: PayloadAuthenticate
|
|
||||||
|
|
||||||
collections: {
|
|
||||||
[slug: number | string | symbol]: Collection
|
|
||||||
} = {}
|
|
||||||
|
|
||||||
config: SanitizedConfig
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @description Performs create operation
|
|
||||||
* @param options
|
|
||||||
* @returns created document
|
|
||||||
*/
|
|
||||||
create = async <T extends keyof TGeneratedTypes['collections']>(
|
|
||||||
options: CreateOptions<T>,
|
|
||||||
): Promise<TGeneratedTypes['collections'][T]> => {
|
|
||||||
const { create } = localOperations
|
|
||||||
return create<T>(this, options)
|
|
||||||
}
|
|
||||||
|
|
||||||
db: DatabaseAdapter
|
|
||||||
|
|
||||||
decrypt = decrypt
|
|
||||||
|
|
||||||
email: BuildEmailResult
|
|
||||||
|
|
||||||
emailOptions: EmailOptions
|
|
||||||
|
|
||||||
encrypt = encrypt
|
|
||||||
|
|
||||||
errorHandler: ErrorHandler
|
|
||||||
|
|
||||||
express?: Express
|
|
||||||
|
|
||||||
extensions: (args: {
|
|
||||||
args: OperationArgs<any>
|
|
||||||
req: graphQLRequest<unknown, unknown>
|
|
||||||
result: ExecutionResult
|
|
||||||
}) => Promise<any>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @description Find documents with criteria
|
|
||||||
* @param options
|
|
||||||
* @returns documents satisfying query
|
|
||||||
*/
|
|
||||||
find = async <T extends keyof TGeneratedTypes['collections']>(
|
|
||||||
options: FindOptions<T>,
|
|
||||||
): Promise<PaginatedDocs<TGeneratedTypes['collections'][T]>> => {
|
|
||||||
const { find } = localOperations
|
|
||||||
return find<T>(this, options)
|
|
||||||
}
|
|
||||||
|
|
||||||
findByID = async <T extends keyof TGeneratedTypes['collections']>(
|
|
||||||
options: FindByIDOptions<T>,
|
|
||||||
): Promise<TGeneratedTypes['collections'][T]> => {
|
|
||||||
const { findByID } = localOperations
|
|
||||||
return findByID<T>(this, options)
|
|
||||||
}
|
|
||||||
|
|
||||||
findGlobal = async <T extends keyof TGeneratedTypes['globals']>(
|
|
||||||
options: FindGlobalOptions<T>,
|
|
||||||
): Promise<TGeneratedTypes['globals'][T]> => {
|
|
||||||
const { findOne } = localGlobalOperations
|
|
||||||
return findOne<T>(this, options)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @description Find global version by ID
|
|
||||||
* @param options
|
|
||||||
* @returns global version with specified ID
|
|
||||||
*/
|
|
||||||
findGlobalVersionByID = async <T extends keyof TGeneratedTypes['globals']>(
|
|
||||||
options: FindGlobalVersionByIDOptions<T>,
|
|
||||||
): Promise<TypeWithVersion<TGeneratedTypes['globals'][T]>> => {
|
|
||||||
const { findVersionByID } = localGlobalOperations
|
|
||||||
return findVersionByID<T>(this, options)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @description Find global versions with criteria
|
|
||||||
* @param options
|
|
||||||
* @returns versions satisfying query
|
|
||||||
*/
|
|
||||||
findGlobalVersions = async <T extends keyof TGeneratedTypes['globals']>(
|
|
||||||
options: FindGlobalVersionsOptions<T>,
|
|
||||||
): Promise<PaginatedDocs<TypeWithVersion<TGeneratedTypes['globals'][T]>>> => {
|
|
||||||
const { findVersions } = localGlobalOperations
|
|
||||||
return findVersions<T>(this, options)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @description Find version by ID
|
|
||||||
* @param options
|
|
||||||
* @returns version with specified ID
|
|
||||||
*/
|
|
||||||
findVersionByID = async <T extends keyof TGeneratedTypes['collections']>(
|
|
||||||
options: FindVersionByIDOptions<T>,
|
|
||||||
): Promise<TypeWithVersion<TGeneratedTypes['collections'][T]>> => {
|
|
||||||
const { findVersionByID } = localOperations
|
|
||||||
return findVersionByID<T>(this, options)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @description Find versions with criteria
|
|
||||||
* @param options
|
|
||||||
* @returns versions satisfying query
|
|
||||||
*/
|
|
||||||
findVersions = async <T extends keyof TGeneratedTypes['collections']>(
|
|
||||||
options: FindVersionsOptions<T>,
|
|
||||||
): Promise<PaginatedDocs<TypeWithVersion<TGeneratedTypes['collections'][T]>>> => {
|
|
||||||
const { findVersions } = localOperations
|
|
||||||
return findVersions<T>(this, options)
|
|
||||||
}
|
|
||||||
|
|
||||||
forgotPassword = async <T extends keyof TGeneratedTypes['collections']>(
|
|
||||||
options: ForgotPasswordOptions<T>,
|
|
||||||
): Promise<ForgotPasswordResult> => {
|
|
||||||
const { forgotPassword } = localOperations.auth
|
|
||||||
return forgotPassword<T>(this, options)
|
|
||||||
}
|
|
||||||
|
|
||||||
getAPIURL = (): string => `${this.config.serverURL}${this.config.routes.api}`
|
|
||||||
|
|
||||||
getAdminURL = (): string => `${this.config.serverURL}${this.config.routes.admin}`
|
|
||||||
|
|
||||||
globals: Globals
|
|
||||||
|
|
||||||
local: boolean
|
|
||||||
|
|
||||||
logger: pino.Logger
|
|
||||||
|
|
||||||
login = async <T extends keyof TGeneratedTypes['collections']>(
|
|
||||||
options: LoginOptions<T>,
|
|
||||||
): Promise<LoginResult & { user: TGeneratedTypes['collections'][T] }> => {
|
|
||||||
const { login } = localOperations.auth
|
|
||||||
return login<T>(this, options)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @description Find document by ID
|
|
||||||
* @param options
|
|
||||||
* @returns document with specified ID
|
|
||||||
*/
|
|
||||||
|
|
||||||
resetPassword = async <T extends keyof TGeneratedTypes['collections']>(
|
|
||||||
options: ResetPasswordOptions<T>,
|
|
||||||
): Promise<ResetPasswordResult> => {
|
|
||||||
const { resetPassword } = localOperations.auth
|
|
||||||
return resetPassword<T>(this, options)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @description Restore global version by ID
|
|
||||||
* @param options
|
|
||||||
* @returns version with specified ID
|
|
||||||
*/
|
|
||||||
restoreGlobalVersion = async <T extends keyof TGeneratedTypes['globals']>(
|
|
||||||
options: RestoreGlobalVersionOptions<T>,
|
|
||||||
): Promise<TGeneratedTypes['globals'][T]> => {
|
|
||||||
const { restoreVersion } = localGlobalOperations
|
|
||||||
return restoreVersion<T>(this, options)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @description Restore version by ID
|
|
||||||
* @param options
|
|
||||||
* @returns version with specified ID
|
|
||||||
*/
|
|
||||||
restoreVersion = async <T extends keyof TGeneratedTypes['collections']>(
|
|
||||||
options: RestoreVersionOptions<T>,
|
|
||||||
): Promise<TGeneratedTypes['collections'][T]> => {
|
|
||||||
const { restoreVersion } = localOperations
|
|
||||||
return restoreVersion<T>(this, options)
|
|
||||||
}
|
|
||||||
|
|
||||||
router?: Router
|
|
||||||
|
|
||||||
schema: GraphQLSchema
|
|
||||||
|
|
||||||
secret: string
|
|
||||||
|
|
||||||
sendEmail: (message: SendMailOptions) => Promise<unknown>
|
|
||||||
|
|
||||||
types: {
|
|
||||||
arrayTypes: any
|
|
||||||
blockInputTypes: any
|
|
||||||
blockTypes: any
|
|
||||||
fallbackLocaleInputType?: any
|
|
||||||
groupTypes: any
|
|
||||||
localeInputType?: any
|
|
||||||
tabTypes: any
|
|
||||||
}
|
|
||||||
|
|
||||||
unlock = async <T extends keyof TGeneratedTypes['collections']>(
|
|
||||||
options: UnlockOptions<T>,
|
|
||||||
): Promise<boolean> => {
|
|
||||||
const { unlock } = localOperations.auth
|
|
||||||
return unlock(this, options)
|
|
||||||
}
|
|
||||||
|
|
||||||
updateGlobal = async <T extends keyof TGeneratedTypes['globals']>(
|
|
||||||
options: UpdateGlobalOptions<T>,
|
|
||||||
): Promise<TGeneratedTypes['globals'][T]> => {
|
|
||||||
const { update } = localGlobalOperations
|
|
||||||
return update<T>(this, options)
|
|
||||||
}
|
|
||||||
|
|
||||||
validationRules: (args: OperationArgs<any>) => ValidationRule[]
|
|
||||||
|
|
||||||
verifyEmail = async <T extends keyof TGeneratedTypes['collections']>(
|
|
||||||
options: VerifyEmailOptions<T>,
|
|
||||||
): Promise<boolean> => {
|
|
||||||
const { verifyEmail } = localOperations.auth
|
|
||||||
return verifyEmail(this, options)
|
|
||||||
}
|
|
||||||
|
|
||||||
versions: {
|
|
||||||
[slug: string]: any // TODO: Type this
|
|
||||||
} = {}
|
|
||||||
|
|
||||||
delete<T extends keyof TGeneratedTypes['collections']>(
|
|
||||||
options: DeleteOptions<T>,
|
|
||||||
): Promise<BulkOperationResult<T> | TGeneratedTypes['collections'][T]> {
|
|
||||||
const { deleteLocal } = localOperations
|
|
||||||
return deleteLocal<T>(this, options)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @description delete one or more documents
|
|
||||||
* @param options
|
|
||||||
* @returns Updated document(s)
|
|
||||||
*/
|
|
||||||
delete<T extends keyof TGeneratedTypes['collections']>(
|
|
||||||
options: DeleteByIDOptions<T>,
|
|
||||||
): Promise<TGeneratedTypes['collections'][T]>
|
|
||||||
|
|
||||||
delete<T extends keyof TGeneratedTypes['collections']>(
|
|
||||||
options: DeleteManyOptions<T>,
|
|
||||||
): Promise<BulkOperationResult<T>>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @description Initializes Payload
|
|
||||||
* @param options
|
|
||||||
*/
|
|
||||||
// @ts-expect-error // TODO: TypeScript hallucinating again. fix later
|
|
||||||
async init(options: InitOptions): Promise<Payload> {
|
|
||||||
this.logger = Logger('payload', options.loggerOptions, options.loggerDestination)
|
|
||||||
|
|
||||||
if (!options.secret) {
|
|
||||||
throw new Error('Error: missing secret key. A secret key is needed to secure Payload.')
|
|
||||||
}
|
|
||||||
|
|
||||||
this.secret = crypto.createHash('sha256').update(options.secret).digest('hex').slice(0, 32)
|
|
||||||
|
|
||||||
this.local = options.local
|
|
||||||
|
|
||||||
if (options.config) {
|
|
||||||
this.config = await options.config
|
|
||||||
const configPath = findConfig()
|
|
||||||
|
|
||||||
this.config = {
|
|
||||||
...this.config,
|
|
||||||
paths: {
|
|
||||||
config: configPath,
|
|
||||||
configDir: path.dirname(configPath),
|
|
||||||
rawConfig: configPath,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-var-requires, global-require
|
|
||||||
const loadConfig = require('./config/load').default
|
|
||||||
this.config = await loadConfig(this.logger)
|
|
||||||
}
|
|
||||||
|
|
||||||
this.globals = {
|
|
||||||
config: this.config.globals,
|
|
||||||
}
|
|
||||||
this.config.collections.forEach((collection) => {
|
|
||||||
this.collections[collection.slug] = {
|
|
||||||
config: collection,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
this.db = this.config.db({ payload: this })
|
|
||||||
this.db.payload = this
|
|
||||||
|
|
||||||
if (this.db?.init) {
|
|
||||||
await this.db.init(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!options.disableDBConnect && this.db.connect) {
|
|
||||||
await this.db.connect(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
this.logger.info('Starting Payload...')
|
|
||||||
|
|
||||||
// Configure email service
|
|
||||||
const emailOptions = options.email ? { ...options.email } : this.config.email
|
|
||||||
if (options.email && this.config.email) {
|
|
||||||
this.logger.warn(
|
|
||||||
'Email options provided in both init options and config. Using init options.',
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
this.emailOptions = emailOptions ?? emailDefaults
|
|
||||||
this.email = buildEmail(this.emailOptions, this.logger)
|
|
||||||
this.sendEmail = sendEmail.bind(this)
|
|
||||||
|
|
||||||
if (!this.config.graphQL.disable) {
|
|
||||||
registerGraphQLSchema(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
serverInitTelemetry(this)
|
|
||||||
|
|
||||||
if (!options.disableOnInit) {
|
|
||||||
if (typeof options.onInit === 'function') await options.onInit(this)
|
|
||||||
if (typeof this.config.onInit === 'function') await this.config.onInit(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
update<T extends keyof TGeneratedTypes['collections']>(
|
|
||||||
options: UpdateManyOptions<T>,
|
|
||||||
): Promise<BulkOperationResult<T>>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @description Update one or more documents
|
|
||||||
* @param options
|
|
||||||
* @returns Updated document(s)
|
|
||||||
*/
|
|
||||||
update<T extends keyof TGeneratedTypes['collections']>(
|
|
||||||
options: UpdateByIDOptions<T>,
|
|
||||||
): Promise<TGeneratedTypes['collections'][T]>
|
|
||||||
|
|
||||||
update<T extends keyof TGeneratedTypes['collections']>(
|
|
||||||
options: UpdateOptions<T>,
|
|
||||||
): Promise<BulkOperationResult<T> | TGeneratedTypes['collections'][T]> {
|
|
||||||
const { update } = localOperations
|
|
||||||
return update<T>(this, options)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export type Payload = BasePayload<GeneratedTypes>
|
|
||||||
|
|
||||||
let cached = global._payload
|
|
||||||
|
|
||||||
if (!cached) {
|
|
||||||
// eslint-disable-next-line no-multi-assign
|
|
||||||
cached = global._payload = { payload: null, promise: null }
|
|
||||||
}
|
|
||||||
|
|
||||||
export const getPayload = async (options: InitOptions): Promise<Payload> => {
|
|
||||||
if (cached.payload) {
|
|
||||||
return cached.payload
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!cached.promise) {
|
|
||||||
cached.promise = new BasePayload<GeneratedTypes>().init(options)
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
cached.payload = await cached.promise
|
|
||||||
} catch (e) {
|
|
||||||
cached.promise = null
|
|
||||||
throw e
|
|
||||||
}
|
|
||||||
|
|
||||||
return cached.payload
|
|
||||||
}
|
|
||||||
777
pnpm-lock.yaml
generated
777
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user