fix(next): proper 404 handling
This commit is contained in:
@@ -1,7 +1,9 @@
|
|||||||
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
|
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
|
||||||
|
import type { Metadata } from 'next'
|
||||||
|
|
||||||
import config from '@payload-config'
|
import config from '@payload-config'
|
||||||
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
|
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
|
||||||
import { NotFoundView } from '@payloadcms/next/views/NotFound/index.js'
|
import { NotFoundPage, generatePageMetadata } from '@payloadcms/next/views/NotFound/index.js'
|
||||||
|
|
||||||
type Args = {
|
type Args = {
|
||||||
params: {
|
params: {
|
||||||
@@ -12,6 +14,9 @@ type Args = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const NotFound = ({ params, searchParams }: Args) => NotFoundView({ config, params, searchParams })
|
export const generateMetadata = ({ params, searchParams }: Args): Promise<Metadata> =>
|
||||||
|
generatePageMetadata({ config, params, searchParams })
|
||||||
|
|
||||||
|
const NotFound = ({ params, searchParams }: Args) => NotFoundPage({ config, params, searchParams })
|
||||||
|
|
||||||
export default NotFound
|
export default NotFound
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
export { EditView } from '../views/Edit/index.js'
|
export { EditView } from '../views/Edit/index.js'
|
||||||
export { NotFoundView } from '../views/NotFound/index.js'
|
export { NotFoundPage } from '../views/NotFound/index.js'
|
||||||
export { type GenerateViewMetadata, RootPage, generatePageMetadata } from '../views/Root/index.js'
|
export { type GenerateViewMetadata, RootPage, generatePageMetadata } from '../views/Root/index.js'
|
||||||
|
|||||||
@@ -22,8 +22,8 @@ import { getRequestLanguage } from './getRequestLanguage.js'
|
|||||||
type Args = {
|
type Args = {
|
||||||
config: Promise<SanitizedConfig> | SanitizedConfig
|
config: Promise<SanitizedConfig> | SanitizedConfig
|
||||||
redirectUnauthenticatedUser?: boolean
|
redirectUnauthenticatedUser?: boolean
|
||||||
route?: string
|
route: string
|
||||||
searchParams?: { [key: string]: string | string[] | undefined }
|
searchParams: { [key: string]: string | string[] | undefined }
|
||||||
}
|
}
|
||||||
|
|
||||||
export const initPage = async ({
|
export const initPage = async ({
|
||||||
@@ -59,7 +59,7 @@ export const initPage = async ({
|
|||||||
const { collections, globals, localization, routes } = payload.config
|
const { collections, globals, localization, routes } = payload.config
|
||||||
|
|
||||||
if (redirectUnauthenticatedUser && !user && route !== '/login') {
|
if (redirectUnauthenticatedUser && !user && route !== '/login') {
|
||||||
if ('redirect' in searchParams) delete searchParams.redirect
|
if (searchParams && 'redirect' in searchParams) delete searchParams.redirect
|
||||||
|
|
||||||
const stringifiedSearchParams = Object.keys(searchParams ?? {}).length
|
const stringifiedSearchParams = Object.keys(searchParams ?? {}).length
|
||||||
? `?${qs.stringify(searchParams)}`
|
? `?${qs.stringify(searchParams)}`
|
||||||
@@ -81,7 +81,7 @@ export const initPage = async ({
|
|||||||
translations,
|
translations,
|
||||||
})
|
})
|
||||||
|
|
||||||
const queryString = `${qs.stringify(searchParams, { addQueryPrefix: true })}`
|
const queryString = `${qs.stringify(searchParams ?? {}, { addQueryPrefix: true })}`
|
||||||
|
|
||||||
const req = createLocalReq(
|
const req = createLocalReq(
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -7,12 +7,9 @@ import type {
|
|||||||
SanitizedGlobalConfig,
|
SanitizedGlobalConfig,
|
||||||
} from 'payload/types'
|
} from 'payload/types'
|
||||||
|
|
||||||
import React from 'react'
|
|
||||||
|
|
||||||
import { APIView as DefaultAPIView } from '../API/index.js'
|
import { APIView as DefaultAPIView } from '../API/index.js'
|
||||||
import { EditView as DefaultEditView } from '../Edit/index.js'
|
import { EditView as DefaultEditView } from '../Edit/index.js'
|
||||||
import { LivePreviewView as DefaultLivePreviewView } from '../LivePreview/index.js'
|
import { LivePreviewView as DefaultLivePreviewView } from '../LivePreview/index.js'
|
||||||
import { NotFoundClient } from '../NotFound/index.client.js'
|
|
||||||
import { Unauthorized } from '../Unauthorized/index.js'
|
import { Unauthorized } from '../Unauthorized/index.js'
|
||||||
import { VersionView as DefaultVersionView } from '../Version/index.js'
|
import { VersionView as DefaultVersionView } from '../Version/index.js'
|
||||||
import { VersionsView as DefaultVersionsView } from '../Versions/index.js'
|
import { VersionsView as DefaultVersionsView } from '../Versions/index.js'
|
||||||
@@ -140,9 +137,6 @@ export const getViewsFromConfig = ({
|
|||||||
currentRoute,
|
currentRoute,
|
||||||
views,
|
views,
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!CustomView) ErrorView = () => <NotFoundClient />
|
|
||||||
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -172,8 +166,6 @@ export const getViewsFromConfig = ({
|
|||||||
currentRoute,
|
currentRoute,
|
||||||
views,
|
views,
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!CustomView) ErrorView = () => <NotFoundClient />
|
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -266,8 +258,6 @@ export const getViewsFromConfig = ({
|
|||||||
currentRoute,
|
currentRoute,
|
||||||
views,
|
views,
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!CustomView) ErrorView = () => <NotFoundClient />
|
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,13 +9,12 @@ import { RenderCustomComponent } from '@payloadcms/ui/elements/RenderCustomCompo
|
|||||||
import { DocumentInfoProvider } from '@payloadcms/ui/providers/DocumentInfo'
|
import { DocumentInfoProvider } from '@payloadcms/ui/providers/DocumentInfo'
|
||||||
import { EditDepthProvider } from '@payloadcms/ui/providers/EditDepth'
|
import { EditDepthProvider } from '@payloadcms/ui/providers/EditDepth'
|
||||||
import { FormQueryParamsProvider } from '@payloadcms/ui/providers/FormQueryParams'
|
import { FormQueryParamsProvider } from '@payloadcms/ui/providers/FormQueryParams'
|
||||||
|
import { notFound } from 'next/navigation.js'
|
||||||
import { docAccessOperation } from 'payload/operations'
|
import { docAccessOperation } from 'payload/operations'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
import type { GenerateEditViewMetadata } from './getMetaBySegment.js'
|
import type { GenerateEditViewMetadata } from './getMetaBySegment.js'
|
||||||
|
|
||||||
import { NotFoundClient } from '../NotFound/index.client.js'
|
|
||||||
import { NotFoundView } from '../NotFound/index.js'
|
|
||||||
import { getMetaBySegment } from './getMetaBySegment.js'
|
import { getMetaBySegment } from './getMetaBySegment.js'
|
||||||
import { getViewsFromConfig } from './getViewsFromConfig.js'
|
import { getViewsFromConfig } from './getViewsFromConfig.js'
|
||||||
|
|
||||||
@@ -57,7 +56,7 @@ export const Document: React.FC<AdminViewProps> = async ({
|
|||||||
let ViewOverride: EditViewComponent
|
let ViewOverride: EditViewComponent
|
||||||
let CustomView: EditViewComponent
|
let CustomView: EditViewComponent
|
||||||
let DefaultView: EditViewComponent
|
let DefaultView: EditViewComponent
|
||||||
let ErrorView: AdminViewComponent = NotFoundView
|
let ErrorView: AdminViewComponent
|
||||||
|
|
||||||
let docPermissions: DocumentPermissions
|
let docPermissions: DocumentPermissions
|
||||||
let hasSavePermission: boolean
|
let hasSavePermission: boolean
|
||||||
@@ -66,7 +65,7 @@ export const Document: React.FC<AdminViewProps> = async ({
|
|||||||
|
|
||||||
if (collectionConfig) {
|
if (collectionConfig) {
|
||||||
if (!visibleEntities?.collections?.find((visibleSlug) => visibleSlug === collectionSlug)) {
|
if (!visibleEntities?.collections?.find((visibleSlug) => visibleSlug === collectionSlug)) {
|
||||||
return <NotFoundClient />
|
notFound()
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -78,7 +77,7 @@ export const Document: React.FC<AdminViewProps> = async ({
|
|||||||
req,
|
req,
|
||||||
})
|
})
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return <NotFoundClient />
|
notFound()
|
||||||
}
|
}
|
||||||
|
|
||||||
action = `${serverURL}${apiRoute}/${collectionSlug}${isEditing ? `/${id}` : ''}`
|
action = `${serverURL}${apiRoute}/${collectionSlug}${isEditing ? `/${id}` : ''}`
|
||||||
@@ -108,13 +107,17 @@ export const Document: React.FC<AdminViewProps> = async ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!CustomView && !DefaultView && !ViewOverride) {
|
if (!CustomView && !DefaultView && !ViewOverride) {
|
||||||
return <ErrorView initPageResult={initPageResult} searchParams={searchParams} />
|
if (ErrorView) {
|
||||||
|
return <ErrorView initPageResult={initPageResult} searchParams={searchParams} />
|
||||||
|
}
|
||||||
|
|
||||||
|
notFound()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (globalConfig) {
|
if (globalConfig) {
|
||||||
if (!visibleEntities?.globals?.find((visibleSlug) => visibleSlug === globalSlug)) {
|
if (!visibleEntities?.globals?.find((visibleSlug) => visibleSlug === globalSlug)) {
|
||||||
return <NotFoundClient />
|
notFound()
|
||||||
}
|
}
|
||||||
|
|
||||||
docPermissions = permissions?.globals?.[globalSlug]
|
docPermissions = permissions?.globals?.[globalSlug]
|
||||||
@@ -141,7 +144,11 @@ export const Document: React.FC<AdminViewProps> = async ({
|
|||||||
ErrorView = globalViews?.ErrorView
|
ErrorView = globalViews?.ErrorView
|
||||||
|
|
||||||
if (!CustomView && !DefaultView && !ViewOverride) {
|
if (!CustomView && !DefaultView && !ViewOverride) {
|
||||||
return <ErrorView initPageResult={initPageResult} searchParams={searchParams} />
|
if (ErrorView) {
|
||||||
|
return <ErrorView initPageResult={initPageResult} searchParams={searchParams} />
|
||||||
|
}
|
||||||
|
|
||||||
|
notFound()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,61 @@
|
|||||||
import type { AdminViewComponent } from 'payload/types'
|
import type { I18n } from '@payloadcms/translations'
|
||||||
|
import type { Metadata } from 'next'
|
||||||
|
import type { SanitizedConfig } from 'payload/types'
|
||||||
|
|
||||||
|
import { HydrateClientUser } from '@payloadcms/ui/elements/HydrateClientUser'
|
||||||
import { DefaultTemplate } from '@payloadcms/ui/templates/Default'
|
import { DefaultTemplate } from '@payloadcms/ui/templates/Default'
|
||||||
import React from 'react'
|
import React, { Fragment } from 'react'
|
||||||
|
|
||||||
|
import { initPage } from '../../utilities/initPage.js'
|
||||||
import { NotFoundClient } from './index.client.js'
|
import { NotFoundClient } from './index.client.js'
|
||||||
|
|
||||||
export const NotFoundView: AdminViewComponent = ({ initPageResult }) => {
|
export const generatePageMetadata = async ({
|
||||||
|
i18n,
|
||||||
|
}: {
|
||||||
|
config: SanitizedConfig
|
||||||
|
i18n: I18n
|
||||||
|
params?: { [key: string]: string | string[] }
|
||||||
|
//eslint-disable-next-line @typescript-eslint/require-await
|
||||||
|
}): Promise<Metadata> => {
|
||||||
|
return {
|
||||||
|
title: i18n.t('general:notFound'),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export type GenerateViewMetadata = (args: {
|
||||||
|
config: SanitizedConfig
|
||||||
|
i18n: I18n
|
||||||
|
params?: { [key: string]: string | string[] }
|
||||||
|
}) => Promise<Metadata>
|
||||||
|
|
||||||
|
export const NotFoundPage = async ({
|
||||||
|
config: configPromise,
|
||||||
|
searchParams,
|
||||||
|
}: {
|
||||||
|
config: Promise<SanitizedConfig>
|
||||||
|
params: {
|
||||||
|
segments: string[]
|
||||||
|
}
|
||||||
|
searchParams: {
|
||||||
|
[key: string]: string | string[]
|
||||||
|
}
|
||||||
|
}) => {
|
||||||
|
const initPageResult = await initPage({
|
||||||
|
config: configPromise,
|
||||||
|
redirectUnauthenticatedUser: true,
|
||||||
|
route: '/not-found',
|
||||||
|
searchParams,
|
||||||
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DefaultTemplate config={initPageResult?.req?.payload.config}>
|
<Fragment>
|
||||||
<NotFoundClient />
|
<HydrateClientUser permissions={initPageResult.permissions} user={initPageResult.req.user} />
|
||||||
</DefaultTemplate>
|
<DefaultTemplate
|
||||||
|
config={initPageResult.req.payload.config}
|
||||||
|
visibleEntities={initPageResult.visibleEntities}
|
||||||
|
>
|
||||||
|
<NotFoundClient />
|
||||||
|
</DefaultTemplate>
|
||||||
|
</Fragment>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,11 +2,10 @@ import type { I18n } from '@payloadcms/translations'
|
|||||||
import type { Metadata } from 'next'
|
import type { Metadata } from 'next'
|
||||||
import type { SanitizedConfig } from 'payload/types'
|
import type { SanitizedConfig } from 'payload/types'
|
||||||
|
|
||||||
import { EntityVisibilityProvider } from '@payloadcms/ui/providers/EntityVisibility'
|
|
||||||
import { DefaultTemplate } from '@payloadcms/ui/templates/Default'
|
import { DefaultTemplate } from '@payloadcms/ui/templates/Default'
|
||||||
import { MinimalTemplate } from '@payloadcms/ui/templates/Minimal'
|
import { MinimalTemplate } from '@payloadcms/ui/templates/Minimal'
|
||||||
import { notFound, redirect } from 'next/navigation.js'
|
import { notFound, redirect } from 'next/navigation.js'
|
||||||
import React from 'react'
|
import React, { Fragment } from 'react'
|
||||||
|
|
||||||
import { initPage } from '../../utilities/initPage.js'
|
import { initPage } from '../../utilities/initPage.js'
|
||||||
import { getViewFromConfig } from './getViewFromConfig.js'
|
import { getViewFromConfig } from './getViewFromConfig.js'
|
||||||
@@ -83,13 +82,15 @@ export const RootPage = async ({
|
|||||||
)
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<EntityVisibilityProvider visibleEntities={initPageResult.visibleEntities}>
|
<Fragment>
|
||||||
{templateType === 'minimal' && (
|
{templateType === 'minimal' && (
|
||||||
<MinimalTemplate className={templateClassName}>{RenderedView}</MinimalTemplate>
|
<MinimalTemplate className={templateClassName}>{RenderedView}</MinimalTemplate>
|
||||||
)}
|
)}
|
||||||
{templateType === 'default' && (
|
{templateType === 'default' && (
|
||||||
<DefaultTemplate config={config}>{RenderedView}</DefaultTemplate>
|
<DefaultTemplate config={config} visibleEntities={initPageResult.visibleEntities}>
|
||||||
|
{RenderedView}
|
||||||
|
</DefaultTemplate>
|
||||||
)}
|
)}
|
||||||
</EntityVisibilityProvider>
|
</Fragment>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import type { SanitizedConfig } from 'payload/types'
|
import type { SanitizedConfig, VisibleEntities } from 'payload/types'
|
||||||
|
|
||||||
|
import { EntityVisibilityProvider } from '@payloadcms/ui/providers/EntityVisibility'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
import type { NavProps } from '../../elements/Nav/index.js'
|
import type { NavProps } from '../../elements/Nav/index.js'
|
||||||
@@ -18,12 +19,14 @@ export type DefaultTemplateProps = {
|
|||||||
children?: React.ReactNode
|
children?: React.ReactNode
|
||||||
className?: string
|
className?: string
|
||||||
config: Promise<SanitizedConfig> | SanitizedConfig
|
config: Promise<SanitizedConfig> | SanitizedConfig
|
||||||
|
visibleEntities?: VisibleEntities
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DefaultTemplate: React.FC<DefaultTemplateProps> = async ({
|
export const DefaultTemplate: React.FC<DefaultTemplateProps> = async ({
|
||||||
children,
|
children,
|
||||||
className,
|
className,
|
||||||
config: configPromise,
|
config: configPromise,
|
||||||
|
visibleEntities,
|
||||||
}) => {
|
}) => {
|
||||||
const config = await configPromise
|
const config = await configPromise
|
||||||
|
|
||||||
@@ -40,23 +43,25 @@ export const DefaultTemplate: React.FC<DefaultTemplateProps> = async ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<EntityVisibilityProvider visibleEntities={visibleEntities}>
|
||||||
<div className={`${baseClass}__nav-toggler-wrapper`} id="nav-toggler">
|
<div>
|
||||||
<NavToggler className={`${baseClass}__nav-toggler`}>
|
<div className={`${baseClass}__nav-toggler-wrapper`} id="nav-toggler">
|
||||||
<NavHamburger />
|
<NavToggler className={`${baseClass}__nav-toggler`}>
|
||||||
</NavToggler>
|
<NavHamburger />
|
||||||
</div>
|
</NavToggler>
|
||||||
<Wrapper baseClass={baseClass} className={className}>
|
|
||||||
<RenderCustomComponent
|
|
||||||
CustomComponent={CustomNav}
|
|
||||||
DefaultComponent={DefaultNav}
|
|
||||||
componentProps={navProps}
|
|
||||||
/>
|
|
||||||
<div className={`${baseClass}__wrap`}>
|
|
||||||
<AppHeader />
|
|
||||||
{children}
|
|
||||||
</div>
|
</div>
|
||||||
</Wrapper>
|
<Wrapper baseClass={baseClass} className={className}>
|
||||||
</div>
|
<RenderCustomComponent
|
||||||
|
CustomComponent={CustomNav}
|
||||||
|
DefaultComponent={DefaultNav}
|
||||||
|
componentProps={navProps}
|
||||||
|
/>
|
||||||
|
<div className={`${baseClass}__wrap`}>
|
||||||
|
<AppHeader />
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
</Wrapper>
|
||||||
|
</div>
|
||||||
|
</EntityVisibilityProvider>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user