Compare commits
1 Commits
v3.0.0-alp
...
v3.0.0-alp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
67e9306f7a |
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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',
|
||||
})
|
||||
|
||||
@@ -6,14 +6,5 @@ const withBundleAnalyzer = require('@next/bundle-analyzer')({
|
||||
module.exports = withBundleAnalyzer(
|
||||
withPayload({
|
||||
reactStrictMode: false,
|
||||
async redirects() {
|
||||
return [
|
||||
{
|
||||
destination: '/admin',
|
||||
permanent: true,
|
||||
source: '/',
|
||||
},
|
||||
]
|
||||
},
|
||||
}),
|
||||
)
|
||||
|
||||
20
package.json
20
package.json
@@ -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": {
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -158,9 +158,7 @@ export const processMultipart: ProcessMultipart = async ({ options, request }) =
|
||||
parsingRequest = false
|
||||
}
|
||||
|
||||
if (value) {
|
||||
busboy.write(value)
|
||||
}
|
||||
busboy.write(value)
|
||||
}
|
||||
|
||||
return result
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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 + '',
|
||||
|
||||
@@ -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 })
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
'use client'
|
||||
|
||||
import type { EditViewProps } from 'payload/types'
|
||||
import type { EditViewProps } from 'payload/config'
|
||||
|
||||
import {
|
||||
Checkbox,
|
||||
|
||||
@@ -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'
|
||||
|
||||
|
||||
@@ -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 (
|
||||
|
||||
@@ -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'),
|
||||
})
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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'),
|
||||
})
|
||||
}
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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'),
|
||||
})
|
||||
}
|
||||
@@ -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' &&
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
import { GenerateEditViewMetadata, getMetaBySegment } from './getMetaBySegment'
|
||||
|
||||
export const generateDocumentMetadata: GenerateEditViewMetadata = async (args) =>
|
||||
getMetaBySegment(args)
|
||||
@@ -1,6 +1,5 @@
|
||||
'use client'
|
||||
|
||||
import type { EditViewProps } from 'payload/types'
|
||||
import type { EditViewProps } from 'payload/config'
|
||||
|
||||
import {
|
||||
LoadingOverlay,
|
||||
|
||||
@@ -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'
|
||||
|
||||
|
||||
@@ -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
43
packages/next/src/views/Edit/types.d.ts
vendored
Normal 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
|
||||
1
packages/next/src/views/Edit/types.d.ts.map
Normal file
1
packages/next/src/views/Edit/types.d.ts.map
Normal 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"}
|
||||
37
packages/next/src/views/Edit/types.ts
Normal file
37
packages/next/src/views/Edit/types.ts
Normal 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
|
||||
}
|
||||
@@ -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 },
|
||||
|
||||
@@ -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'),
|
||||
})
|
||||
}
|
||||
@@ -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 },
|
||||
|
||||
@@ -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,
|
||||
})
|
||||
}
|
||||
@@ -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'
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
'use client'
|
||||
|
||||
import type { EditViewProps } from 'payload/types'
|
||||
import type { EditViewProps } from 'payload/config'
|
||||
|
||||
import {
|
||||
ShimmerEffect,
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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'),
|
||||
})
|
||||
}
|
||||
@@ -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} />
|
||||
}
|
||||
|
||||
@@ -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'),
|
||||
})
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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'),
|
||||
})
|
||||
}
|
||||
@@ -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'),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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}`} />
|
||||
}
|
||||
|
||||
@@ -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'),
|
||||
})
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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'),
|
||||
})
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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 &&
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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 = {
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.')
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/** @type {import('prettier').Config} */
|
||||
module.exports = {
|
||||
extends: ['@payloadcms'],
|
||||
overrides: [
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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 }),
|
||||
}
|
||||
}
|
||||
|
||||
1
packages/plugin-cloud-storage/src/adapters/azure/mock.js
Normal file
1
packages/plugin-cloud-storage/src/adapters/azure/mock.js
Normal file
@@ -0,0 +1 @@
|
||||
export const azureBlobStorageAdapter = () => {}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
22
packages/plugin-cloud-storage/src/adapters/azure/webpack.ts
Normal file
22
packages/plugin-cloud-storage/src/adapters/azure/webpack.ts
Normal 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
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
1
packages/plugin-cloud-storage/src/adapters/gcs/mock.js
Normal file
1
packages/plugin-cloud-storage/src/adapters/gcs/mock.js
Normal file
@@ -0,0 +1 @@
|
||||
export const gcsAdapter = () => {}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
22
packages/plugin-cloud-storage/src/adapters/gcs/webpack.ts
Normal file
22
packages/plugin-cloud-storage/src/adapters/gcs/webpack.ts
Normal 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
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
1
packages/plugin-cloud-storage/src/adapters/s3/mock.js
Normal file
1
packages/plugin-cloud-storage/src/adapters/s3/mock.js
Normal file
@@ -0,0 +1 @@
|
||||
export const s3Adapter = () => {}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
44
packages/plugin-cloud-storage/src/adapters/s3/webpack.ts
Normal file
44
packages/plugin-cloud-storage/src/adapters/s3/webpack.ts
Normal 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
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
52
packages/plugin-cloud-storage/src/webpack.ts
Normal file
52
packages/plugin-cloud-storage/src/webpack.ts
Normal 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
|
||||
}
|
||||
14
packages/plugin-cloud/dev/.env.example
Normal file
14
packages/plugin-cloud/dev/.env.example
Normal 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=
|
||||
5
packages/plugin-cloud/dev/nodemon.json
Normal file
5
packages/plugin-cloud/dev/nodemon.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"exec": "ts-node src/server.ts",
|
||||
"ext": "ts",
|
||||
"watch": ["src/**/*.ts", "../src/**/*.ts"]
|
||||
}
|
||||
28
packages/plugin-cloud/dev/package.json
Normal file
28
packages/plugin-cloud/dev/package.json
Normal 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"
|
||||
}
|
||||
}
|
||||
56
packages/plugin-cloud/dev/src/collections/Media.ts
Normal file
56
packages/plugin-cloud/dev/src/collections/Media.ts
Normal 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],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
23
packages/plugin-cloud/dev/src/collections/Users.ts
Normal file
23
packages/plugin-cloud/dev/src/collections/Users.ts
Normal 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
|
||||
49
packages/plugin-cloud/dev/src/payload.config.ts
Normal file
49
packages/plugin-cloud/dev/src/payload.config.ts
Normal 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',
|
||||
},
|
||||
})
|
||||
}
|
||||
},
|
||||
})
|
||||
26
packages/plugin-cloud/dev/src/server.ts
Normal file
26
packages/plugin-cloud/dev/src/server.ts
Normal 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)
|
||||
16
packages/plugin-cloud/dev/tsconfig.json
Normal file
16
packages/plugin-cloud/dev/tsconfig.json
Normal 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
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
|
||||
@@ -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'
|
||||
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user