Compare commits

..

1 Commits

Author SHA1 Message Date
Elliot DeNolf
67e9306f7a chore(release): v3.0.0-alpha.10 [skip ci] 2024-03-04 11:37:07 -05:00
225 changed files with 1778 additions and 2809 deletions

View File

@@ -1,7 +1,7 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
import config from '@payload-config'
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import { RootPage, generatePageMetadata } from '@payloadcms/next/views/Root/index'
import { RootPage } from '@payloadcms/next/views/Root/index'
type Args = {
params: {
@@ -12,9 +12,6 @@ type Args = {
}
}
export const generateMetadata = ({ params, searchParams }: Args) =>
generatePageMetadata({ config, params, searchParams })
const Page = ({ params, searchParams }: Args) => RootPage({ config, params, searchParams })
export default Page

View File

@@ -1,7 +1,7 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
/* DO NOT MODIFY it because it could be re-written at any time. */
import config from '@payload-config'
import { REST_DELETE, REST_GET, REST_PATCH, REST_POST } from '@payloadcms/next/routes'
import { REST_GET, REST_DELETE, REST_PATCH, REST_POST } from '@payloadcms/next/routes'
export const GET = REST_GET(config)
export const POST = REST_POST(config)

View File

@@ -1,4 +1,9 @@
export const GET = () => {
export const GET = async () => {
console.log('1')
console.log('1')
console.log('1')
console.log('1')
console.log('1')
return Response.json({
hello: 'elliot',
})

View File

@@ -6,14 +6,5 @@ const withBundleAnalyzer = require('@next/bundle-analyzer')({
module.exports = withBundleAnalyzer(
withPayload({
reactStrictMode: false,
async redirects() {
return [
{
destination: '/admin',
permanent: true,
source: '/',
},
]
},
}),
)

View File

@@ -1,6 +1,6 @@
{
"name": "payload-monorepo",
"version": "3.0.0-alpha.12",
"version": "3.0.0-alpha.10",
"private": true,
"workspaces:": [
"packages/*"
@@ -36,9 +36,9 @@
"clean": "turbo clean",
"clean:cache": "rimraf node_modules/.cache && rimraf packages/payload/node_modules/.cache && rimraf .next",
"clean:all": "find . \\( -type d \\( -name node_modules -o -name dist -o -name .cache \\) -o -type f -name tsconfig.tsbuildinfo \\) -exec rm -rf {} +",
"dev": "cross-env node --no-deprecation ./test/dev.js",
"dev:generate-graphql-schema": "NODE_OPTIONS=--no-deprecation ts-node -T ./test/generateGraphQLSchema.ts",
"dev:generate-types": "NODE_OPTIONS=--no-deprecation ts-node -T ./test/generateTypes.ts",
"dev": "cross-env node ./test/dev.js",
"dev:generate-graphql-schema": "ts-node -T ./test/generateGraphQLSchema.ts",
"dev:generate-types": "ts-node -T ./test/generateTypes.ts",
"dev:postgres": "pnpm --filter payload run dev:postgres",
"docker:restart": "pnpm docker:stop --remove-orphans && pnpm docker:start",
"docker:start": "docker-compose -f packages/plugin-cloud-storage/docker-compose.yml up -d",
@@ -53,12 +53,12 @@
"release:alpha": "tsx ./scripts/release.ts --bump prerelease --tag alpha",
"release:beta": "tsx ./scripts/release.ts --bump prerelease --tag beta",
"test": "pnpm test:int && pnpm test:components && pnpm test:e2e",
"test:components": "cross-env NODE_OPTIONS=--no-deprecation jest --config=jest.components.config.js",
"test:e2e": "NODE_OPTIONS=--no-deprecation npx playwright install --with-deps chromium && NODE_OPTIONS=--no-deprecation ts-node -T ./test/runE2E.ts",
"test:e2e:debug": "cross-env NODE_OPTIONS=--no-deprecation PWDEBUG=1 DISABLE_LOGGING=true playwright test",
"test:e2e:headed": "cross-env NODE_OPTIONS=--no-deprecation DISABLE_LOGGING=true playwright test --headed",
"test:int:postgres": "cross-env NODE_OPTIONS=--no-deprecation PAYLOAD_DATABASE=postgres DISABLE_LOGGING=true jest --forceExit --detectOpenHandles",
"test:int": "cross-env NODE_OPTIONS=--no-deprecation DISABLE_LOGGING=true jest --forceExit --detectOpenHandles",
"test:components": "cross-env jest --config=jest.components.config.js",
"test:e2e": "npx playwright install --with-deps chromium && ts-node -T ./test/runE2E.ts",
"test:e2e:debug": "cross-env PWDEBUG=1 DISABLE_LOGGING=true playwright test",
"test:e2e:headed": "cross-env DISABLE_LOGGING=true playwright test --headed",
"test:int:postgres": "cross-env PAYLOAD_DATABASE=postgres DISABLE_LOGGING=true jest --forceExit --detectOpenHandles",
"test:int": "cross-env DISABLE_LOGGING=true jest --forceExit --detectOpenHandles",
"translateNewKeys": "pnpm --filter payload run translateNewKeys"
},
"devDependencies": {

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/db-mongodb",
"version": "3.0.0-alpha.12",
"version": "3.0.0-alpha.10",
"description": "The officially supported MongoDB database adapter for Payload - Update 2",
"repository": "https://github.com/payloadcms/payload",
"license": "MIT",

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/graphql",
"version": "3.0.0-alpha.12",
"version": "3.0.0-alpha.10",
"main": "./src/index.ts",
"types": "./src/index.d.ts",
"scripts": {

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/next",
"version": "3.0.0-alpha.12",
"version": "3.0.0-alpha.10",
"main": "./src/index.ts",
"types": "./src/index.d.ts",
"bin": {
@@ -77,8 +77,7 @@
"graphql-playground-html": "1.6.30",
"path-to-regexp": "^6.2.1",
"react-diff-viewer-continued": "3.2.6",
"react-toastify": "8.2.0",
"sass": "^1.71.1"
"react-toastify": "8.2.0"
},
"peerDependencies": {
"http-status": "1.6.2",

View File

@@ -1,6 +1,6 @@
export { AdminLayout } from './layouts/Admin'
export { DocumentLayout } from './layouts/Document'
export { RootLayout } from './layouts/Root'
export { Dashboard as DashboardPage } from './views/Dashboard'
export { Login } from './views/Login'
export { Dashboard as DashboardPage, generateMetadata as dashboardMeta } from './views/Dashboard'
export { Login, generateMetadata as loginMeta } from './views/Login'
export { RootPage } from './views/Root'

View File

@@ -158,9 +158,7 @@ export const processMultipart: ProcessMultipart = async ({ options, request }) =
parsingRequest = false
}
if (value) {
busboy.write(value)
}
busboy.write(value)
}
return result

View File

@@ -1,6 +1,6 @@
import fs from 'fs'
export function iteratorToStream(iterator) {
function iteratorToStream(iterator) {
return new ReadableStream({
async pull(controller) {
const { done, value } = await iterator.next()
@@ -13,7 +13,7 @@ export function iteratorToStream(iterator) {
})
}
export async function* nodeStreamToIterator(stream: fs.ReadStream) {
async function* nodeStreamToIterator(stream: fs.ReadStream) {
for await (const chunk of stream) {
yield new Uint8Array(chunk)
}

View File

@@ -77,7 +77,6 @@ export const GET =
params: { collection: collectionSlug },
request,
})
collection = req.payload.collections?.[collectionSlug]
if (!collection) {
@@ -91,7 +90,7 @@ export const GET =
)
}
if (collection.config.upload.disableLocalStorage && !collection.config.upload.handlers) {
if (collection.config.upload.disableLocalStorage) {
throw new APIError(
`This collection has local storage disabled: ${collectionSlug}`,
httpStatus.BAD_REQUEST,
@@ -104,21 +103,10 @@ export const GET =
req,
})
let response: Response = null
if (collection.config.upload.handlers?.length) {
for (const handler of collection.config.upload.handlers) {
response = await handler(req, { params })
}
return response
}
const fileDir = collection.config.upload?.staticDir || collection.config.slug
const filePath = path.resolve(`${fileDir}/${filename}`)
const stats = await fsPromises.stat(filePath)
const data = streamFile(filePath)
return new Response(data, {
headers: new Headers({
'content-length': stats.size + '',

View File

@@ -1,5 +1,6 @@
import type { Permissions } from 'payload/auth'
import type { Locale } from 'payload/config'
import type {
InitPageResult,
PayloadRequest,
SanitizedCollectionConfig,
SanitizedConfig,
@@ -28,6 +29,14 @@ type Args = {
searchParams?: { [key: string]: string | string[] | undefined }
}
export type InitPageResult = {
collectionConfig?: SanitizedCollectionConfig
globalConfig?: SanitizedGlobalConfig
locale: Locale
permissions: Permissions
req: PayloadRequest
}
export const initPage = async ({
config: configPromise,
localeParam,
@@ -54,7 +63,7 @@ export const initPage = async ({
? `?${qs.stringify(searchParams)}`
: ''
redirect(`${routes.admin}/login?redirect=${route + stringifiedSearchParams}`)
redirect(`${routes.admin}/login?redirect=${routes.admin + route + stringifiedSearchParams}`)
}
const payload = await getPayload({ config })

View File

@@ -1,6 +1,5 @@
'use client'
import type { EditViewProps } from 'payload/types'
import type { EditViewProps } from 'payload/config'
import {
Checkbox,

View File

@@ -1,7 +1,7 @@
import type { ServerSideEditViewProps } from 'payload/types'
import React from 'react'
import type { ServerSideEditViewProps } from '../Edit/types'
import { sanitizeEditViewProps } from '../Edit/sanitizeEditViewProps'
import { APIViewClient } from './index.client'

View File

@@ -1,4 +1,5 @@
import type { Data, DocumentPreferences, ServerSideEditViewProps } from 'payload/types'
import type { Metadata } from 'next'
import type { Data, DocumentPreferences, SanitizedConfig } from 'payload/types'
import {
HydrateClientUser,
@@ -10,24 +11,47 @@ import {
import { notFound } from 'next/navigation'
import React, { Fragment } from 'react'
import type { AdminViewProps } from '../Root'
import type { InitPageResult } from '../../utilities/initPage'
import type { ServerSideEditViewProps } from '../Edit/types'
import { getNextI18n } from '../../utilities/getNextI18n'
import { meta } from '../../utilities/meta'
import { EditView } from '../Edit'
import { Settings } from './Settings'
export { generateAccountMetadata } from './meta'
export const generateMetadata = async ({
config: configPromise,
}: {
config: Promise<SanitizedConfig>
}): Promise<Metadata> => {
const config = await configPromise
const { t } = await getNextI18n({
config,
})
return meta({
config,
description: `${t('authentication:accountOfCurrentUser')}`,
keywords: `${t('authentication:account')}`,
title: t('authentication:account'),
})
}
export const Account = async ({
page,
searchParams,
}: {
page: InitPageResult
searchParams: { [key: string]: string | string[] | undefined }
}) => {
const { locale, permissions, req } = page
export const Account: React.FC<AdminViewProps> = async ({ initPageResult, searchParams }) => {
const {
locale,
permissions,
req: {
payload,
payload: { config },
user,
},
req,
} = initPageResult
payload,
payload: { config },
user,
} = req
const {
admin: { components: { views: { Account: CustomAccountComponent } = {} } = {}, user: userSlug },
@@ -88,14 +112,19 @@ export const Account: React.FC<AdminViewProps> = async ({ initPageResult, search
action: `${serverURL}${api}/${userSlug}/${data?.id}?locale=${locale.code}`,
apiURL: `${serverURL}${api}/${userSlug}/${data?.id}?locale=${locale.code}`,
collectionSlug: userSlug,
config,
data,
docPermissions: collectionPermissions,
docPreferences,
hasSavePermission: collectionPermissions?.update?.permission,
initPageResult,
i18n: req.i18n,
initialState,
locale,
payload,
permissions,
searchParams,
updatedAt: '', // TODO
user,
}
return (

View File

@@ -1,11 +0,0 @@
import { meta } from '../../utilities/meta'
import { GenerateViewMetadata } from '../Root'
export const generateAccountMetadata: GenerateViewMetadata = async ({ config, i18n: { t } }) => {
return meta({
config,
description: `${t('authentication:accountOfCurrentUser')}`,
keywords: `${t('authentication:account')}`,
title: t('authentication:account'),
})
}

View File

@@ -1,29 +1,50 @@
import type { Metadata } from 'next'
import type { Field } from 'payload/types'
import type { SanitizedConfig } from 'payload/types'
import { Form, FormSubmit, buildStateFromSchema } from '@payloadcms/ui'
import { redirect } from 'next/navigation'
import React from 'react'
import type { AdminViewProps } from '../Root'
import type { InitPageResult } from '../../utilities/initPage'
import { getNextI18n } from '../../utilities/getNextI18n'
import { meta } from '../../utilities/meta'
import { CreateFirstUserFields } from './index.client'
import './index.scss'
export { generateCreateFirstUserMetadata } from './meta'
export const generateMetadata = async ({
config: configPromise,
}: {
config: Promise<SanitizedConfig>
}): Promise<Metadata> => {
const config = await configPromise
export const CreateFirstUser: React.FC<AdminViewProps> = async ({ initPageResult }) => {
const { t } = await getNextI18n({
config,
})
return meta({
config,
description: t('authentication:createFirstUser'),
keywords: t('general:create'),
title: t('authentication:createFirstUser'),
})
}
type Props = {
page: InitPageResult
params: { [key: string]: string | string[] }
searchParams: { [key: string]: string | string[] }
}
export const CreateFirstUser: React.FC<Props> = async ({ page }) => {
const { req } = page
const { config } = req.payload
const {
req,
req: {
payload: {
config: {
admin: { user: userSlug },
routes: { admin: adminRoute, api: apiRoute },
serverURL,
},
},
},
} = initPageResult
admin: { user: userSlug },
routes: { admin: adminRoute, api: apiRoute },
serverURL,
} = config
if (req.user) {
redirect(adminRoute)

View File

@@ -1,14 +0,0 @@
import { meta } from '../../utilities/meta'
import { GenerateViewMetadata } from '../Root'
export const generateCreateFirstUserMetadata: GenerateViewMetadata = async ({
config,
i18n: { t },
}) => {
return meta({
config,
description: t('authentication:createFirstUser'),
keywords: t('general:create'),
title: t('authentication:createFirstUser'),
})
}

View File

@@ -1,26 +1,49 @@
import type { Metadata } from 'next'
import type { SanitizedConfig } from 'payload/types'
import { HydrateClientUser, RenderCustomComponent } from '@payloadcms/ui'
import Link from 'next/link'
import { isEntityHidden } from 'payload/utilities'
import React, { Fragment } from 'react'
import type { AdminViewProps } from '../Root'
import type { InitPageResult } from '../../utilities/initPage'
import type { DashboardProps } from './Default'
import { getNextI18n } from '../../utilities/getNextI18n'
import { meta } from '../../utilities/meta'
import { DefaultDashboard } from './Default'
export { generateDashboardMetadata } from './meta'
export const generateMetadata = async ({
config: configPromise,
}: {
config: Promise<SanitizedConfig>
}): Promise<Metadata> => {
const config = await configPromise
const { t } = await getNextI18n({
config,
})
return meta({
config,
description: `${t('general:dashboard')} Payload`,
keywords: `${t('general:dashboard')}, Payload`,
title: t('general:dashboard'),
})
}
type Args = {
page: InitPageResult
searchParams: { [key: string]: string | string[] | undefined }
}
export const Dashboard = async ({ page, searchParams }: Args) => {
const { permissions, req } = page
export const Dashboard: React.FC<AdminViewProps> = ({
initPageResult,
// searchParams,
}) => {
const {
permissions,
req: {
payload: { config },
user,
},
} = initPageResult
payload: { config },
user,
} = req
const CustomDashboardComponent = config.admin.components?.views?.Dashboard

View File

@@ -1,11 +0,0 @@
import { meta } from '../../utilities/meta'
import { GenerateViewMetadata } from '../Root'
export const generateDashboardMetadata: GenerateViewMetadata = async ({ config, i18n: { t } }) => {
return meta({
config,
description: `${t('general:dashboard')} Payload`,
keywords: `${t('general:dashboard')}, Payload`,
title: t('general:dashboard'),
})
}

View File

@@ -1,4 +1,4 @@
import type { EditViewComponent } from 'payload/config'
import type { AdminViewComponent } from 'payload/config'
import type { SanitizedCollectionConfig, SanitizedGlobalConfig } from 'payload/types'
export const getCustomViewByKey = (
@@ -6,7 +6,7 @@ export const getCustomViewByKey = (
| SanitizedCollectionConfig['admin']['components']['views']
| SanitizedGlobalConfig['admin']['components']['views'],
customViewKey: string,
): EditViewComponent => {
): AdminViewComponent => {
return typeof views?.Edit === 'function'
? views?.Edit
: typeof views?.Edit === 'object' &&

View File

@@ -1,4 +1,4 @@
import type { EditViewComponent } from 'payload/config'
import type { AdminViewComponent } from 'payload/config'
import type { SanitizedCollectionConfig, SanitizedGlobalConfig } from 'payload/types'
export const getCustomViewByPath = (
@@ -6,7 +6,7 @@ export const getCustomViewByPath = (
| SanitizedCollectionConfig['admin']['components']['views']
| SanitizedGlobalConfig['admin']['components']['views'],
path: string,
): EditViewComponent => {
): AdminViewComponent => {
if (typeof views?.Edit === 'object' && typeof views?.Edit !== 'function') {
const foundViewConfig = Object.entries(views.Edit).find(([, view]) => {
if (typeof view === 'object' && typeof view !== 'function' && 'path' in view) {

View File

@@ -1,90 +1,92 @@
import type { I18n } from '@payloadcms/translations'
import type { Metadata } from 'next'
import type { SanitizedCollectionConfig, SanitizedGlobalConfig } from 'payload/types'
import type {
SanitizedCollectionConfig,
SanitizedConfig,
SanitizedGlobalConfig,
} from 'payload/types'
import { getNextI18n } from '../../utilities/getNextI18n'
import { meta } from '../../utilities/meta'
import { generateMetadata as apiMeta } from '../API/meta'
import { generateMetadata as editMeta } from '../Edit/meta'
import { generateMetadata as livePreviewMeta } from '../LivePreview/meta'
import { generateMetadata as versionMeta } from '../Version/meta'
import { generateMetadata as versionsMeta } from '../Versions/meta'
import { GenerateViewMetadata } from '../Root'
import { getNextI18n } from '../../utilities/getNextI18n.ts'
import { meta } from '../../utilities/meta.ts'
export type GenerateEditViewMetadata = (
args: Parameters<GenerateViewMetadata>[0] & {
collectionConfig?: SanitizedCollectionConfig | null
globalConfig?: SanitizedGlobalConfig | null
isEditing: boolean
},
) => Promise<Metadata>
export type GenerateEditViewMetadata = (args: {
collectionConfig?: SanitizedCollectionConfig
config: SanitizedConfig
globalConfig?: SanitizedGlobalConfig
i18n: I18n
isEditing: boolean
}) => Promise<Metadata>
export const getMetaBySegment: GenerateEditViewMetadata = async ({
config,
export const getMetaBySegment = async ({
config: configPromise,
params,
collectionConfig,
globalConfig,
}) => {
const { segments } = params
}: {
config: Promise<SanitizedConfig>
params: {
collection?: string
global?: string
segments: string[]
}
}): Promise<Metadata> => {
const config = await configPromise
let fn: GenerateEditViewMetadata | null = null
const [segmentOne] = segments
const isCollection = segmentOne === 'collections'
const isGlobal = segmentOne === 'globals'
const isEditing = Boolean(
params.collection && params.segments?.length > 0 && params.segments[0] !== 'create',
)
const isEditing = Boolean(isCollection && segments?.length > 2 && segments[2] !== 'create')
if (isCollection) {
if (params.collection && params?.segments?.length) {
// `/:id`
if (params.segments.length === 3) {
fn = editMeta
if (params.segments.length === 1) {
fn = await import('../Edit/meta.ts').then((mod) => mod.generateMetadata)
}
// `/:id/api`
if (params.segments.length === 4 && params.segments[3] === 'api') {
fn = apiMeta
if (params.segments.length === 2 && params.segments[1] === 'api') {
fn = await import('../API/meta.ts').then((mod) => mod.generateMetadata)
}
// `/:id/preview`
if (params.segments.length === 4 && params.segments[3] === 'preview') {
fn = livePreviewMeta
if (params.segments.length === 2 && params.segments[1] === 'preview') {
fn = await import('../LivePreview/meta.ts').then((mod) => mod.generateMetadata)
}
// `/:id/versions`
if (params.segments.length === 4 && params.segments[3] === 'versions') {
fn = versionsMeta
if (params.segments.length === 2 && params.segments[1] === 'versions') {
fn = await import('../Versions/meta.ts').then((mod) => mod.generateMetadata)
}
// `/:id/versions/:version`
if (params.segments.length === 5 && params.segments[3] === 'versions') {
fn = versionMeta
if (params.segments.length === 3 && params.segments[1] === 'versions') {
fn = await import('../Version/meta.ts').then((mod) => mod.generateMetadata)
}
}
if (isGlobal) {
if (params.global) {
// `/:slug`
if (params.segments?.length === 2) {
fn = editMeta
if (!params.segments?.length) {
fn = await import('../Edit/meta.ts').then((mod) => mod.generateMetadata)
}
// `/:slug/api`
if (params.segments?.length === 3 && params.segments[2] === 'api') {
fn = apiMeta
if (params.segments?.length === 1 && params.segments[0] === 'api') {
fn = await import('../API/meta.ts').then((mod) => mod.generateMetadata)
}
// `/:slug/preview`
if (params.segments?.length === 3 && params.segments[2] === 'preview') {
fn = livePreviewMeta
if (params.segments?.length === 1 && params.segments[0] === 'preview') {
fn = await import('../LivePreview/meta.ts').then((mod) => mod.generateMetadata)
}
// `/:slug/versions`
if (params.segments?.length === 3 && params.segments[2] === 'versions') {
fn = versionsMeta
if (params.segments?.length === 1 && params.segments[0] === 'versions') {
fn = await import('../Versions/meta.ts').then((mod) => mod.generateMetadata)
}
// `/:slug/versions/:version`
if (params.segments?.length === 4 && params.segments[2] === 'versions') {
fn = versionMeta
if (params.segments?.length === 2 && params.segments[0] === 'versions') {
fn = await import('../Version/meta.ts').then((mod) => mod.generateMetadata)
}
}
@@ -92,6 +94,14 @@ export const getMetaBySegment: GenerateEditViewMetadata = async ({
config,
})
const collectionConfig = params.collection
? config?.collections?.find((collection) => collection.slug === params.collection)
: null
const globalConfig = params.global
? config?.globals?.find((global) => global.slug === params.global)
: null
if (typeof fn === 'function') {
return fn({
collectionConfig,

View File

@@ -1,5 +1,5 @@
import type { CollectionPermission, GlobalPermission, User } from 'payload/auth'
import type { EditViewComponent } from 'payload/config'
import type { AdminViewComponent } from 'payload/config'
import type {
SanitizedCollectionConfig,
SanitizedConfig,
@@ -31,15 +31,13 @@ export const getViewsFromConfig = async ({
routeSegments: string[]
user: User
}): Promise<{
CustomView: EditViewComponent
DefaultView: EditViewComponent
CustomView: AdminViewComponent
DefaultView: AdminViewComponent
} | null> => {
// Conditionally import and lazy load the default view
let DefaultView: EditViewComponent = null
let CustomView: EditViewComponent = null
let DefaultView: AdminViewComponent = null
let CustomView: AdminViewComponent = null
const [entityType, entitySlug, createOrID, tabViewName, segmentFive] = routeSegments
const views =
(collectionConfig && collectionConfig?.admin?.components?.views) ||
(globalConfig && globalConfig?.admin?.components?.views)

View File

@@ -1,11 +1,10 @@
import type { QueryParamTypes } from '@payloadcms/ui'
import type { EditViewComponent } from 'payload/config'
import type { AdminViewComponent } from 'payload/config'
import type {
DocumentPreferences,
Document as DocumentType,
Field,
SanitizedConfig,
ServerSideEditViewProps,
} from 'payload/types'
import type { DocumentPermissions } from 'payload/types'
@@ -23,38 +22,27 @@ import { notFound } from 'next/navigation'
import queryString from 'qs'
import React, { Fragment } from 'react'
import type { AdminViewProps } from '../Root'
import type { InitPageResult } from '../../utilities/initPage'
import type { ServerSideEditViewProps } from '../Edit/types'
import { GenerateEditViewMetadata, getMetaBySegment } from './getMetaBySegment'
import { getMetaBySegment } from './getMetaBySegment'
import { getViewsFromConfig } from './getViewsFromConfig'
export const generateMetadata: GenerateEditViewMetadata = async (args) => getMetaBySegment(args)
export const Document: React.FC<AdminViewProps> = async ({
initPageResult,
params,
searchParams,
}) => {
const {
collectionConfig,
globalConfig,
locale,
permissions,
req,
req: {
i18n,
payload,
payload: {
config,
config: {
routes: { api: apiRoute },
serverURL,
},
},
user,
},
} = initPageResult
export const generateMetadata = async (args: {
config: Promise<SanitizedConfig>
params: {
collection?: string
global?: string
segments: string[]
}
}) => getMetaBySegment(args)
type Props = {
page: InitPageResult
params: { [key: string]: string | string[] }
searchParams: { [key: string]: string | string[] | undefined }
}
export const Document = async ({ page, params, searchParams }: Props) => {
const segments = Array.isArray(params?.segments) ? params.segments : []
const [entityType, entitySlug, createOrID] = segments
const collectionSlug = entityType === 'collections' ? entitySlug : undefined
@@ -64,8 +52,21 @@ export const Document: React.FC<AdminViewProps> = async ({
const isEditing = Boolean(globalSlug || (collectionSlug && !!id))
let CustomView: EditViewComponent
let DefaultView: EditViewComponent
const {
i18n,
payload,
payload: { config },
user,
} = page.req
const {
routes: { api },
serverURL,
} = config
const { collectionConfig, globalConfig, locale, permissions, req } = page
let CustomView: SanitizedConfig['admin']['components']['views'][0]
let DefaultView: AdminViewComponent
let data: DocumentType
let docPermissions: DocumentPermissions
let preferencesKey: string
@@ -77,13 +78,13 @@ export const Document: React.FC<AdminViewProps> = async ({
if (collectionConfig) {
docPermissions = permissions?.collections?.[collectionSlug]
fields = collectionConfig.fields
action = `${serverURL}${apiRoute}/${collectionSlug}${isEditing ? `/${id}` : ''}`
action = `${serverURL}${api}/${collectionSlug}${isEditing ? `/${id}` : ''}`
hasSavePermission =
(isEditing && permissions?.collections?.[collectionSlug]?.update?.permission) ||
(!isEditing && permissions?.collections?.[collectionSlug]?.create?.permission)
apiURL = `${serverURL}${apiRoute}/${collectionSlug}/${id}?locale=${locale.code}${
apiURL = `${serverURL}${api}/${collectionSlug}/${id}?locale=${locale.code}${
collectionConfig.versions?.drafts ? '&draft=true' : ''
}`
@@ -110,7 +111,7 @@ export const Document: React.FC<AdminViewProps> = async ({
locale: locale.code,
user,
})
} catch (error) {} // eslint-disable-line no-empty
} catch (error) {}
if (id) {
preferencesKey = `collection-${collectionSlug}-${id}`
@@ -121,9 +122,9 @@ export const Document: React.FC<AdminViewProps> = async ({
docPermissions = permissions?.globals?.[globalSlug]
fields = globalConfig.fields
hasSavePermission = isEditing && docPermissions?.update?.permission
action = `${serverURL}${apiRoute}/${globalSlug}`
action = `${serverURL}${api}/${globalSlug}`
apiURL = `${serverURL}${apiRoute}/${globalSlug}?locale=${locale.code}${
apiURL = `${serverURL}${api}/${globalSlug}?locale=${locale.code}${
globalConfig.versions?.drafts ? '&draft=true' : ''
}`
@@ -161,7 +162,7 @@ export const Document: React.FC<AdminViewProps> = async ({
equals: preferencesKey,
},
},
})) as any as { docs: { value: DocumentPreferences }[] } // eslint-disable-line @typescript-eslint/no-explicit-any
})) as any as { docs: { value: DocumentPreferences }[] }
const initialState = await buildStateFromSchema({
id,
@@ -184,27 +185,33 @@ export const Document: React.FC<AdminViewProps> = async ({
action: `${action}?${queryString.stringify(formQueryParams)}`,
apiURL,
canAccessAdmin: permissions?.canAccessAdmin,
collectionConfig,
collectionSlug,
config,
data,
docPermissions,
docPreferences,
globalConfig,
globalSlug,
hasSavePermission,
initPageResult,
i18n,
initialState,
isEditing,
params,
locale,
payload,
permissions,
searchParams,
updatedAt: data?.updatedAt?.toString(),
user,
}
return (
<Fragment>
<DocumentHeader
collectionConfig={collectionConfig}
config={payload.config}
config={req.payload.config}
globalConfig={globalConfig}
i18n={i18n}
i18n={req.i18n}
/>
<HydrateClientUser permissions={permissions} user={user} />
<SetDocumentInfo

View File

@@ -1,4 +0,0 @@
import { GenerateEditViewMetadata, getMetaBySegment } from './getMetaBySegment'
export const generateDocumentMetadata: GenerateEditViewMetadata = async (args) =>
getMetaBySegment(args)

View File

@@ -1,6 +1,5 @@
'use client'
import type { EditViewProps } from 'payload/types'
import type { EditViewProps } from 'payload/config'
import {
LoadingOverlay,

View File

@@ -1,7 +1,7 @@
import type { ServerSideEditViewProps } from 'payload/types'
import React from 'react'
import type { ServerSideEditViewProps } from './types'
import { EditViewClient } from './index.client'
import { sanitizeEditViewProps } from './sanitizeEditViewProps'

View File

@@ -1,10 +1,15 @@
import type { EditViewProps, ServerSideEditViewProps } from 'payload/types'
import type { EditViewProps } from 'payload/config'
import type { ServerSideEditViewProps } from './types'
export const sanitizeEditViewProps = (props: ServerSideEditViewProps) => {
const clientSideProps = { ...props }
delete clientSideProps.initPageResult.req
delete clientSideProps.initPageResult.collectionConfig
delete clientSideProps.initPageResult.globalConfig
delete clientSideProps.payload
delete clientSideProps.config
delete clientSideProps.searchParams
delete clientSideProps.i18n
delete clientSideProps.collectionConfig
delete clientSideProps.globalConfig
return clientSideProps as EditViewProps
}

43
packages/next/src/views/Edit/types.d.ts vendored Normal file
View File

@@ -0,0 +1,43 @@
import type { I18n } from '@payloadcms/translations'
import type { FormState } from '@payloadcms/ui'
import type { Permissions, User } from 'payload/auth'
import type { EditViewProps, Locale } from 'payload/config'
import type {
Data,
DocumentPermissions,
DocumentPreferences,
Payload,
SanitizedConfig,
} from 'payload/types'
export type ServerSideEditViewProps = EditViewProps & {
action?: string
apiURL: string
canAccessAdmin?: boolean
collectionConfig?: SanitizedConfig['collections'][0]
config: SanitizedConfig
data: Data
disableActions?: boolean
disableLeaveWithoutSaving?: boolean
docPermissions: DocumentPermissions
docPreferences: DocumentPreferences
globalConfig?: SanitizedConfig['globals'][0]
hasSavePermission?: boolean
i18n: I18n
id?: string
initialState?: FormState
isEditing?: boolean
locale: Locale
params?: {
collection?: string
global?: string
segments: string[]
}
payload: Payload
permissions: Permissions
searchParams: {
[key: string]: string | string[] | undefined
}
updatedAt: string
user: User
}
//# sourceMappingURL=types.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,0BAA0B,CAAA;AACpD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAC/C,OAAO,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,cAAc,CAAA;AACrD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAA;AAC3D,OAAO,KAAK,EACV,IAAI,EACJ,mBAAmB,EACnB,mBAAmB,EACnB,OAAO,EACP,eAAe,EAChB,MAAM,eAAe,CAAA;AAEtB,MAAM,MAAM,uBAAuB,GAAG,aAAa,GAAG;IACpD,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,MAAM,CAAA;IACd,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB,gBAAgB,CAAC,EAAE,eAAe,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAA;IACpD,MAAM,EAAE,eAAe,CAAA;IACvB,IAAI,EAAE,IAAI,CAAA;IACV,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB,yBAAyB,CAAC,EAAE,OAAO,CAAA;IACnC,cAAc,EAAE,mBAAmB,CAAA;IACnC,cAAc,EAAE,mBAAmB,CAAA;IACnC,YAAY,CAAC,EAAE,eAAe,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAA;IAC5C,iBAAiB,CAAC,EAAE,OAAO,CAAA;IAC3B,IAAI,EAAE,IAAI,CAAA;IACV,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,YAAY,CAAC,EAAE,SAAS,CAAA;IACxB,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE;QACP,UAAU,CAAC,EAAE,MAAM,CAAA;QACnB,MAAM,CAAC,EAAE,MAAM,CAAA;QACf,QAAQ,EAAE,MAAM,EAAE,CAAA;KACnB,CAAA;IACD,OAAO,EAAE,OAAO,CAAA;IAChB,WAAW,EAAE,WAAW,CAAA;IACxB,YAAY,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAA;KAAE,CAAA;IAE9D,SAAS,EAAE,MAAM,CAAA;IACjB,IAAI,EAAE,IAAI,CAAA;CACX,CAAA"}

View File

@@ -0,0 +1,37 @@
import type { I18n } from '@payloadcms/translations'
import type { FormState } from '@payloadcms/ui'
import type { Permissions, User } from 'payload/auth'
import type { EditViewProps, Locale } from 'payload/config'
import type {
Data,
DocumentPermissions,
DocumentPreferences,
Payload,
SanitizedConfig,
} from 'payload/types'
export type ServerSideEditViewProps = EditViewProps & {
action?: string
apiURL: string
canAccessAdmin?: boolean
collectionConfig?: SanitizedConfig['collections'][0]
config: SanitizedConfig
data: Data
disableActions?: boolean
disableLeaveWithoutSaving?: boolean
docPermissions: DocumentPermissions
docPreferences: DocumentPreferences
globalConfig?: SanitizedConfig['globals'][0]
hasSavePermission?: boolean
i18n: I18n
id?: string
initialState?: FormState
isEditing?: boolean
locale: Locale
payload: Payload
permissions: Permissions
searchParams: { [key: string]: string | string[] | undefined }
// isLoading: boolean
updatedAt: string
user: User
}

View File

@@ -1,19 +1,46 @@
import { Button, Email, Form, FormSubmit, Translation } from '@payloadcms/ui'
import type { Metadata } from 'next'
import type { SanitizedConfig } from 'payload/types'
import { Button, Email, Form, FormSubmit, MinimalTemplate, Translation } from '@payloadcms/ui'
import Link from 'next/link'
import React, { Fragment } from 'react'
import type { AdminViewProps } from '../Root'
import type { InitPageResult } from '../../utilities/initPage'
export { generateForgotPasswordMetadata } from './meta'
import { getNextI18n } from '../../utilities/getNextI18n'
import { meta } from '../../utilities/meta'
export const generateMetadata = async ({
config: configPromise,
}: {
config: Promise<SanitizedConfig>
}): Promise<Metadata> => {
const config = await configPromise
const { t } = await getNextI18n({
config,
})
return meta({
config,
description: t('authentication:forgotPassword'),
keywords: t('authentication:forgotPassword'),
title: t('authentication:forgotPassword'),
})
}
type Props = {
baseClass: string
page: InitPageResult
}
export const ForgotPassword: React.FC<Props> = async ({ page }) => {
const { req } = page
export const ForgotPassword: React.FC<AdminViewProps> = ({ initPageResult }) => {
const {
req: {
i18n,
payload: { config },
user,
},
} = initPageResult
i18n,
payload: { config },
user,
} = req
const {
admin: { user: userSlug },

View File

@@ -1,10 +0,0 @@
import { meta } from '../../utilities/meta'
export const generateForgotPasswordMetadata = async ({ config, i18n: { t } }) => {
return meta({
config,
description: t('authentication:forgotPassword'),
keywords: t('authentication:forgotPassword'),
title: t('authentication:forgotPassword'),
})
}

View File

@@ -1,3 +1,7 @@
import type { Metadata } from 'next'
import type { SanitizedConfig } from 'payload/types'
import { getTranslation } from '@payloadcms/translations'
import {
HydrateClientUser,
ListInfoProvider,
@@ -8,24 +12,62 @@ import { notFound } from 'next/navigation'
import { isEntityHidden } from 'payload/utilities'
import React, { Fragment } from 'react'
import type { AdminViewProps } from '../Root'
import type { InitPageResult } from '../../utilities/initPage'
import type { DefaultListViewProps, ListPreferences } from './Default/types'
import { getNextI18n } from '../../utilities/getNextI18n'
import { meta } from '../../utilities/meta'
import { DefaultListView } from './Default'
export { generateListMetadata } from './meta'
export const generateMetadata = async ({
config: configPromise,
params,
}: {
config: Promise<SanitizedConfig>
params: {
collection: string
}
}): Promise<Metadata> => {
let title: string = ''
const description: string = ''
const keywords: string = ''
export const ListView: React.FC<AdminViewProps> = async ({ initPageResult, searchParams }) => {
const collectionSlug = params.collection
const config = await configPromise
const i18n = await getNextI18n({
config,
})
const collectionConfig = collectionSlug
? config?.collections?.find((collection) => collection.slug === collectionSlug)
: null
if (collectionConfig) {
title = getTranslation(collectionConfig.labels.plural, i18n)
}
return meta({
config,
description,
keywords,
title,
})
}
type Props = {
page: InitPageResult
params: { [key: string]: string | string[] }
searchParams: { [key: string]: string | string[] | undefined }
}
export const ListView = async ({ page, searchParams }: Props) => {
const { collectionConfig, permissions } = page
const {
collectionConfig,
permissions,
req: {
payload,
payload: { config },
user,
},
} = initPageResult
payload,
payload: { config },
user,
} = page.req
const collectionSlug = collectionConfig?.slug
let listPreferences: ListPreferences
@@ -43,7 +85,7 @@ export const ListView: React.FC<AdminViewProps> = async ({ initPageResult, searc
},
})
?.then((res) => res?.docs?.[0]?.value)) as ListPreferences
} catch (error) {} // eslint-disable-line no-empty
} catch (error) {}
const {
routes: { admin },

View File

@@ -1,28 +0,0 @@
import { meta } from '../../utilities/meta'
import { getTranslation } from '@payloadcms/translations'
import { GenerateViewMetadata } from '../Root'
import { Metadata } from 'next'
import { SanitizedCollectionConfig } from 'payload/types'
export const generateListMetadata = async (
args: Parameters<GenerateViewMetadata>[0] & {
collectionConfig: SanitizedCollectionConfig
},
): Promise<Metadata> => {
const { collectionConfig, config, i18n } = args
let title: string = ''
const description: string = ''
const keywords: string = ''
if (collectionConfig) {
title = getTranslation(collectionConfig.labels.plural, i18n)
}
return meta({
config,
description,
keywords,
title,
})
}

View File

@@ -1,6 +1,5 @@
'use client'
import type { LivePreviewConfig } from 'payload/config'
import type { EditViewProps } from 'payload/types'
import type { EditViewProps, LivePreviewConfig } from 'payload/config'
import { DndContext } from '@dnd-kit/core'
import React, { useCallback, useEffect, useState } from 'react'

View File

@@ -1,6 +1,5 @@
'use client'
import type { EditViewProps } from 'payload/types'
import type { EditViewProps } from 'payload/config'
import {
ShimmerEffect,

View File

@@ -1,6 +1,5 @@
'use client'
import type { EditViewProps } from 'payload/types'
import type { EditViewProps } from 'payload/config'
import { Chevron, LinkIcon, Popup, PopupList, X } from '@payloadcms/ui'
import React from 'react'

View File

@@ -1,5 +1,5 @@
'use client'
import type { EditViewProps } from 'payload/types'
import type { EditViewProps } from 'payload/config'
import { useDraggable } from '@dnd-kit/core'
import { DragHandle } from '@payloadcms/ui'

View File

@@ -1,7 +1,7 @@
'use client'
import type { FormProps } from '@payloadcms/ui'
import type { LivePreviewConfig } from 'payload/config'
import type { Data, EditViewProps } from 'payload/types'
import type { EditViewProps, LivePreviewConfig } from 'payload/config'
import type { Data } from 'payload/types'
import {
DocumentControls,

View File

@@ -1,14 +1,21 @@
import type { LivePreviewConfig } from 'payload/config'
import type { ServerSideEditViewProps } from 'payload/types'
import type { Data } from 'payload/types'
import React from 'react'
import type { InitPageResult } from '../../utilities/initPage'
import { LivePreviewClient } from './index.client'
import './index.scss'
export const LivePreviewView: React.FC = async (props: ServerSideEditViewProps) => {
const { initPageResult } = props
type Props = {
data: Data
page: InitPageResult
params: { [key: string]: string | string[] }
searchParams: { [key: string]: string | string[] | undefined }
}
export const LivePreviewView: React.FC = async (props: Props) => {
const { page } = props
const {
collectionConfig,
globalConfig,
@@ -20,7 +27,7 @@ export const LivePreviewView: React.FC = async (props: ServerSideEditViewProps)
},
} = {},
} = {},
} = initPageResult
} = page
// TODO(JAKE): not sure what `data` is or what it should be
const { data = {} } = props
@@ -56,7 +63,6 @@ export const LivePreviewView: React.FC = async (props: ServerSideEditViewProps)
? await livePreviewConfig.url({
data,
documentInfo: {}, // TODO: recreate this object server-side, see `useDocumentInfo`
// @ts-expect-error
locale,
})
: livePreviewConfig?.url

View File

@@ -1,4 +1,5 @@
'use client'
import type { FormState } from '@payloadcms/ui'
import {
Email,
@@ -14,8 +15,6 @@ import React from 'react'
const baseClass = 'login__form'
import type { FormState } from 'payload/types'
import { useRouter } from 'next/navigation'
import './index.scss'

View File

@@ -1,16 +1,43 @@
import { Logo } from '@payloadcms/ui'
import type { Metadata } from 'next'
import type { SanitizedConfig } from 'payload/types'
import { Logo, MinimalTemplate } from '@payloadcms/ui'
import { redirect } from 'next/navigation'
import React, { Fragment } from 'react'
import type { AdminViewProps } from '../Root'
import type { InitPageResult } from '../../utilities/initPage'
import { getNextI18n } from '../../utilities/getNextI18n'
import { meta } from '../../utilities/meta'
import { LoginForm } from './LoginForm'
import './index.scss'
export { generateLoginMetadata } from './meta'
export const generateMetadata = async ({
config: configPromise,
}: {
config: Promise<SanitizedConfig>
}): Promise<Metadata> => {
const config = await configPromise
export const Login: React.FC<AdminViewProps> = ({ baseClass, initPageResult, searchParams }) => {
const { req } = initPageResult
const { t } = await getNextI18n({
config,
})
return meta({
config,
description: `${t('authentication:login')}`,
keywords: `${t('authentication:login')}`,
title: t('authentication:login'),
})
}
type Props = {
baseClass: string
page: InitPageResult
searchParams: { [key: string]: string | string[] | undefined }
}
export const Login: React.FC<Props> = ({ baseClass, page, searchParams }) => {
const { req } = page
const {
payload: { config },

View File

@@ -1,11 +0,0 @@
import { meta } from '../../utilities/meta'
import { GenerateViewMetadata } from '../Root'
export const generateLoginMetadata: GenerateViewMetadata = async ({ config, i18n: { t } }) => {
return meta({
config,
description: `${t('authentication:login')}`,
keywords: `${t('authentication:login')}`,
title: t('authentication:login'),
})
}

View File

@@ -1,29 +1,55 @@
import type { Metadata } from 'next'
import type { SanitizedConfig } from 'payload/types'
import { MinimalTemplate } from '@payloadcms/ui'
import React from 'react'
import type { AdminViewProps } from '../Root'
import type { InitPageResult } from '../../utilities/initPage'
import { getNextI18n } from '../../utilities/getNextI18n'
import { meta } from '../../utilities/meta'
import { LogoutClient } from './LogoutClient'
import './index.scss'
const baseClass = 'logout'
export { generateLogoutMetadata } from './meta'
export const generateMetadata = async ({
config: configPromise,
}: {
config: Promise<SanitizedConfig>
}): Promise<Metadata> => {
const config = await configPromise
export const Logout: React.FC<
AdminViewProps & {
inactivity?: boolean
}
> = ({ inactivity, initPageResult, searchParams }) => {
const { t } = await getNextI18n({
config,
})
return meta({
config,
description: `${t('authentication:logoutUser')}`,
keywords: `${t('authentication:logout')}`,
title: t('authentication:logout'),
})
}
type Props = {
baseClass: string
page: InitPageResult
searchParams: { [key: string]: string | string[] }
} & {
inactivity?: boolean
}
export const Logout: React.FC<Props> = ({ inactivity, page, searchParams }) => {
const {
req: {
payload: {
config: {
routes: { admin },
},
},
payload: { config },
},
} = initPageResult
} = page
const {
routes: { admin },
} = config
return (
<MinimalTemplate className={baseClass}>
@@ -38,6 +64,6 @@ export const Logout: React.FC<
)
}
export const LogoutInactivity: React.FC<AdminViewProps> = (props) => {
export const LogoutInactivity: React.FC<Props> = (props) => {
return <Logout inactivity {...props} />
}

View File

@@ -1,11 +0,0 @@
import { meta } from '../../utilities/meta'
import { GenerateViewMetadata } from '../Root'
export const generateLogoutMetadata: GenerateViewMetadata = async ({ config, i18n: { t } }) => {
return meta({
config,
description: `${t('authentication:logoutUser')}`,
keywords: `${t('authentication:logout')}`,
title: t('authentication:logout'),
})
}

View File

@@ -1,3 +1,6 @@
import type { Metadata } from 'next'
import type { SanitizedConfig } from 'payload/types'
import {
Button,
ConfirmPassword,
@@ -11,17 +14,40 @@ import {
import Link from 'next/link'
import React from 'react'
import type { AdminViewProps } from '../Root'
import type { InitPageResult } from '../../utilities/initPage'
import { getNextI18n } from '../../utilities/getNextI18n'
import { meta } from '../../utilities/meta'
import './index.scss'
const baseClass = 'reset-password'
export { generateResetPasswordMetadata } from './meta'
export const generateMetadata = async ({
config: configPromise,
}: {
config: Promise<SanitizedConfig>
}): Promise<Metadata> => {
const config = await configPromise
export const ResetPassword: React.FC<AdminViewProps> = ({ initPageResult, params }) => {
const { req } = initPageResult
const { t } = await getNextI18n({
config,
})
return meta({
config,
description: t('authentication:resetPassword'),
keywords: t('authentication:resetPassword'),
title: t('authentication:resetPassword'),
})
}
type Props = {
page: InitPageResult
params: { [key: string]: string | string[] }
searchParams: { [key: string]: string | string[] }
}
export const ResetPassword: React.FC<Props> = ({ page, params }) => {
const { req } = page
const { token } = params
const {

View File

@@ -1,14 +0,0 @@
import { meta } from '../../utilities/meta'
import { GenerateViewMetadata } from '../Root'
export const generateResetPasswordMetadata: GenerateViewMetadata = async ({
config,
i18n: { t },
}) => {
return meta({
config,
description: t('authentication:resetPassword'),
keywords: t('authentication:resetPassword'),
title: t('authentication:resetPassword'),
})
}

View File

@@ -1,9 +1,11 @@
import type { InitPageResult, SanitizedConfig } from 'payload/types'
import type { SanitizedConfig } from 'payload/types'
import { DefaultTemplate, MinimalTemplate } from '@payloadcms/ui'
import React from 'react'
import { getNextI18n } from '../../utilities/getNextI18n'
import { initPage } from '../../utilities/initPage'
import { meta } from '../../utilities/meta'
import { Account } from '../Account'
import { CreateFirstUser } from '../CreateFirstUser'
import { Dashboard } from '../Dashboard'
@@ -14,11 +16,7 @@ import { Login } from '../Login'
import { Logout, LogoutInactivity } from '../Logout'
import { ResetPassword } from '../ResetPassword'
import { Unauthorized } from '../Unauthorized'
import { Verify } from '../Verify'
import { Metadata } from 'next'
import { I18n } from '@payloadcms/translations'
export { generatePageMetadata } from './meta'
import Verify from '../Verify'
type Args = {
config: Promise<SanitizedConfig>
@@ -30,19 +28,6 @@ type Args = {
}
}
export type GenerateViewMetadata = (args: {
config: SanitizedConfig
i18n: I18n
params?: { [key: string]: string | string[] }
}) => Promise<Metadata>
export type AdminViewProps = {
baseClass?: string
initPageResult: InitPageResult
params?: { [key: string]: string | string[] | undefined }
searchParams: { [key: string]: string | string[] | undefined }
}
const baseClasses = {
forgot: 'forgot-password',
login: 'login',
@@ -61,13 +46,12 @@ const oneSegmentViews = {
export const RootPage = async ({ config: configPromise, params, searchParams }: Args) => {
const config = await configPromise
let ViewToRender: React.FC<AdminViewProps>
let ViewToRender
let templateClassName
let initPageResult: InitPageResult
let pageData
let templateType: 'default' | 'minimal' = 'default'
let route = config.routes.admin
if (Array.isArray(params.segments)) {
route = route + '/' + params.segments.join('/')
}
@@ -86,12 +70,7 @@ export const RootPage = async ({ config: configPromise, params, searchParams }:
ViewToRender = Dashboard
templateClassName = 'dashboard'
templateType = 'default'
initPageResult = await initPage({
config,
redirectUnauthenticatedUser: true,
route,
searchParams,
})
pageData = await initPage({ config, redirectUnauthenticatedUser: true, route, searchParams })
break
}
case 1: {
@@ -102,13 +81,13 @@ export const RootPage = async ({ config: configPromise, params, searchParams }:
// --> /logout
// --> /logout-inactivity
// --> /unauthorized
initPageResult = await initPage({ config, route, searchParams })
pageData = await initPage({ config, route, searchParams })
ViewToRender = oneSegmentViews[segmentOne]
templateClassName = baseClasses[segmentOne]
templateType = 'minimal'
} else if (segmentOne === 'account') {
// --> /account
initPageResult = await initPage({
pageData = await initPage({
config,
redirectUnauthenticatedUser: true,
route,
@@ -123,14 +102,14 @@ export const RootPage = async ({ config: configPromise, params, searchParams }:
case 2: {
if (segmentOne === 'reset') {
// --> /reset/:token
initPageResult = await initPage({ config, route, searchParams })
pageData = await initPage({ config, route, searchParams })
ViewToRender = ResetPassword
templateClassName = baseClasses[segmentTwo]
templateType = 'minimal'
}
if (isCollection) {
// --> /collections/:collectionSlug
initPageResult = await initPage({
pageData = await initPage({
config,
redirectUnauthenticatedUser: true,
route,
@@ -141,7 +120,7 @@ export const RootPage = async ({ config: configPromise, params, searchParams }:
templateType = 'default'
} else if (isGlobal) {
// --> /globals/:globalSlug
initPageResult = await initPage({
pageData = await initPage({
config,
redirectUnauthenticatedUser: true,
route,
@@ -156,7 +135,7 @@ export const RootPage = async ({ config: configPromise, params, searchParams }:
default:
if (segmentTwo === 'verify') {
// --> /:collectionSlug/verify/:token
initPageResult = await initPage({ config, route, searchParams })
pageData = await initPage({ config, route, searchParams })
ViewToRender = Verify
templateClassName = 'verify'
templateType = 'minimal'
@@ -167,7 +146,7 @@ export const RootPage = async ({ config: configPromise, params, searchParams }:
// --> /collections/:collectionSlug/:id/versions
// --> /collections/:collectionSlug/:id/versions/:versionId
// --> /collections/:collectionSlug/:id/api
initPageResult = await initPage({
pageData = await initPage({
config,
redirectUnauthenticatedUser: true,
route,
@@ -182,7 +161,7 @@ export const RootPage = async ({ config: configPromise, params, searchParams }:
// --> /globals/:globalSlug/preview
// --> /globals/:globalSlug/versions/:versionId
// --> /globals/:globalSlug/api
initPageResult = await initPage({
pageData = await initPage({
config,
redirectUnauthenticatedUser: true,
route,
@@ -195,30 +174,22 @@ export const RootPage = async ({ config: configPromise, params, searchParams }:
break
}
if (initPageResult) {
if (pageData) {
if (templateType === 'minimal') {
return (
<MinimalTemplate className={templateClassName}>
<ViewToRender
initPageResult={initPageResult}
params={params}
searchParams={searchParams}
/>
<ViewToRender page={pageData} params={params} searchParams={searchParams} />
</MinimalTemplate>
)
} else {
return (
<DefaultTemplate
config={config}
i18n={initPageResult.req.i18n}
permissions={initPageResult.permissions}
user={initPageResult.req.user}
i18n={pageData.req.i18n}
permissions={pageData.permissions}
user={pageData.req.user}
>
<ViewToRender
initPageResult={initPageResult}
params={params}
searchParams={searchParams}
/>
<ViewToRender page={pageData} params={params} searchParams={searchParams} />
</DefaultTemplate>
)
}
@@ -226,3 +197,21 @@ export const RootPage = async ({ config: configPromise, params, searchParams }:
return null
}
export const generateMeta = async ({ config: configPromise, params, searchParams }: Args) => {
const config = await configPromise
const { t } = await getNextI18n({
config,
})
return meta({
config,
description: 'Payload',
keywords: 'Payload',
title: 'Payload',
// description: `${t('authentication:logoutUser')}`,
// keywords: `${t('authentication:logout')}`,
// title: t('authentication:logout'),
})
}

View File

@@ -1,135 +0,0 @@
import { generateAccountMetadata } from '../Account'
import { generateCreateFirstUserMetadata } from '../CreateFirstUser'
import { generateDashboardMetadata } from '../Dashboard'
import { generateForgotPasswordMetadata } from '../ForgotPassword'
import { generateListMetadata } from '../List'
import { generateLoginMetadata } from '../Login'
import { generateResetPasswordMetadata } from '../ResetPassword'
import { generateUnauthorizedMetadata } from '../Unauthorized'
import { generateVerifyMetadata } from '../Verify'
import { Metadata } from 'next'
import { generateDocumentMetadata } from '../Document/meta'
import { getNextI18n } from '../../utilities/getNextI18n'
import { SanitizedConfig } from 'payload/types'
const oneSegmentMeta = {
'create-first-user': generateCreateFirstUserMetadata,
forgot: generateForgotPasswordMetadata,
login: generateLoginMetadata,
logout: generateUnauthorizedMetadata,
'logout-inactivity': generateUnauthorizedMetadata,
unauthorized: generateUnauthorizedMetadata,
}
type Args = {
config: Promise<SanitizedConfig>
params: {
[key: string]: string | string[]
}
searchParams: {
[key: string]: string | string[]
}
}
export const generatePageMetadata = async ({ config: configPromise, params }: Args) => {
const config = await configPromise
let route = config.routes.admin
if (Array.isArray(params.segments)) {
route = route + '/' + params.segments.join('/')
}
const segments = Array.isArray(params.segments) ? params.segments : []
const [segmentOne, segmentTwo] = segments
const isGlobal = segmentOne === 'globals'
const isCollection = segmentOne === 'collections'
const i18n = await getNextI18n({
config,
})
let meta: Metadata
// TODO: handle custom routes
const collectionConfig =
isCollection &&
segments.length > 1 &&
config?.collections?.find((collection) => collection.slug === segmentTwo)
const globalConfig =
isGlobal && segments.length > 1 && config?.globals?.find((global) => global.slug === segmentTwo)
switch (segments.length) {
case 0: {
meta = await generateDashboardMetadata({ config, i18n })
break
}
case 1: {
if (oneSegmentMeta[segmentOne] && segmentOne !== 'account') {
// --> /create-first-user
// --> /forgot
// --> /login
// --> /logout
// --> /logout-inactivity
// --> /unauthorized
meta = await oneSegmentMeta[segmentOne]({ config, i18n })
break
} else if (segmentOne === 'account') {
// --> /account
meta = await generateAccountMetadata({ config, i18n })
break
}
break
}
case 2: {
if (segmentOne === 'reset') {
// --> /reset/:token
meta = await generateResetPasswordMetadata({ config, i18n })
}
if (isCollection) {
// --> /collections/:collectionSlug
meta = await generateListMetadata({ config, i18n, collectionConfig })
} else if (isGlobal) {
// --> /globals/:globalSlug
meta = await generateDocumentMetadata({
config,
i18n,
globalConfig,
isEditing: false,
params,
})
}
break
}
default: {
if (segmentTwo === 'verify') {
// --> /:collectionSlug/verify/:token
meta = await generateVerifyMetadata({ config, i18n })
} else if (isCollection) {
// Custom Views
// --> /collections/:collectionSlug/:id
// --> /collections/:collectionSlug/:id/preview
// --> /collections/:collectionSlug/:id/versions
// --> /collections/:collectionSlug/:id/versions/:version
// --> /collections/:collectionSlug/:id/api
const isEditing = ['preview', 'versions', 'api', 'create'].includes(segmentTwo)
meta = await generateDocumentMetadata({ config, i18n, collectionConfig, isEditing, params })
} else if (isGlobal) {
// Custom Views
// --> /globals/:globalSlug/versions
// --> /globals/:globalSlug/versions/:version
// --> /globals/:globalSlug/preview
// --> /globals/:globalSlug/api
const isEditing = ['preview', 'versions', 'api', 'create'].includes(segmentTwo)
meta = await generateDocumentMetadata({ config, i18n, globalConfig, isEditing, params })
}
break
}
}
return meta
}

View File

@@ -1,22 +1,43 @@
import type { Metadata } from 'next'
import { MinimalTemplate } from '@payloadcms/ui'
import React from 'react'
import type { AdminViewProps } from '../Root'
import type { InitPageResult } from '../../utilities/initPage'
import { meta } from '../../utilities/meta'
import { UnauthorizedClient } from './UnauthorizedClient'
export { generateUnauthorizedMetadata } from './meta'
export const Unauthorized: React.FC<AdminViewProps> = ({ initPageResult }) => {
export const generateMetadata = async ({ page }: { page: InitPageResult }): Promise<Metadata> => {
const {
req: {
payload: {
config: {
admin: { logoutRoute },
routes: { admin },
},
},
payload: { config },
t,
},
} = initPageResult
} = page
return meta({
config,
description: t('error:unauthorized'),
keywords: t('error:unauthorized'),
title: t('error:unauthorized'),
})
}
type Props = {
page: InitPageResult
}
export const Unauthorized: React.FC<Props> = ({ page }) => {
const {
req: {
payload: { config },
},
} = page
const {
admin: { logoutRoute },
routes: { admin },
} = config
return <UnauthorizedClient logoutRoute={`${admin}${logoutRoute}`} />
}

View File

@@ -1,14 +0,0 @@
import { meta } from '../../utilities/meta'
import { GenerateViewMetadata } from '../Root'
export const generateUnauthorizedMetadata: GenerateViewMetadata = async ({
config,
i18n: { t },
}) => {
return meta({
config,
description: t('error:unauthorized'),
keywords: t('error:unauthorized'),
title: t('error:unauthorized'),
})
}

View File

@@ -1,20 +1,47 @@
import type { Metadata } from 'next'
import type { SanitizedConfig } from 'payload/types'
import { Logo } from '@payloadcms/ui'
import { redirect } from 'next/navigation'
import React from 'react'
import type { AdminViewProps } from '../Root'
import type { InitPageResult } from '../../utilities/initPage'
import { getNextI18n } from '../../utilities/getNextI18n'
import { meta } from '../../utilities/meta'
import './index.scss'
const baseClass = 'verify'
export { generateVerifyMetadata } from './meta'
export const generateMetadata = async ({
config: configPromise,
}: {
config: Promise<SanitizedConfig>
}): Promise<Metadata> => {
const config = await configPromise
export const Verify: React.FC<AdminViewProps> = async ({ initPageResult, params }) => {
const { t } = await getNextI18n({
config,
})
return meta({
config,
description: t('authentication:verifyUser'),
keywords: t('authentication:verify'),
title: t('authentication:verify'),
})
}
type Props = {
page: InitPageResult
params: { [key: string]: string | string[] }
searchParams: { [key: string]: string | string[] }
}
export const Verify: React.FC<Props> = async ({ page, params }) => {
// /:collectionSlug/verify/:token
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [collectionSlug, verify, token] = params.segments
const { req } = initPageResult
const { req } = page
const {
payload: { config },
@@ -48,3 +75,4 @@ export const Verify: React.FC<AdminViewProps> = async ({ initPageResult, params
</React.Fragment>
)
}
export default Verify

View File

@@ -1,11 +0,0 @@
import { meta } from '../../utilities/meta'
import { GenerateViewMetadata } from '../Root'
export const generateVerifyMetadata: GenerateViewMetadata = async ({ config, i18n: { t } }) => {
return meta({
config,
description: t('authentication:verifyUser'),
keywords: t('authentication:verify'),
title: t('authentication:verify'),
})
}

View File

@@ -1,21 +1,27 @@
import type { Option } from '@payloadcms/ui'
import type { CollectionPermission, GlobalPermission } from 'payload/auth'
import type { Document, ServerSideEditViewProps } from 'payload/types'
import type { Document } from 'payload/types'
import { notFound } from 'next/navigation'
import React from 'react'
import type { InitPageResult } from '../../utilities/initPage'
import { DefaultVersionView } from './Default'
export const VersionView: React.FC = async (props: ServerSideEditViewProps) => {
const { initPageResult, params } = props
type Props = {
page: InitPageResult
params: { [key: string]: string | string[] }
searchParams: { [key: string]: string | string[] | undefined }
}
export const VersionView: React.FC = async (props: Props) => {
const { page, params } = props
const {
collectionConfig,
globalConfig,
permissions,
req: { payload, payload: { config } = {}, user } = {},
} = initPageResult
} = page
// /entityType/:entitySlug/:id/versions/:versionID
const [entityType, entitySlug, id, versions, versionID] = params.segments

View File

@@ -1,9 +1,10 @@
import type { ServerSideEditViewProps } from 'payload/types'
import { getTranslation } from '@payloadcms/translations'
import { Gutter } from '@payloadcms/ui'
import { notFound } from 'next/navigation'
import React from 'react'
import type { ServerSideEditViewProps } from '../Edit/types'
import { SetStepNav } from '../Edit/Default/SetStepNav'
import { sanitizeEditViewProps } from '../Edit/sanitizeEditViewProps'
import { buildVersionColumns } from './buildColumns'
@@ -13,28 +14,25 @@ import './index.scss'
export const baseClass = 'versions'
export const VersionsView: React.FC<ServerSideEditViewProps> = async (props) => {
const { id, initPageResult, searchParams } = props
const { config, i18n, payload, searchParams, user } = props
const {
collectionConfig,
globalConfig,
req: {
i18n,
payload,
payload: { config },
user,
},
} = initPageResult
const id = 'id' in props ? props.id : undefined
const collectionConfig = 'collectionConfig' in props && props?.collectionConfig
const globalConfig = 'globalConfig' in props && props?.globalConfig
const collectionSlug = collectionConfig?.slug
const globalSlug = globalConfig?.slug
const { limit, page, sort } = searchParams
const {
routes: { api: apiRoute },
routes: { admin: adminRoute, api: apiRoute },
serverURL,
} = config
let docURL: string
let entityLabel: string
let slug: string
let editURL: string
let versionsData
if (collectionSlug) {
@@ -53,8 +51,12 @@ export const VersionsView: React.FC<ServerSideEditViewProps> = async (props) =>
},
})
} catch (error) {
console.error(error) // eslint-disable-line no-console
console.error(error)
}
docURL = `${serverURL}${apiRoute}/${slug}/${id}`
entityLabel = getTranslation(collectionConfig.labels.singular, i18n)
editURL = `${adminRoute}/collections/${collectionSlug}/${id}`
}
if (globalSlug) {
@@ -72,12 +74,16 @@ export const VersionsView: React.FC<ServerSideEditViewProps> = async (props) =>
},
})
} catch (error) {
console.error(error) // eslint-disable-line no-console
console.error(error)
}
if (!versionsData) {
return notFound()
}
docURL = `${serverURL}${apiRoute}/globals/${globalSlug}`
entityLabel = getTranslation(globalConfig.label, i18n)
editURL = `${adminRoute}/globals/${globalSlug}`
}
const columns = buildVersionColumns({

View File

@@ -1,6 +1,6 @@
{
"name": "payload",
"version": "3.0.0-alpha.12",
"version": "3.0.0-alpha.10",
"description": "Node, React and MongoDB Headless CMS and Application Framework",
"license": "MIT",
"main": "./dist/index.js",

View File

@@ -1,29 +1,3 @@
import type { ClientValidate, Field } from '../../fields/config/types'
export type Data = {
[key: string]: any
}
export type Row = {
blockType?: string
collapsed?: boolean
errorPaths?: Set<string>
id: string
}
export type FormField = {
disableFormData?: boolean
errorMessage?: string
errorPaths?: Set<string>
fieldSchema?: Field
initialValue: unknown
passesCondition?: boolean
rows?: Row[]
valid: boolean
validate?: ClientValidate
value: unknown
}
export type FormState = {
[path: string]: FormField
}

View File

@@ -23,12 +23,6 @@ export type {
DescriptionComponent,
DescriptionFunction,
} from './forms/FieldDescription'
export type { Data, FormField, FormState, Row } from './forms/Form'
export type { Data } from './forms/Form'
export type { LabelProps } from './forms/Label'
export type { RowLabel, RowLabelComponent } from './forms/RowLabel'
export type {
AdminViewComponent,
EditViewProps,
InitPageResult,
ServerSideEditViewProps,
} from './views/types'

View File

@@ -1,57 +0,0 @@
import type { DocumentPermissions, Permissions, User } from '../../auth'
import type { SanitizedCollectionConfig } from '../../collections/config/types'
import type { SanitizedGlobalConfig } from '../../globals/config/types'
import type { DocumentPreferences } from '../../preferences/types'
import type { PayloadRequest } from '../../types'
import type { Data, FormState } from '../types'
export type AdminViewConfig = {
Component: AdminViewComponent
/** Whether the path should be matched exactly or as a prefix */
exact?: boolean
path: string
sensitive?: boolean
strict?: boolean
}
export type AdminViewProps = {
canAccessAdmin?: boolean
user: User | null | undefined
}
export type AdminViewComponent = React.ComponentType<AdminViewProps>
export type AdminView = AdminViewComponent | AdminViewConfig
export type EditViewProps = {
collectionSlug?: string
globalSlug?: string
}
export type InitPageResult = {
collectionConfig?: SanitizedCollectionConfig
globalConfig?: SanitizedGlobalConfig
locale: Locale
permissions: Permissions
req: PayloadRequest
}
export type ServerSideEditViewProps = EditViewProps & {
action?: string
apiURL: string
canAccessAdmin?: boolean
data: Data
disableActions?: boolean
disableLeaveWithoutSaving?: boolean
docPermissions: DocumentPermissions
docPreferences: DocumentPreferences
hasSavePermission?: boolean
id?: string
initPageResult: InitPageResult
initialState?: FormState
isEditing?: boolean
params?: { [key: string]: string | string[] }
searchParams: { [key: string]: string | string[] | undefined }
// isLoading: boolean
updatedAt: string
}

View File

@@ -11,6 +11,7 @@ import type {
import type { Auth, IncomingAuthType, User } from '../../auth/types'
import type {
Access,
AdminViewComponent,
EditViewComponent,
EditViewConfig,
Endpoint,
@@ -255,11 +256,11 @@ export type CollectionAdminOptions = {
* + `Version` - `/admin/collections/:collection/:id/versions/:version`
* + `CustomView` - `/admin/collections/:collection/:id/:path`
*/
API?: EditViewComponent | Partial<EditViewConfig>
API?: AdminViewComponent | Partial<EditViewConfig>
Default?: EditViewComponent | Partial<EditViewConfig>
LivePreview?: EditViewComponent | Partial<EditViewConfig>
Version?: EditViewComponent | Partial<EditViewConfig>
Versions?: EditViewComponent | Partial<EditViewConfig>
LivePreview?: AdminViewComponent | Partial<EditViewConfig>
Version?: AdminViewComponent | Partial<EditViewConfig>
Versions?: AdminViewComponent | Partial<EditViewConfig>
// TODO: uncomment these as they are built
// References?: EditView
// Relationships?: EditView

View File

@@ -62,11 +62,6 @@ const sanitizeCollections = (
if ('editor' in sanitized) delete sanitized.editor
if ('upload' in sanitized && typeof sanitized.upload === 'object') {
sanitized.upload = { ...sanitized.upload }
delete sanitized.upload.handlers
}
if ('admin' in sanitized) {
sanitized.admin = { ...sanitized.admin }
@@ -124,7 +119,6 @@ export const createClientConfig = async (
delete clientConfig.endpoints
delete clientConfig.db
delete clientConfig.editor
delete clientConfig.plugins
'localization' in clientConfig &&
clientConfig.localization &&

View File

@@ -1,12 +1,20 @@
/* eslint-disable import/no-dynamic-require */
/* eslint-disable global-require */
import type pino from 'pino'
// eslint-disable-next-line import/no-extraneous-dependencies
import path from 'path'
import type { SanitizedConfig } from './types'
import Logger from '../utilities/logger'
import { clientFiles } from './clientFiles'
import findConfig from './find'
import validate from './validate'
const loadConfig = async (logger?: pino.Logger): Promise<SanitizedConfig> => {
const localLogger = logger ?? Logger()
const loadConfig = async (): Promise<SanitizedConfig> => {
const configPath = findConfig()
clientFiles.forEach((ext) => {
@@ -20,7 +28,18 @@ const loadConfig = async (): Promise<SanitizedConfig> => {
if (config.default) config = await config.default
return config
if (process.env.NODE_ENV !== 'production') {
config = await validate(config, localLogger)
}
return {
...config,
paths: {
config: configPath,
configDir: path.dirname(configPath),
rawConfig: configPath,
},
}
}
export default loadConfig

View File

@@ -73,12 +73,6 @@ export default joi.object({
}),
user: joi.string(),
}),
bin: joi.array().items(
joi.object().keys({
key: joi.string(),
scriptPath: joi.string(),
}),
),
collections: joi.array(),
cookiePrefix: joi.string(),
cors: [joi.string().valid('*'), joi.array().items(joi.string())],
@@ -154,7 +148,6 @@ export default joi.object({
graphQL: joi.string(),
graphQLPlayground: joi.string(),
}),
secret: joi.string(),
serverURL: joi
.string()
.uri()

View File

@@ -9,7 +9,6 @@ import type { DeepRequired } from 'ts-essentials'
import type { Payload } from '..'
import type { DocumentTab, RichTextAdapter } from '../admin/types'
import type { AdminView, ServerSideEditViewProps } from '../admin/views/types'
import type { User } from '../auth/types'
import type {
AfterErrorHook,
@@ -257,7 +256,30 @@ export type Endpoint<U = User> = {
root?: never
}
export type EditViewComponent = React.ComponentType<ServerSideEditViewProps>
export type AdminViewConfig = {
Component: AdminViewComponent
/** Whether the path should be matched exactly or as a prefix */
exact?: boolean
path: string
sensitive?: boolean
strict?: boolean
}
export type AdminViewProps = {
canAccessAdmin?: boolean
user: User | null | undefined
}
export type AdminViewComponent = React.ComponentType<AdminViewProps>
export type AdminView = AdminViewComponent | AdminViewConfig
export type EditViewProps = {
collectionSlug?: string
globalSlug?: string
}
export type EditViewComponent = React.ComponentType<EditViewProps>
export type EditViewConfig =
| {
@@ -271,7 +293,7 @@ export type EditViewConfig =
path?: string
}
| {
Component: EditViewComponent
Component: AdminViewComponent // TODO: The `Edit` view Component is of type `React.FC<EditViewProps>`
path: string
}
| {
@@ -285,7 +307,7 @@ export type EditViewConfig =
* All Tab properties become optional
* i.e. you can change just the label, if desired
*/
export type EditView = EditViewComponent | EditViewConfig
export type EditView = AdminViewComponent | EditViewConfig
export type Locale = {
/**

View File

@@ -10,6 +10,7 @@ import type {
import type { User } from '../../auth/types'
import type {
Access,
AdminViewComponent,
EditViewComponent,
EditViewConfig,
Endpoint,
@@ -117,11 +118,11 @@ export type GlobalAdminOptions = {
* + `Version` - `/admin/globals/:id/versions/:version`
* + `CustomView` - `/admin/globals/:id/:path`
*/
API?: EditViewComponent | Partial<EditViewConfig>
API?: AdminViewComponent | Partial<EditViewConfig>
Default?: EditViewComponent | Partial<EditViewConfig>
LivePreview?: EditViewComponent | Partial<EditViewConfig>
Version?: EditViewComponent | Partial<EditViewConfig>
Versions?: EditViewComponent | Partial<EditViewConfig>
LivePreview?: AdminViewComponent | Partial<EditViewConfig>
Version?: AdminViewComponent | Partial<EditViewConfig>
Versions?: AdminViewComponent | Partial<EditViewConfig>
// TODO: uncomment these as they are built
// References?: EditView
// Relationships?: EditView

View File

@@ -46,7 +46,6 @@ import { decrypt, encrypt } from './auth/crypto'
import { APIKeyAuthentication } from './auth/strategies/apiKey'
import { JWTAuthentication } from './auth/strategies/jwt'
import localOperations from './collections/operations/local'
import validate from './config/validate'
import buildEmail from './email/build'
import { defaults as emailDefaults } from './email/defaults'
import sendEmail from './email/sendEmail'
@@ -303,10 +302,6 @@ export class BasePayload<TGeneratedTypes extends GeneratedTypes> {
this.config = await options.config
if (process.env.NODE_ENV !== 'production') {
await validate(this.config, this.logger)
}
if (!this.config.secret) {
throw new Error('Error: missing secret key. A secret key is needed to secure Payload.')
}

View File

@@ -1,5 +1,4 @@
import type express from 'express'
import type { PayloadRequest } from 'payload/types'
import type serveStatic from 'serve-static'
import type { ResizeOptions, Sharp } from 'sharp'
@@ -83,10 +82,7 @@ export type UploadConfig = {
focalPoint?: boolean
/** Options for original upload file only. For sizes, set each formatOptions individually. */
formatOptions?: ImageUploadFormatOptions
handlers?: ((
req: PayloadRequest,
args: { params: { collection: string; filename: string } },
) => Promise<Response> | Response)[]
handlers?: any[]
imageSizes?: ImageSize[]
mimeTypes?: string[]
resizeOptions?: ResizeOptions

View File

@@ -1,3 +1,4 @@
/** @type {import('prettier').Config} */
module.exports = {
extends: ['@payloadcms'],
overrides: [

View File

@@ -15,7 +15,7 @@ services:
- '/var/run/docker.sock:/var/run/docker.sock'
azure-storage:
image: mcr.microsoft.com/azure-storage/azurite:3.29.0
image: mcr.microsoft.com/azure-storage/azurite:3.18.0
restart: always
command: 'azurite --loose --blobHost 0.0.0.0 --tableHost 0.0.0.0 --queueHost 0.0.0.0'
ports:

View File

@@ -6,7 +6,7 @@
"types": "dist/index.d.ts",
"license": "MIT",
"scripts": {
"build": "pnpm build:swc && pnpm build:types",
"build": "echo \"Build temporarily disabled.\" && exit 0",
"build:swc": "swc ./src -d ./dist --config-file .swcrc",
"build:types": "tsc --emitDeclarationOnly --outDir dist",
"clean": "rimraf {dist,*.tsbuildinfo}",
@@ -14,12 +14,12 @@
"test": "echo \"No tests available.\""
},
"peerDependencies": {
"@aws-sdk/client-s3": "^3.525.0",
"@aws-sdk/lib-storage": "^3.525.0",
"@aws-sdk/client-s3": "^3.142.0",
"@aws-sdk/lib-storage": "^3.267.0",
"@azure/abort-controller": "^1.0.0",
"@azure/storage-blob": "^12.11.0",
"@google-cloud/storage": "^7.7.0",
"payload": "workspace:*"
"payload": "^1.7.2 || ^2.0.0"
},
"peerDependenciesMeta": {
"@aws-sdk/client-s3": {
@@ -44,12 +44,18 @@
"*.d.ts"
],
"devDependencies": {
"@aws-sdk/client-s3": "^3.525.0",
"@aws-sdk/lib-storage": "^3.525.0",
"@aws-sdk/client-s3": "^3.142.0",
"@aws-sdk/lib-storage": "^3.267.0",
"@azure/storage-blob": "^12.11.0",
"@google-cloud/storage": "^7.7.0",
"@types/express": "^4.17.9",
"@types/find-node-modules": "^2.1.2",
"payload": "workspace:*"
"cross-env": "^7.0.3",
"dotenv": "^8.2.0",
"nodemon": "3.0.3",
"payload": "workspace:*",
"rimraf": "^4.1.2",
"webpack": "^5.78.0"
},
"dependencies": {
"find-node-modules": "^2.1.3",

View File

@@ -8,6 +8,7 @@ import { getGenerateURL } from './generateURL'
import { getHandleDelete } from './handleDelete'
import { getHandleUpload } from './handleUpload'
import { getHandler } from './staticHandler'
import { extendWebpackConfig } from './webpack'
export interface Args {
allowContainerCreate: boolean
@@ -43,6 +44,7 @@ export const azureBlobStorageAdapter = ({
prefix,
}),
staticHandler: getHandler({ collection, getStorageClient }),
webpack: extendWebpackConfig,
...(allowContainerCreate && { onInit: createContainerIfNotExists }),
}
}

View File

@@ -0,0 +1 @@
export const azureBlobStorageAdapter = () => {}

View File

@@ -14,11 +14,13 @@ interface Args {
}
export const getHandler = ({ collection, getStorageClient }: Args): StaticHandler => {
return async (req, { params }) => {
return async (req, res, next) => {
try {
const prefix = await getFilePrefix({ collection, req })
const blockBlobClient = getStorageClient().getBlockBlobClient(
path.posix.join(prefix, params.filename),
// WARNING:
// TODO: Untested for 3.0
path.posix.join(prefix, req.routeParams.filename as string),
)
const { end, start } = await getRangeFromHeader(blockBlobClient, req.headers.get('range'))
@@ -26,30 +28,16 @@ export const getHandler = ({ collection, getStorageClient }: Args): StaticHandle
const blob = await blockBlobClient.download(start, end)
// eslint-disable-next-line no-underscore-dangle
const response = blob._response
res.header(response.headers.rawHeaders())
res.status(response.status)
// Manually create a ReadableStream for the web from a Node.js stream.
const readableStream = new ReadableStream({
start(controller) {
const nodeStream = blob.readableStreamBody
nodeStream.on('data', (chunk) => {
controller.enqueue(new Uint8Array(chunk))
})
nodeStream.on('end', () => {
controller.close()
})
nodeStream.on('error', (err) => {
controller.error(err)
})
},
})
if (blob?.readableStreamBody) {
return blob.readableStreamBody.pipe(res)
}
return new Response(readableStream, {
headers: response.headers.rawHeaders(),
status: response.status,
})
return next()
} catch (err: unknown) {
req.payload.logger.error(err)
return new Response('Internal Server Error', { status: 500 })
return next()
}
}
}

View File

@@ -0,0 +1,22 @@
import type { Configuration as WebpackConfig } from 'webpack'
import path from 'path'
export const extendWebpackConfig = (existingWebpackConfig: WebpackConfig): WebpackConfig => {
const newConfig: WebpackConfig = {
...existingWebpackConfig,
resolve: {
...(existingWebpackConfig.resolve || {}),
alias: {
...(existingWebpackConfig.resolve?.alias ? existingWebpackConfig.resolve.alias : {}),
'@payloadcms/plugin-cloud-storage/azure': path.resolve(__dirname, './mock.js'),
},
fallback: {
...(existingWebpackConfig.resolve?.fallback ? existingWebpackConfig.resolve.fallback : {}),
stream: false,
},
},
}
return newConfig
}

View File

@@ -8,6 +8,7 @@ import { getGenerateURL } from './generateURL'
import { getHandleDelete } from './handleDelete'
import { getHandleUpload } from './handleUpload'
import { getHandler } from './staticHandler'
import { extendWebpackConfig } from './webpack'
export interface Args {
acl?: 'Private' | 'Public'
@@ -37,5 +38,6 @@ export const gcsAdapter =
prefix,
}),
staticHandler: getHandler({ bucket, collection, getStorageClient }),
webpack: extendWebpackConfig,
}
}

View File

@@ -0,0 +1 @@
export const gcsAdapter = () => {}

View File

@@ -14,40 +14,26 @@ interface Args {
}
export const getHandler = ({ bucket, collection, getStorageClient }: Args): StaticHandler => {
return async (req, { params }) => {
return async (req, res, next) => {
try {
const prefix = await getFilePrefix({ collection, req })
const file = getStorageClient().bucket(bucket).file(path.posix.join(prefix, params.filename))
const file = getStorageClient()
.bucket(bucket)
// WARNING:
// TODO: Untested for 3.0
.file(path.posix.join(prefix, req.routeParams.filename as string))
const [metadata] = await file.getMetadata()
// Manually create a ReadableStream for the web from a Node.js stream.
const readableStream = new ReadableStream({
start(controller) {
const nodeStream = file.createReadStream()
nodeStream.on('data', (chunk) => {
controller.enqueue(new Uint8Array(chunk))
})
nodeStream.on('end', () => {
controller.close()
})
nodeStream.on('error', (err) => {
controller.error(err)
})
},
res.set({
'Content-Length': metadata.size,
'Content-Type': metadata.contentType,
ETag: metadata.etag,
})
return new Response(readableStream, {
headers: new Headers({
'Content-Length': String(metadata.size),
'Content-Type': metadata.contentType,
ETag: metadata.etag,
}),
status: 200,
})
return file.createReadStream().pipe(res)
} catch (err: unknown) {
req.payload.logger.error(err)
return new Response('Internal Server Error', { status: 500 })
return next()
}
}
}

View File

@@ -0,0 +1,22 @@
import type { Configuration as WebpackConfig } from 'webpack'
import path from 'path'
export const extendWebpackConfig = (existingWebpackConfig: WebpackConfig): WebpackConfig => {
const newConfig: WebpackConfig = {
...existingWebpackConfig,
resolve: {
...(existingWebpackConfig.resolve || {}),
alias: {
...(existingWebpackConfig.resolve?.alias ? existingWebpackConfig.resolve.alias : {}),
'@google-cloud/storage': path.resolve(__dirname, './mock.js'),
},
fallback: {
...(existingWebpackConfig.resolve?.fallback ? existingWebpackConfig.resolve.fallback : {}),
stream: false,
},
},
}
return newConfig
}

View File

@@ -6,6 +6,7 @@ import { getGenerateURL } from './generateURL'
import { getHandleDelete } from './handleDelete'
import { getHandleUpload } from './handleUpload'
import { getHandler } from './staticHandler'
import { extendWebpackConfig } from './webpack'
export interface Args {
acl?: 'private' | 'public-read'
@@ -44,5 +45,6 @@ export const s3Adapter =
prefix,
}),
staticHandler: getHandler({ bucket, collection, getStorageClient }),
webpack: extendWebpackConfig,
}
}

View File

@@ -0,0 +1 @@
export const s3Adapter = () => {}

View File

@@ -1,5 +1,6 @@
import type * as AWS from '@aws-sdk/client-s3'
import type { CollectionConfig } from 'payload/types'
import type { Readable } from 'stream'
import path from 'path'
@@ -13,43 +14,33 @@ interface Args {
getStorageClient: () => AWS.S3
}
// Convert a stream into a promise that resolves with a Buffer
const streamToBuffer = async (readableStream) => {
const chunks = []
for await (const chunk of readableStream) {
chunks.push(typeof chunk === 'string' ? Buffer.from(chunk) : chunk)
}
return Buffer.concat(chunks)
}
export const getHandler = ({ bucket, collection, getStorageClient }: Args): StaticHandler => {
return async (req, { params }) => {
return async (req, res, next) => {
try {
const prefix = await getFilePrefix({ collection, req })
const object = await getStorageClient().getObject({
Bucket: bucket,
Key: path.posix.join(prefix, params.filename),
// WARNING:
// TODO: Untested for 3.0
Key: path.posix.join(prefix, req.routeParams.filename as string),
})
if (!object.Body) {
return new Response(null, { status: 404, statusText: 'Not Found' })
res.set({
'Accept-Ranges': object.AcceptRanges,
'Content-Length': object.ContentLength,
'Content-Type': object.ContentType,
ETag: object.ETag,
})
if (object?.Body) {
return (object.Body as Readable).pipe(res)
}
const bodyBuffer = await streamToBuffer(object.Body)
return new Response(bodyBuffer, {
headers: new Headers({
'Accept-Ranges': object.AcceptRanges,
'Content-Length': String(object.ContentLength),
'Content-Type': object.ContentType,
ETag: object.ETag,
}),
status: 200,
})
} catch (err) {
return next()
} catch (err: unknown) {
req.payload.logger.error(err)
return new Response('Internal Server Error', { status: 500 })
return next()
}
}
}

View File

@@ -0,0 +1,44 @@
import type { Configuration as WebpackConfig } from 'webpack'
import findNodeModules from 'find-node-modules'
import fs from 'fs'
import path from 'path'
const packageName = '@payloadcms/plugin-cloud-storage'
const nodeModulesPaths = findNodeModules({ cwd: __dirname, relative: false })
export const extendWebpackConfig = (existingWebpackConfig: WebpackConfig): WebpackConfig => {
let nodeModulesPath = nodeModulesPaths.find((p) => {
const guess = path.resolve(p, `${packageName}/dist`)
if (fs.existsSync(guess)) {
return true
}
return false
})
if (!nodeModulesPath) {
nodeModulesPath = process.cwd()
}
const newConfig: WebpackConfig = {
...existingWebpackConfig,
resolve: {
...(existingWebpackConfig.resolve || {}),
alias: {
...(existingWebpackConfig.resolve?.alias ? existingWebpackConfig.resolve.alias : {}),
'@payloadcms/plugin-cloud-storage/s3$': path.resolve(
nodeModulesPath,
`./${packageName}/dist/adapters/s3/mock.js`,
),
},
fallback: {
...(existingWebpackConfig.resolve?.fallback ? existingWebpackConfig.resolve.fallback : {}),
stream: false,
},
},
}
return newConfig
}

View File

@@ -5,6 +5,7 @@ import type { PluginOptions } from './types'
import { getFields } from './fields/getFields'
import { getAfterDeleteHook } from './hooks/afterDelete'
import { getBeforeChangeHook } from './hooks/beforeChange'
import { extendWebpackConfig } from './webpack'
// This plugin extends all targeted collections by offloading uploaded files
// to cloud storage instead of solely storing files locally.
@@ -21,6 +22,13 @@ export const cloudStorage =
const { collections: allCollectionOptions, enabled } = pluginOptions
const config = { ...incomingConfig }
const webpack = extendWebpackConfig({ config: incomingConfig, options: pluginOptions })
config.admin = {
...(config.admin || {}),
webpack,
}
// Return early if disabled. Only webpack config mods are applied.
if (enabled === false) {
return config

View File

@@ -1,6 +1,8 @@
import type { NextFunction, Response } from 'express'
import type { FileData, ImageSize } from 'payload/types'
import type { TypeWithID } from 'payload/types'
import type { CollectionConfig, PayloadRequest } from 'payload/types'
import type { Configuration as WebpackConfig } from 'webpack'
export interface File {
buffer: Buffer
@@ -36,8 +38,9 @@ export type GenerateURL = (args: {
export type StaticHandler = (
req: PayloadRequest,
args: { params: { collection: string; filename: string } },
) => Promise<Response> | Response
res: Response,
next: NextFunction,
) => Promise<unknown> | unknown
export interface GeneratedAdapter {
generateURL: GenerateURL
@@ -45,6 +48,7 @@ export interface GeneratedAdapter {
handleUpload: HandleUpload
onInit?: () => void
staticHandler: StaticHandler
webpack?: (config: WebpackConfig) => WebpackConfig
}
export type Adapter = (args: { collection: CollectionConfig; prefix?: string }) => GeneratedAdapter

View File

@@ -0,0 +1,52 @@
import type { Config } from 'payload/config'
import type { Configuration as WebpackConfig } from 'webpack'
import path from 'path'
import type { GeneratedAdapter, PluginOptions } from './types'
interface Args {
config: Config
options: PluginOptions
}
export const extendWebpackConfig =
({ config, options }: Args): ((webpackConfig: WebpackConfig) => WebpackConfig) =>
(webpackConfig) => {
const existingWebpackConfig =
typeof config.admin?.webpack === 'function'
? config.admin.webpack(webpackConfig)
: webpackConfig
const newConfig: WebpackConfig = {
...existingWebpackConfig,
resolve: {
...(existingWebpackConfig.resolve || {}),
alias: {
...(existingWebpackConfig.resolve?.alias ? existingWebpackConfig.resolve.alias : {}),
'@payloadcms/plugin-cloud-storage$': path.resolve(__dirname, './admin/index.js'),
},
},
}
const modifiedConfig = Object.entries(options.collections).reduce(
(resultingWebpackConfig, [slug, collectionOptions]) => {
const matchedCollection = config.collections?.find((coll) => coll.slug === slug)
if (matchedCollection && typeof collectionOptions.adapter === 'function') {
const adapter: GeneratedAdapter = collectionOptions.adapter({
collection: matchedCollection,
})
if (adapter.webpack) {
return adapter.webpack(resultingWebpackConfig)
}
}
return resultingWebpackConfig
},
newConfig,
)
return modifiedConfig
}

View File

@@ -0,0 +1,14 @@
MONGODB_URI=mongodb://localhost/payload-plugin-cloud
PAYLOAD_PUBLIC_SERVER_URL=http://localhost:3000
PAYLOAD_SECRET=45ligj345ligj4wl5igj4lw5igj45ligj45wlijl
PAYLOAD_CONFIG_PATH=src/payload.config.ts
PAYLOAD_CLOUD=true
PAYLOAD_CLOUD_BUCKET=
PAYLOAD_CLOUD_COGNITO_IDENTITY_POOL_ID=
PAYLOAD_CLOUD_COGNITO_USER_POOL_CLIENT_ID=
PAYLOAD_CLOUD_COGNITO_USER_POOL_ID=
PAYLOAD_CLOUD_ENVIRONMENT=
PAYLOAD_CLOUD_PROJECT_ID=
PAYLOAD_CLOUD_COGNITO_PASSWORD=
PAYLOAD_CLOUD_BUCKET_REGION=

View File

@@ -0,0 +1,5 @@
{
"exec": "ts-node src/server.ts",
"ext": "ts",
"watch": ["src/**/*.ts", "../src/**/*.ts"]
}

View File

@@ -0,0 +1,28 @@
{
"name": "payload-plugin-cloud-demo",
"version": "1.0.0",
"main": "dist/server.js",
"license": "MIT",
"private": true,
"scripts": {
"dev": "cross-env PAYLOAD_CONFIG_PATH=src/payload.config.ts nodemon",
"build:payload": "cross-env PAYLOAD_CONFIG_PATH=src/payload.config.ts payload build",
"build:server": "tsc",
"build": "yarn build:payload && yarn build:server",
"serve": "cross-env PAYLOAD_CONFIG_PATH=dist/payload.config.js NODE_ENV=production node dist/server.js",
"generate:types": "cross-env PAYLOAD_CONFIG_PATH=src/payload.config.ts payload generate:types"
},
"dependencies": {
"@aws-sdk/client-s3": "^3.142.0",
"dotenv": "^8.2.0",
"express": "^4.17.1",
"payload": "^1.8.2"
},
"devDependencies": {
"@types/express": "^4.17.9",
"cross-env": "^7.0.3",
"nodemon": "^2.0.6",
"ts-node": "10.9.1",
"typescript": "^4.1.3"
}
}

View File

@@ -0,0 +1,56 @@
/* eslint-disable no-console */
import type { CollectionConfig, Field } from 'payload/types'
const urlField: Field = {
name: 'url',
type: 'text',
hooks: {
afterRead: [
({ value }) => {
console.log('hello from hook')
return value
},
],
},
}
export const Media: CollectionConfig = {
slug: 'media',
upload: {
imageSizes: [
{
height: 400,
width: 400,
crop: 'center',
name: 'square',
},
{
width: 900,
height: 450,
crop: 'center',
name: 'sixteenByNineMedium',
},
],
},
fields: [
{
name: 'alt',
label: 'Alt Text',
type: 'text',
},
// The following fields should be able to be merged in to default upload fields
urlField,
{
name: 'sizes',
type: 'group',
fields: [
{
name: 'square',
type: 'group',
fields: [urlField],
},
],
},
],
}

View File

@@ -0,0 +1,23 @@
import type { CollectionConfig } from 'payload/types'
const Users: CollectionConfig = {
slug: 'users',
auth: true,
access: {
read: () => true,
},
fields: [
{
name: 'avatar',
type: 'upload',
relationTo: 'media',
},
{
name: 'background',
type: 'upload',
relationTo: 'media',
},
],
}
export default Users

View File

@@ -0,0 +1,49 @@
import { buildConfig } from 'payload/config'
import path from 'path'
import Users from './collections/Users'
import { payloadCloud } from '../../src'
import { Media } from './collections/Media'
export default buildConfig({
serverURL: 'http://localhost:3000',
collections: [Media, Users],
admin: {
// NOTE - these webpack extensions are only required
// for development of this plugin.
// No need to use these aliases within your own projects.
webpack: (config) => {
return {
...config,
resolve: {
...(config.resolve || {}),
alias: {
...(config.resolve.alias || {}),
[path.resolve(__dirname, '../../src')]: path.resolve(__dirname, '../../src/admin.js'),
react: path.resolve(__dirname, '../node_modules/react'),
},
},
}
},
},
typescript: {
outputFile: path.resolve(__dirname, 'payload-types.ts'),
},
// @ts-expect-error local reference
plugins: [payloadCloud()],
onInit: async (payload) => {
const users = await payload.find({
collection: 'users',
limit: 1,
})
if (!users.docs.length) {
await payload.create({
collection: 'users',
data: {
email: 'dev@payloadcms.com',
password: 'test',
},
})
}
},
})

View File

@@ -0,0 +1,26 @@
import dotenv from 'dotenv'
import express from 'express'
import payload from 'payload'
dotenv.config()
const app = express()
// Redirect root to Admin panel
app.get('/', (_, res) => {
res.redirect('/admin')
})
// Initialize Payload
payload.init({
secret: process.env.PAYLOAD_SECRET,
mongoURL: process.env.MONGODB_URI,
express: app,
onInit: () => {
payload.logger.info(`Payload Admin URL: ${payload.getAdminURL()}`)
},
})
// Add your own express routes here
app.listen(3000)

View File

@@ -0,0 +1,16 @@
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"strict": false,
"esModuleInterop": true,
"skipLibCheck": true,
"outDir": "./dist",
"rootDir": "../",
"jsx": "react",
"sourceMap": true
},
"ts-node": {
"transpileOnly": true
}
}

View File

@@ -14,22 +14,24 @@
"test": "jest"
},
"peerDependencies": {
"payload": "workspace:*"
"payload": "^1.8.2 || ^2.0.0"
},
"dependencies": {
"@aws-sdk/client-cognito-identity": "^3.525.0",
"@aws-sdk/client-s3": "^3.525.0",
"@aws-sdk/credential-providers": "^3.525.0",
"@aws-sdk/lib-storage": "^3.525.0",
"@aws-sdk/client-cognito-identity": "^3.289.0",
"@aws-sdk/client-s3": "^3.142.0",
"@aws-sdk/credential-providers": "^3.289.0",
"@aws-sdk/lib-storage": "^3.267.0",
"amazon-cognito-identity-js": "^6.1.2",
"nodemailer": "6.9.10",
"resend": "^0.17.2"
},
"devDependencies": {
"@types/express": "^4.17.9",
"@types/jest": "^29.5.1",
"@types/nodemailer": "6.4.14",
"payload": "workspace:*",
"ts-jest": "^29.1.0"
"ts-jest": "^29.1.0",
"webpack": "^5.78.0"
},
"files": [
"dist"

View File

@@ -1,9 +1,6 @@
import type {
CollectionAfterDeleteHook,
CollectionConfig,
FileData,
TypeWithID,
} from 'payload/types'
import type { TypeWithID } from 'payload/types'
import type { FileData } from 'payload/types'
import type { CollectionAfterDeleteHook, CollectionConfig } from 'payload/types'
import type { TypeWithPrefix } from '../types'

View File

@@ -1,9 +1,5 @@
import type {
CollectionBeforeChangeHook,
CollectionConfig,
FileData,
TypeWithID,
} from 'payload/types'
import type { TypeWithID, FileData } from 'payload/types'
import type { CollectionBeforeChangeHook, CollectionConfig } from 'payload/types'
import type stream from 'stream'
import { Upload } from '@aws-sdk/lib-storage'

View File

@@ -10,7 +10,7 @@ interface Args {
export const getCacheUploadsAfterChangeHook =
({ endpoint }: Args): CollectionAfterChangeHook =>
({ doc, operation, req }) => {
async ({ doc, operation, req }) => {
if (!req || !process.env.PAYLOAD_CLOUD_CACHE_KEY) return doc
// WARNING:
@@ -27,7 +27,7 @@ export const getCacheUploadsAfterChangeHook =
export const getCacheUploadsAfterDeleteHook =
({ endpoint }: Args): CollectionAfterDeleteHook =>
({ doc, req }) => {
async ({ doc, req }) => {
if (!req || !process.env.PAYLOAD_CLOUD_CACHE_KEY) return doc
const { payloadAPI } = req

Some files were not shown because too many files have changed in this diff Show More