From 018755516b27b9bef03e2b334ab75ebbb95373fc Mon Sep 17 00:00:00 2001 From: Jarrod Flesch Date: Tue, 13 Feb 2024 23:30:02 -0500 Subject: [PATCH] chore: pass config to route handlers --- .../src/app/(payload)/api/[...slug]/route.ts | 7 +- .../api/[collection]/file/[filename]/route.ts | 4 +- .../(payload)/api/graphql-playground/route.ts | 3 +- .../src/app/(payload)/api/graphql/route.ts | 4 +- packages/next/package.json | 9 - packages/next/src/app/api/[...slug]/route.ts | 7 +- .../api/[collection]/file/[filename]/route.ts | 4 +- .../src/app/api/graphql-playground/route.ts | 3 +- packages/next/src/app/api/graphql/route.ts | 4 +- .../[collection]/file/[filename]/route.ts | 119 ----- packages/next/src/routes/graphql/handler.ts | 9 +- packages/next/src/routes/graphql/index.ts | 5 +- .../next/src/routes/graphql/playground.ts | 4 +- packages/next/src/routes/index.ts | 465 +---------------- .../next/src/routes/{ => rest}/RouteError.ts | 0 .../[collection]/file}/[filename]/route.ts | 22 +- .../next/src/routes/{ => rest}/auth/access.ts | 0 .../routes/{ => rest}/auth/forgotPassword.ts | 0 .../next/src/routes/{ => rest}/auth/init.ts | 0 .../next/src/routes/{ => rest}/auth/login.ts | 0 .../next/src/routes/{ => rest}/auth/logout.ts | 0 .../next/src/routes/{ => rest}/auth/me.ts | 0 .../src/routes/{ => rest}/auth/refresh.ts | 0 .../{ => rest}/auth/registerFirstUser.ts | 0 .../routes/{ => rest}/auth/resetPassword.ts | 0 .../next/src/routes/{ => rest}/auth/unlock.ts | 0 .../src/routes/{ => rest}/auth/verifyEmail.ts | 0 .../routes/{ => rest}/collections/create.ts | 0 .../routes/{ => rest}/collections/delete.ts | 0 .../{ => rest}/collections/deleteByID.ts | 0 .../{ => rest}/collections/docAccess.ts | 0 .../src/routes/{ => rest}/collections/find.ts | 0 .../routes/{ => rest}/collections/findByID.ts | 0 .../{ => rest}/collections/findVersionByID.ts | 0 .../{ => rest}/collections/findVersions.ts | 0 .../{ => rest}/collections/restoreVersion.ts | 0 .../routes/{ => rest}/collections/update.ts | 0 .../{ => rest}/collections/updateByID.ts | 0 .../routes/{ => rest}/globals/docAccess.ts | 0 .../src/routes/{ => rest}/globals/findOne.ts | 0 .../{ => rest}/globals/findVersionByID.ts | 0 .../routes/{ => rest}/globals/findVersions.ts | 0 .../{ => rest}/globals/restoreVersion.ts | 0 .../src/routes/{ => rest}/globals/update.ts | 0 packages/next/src/routes/rest/index.ts | 484 ++++++++++++++++++ packages/next/src/routes/{ => rest}/types.ts | 0 46 files changed, 536 insertions(+), 617 deletions(-) delete mode 100644 packages/next/src/routes/[collection]/file/[filename]/route.ts rename packages/next/src/routes/{ => rest}/RouteError.ts (100%) rename packages/next/src/routes/{fileHandler => rest/[collection]/file}/[filename]/route.ts (80%) rename packages/next/src/routes/{ => rest}/auth/access.ts (100%) rename packages/next/src/routes/{ => rest}/auth/forgotPassword.ts (100%) rename packages/next/src/routes/{ => rest}/auth/init.ts (100%) rename packages/next/src/routes/{ => rest}/auth/login.ts (100%) rename packages/next/src/routes/{ => rest}/auth/logout.ts (100%) rename packages/next/src/routes/{ => rest}/auth/me.ts (100%) rename packages/next/src/routes/{ => rest}/auth/refresh.ts (100%) rename packages/next/src/routes/{ => rest}/auth/registerFirstUser.ts (100%) rename packages/next/src/routes/{ => rest}/auth/resetPassword.ts (100%) rename packages/next/src/routes/{ => rest}/auth/unlock.ts (100%) rename packages/next/src/routes/{ => rest}/auth/verifyEmail.ts (100%) rename packages/next/src/routes/{ => rest}/collections/create.ts (100%) rename packages/next/src/routes/{ => rest}/collections/delete.ts (100%) rename packages/next/src/routes/{ => rest}/collections/deleteByID.ts (100%) rename packages/next/src/routes/{ => rest}/collections/docAccess.ts (100%) rename packages/next/src/routes/{ => rest}/collections/find.ts (100%) rename packages/next/src/routes/{ => rest}/collections/findByID.ts (100%) rename packages/next/src/routes/{ => rest}/collections/findVersionByID.ts (100%) rename packages/next/src/routes/{ => rest}/collections/findVersions.ts (100%) rename packages/next/src/routes/{ => rest}/collections/restoreVersion.ts (100%) rename packages/next/src/routes/{ => rest}/collections/update.ts (100%) rename packages/next/src/routes/{ => rest}/collections/updateByID.ts (100%) rename packages/next/src/routes/{ => rest}/globals/docAccess.ts (100%) rename packages/next/src/routes/{ => rest}/globals/findOne.ts (100%) rename packages/next/src/routes/{ => rest}/globals/findVersionByID.ts (100%) rename packages/next/src/routes/{ => rest}/globals/findVersions.ts (100%) rename packages/next/src/routes/{ => rest}/globals/restoreVersion.ts (100%) rename packages/next/src/routes/{ => rest}/globals/update.ts (100%) create mode 100644 packages/next/src/routes/rest/index.ts rename packages/next/src/routes/{ => rest}/types.ts (100%) diff --git a/packages/dev/src/app/(payload)/api/[...slug]/route.ts b/packages/dev/src/app/(payload)/api/[...slug]/route.ts index 2bde406f43..109b360f28 100644 --- a/packages/dev/src/app/(payload)/api/[...slug]/route.ts +++ b/packages/dev/src/app/(payload)/api/[...slug]/route.ts @@ -1,4 +1,9 @@ /* 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_GET, REST_DELETE, REST_PATCH, REST_POST } from '@payloadcms/next/routes' -export { GET, POST, DELETE, PATCH } from '@payloadcms/next/routes' +export const GET = REST_GET(config) +export const POST = REST_POST(config) +export const DELETE = REST_DELETE(config) +export const PATCH = REST_PATCH(config) diff --git a/packages/dev/src/app/(payload)/api/[collection]/file/[filename]/route.ts b/packages/dev/src/app/(payload)/api/[collection]/file/[filename]/route.ts index 755677b28e..7b210c55d7 100644 --- a/packages/dev/src/app/(payload)/api/[collection]/file/[filename]/route.ts +++ b/packages/dev/src/app/(payload)/api/[collection]/file/[filename]/route.ts @@ -1,4 +1,6 @@ /* 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 { GET_STATIC_FILE } from '@payloadcms/next/routes' -export { GET } from '@payloadcms/next/routes/[collection]/file/[filename]/route' +export const GET = GET_STATIC_FILE(config) diff --git a/packages/dev/src/app/(payload)/api/graphql-playground/route.ts b/packages/dev/src/app/(payload)/api/graphql-playground/route.ts index 8181c87a43..0c03d920a4 100644 --- a/packages/dev/src/app/(payload)/api/graphql-playground/route.ts +++ b/packages/dev/src/app/(payload)/api/graphql-playground/route.ts @@ -1,4 +1,5 @@ /* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */ /* DO NOT MODIFY it because it could be re-written at any time. */ +import { GRAPHQL_PLAYGROUND_GET } from '@payloadcms/next/routes' -export { GET_GraphQLPlayground as GET } from '@payloadcms/next/routes/graphql' +export const GET = GRAPHQL_PLAYGROUND_GET diff --git a/packages/dev/src/app/(payload)/api/graphql/route.ts b/packages/dev/src/app/(payload)/api/graphql/route.ts index 9bf3f9d0e4..06313365e1 100644 --- a/packages/dev/src/app/(payload)/api/graphql/route.ts +++ b/packages/dev/src/app/(payload)/api/graphql/route.ts @@ -1,4 +1,6 @@ /* 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 { GRAPHQL_POST } from '@payloadcms/next/routes' -export { POST_GraphQLHandler as POST } from '@payloadcms/next/routes/graphql' +export const POST = GRAPHQL_POST(config) diff --git a/packages/next/package.json b/packages/next/package.json index b5a13d02e3..c85e725b42 100644 --- a/packages/next/package.json +++ b/packages/next/package.json @@ -35,10 +35,6 @@ "./routes": { "import": "./src/routes/index.ts", "require": "./src/routes/index.ts" - }, - "./routes/*": { - "import": "./src/routes/*.ts", - "require": "./src/routes/*.ts" } }, "devDependencies": { @@ -95,11 +91,6 @@ "import": "./dist/routes/index.js", "require": "./dist/routes/index.js", "types": "./dist/routes/index.d.ts" - }, - "./routes/*": { - "import": "./dist/routes/*.js", - "require": "./dist/routes/*.js", - "types": "./dist/routes/*.d.ts" } }, "registry": "https://registry.npmjs.org/" diff --git a/packages/next/src/app/api/[...slug]/route.ts b/packages/next/src/app/api/[...slug]/route.ts index 2bde406f43..109b360f28 100644 --- a/packages/next/src/app/api/[...slug]/route.ts +++ b/packages/next/src/app/api/[...slug]/route.ts @@ -1,4 +1,9 @@ /* 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_GET, REST_DELETE, REST_PATCH, REST_POST } from '@payloadcms/next/routes' -export { GET, POST, DELETE, PATCH } from '@payloadcms/next/routes' +export const GET = REST_GET(config) +export const POST = REST_POST(config) +export const DELETE = REST_DELETE(config) +export const PATCH = REST_PATCH(config) diff --git a/packages/next/src/app/api/[collection]/file/[filename]/route.ts b/packages/next/src/app/api/[collection]/file/[filename]/route.ts index 755677b28e..7b210c55d7 100644 --- a/packages/next/src/app/api/[collection]/file/[filename]/route.ts +++ b/packages/next/src/app/api/[collection]/file/[filename]/route.ts @@ -1,4 +1,6 @@ /* 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 { GET_STATIC_FILE } from '@payloadcms/next/routes' -export { GET } from '@payloadcms/next/routes/[collection]/file/[filename]/route' +export const GET = GET_STATIC_FILE(config) diff --git a/packages/next/src/app/api/graphql-playground/route.ts b/packages/next/src/app/api/graphql-playground/route.ts index 8181c87a43..0c03d920a4 100644 --- a/packages/next/src/app/api/graphql-playground/route.ts +++ b/packages/next/src/app/api/graphql-playground/route.ts @@ -1,4 +1,5 @@ /* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */ /* DO NOT MODIFY it because it could be re-written at any time. */ +import { GRAPHQL_PLAYGROUND_GET } from '@payloadcms/next/routes' -export { GET_GraphQLPlayground as GET } from '@payloadcms/next/routes/graphql' +export const GET = GRAPHQL_PLAYGROUND_GET diff --git a/packages/next/src/app/api/graphql/route.ts b/packages/next/src/app/api/graphql/route.ts index 9bf3f9d0e4..06313365e1 100644 --- a/packages/next/src/app/api/graphql/route.ts +++ b/packages/next/src/app/api/graphql/route.ts @@ -1,4 +1,6 @@ /* 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 { GRAPHQL_POST } from '@payloadcms/next/routes' -export { POST_GraphQLHandler as POST } from '@payloadcms/next/routes/graphql' +export const POST = GRAPHQL_POST(config) diff --git a/packages/next/src/routes/[collection]/file/[filename]/route.ts b/packages/next/src/routes/[collection]/file/[filename]/route.ts deleted file mode 100644 index 9e077e429a..0000000000 --- a/packages/next/src/routes/[collection]/file/[filename]/route.ts +++ /dev/null @@ -1,119 +0,0 @@ -import path from 'path' -import config from 'payload-config' -import { streamFile } from '../../../../next-stream-file' -import fsPromises from 'fs/promises' -import { Collection, PayloadRequest, Where } from 'payload/types' -import executeAccess from 'payload/dist/auth/executeAccess' -import { APIError, Forbidden } from 'payload/errors' -import { RouteError } from '../../../RouteError' -import { createPayloadRequest } from '../../../../utilities/createPayloadRequest' -import httpStatus from 'http-status' - -async function checkFileAccess({ - req, - filename, - collection, -}: { - req: PayloadRequest - filename: string - collection: Collection -}) { - const { config } = collection - const accessResult = await executeAccess({ isReadingStaticFile: true, req }, config.access.read) - - if (typeof accessResult === 'object') { - const queryToBuild: Where = { - and: [ - { - or: [ - { - filename: { - equals: filename, - }, - }, - ], - }, - accessResult, - ], - } - - if (config.upload.imageSizes) { - config.upload.imageSizes.forEach(({ name }) => { - queryToBuild.and[0].or.push({ - [`sizes.${name}.filename`]: { - equals: filename, - }, - }) - }) - } - - const doc = await req.payload.db.findOne({ - collection: config.slug, - req, - where: queryToBuild, - }) - - if (!doc) { - throw new Forbidden(req.t) - } - } -} - -export const GET = async ( - request: Request, - { params }: { params: { collection: string; filename: string } }, -) => { - const { collection: collectionSlug, filename } = params - let req: PayloadRequest - let collection: Collection - - try { - req = await createPayloadRequest({ - request, - config, - params: { collection: collectionSlug }, - }) - collection = req.payload.collections?.[collectionSlug] - - if (!collection) { - throw new APIError(`Media collection not found: ${collectionSlug}`, httpStatus.BAD_REQUEST) - } - - if (!collection.config.upload) { - throw new APIError( - `This collection is not an upload collection: ${collectionSlug}`, - httpStatus.BAD_REQUEST, - ) - } - - if (collection.config.upload.disableLocalStorage) { - throw new APIError( - `This collection has local storage disabled: ${collectionSlug}`, - httpStatus.BAD_REQUEST, - ) - } - - await checkFileAccess({ - req, - filename, - collection, - }) - - 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, { - status: httpStatus.OK, - headers: new Headers({ - 'content-length': stats.size + '', - }), - }) - } catch (error) { - return RouteError({ - req, - collection, - err: error, - }) - } -} diff --git a/packages/next/src/routes/graphql/handler.ts b/packages/next/src/routes/graphql/handler.ts index 41b44b112a..92635523f5 100644 --- a/packages/next/src/routes/graphql/handler.ts +++ b/packages/next/src/routes/graphql/handler.ts @@ -1,8 +1,7 @@ -import config from 'payload-config' import { GraphQLError } from 'graphql' import httpStatus from 'http-status' import { configToSchema } from '@payloadcms/graphql' -import type { Payload, CollectionAfterErrorHook } from 'payload/types' +import type { Payload, CollectionAfterErrorHook, SanitizedConfig } from 'payload/types' import type { GraphQLFormattedError } from 'graphql' import { createPayloadRequest } from '../../utilities/createPayloadRequest' import { createHandler } from 'graphql-http/lib/use/fetch' @@ -49,7 +48,7 @@ if (!cached) { cached = global._payload_graphql = { graphql: null, promise: null } } -export const getGraphql = async () => { +export const getGraphql = async (config: Promise) => { if (cached.graphql) { return cached.graphql } @@ -72,13 +71,13 @@ export const getGraphql = async () => { return cached.graphql } -export const POST = async (request: Request) => { +export const POST = (config: Promise) => async (request: Request) => { const originalRequest = request.clone() const req = await createPayloadRequest({ request, config, }) - const { schema, validationRules } = await getGraphql() + const { schema, validationRules } = await getGraphql(config) const { payload } = req diff --git a/packages/next/src/routes/graphql/index.ts b/packages/next/src/routes/graphql/index.ts index 746a6a16a5..1d209ef597 100644 --- a/packages/next/src/routes/graphql/index.ts +++ b/packages/next/src/routes/graphql/index.ts @@ -1,2 +1,3 @@ -export { POST as POST_GraphQLHandler } from './handler' -export { GET as GET_GraphQLPlayground } from './playground' +export { POST as GRAPHQL_POST } from './handler' + +export { GET as GRAPHQL_PLAYGROUND_GET } from './playground' diff --git a/packages/next/src/routes/graphql/playground.ts b/packages/next/src/routes/graphql/playground.ts index 49370cd95e..a84863112e 100644 --- a/packages/next/src/routes/graphql/playground.ts +++ b/packages/next/src/routes/graphql/playground.ts @@ -1,8 +1,8 @@ -import config from 'payload-config' import { renderPlaygroundPage } from 'graphql-playground-html' import { createPayloadRequest } from '../../utilities/createPayloadRequest' +import type { SanitizedConfig } from 'payload/types' -export const GET = async (request: Request) => { +export const GET = (config: Promise) => async (request: Request) => { const req = await createPayloadRequest({ request, config, diff --git a/packages/next/src/routes/index.ts b/packages/next/src/routes/index.ts index 55b5c9e530..675fde3679 100644 --- a/packages/next/src/routes/index.ts +++ b/packages/next/src/routes/index.ts @@ -1,459 +1,10 @@ -import config from 'payload-config' -import httpStatus from 'http-status' -import type { Collection, GlobalConfig, PayloadRequest } from 'payload/types' -import type { Endpoint } from 'payload/config' -import { match } from 'path-to-regexp' +export { + GET as REST_GET, + POST as REST_POST, + DELETE as REST_DELETE, + PATCH as REST_PATCH, +} from './rest' -import { createPayloadRequest } from '../utilities/createPayloadRequest' -import { - CollectionRouteHandler, - CollectionRouteHandlerWithID, - GlobalRouteHandler, - GlobalRouteHandlerWithID, -} from './types' +export { GET as GET_STATIC_FILE } from './rest/[collection]/file/[filename]/route' -import { me } from './auth/me' -import { init } from './auth/init' -import { login } from './auth/login' -import { unlock } from './auth/unlock' -import { access } from './auth/access' -import { logout } from './auth/logout' -import { refresh } from './auth/refresh' - -import { find } from './collections/find' -import { create } from './collections/create' -import { update } from './collections/update' -import { deleteDoc } from './collections/delete' -import { verifyEmail } from './auth/verifyEmail' -import { findByID } from './collections/findByID' -import { docAccess } from './collections/docAccess' -import { resetPassword } from './auth/resetPassword' -import { updateByID } from './collections/updateByID' -import { deleteByID } from './collections/deleteByID' -import { forgotPassword } from './auth/forgotPassword' -import { findVersions } from './collections/findVersions' -import { registerFirstUser } from './auth/registerFirstUser' -import { restoreVersion } from './collections/restoreVersion' -import { findVersionByID } from './collections/findVersionByID' - -import { findOne } from './globals/findOne' -import { update as updateGlobal } from './globals/update' -import { docAccess as docAccessGlobal } from './globals/docAccess' -import { findVersions as findVersionsGlobal } from './globals/findVersions' -import { restoreVersion as restoreVersionGlobal } from './globals/restoreVersion' -import { findVersionByID as findVersionByIdGlobal } from './globals/findVersionByID' -import { RouteError } from './RouteError' - -const endpoints = { - root: { - GET: { - access, - }, - }, - collection: { - GET: { - init, - me, - versions: findVersions, - find, - findByID, - 'doc-access-by-id': docAccess, - 'doc-versions-by-id': findVersionByID, - }, - POST: { - create, - login, - logout, - unlock, - access: docAccess, - 'first-register': registerFirstUser, - 'forgot-password': forgotPassword, - 'reset-password': resetPassword, - 'refresh-token': refresh, - 'doc-access-by-id': docAccess, - 'doc-versions-by-id': restoreVersion, - 'doc-verify-by-id': verifyEmail, - }, - PATCH: { - update, - updateByID, - }, - DELETE: { - delete: deleteDoc, - deleteByID, - }, - }, - global: { - GET: { - findOne, - 'doc-access': docAccessGlobal, - 'doc-versions': findVersionsGlobal, - 'doc-versions-by-id': findVersionByIdGlobal, - }, - POST: { - update: updateGlobal, - 'doc-access': docAccessGlobal, - 'doc-versions-by-id': restoreVersionGlobal, - }, - }, -} - -const handleCustomEndpoints = ({ - entitySlug, - endpoints, - payloadRequest, -}: { - entitySlug?: string - endpoints: Endpoint[] | GlobalConfig['endpoints'] - payloadRequest: PayloadRequest -}): Promise | Response => { - if (endpoints && endpoints.length > 0) { - let handlerParams = {} - const { pathname } = payloadRequest - const pathPrefix = - payloadRequest.payload.config.routes.api + (entitySlug ? `/${entitySlug}` : '') - - const customEndpoint = endpoints.find((endpoint) => { - if (endpoint.method === payloadRequest.method.toLowerCase()) { - const pathMatchFn = match(`${pathPrefix}${endpoint.path}`, { - decode: decodeURIComponent, - }) - const tempParams = pathMatchFn(pathname) - if (tempParams) { - handlerParams = tempParams.params - return true - } - } - }) - - if (customEndpoint) { - return customEndpoint.handler({ - req: payloadRequest, - routeParams: handlerParams, - }) - } - } - - return null -} - -const RouteNotFoundResponse = (slug: string[]) => - Response.json( - { - message: `Route Not Found: "${slug.join('/')}"`, - }, - { status: httpStatus.NOT_FOUND }, - ) - -export const GET = async ( - request: Request, - { params: { slug } }: { params: { slug: string[] } }, -) => { - const [slug1, slug2, slug3, slug4] = slug - let req: PayloadRequest - let collection: Collection - - try { - req = await createPayloadRequest({ - request, - config, - params: { - collection: slug1, - }, - }) - collection = req.payload.collections?.[slug1] - - if (collection) { - const customEndpointResponse = await handleCustomEndpoints({ - entitySlug: slug1, - payloadRequest: req, - endpoints: collection.config?.endpoints || [], - }) - if (customEndpointResponse) return customEndpointResponse - - switch (slug.length) { - case 1: - // /:collection - return endpoints.collection.GET.find({ req, collection }) - case 2: - if (slug2 in endpoints.collection.GET) { - // /:collection/init - // /:collection/me - // /:collection/versions - return (endpoints.collection.GET[slug2] as CollectionRouteHandler)({ req, collection }) - } - // /:collection/:id - return endpoints.collection.GET.findByID({ req, id: slug2, collection }) - case 3: - if (`doc-${slug2}-by-id` in endpoints.collection.GET) { - // /:collection/access/:id - // /:collection/versions/:id - return (endpoints.collection.GET[`doc-${slug2}-by-id`] as CollectionRouteHandlerWithID)( - { req, id: slug3, collection }, - ) - } - break - } - } else if (slug1 === 'globals') { - const globalConfig = req.payload.config.globals.find((global) => global.slug === slug2) - const customEndpointResponse = await handleCustomEndpoints({ - entitySlug: `${slug1}/${slug2}`, - payloadRequest: req, - endpoints: globalConfig?.endpoints || [], - }) - if (customEndpointResponse) return customEndpointResponse - - switch (slug.length) { - case 2: - // /globals/:slug - return endpoints.global.GET.findOne({ req, globalConfig }) - case 3: - if (`doc-${slug3}` in endpoints.global.GET) { - // /globals/:slug/access - // /globals/:slug/versions - return (endpoints.global.GET?.[`doc-${slug3}`] as GlobalRouteHandler)({ - req, - globalConfig, - }) - } - break - case 4: - if (`doc-${slug3}-by-id` in endpoints.global.GET) { - // /globals/:slug/versions/:id - return (endpoints.global.GET?.[`doc-${slug3}-by-id`] as GlobalRouteHandlerWithID)({ - req, - id: slug4, - globalConfig, - }) - } - break - } - } else if (slug.length === 1 && slug1 === 'access') { - return endpoints.root.GET.access({ req }) - } - - // root routes - const customEndpointResponse = await handleCustomEndpoints({ - payloadRequest: req, - endpoints: req.payload.config.endpoints, - }) - if (customEndpointResponse) return customEndpointResponse - - return RouteNotFoundResponse(slug) - } catch (error) { - return RouteError({ - req, - collection, - err: error, - }) - } -} - -export const POST = async ( - request: Request, - { params: { slug } }: { params: { slug: string[] } }, -) => { - const [slug1, slug2, slug3, slug4] = slug - let req: PayloadRequest - let collection: Collection - - try { - req = await createPayloadRequest({ - request, - config, - params: { collection: slug1 }, - }) - collection = req.payload.collections?.[slug1] - - if (collection) { - const customEndpointResponse = await handleCustomEndpoints({ - entitySlug: slug1, - payloadRequest: req, - endpoints: collection.config?.endpoints || [], - }) - if (customEndpointResponse) return customEndpointResponse - - switch (slug.length) { - case 1: - // /:collection - return endpoints.collection.POST.create({ req, collection }) - case 2: - if (slug2 in endpoints.collection.POST) { - // /:collection/login - // /:collection/logout - // /:collection/unlock - // /:collection/access - // /:collection/first-register - // /:collection/forgot-password - // /:collection/reset-password - // /:collection/refresh-token - return (endpoints.collection.POST?.[slug2] as CollectionRouteHandler)({ - req, - collection, - }) - } - break - case 3: - if (`doc-${slug2}-by-id` in endpoints.collection.POST) { - // /:collection/access/:id - // /:collection/versions/:id - // /:collection/verify/:token ("doc-verify-by-id" uses id as token internally) - return ( - endpoints.collection.POST[`doc-${slug2}-by-id`] as CollectionRouteHandlerWithID - )({ req, id: slug3, collection }) - } - break - } - } else if (slug1 === 'globals' && slug2) { - const globalConfig = req.payload.config.globals.find((global) => global.slug === slug2) - - switch (slug.length) { - case 2: - // /globals/:slug - return endpoints.global.POST.update({ req, globalConfig }) - case 3: - if (`doc-${slug3}` in endpoints.global.POST) { - // /globals/:slug/access - return (endpoints.global.POST?.[`doc-${slug3}`] as GlobalRouteHandler)({ - req, - globalConfig, - }) - } - break - case 4: - if (`doc-${slug3}-by-id` in endpoints.global.POST) { - // /globals/:slug/versions/:id - return (endpoints.global.POST?.[`doc-${slug3}-by-id`] as GlobalRouteHandlerWithID)({ - req, - id: slug4, - globalConfig, - }) - } - break - default: - return new Response('Route Not Found', { status: 404 }) - } - } - - // root routes - const customEndpointResponse = await handleCustomEndpoints({ - payloadRequest: req, - endpoints: req.payload.config.endpoints, - }) - if (customEndpointResponse) return customEndpointResponse - - return RouteNotFoundResponse(slug) - } catch (error) { - return RouteError({ - req, - collection, - err: error, - }) - } -} - -export const DELETE = async ( - request: Request, - { params: { slug } }: { params: { slug: string[] } }, -) => { - const [slug1, slug2] = slug - let req: PayloadRequest - let collection: Collection - - try { - req = await createPayloadRequest({ - request, - config, - params: { - collection: slug1, - }, - }) - collection = req.payload.collections?.[slug1] - - if (collection) { - const customEndpointResponse = await handleCustomEndpoints({ - entitySlug: slug1, - payloadRequest: req, - endpoints: collection.config?.endpoints || [], - }) - if (customEndpointResponse) return customEndpointResponse - - switch (slug.length) { - case 1: - // /:collection - return endpoints.collection.DELETE.delete({ req, collection }) - case 2: - // /:collection/:id - return endpoints.collection.DELETE.deleteByID({ req, id: slug2, collection }) - } - } - - // root routes - const customEndpointResponse = await handleCustomEndpoints({ - payloadRequest: req, - endpoints: req.payload.config.endpoints, - }) - if (customEndpointResponse) return customEndpointResponse - - return RouteNotFoundResponse(slug) - } catch (error) { - return RouteError({ - req, - collection, - err: error, - }) - } -} - -export const PATCH = async ( - request: Request, - { params: { slug } }: { params: { slug: string[] } }, -) => { - const [slug1, slug2] = slug - let req: PayloadRequest - let collection: Collection - - try { - req = await createPayloadRequest({ - request, - config, - params: { - collection: slug1, - }, - }) - collection = req.payload.collections?.[slug1] - - if (collection) { - const customEndpointResponse = await handleCustomEndpoints({ - entitySlug: slug1, - payloadRequest: req, - endpoints: collection.config?.endpoints || [], - }) - if (customEndpointResponse) return customEndpointResponse - - switch (slug.length) { - case 1: - // /:collection - return endpoints.collection.PATCH.update({ req, collection }) - case 2: - // /:collection/:id - return endpoints.collection.PATCH.updateByID({ req, id: slug2, collection }) - } - } - - // root routes - const customEndpointResponse = await handleCustomEndpoints({ - payloadRequest: req, - endpoints: req.payload.config.endpoints, - }) - if (customEndpointResponse) return customEndpointResponse - - return RouteNotFoundResponse(slug) - } catch (error) { - return RouteError({ - req, - collection, - err: error, - }) - } -} +export { GRAPHQL_POST, GRAPHQL_PLAYGROUND_GET } from './graphql' diff --git a/packages/next/src/routes/RouteError.ts b/packages/next/src/routes/rest/RouteError.ts similarity index 100% rename from packages/next/src/routes/RouteError.ts rename to packages/next/src/routes/rest/RouteError.ts diff --git a/packages/next/src/routes/fileHandler/[filename]/route.ts b/packages/next/src/routes/rest/[collection]/file/[filename]/route.ts similarity index 80% rename from packages/next/src/routes/fileHandler/[filename]/route.ts rename to packages/next/src/routes/rest/[collection]/file/[filename]/route.ts index 5ce8074ace..7e9ed6a0cb 100644 --- a/packages/next/src/routes/fileHandler/[filename]/route.ts +++ b/packages/next/src/routes/rest/[collection]/file/[filename]/route.ts @@ -1,12 +1,11 @@ import path from 'path' -import config from 'payload-config' -import { streamFile } from '../../../next-stream-file' +import { streamFile } from '../../../../../next-stream-file' import fsPromises from 'fs/promises' -import { Collection, PayloadRequest, Where } from 'payload/types' +import type { Collection, PayloadRequest, SanitizedConfig, Where } from 'payload/types' import executeAccess from 'payload/dist/auth/executeAccess' import { APIError, Forbidden } from 'payload/errors' -import { RouteError } from '../../RouteError' -import { createPayloadRequest } from '../../../utilities/createPayloadRequest' +import { RouteError } from '../../../RouteError' +import { createPayloadRequest } from '../../../../../utilities/createPayloadRequest' import httpStatus from 'http-status' async function checkFileAccess({ @@ -60,8 +59,9 @@ async function checkFileAccess({ } export const GET = - (collectionSlug: string) => - async (request: Request, { params }: { params: { filename: string } }) => { + (config: Promise) => + async (request: Request, { params }: { params: { collection: string; filename: string } }) => { + const { collection: collectionSlug, filename } = params let req: PayloadRequest let collection: Collection @@ -91,14 +91,6 @@ export const GET = ) } - const { filename } = params - if (!filename) { - throw new APIError( - 'No filename provided, ensure this route is within a [filename] folder, i.e. staticDir/[filename]/route.ts', - httpStatus.BAD_REQUEST, - ) - } - await checkFileAccess({ req, filename, diff --git a/packages/next/src/routes/auth/access.ts b/packages/next/src/routes/rest/auth/access.ts similarity index 100% rename from packages/next/src/routes/auth/access.ts rename to packages/next/src/routes/rest/auth/access.ts diff --git a/packages/next/src/routes/auth/forgotPassword.ts b/packages/next/src/routes/rest/auth/forgotPassword.ts similarity index 100% rename from packages/next/src/routes/auth/forgotPassword.ts rename to packages/next/src/routes/rest/auth/forgotPassword.ts diff --git a/packages/next/src/routes/auth/init.ts b/packages/next/src/routes/rest/auth/init.ts similarity index 100% rename from packages/next/src/routes/auth/init.ts rename to packages/next/src/routes/rest/auth/init.ts diff --git a/packages/next/src/routes/auth/login.ts b/packages/next/src/routes/rest/auth/login.ts similarity index 100% rename from packages/next/src/routes/auth/login.ts rename to packages/next/src/routes/rest/auth/login.ts diff --git a/packages/next/src/routes/auth/logout.ts b/packages/next/src/routes/rest/auth/logout.ts similarity index 100% rename from packages/next/src/routes/auth/logout.ts rename to packages/next/src/routes/rest/auth/logout.ts diff --git a/packages/next/src/routes/auth/me.ts b/packages/next/src/routes/rest/auth/me.ts similarity index 100% rename from packages/next/src/routes/auth/me.ts rename to packages/next/src/routes/rest/auth/me.ts diff --git a/packages/next/src/routes/auth/refresh.ts b/packages/next/src/routes/rest/auth/refresh.ts similarity index 100% rename from packages/next/src/routes/auth/refresh.ts rename to packages/next/src/routes/rest/auth/refresh.ts diff --git a/packages/next/src/routes/auth/registerFirstUser.ts b/packages/next/src/routes/rest/auth/registerFirstUser.ts similarity index 100% rename from packages/next/src/routes/auth/registerFirstUser.ts rename to packages/next/src/routes/rest/auth/registerFirstUser.ts diff --git a/packages/next/src/routes/auth/resetPassword.ts b/packages/next/src/routes/rest/auth/resetPassword.ts similarity index 100% rename from packages/next/src/routes/auth/resetPassword.ts rename to packages/next/src/routes/rest/auth/resetPassword.ts diff --git a/packages/next/src/routes/auth/unlock.ts b/packages/next/src/routes/rest/auth/unlock.ts similarity index 100% rename from packages/next/src/routes/auth/unlock.ts rename to packages/next/src/routes/rest/auth/unlock.ts diff --git a/packages/next/src/routes/auth/verifyEmail.ts b/packages/next/src/routes/rest/auth/verifyEmail.ts similarity index 100% rename from packages/next/src/routes/auth/verifyEmail.ts rename to packages/next/src/routes/rest/auth/verifyEmail.ts diff --git a/packages/next/src/routes/collections/create.ts b/packages/next/src/routes/rest/collections/create.ts similarity index 100% rename from packages/next/src/routes/collections/create.ts rename to packages/next/src/routes/rest/collections/create.ts diff --git a/packages/next/src/routes/collections/delete.ts b/packages/next/src/routes/rest/collections/delete.ts similarity index 100% rename from packages/next/src/routes/collections/delete.ts rename to packages/next/src/routes/rest/collections/delete.ts diff --git a/packages/next/src/routes/collections/deleteByID.ts b/packages/next/src/routes/rest/collections/deleteByID.ts similarity index 100% rename from packages/next/src/routes/collections/deleteByID.ts rename to packages/next/src/routes/rest/collections/deleteByID.ts diff --git a/packages/next/src/routes/collections/docAccess.ts b/packages/next/src/routes/rest/collections/docAccess.ts similarity index 100% rename from packages/next/src/routes/collections/docAccess.ts rename to packages/next/src/routes/rest/collections/docAccess.ts diff --git a/packages/next/src/routes/collections/find.ts b/packages/next/src/routes/rest/collections/find.ts similarity index 100% rename from packages/next/src/routes/collections/find.ts rename to packages/next/src/routes/rest/collections/find.ts diff --git a/packages/next/src/routes/collections/findByID.ts b/packages/next/src/routes/rest/collections/findByID.ts similarity index 100% rename from packages/next/src/routes/collections/findByID.ts rename to packages/next/src/routes/rest/collections/findByID.ts diff --git a/packages/next/src/routes/collections/findVersionByID.ts b/packages/next/src/routes/rest/collections/findVersionByID.ts similarity index 100% rename from packages/next/src/routes/collections/findVersionByID.ts rename to packages/next/src/routes/rest/collections/findVersionByID.ts diff --git a/packages/next/src/routes/collections/findVersions.ts b/packages/next/src/routes/rest/collections/findVersions.ts similarity index 100% rename from packages/next/src/routes/collections/findVersions.ts rename to packages/next/src/routes/rest/collections/findVersions.ts diff --git a/packages/next/src/routes/collections/restoreVersion.ts b/packages/next/src/routes/rest/collections/restoreVersion.ts similarity index 100% rename from packages/next/src/routes/collections/restoreVersion.ts rename to packages/next/src/routes/rest/collections/restoreVersion.ts diff --git a/packages/next/src/routes/collections/update.ts b/packages/next/src/routes/rest/collections/update.ts similarity index 100% rename from packages/next/src/routes/collections/update.ts rename to packages/next/src/routes/rest/collections/update.ts diff --git a/packages/next/src/routes/collections/updateByID.ts b/packages/next/src/routes/rest/collections/updateByID.ts similarity index 100% rename from packages/next/src/routes/collections/updateByID.ts rename to packages/next/src/routes/rest/collections/updateByID.ts diff --git a/packages/next/src/routes/globals/docAccess.ts b/packages/next/src/routes/rest/globals/docAccess.ts similarity index 100% rename from packages/next/src/routes/globals/docAccess.ts rename to packages/next/src/routes/rest/globals/docAccess.ts diff --git a/packages/next/src/routes/globals/findOne.ts b/packages/next/src/routes/rest/globals/findOne.ts similarity index 100% rename from packages/next/src/routes/globals/findOne.ts rename to packages/next/src/routes/rest/globals/findOne.ts diff --git a/packages/next/src/routes/globals/findVersionByID.ts b/packages/next/src/routes/rest/globals/findVersionByID.ts similarity index 100% rename from packages/next/src/routes/globals/findVersionByID.ts rename to packages/next/src/routes/rest/globals/findVersionByID.ts diff --git a/packages/next/src/routes/globals/findVersions.ts b/packages/next/src/routes/rest/globals/findVersions.ts similarity index 100% rename from packages/next/src/routes/globals/findVersions.ts rename to packages/next/src/routes/rest/globals/findVersions.ts diff --git a/packages/next/src/routes/globals/restoreVersion.ts b/packages/next/src/routes/rest/globals/restoreVersion.ts similarity index 100% rename from packages/next/src/routes/globals/restoreVersion.ts rename to packages/next/src/routes/rest/globals/restoreVersion.ts diff --git a/packages/next/src/routes/globals/update.ts b/packages/next/src/routes/rest/globals/update.ts similarity index 100% rename from packages/next/src/routes/globals/update.ts rename to packages/next/src/routes/rest/globals/update.ts diff --git a/packages/next/src/routes/rest/index.ts b/packages/next/src/routes/rest/index.ts new file mode 100644 index 0000000000..1402573aa8 --- /dev/null +++ b/packages/next/src/routes/rest/index.ts @@ -0,0 +1,484 @@ +import httpStatus from 'http-status' +import type { Collection, GlobalConfig, PayloadRequest, SanitizedConfig } from 'payload/types' +import type { Endpoint } from 'payload/config' +import { match } from 'path-to-regexp' + +import { createPayloadRequest } from '../../utilities/createPayloadRequest' +import { + CollectionRouteHandler, + CollectionRouteHandlerWithID, + GlobalRouteHandler, + GlobalRouteHandlerWithID, +} from './types' + +import { me } from './auth/me' +import { init } from './auth/init' +import { login } from './auth/login' +import { unlock } from './auth/unlock' +import { access } from './auth/access' +import { logout } from './auth/logout' +import { refresh } from './auth/refresh' + +import { find } from './collections/find' +import { create } from './collections/create' +import { update } from './collections/update' +import { deleteDoc } from './collections/delete' +import { verifyEmail } from './auth/verifyEmail' +import { findByID } from './collections/findByID' +import { docAccess } from './collections/docAccess' +import { resetPassword } from './auth/resetPassword' +import { updateByID } from './collections/updateByID' +import { deleteByID } from './collections/deleteByID' +import { forgotPassword } from './auth/forgotPassword' +import { findVersions } from './collections/findVersions' +import { registerFirstUser } from './auth/registerFirstUser' +import { restoreVersion } from './collections/restoreVersion' +import { findVersionByID } from './collections/findVersionByID' + +import { findOne } from './globals/findOne' +import { update as updateGlobal } from './globals/update' +import { docAccess as docAccessGlobal } from './globals/docAccess' +import { findVersions as findVersionsGlobal } from './globals/findVersions' +import { restoreVersion as restoreVersionGlobal } from './globals/restoreVersion' +import { findVersionByID as findVersionByIdGlobal } from './globals/findVersionByID' +import { RouteError } from './RouteError' + +const endpoints = { + root: { + GET: { + access, + }, + }, + collection: { + GET: { + init, + me, + versions: findVersions, + find, + findByID, + 'doc-access-by-id': docAccess, + 'doc-versions-by-id': findVersionByID, + }, + POST: { + create, + login, + logout, + unlock, + access: docAccess, + 'first-register': registerFirstUser, + 'forgot-password': forgotPassword, + 'reset-password': resetPassword, + 'refresh-token': refresh, + 'doc-access-by-id': docAccess, + 'doc-versions-by-id': restoreVersion, + 'doc-verify-by-id': verifyEmail, + }, + PATCH: { + update, + updateByID, + }, + DELETE: { + delete: deleteDoc, + deleteByID, + }, + }, + global: { + GET: { + findOne, + 'doc-access': docAccessGlobal, + 'doc-versions': findVersionsGlobal, + 'doc-versions-by-id': findVersionByIdGlobal, + }, + POST: { + update: updateGlobal, + 'doc-access': docAccessGlobal, + 'doc-versions-by-id': restoreVersionGlobal, + }, + }, +} + +const handleCustomEndpoints = ({ + entitySlug, + endpoints, + payloadRequest, +}: { + entitySlug?: string + endpoints: Endpoint[] | GlobalConfig['endpoints'] + payloadRequest: PayloadRequest +}): Promise | Response => { + if (endpoints && endpoints.length > 0) { + let handlerParams = {} + const { pathname } = payloadRequest + const pathPrefix = + payloadRequest.payload.config.routes.api + (entitySlug ? `/${entitySlug}` : '') + + const customEndpoint = endpoints.find((endpoint) => { + if (endpoint.method === payloadRequest.method.toLowerCase()) { + const pathMatchFn = match(`${pathPrefix}${endpoint.path}`, { + decode: decodeURIComponent, + }) + const tempParams = pathMatchFn(pathname) + if (tempParams) { + handlerParams = tempParams.params + return true + } + } + }) + + if (customEndpoint) { + return customEndpoint.handler({ + req: payloadRequest, + routeParams: handlerParams, + }) + } + } + + return null +} + +const RouteNotFoundResponse = (slug: string[]) => + Response.json( + { + message: `Route Not Found: "${slug.join('/')}"`, + }, + { status: httpStatus.NOT_FOUND }, + ) + +export const GET = + (config: Promise) => + async (request: Request, { params: { slug } }: { params: { slug: string[] } }) => { + const [slug1, slug2, slug3, slug4] = slug + let req: PayloadRequest + let res: Response + let collection: Collection + + try { + req = await createPayloadRequest({ + request, + config, + params: { + collection: slug1, + }, + }) + + collection = req.payload.collections?.[slug1] + + if (collection) { + const customEndpointResponse = await handleCustomEndpoints({ + entitySlug: slug1, + payloadRequest: req, + endpoints: collection.config?.endpoints || [], + }) + if (customEndpointResponse) return customEndpointResponse + + switch (slug.length) { + case 1: + // /:collection + res = await endpoints.collection.GET.find({ req, collection }) + break + case 2: + if (slug2 in endpoints.collection.GET) { + // /:collection/init + // /:collection/me + // /:collection/versions + res = await (endpoints.collection.GET[slug2] as CollectionRouteHandler)({ + req, + collection, + }) + } else { + // /:collection/:id + res = await endpoints.collection.GET.findByID({ req, id: slug2, collection }) + } + break + case 3: + if (`doc-${slug2}-by-id` in endpoints.collection.GET) { + // /:collection/access/:id + // /:collection/versions/:id + res = await ( + endpoints.collection.GET[`doc-${slug2}-by-id`] as CollectionRouteHandlerWithID + )({ req, id: slug3, collection }) + } + break + } + } else if (slug1 === 'globals') { + const globalConfig = req.payload.config.globals.find((global) => global.slug === slug2) + const customEndpointResponse = await handleCustomEndpoints({ + entitySlug: `${slug1}/${slug2}`, + payloadRequest: req, + endpoints: globalConfig?.endpoints || [], + }) + if (customEndpointResponse) return customEndpointResponse + + switch (slug.length) { + case 2: + // /globals/:slug + res = await endpoints.global.GET.findOne({ req, globalConfig }) + break + case 3: + if (`doc-${slug3}` in endpoints.global.GET) { + // /globals/:slug/access + // /globals/:slug/versions + res = await (endpoints.global.GET?.[`doc-${slug3}`] as GlobalRouteHandler)({ + req, + globalConfig, + }) + } + break + case 4: + if (`doc-${slug3}-by-id` in endpoints.global.GET) { + // /globals/:slug/versions/:id + res = await ( + endpoints.global.GET?.[`doc-${slug3}-by-id`] as GlobalRouteHandlerWithID + )({ + req, + id: slug4, + globalConfig, + }) + } + break + } + } else if (slug.length === 1 && slug1 === 'access') { + res = await endpoints.root.GET.access({ req }) + } + + if (res instanceof Response) return res + + // root routes + const customEndpointResponse = await handleCustomEndpoints({ + payloadRequest: req, + endpoints: req.payload.config.endpoints, + }) + if (customEndpointResponse) return customEndpointResponse + + return RouteNotFoundResponse(slug) + } catch (error) { + return RouteError({ + req, + collection, + err: error, + }) + } + } + +export const POST = + (config: Promise) => + async (request: Request, { params: { slug } }: { params: { slug: string[] } }) => { + const [slug1, slug2, slug3, slug4] = slug + let req: PayloadRequest + let res: Response + let collection: Collection + + try { + req = await createPayloadRequest({ + request, + config, + params: { collection: slug1 }, + }) + collection = req.payload.collections?.[slug1] + + if (collection) { + const customEndpointResponse = await handleCustomEndpoints({ + entitySlug: slug1, + payloadRequest: req, + endpoints: collection.config?.endpoints || [], + }) + if (customEndpointResponse) return customEndpointResponse + + switch (slug.length) { + case 1: + // /:collection + res = await endpoints.collection.POST.create({ req, collection }) + break + case 2: + if (slug2 in endpoints.collection.POST) { + // /:collection/login + // /:collection/logout + // /:collection/unlock + // /:collection/access + // /:collection/first-register + // /:collection/forgot-password + // /:collection/reset-password + // /:collection/refresh-token + res = await (endpoints.collection.POST?.[slug2] as CollectionRouteHandler)({ + req, + collection, + }) + } + break + case 3: + if (`doc-${slug2}-by-id` in endpoints.collection.POST) { + // /:collection/access/:id + // /:collection/versions/:id + // /:collection/verify/:token ("doc-verify-by-id" uses id as token internally) + res = await ( + endpoints.collection.POST[`doc-${slug2}-by-id`] as CollectionRouteHandlerWithID + )({ req, id: slug3, collection }) + } + break + } + } else if (slug1 === 'globals' && slug2) { + const globalConfig = req.payload.config.globals.find((global) => global.slug === slug2) + + switch (slug.length) { + case 2: + // /globals/:slug + res = await endpoints.global.POST.update({ req, globalConfig }) + break + case 3: + if (`doc-${slug3}` in endpoints.global.POST) { + // /globals/:slug/access + res = await (endpoints.global.POST?.[`doc-${slug3}`] as GlobalRouteHandler)({ + req, + globalConfig, + }) + } + break + case 4: + if (`doc-${slug3}-by-id` in endpoints.global.POST) { + // /globals/:slug/versions/:id + res = await ( + endpoints.global.POST?.[`doc-${slug3}-by-id`] as GlobalRouteHandlerWithID + )({ + req, + id: slug4, + globalConfig, + }) + } + break + default: + res = new Response('Route Not Found', { status: 404 }) + } + } + + if (res instanceof Response) return res + + // root routes + const customEndpointResponse = await handleCustomEndpoints({ + payloadRequest: req, + endpoints: req.payload.config.endpoints, + }) + if (customEndpointResponse) return customEndpointResponse + + return RouteNotFoundResponse(slug) + } catch (error) { + return RouteError({ + req, + collection, + err: error, + }) + } + } + +export const DELETE = + (config: Promise) => + async (request: Request, { params: { slug } }: { params: { slug: string[] } }) => { + const [slug1, slug2] = slug + let req: PayloadRequest + let res: Response + let collection: Collection + + try { + req = await createPayloadRequest({ + request, + config, + params: { + collection: slug1, + }, + }) + collection = req.payload.collections?.[slug1] + + if (collection) { + const customEndpointResponse = await handleCustomEndpoints({ + entitySlug: slug1, + payloadRequest: req, + endpoints: collection.config?.endpoints || [], + }) + if (customEndpointResponse) return customEndpointResponse + + switch (slug.length) { + case 1: + // /:collection + res = await endpoints.collection.DELETE.delete({ req, collection }) + break + case 2: + // /:collection/:id + res = await endpoints.collection.DELETE.deleteByID({ req, id: slug2, collection }) + break + } + } + + if (res instanceof Response) return res + + // root routes + const customEndpointResponse = await handleCustomEndpoints({ + payloadRequest: req, + endpoints: req.payload.config.endpoints, + }) + if (customEndpointResponse) return customEndpointResponse + + return RouteNotFoundResponse(slug) + } catch (error) { + return RouteError({ + req, + collection, + err: error, + }) + } + } + +export const PATCH = + (config: Promise) => + async (request: Request, { params: { slug } }: { params: { slug: string[] } }) => { + const [slug1, slug2] = slug + let req: PayloadRequest + let res: Response + let collection: Collection + + try { + req = await createPayloadRequest({ + request, + config, + params: { + collection: slug1, + }, + }) + collection = req.payload.collections?.[slug1] + + if (collection) { + const customEndpointResponse = await handleCustomEndpoints({ + entitySlug: slug1, + payloadRequest: req, + endpoints: collection.config?.endpoints || [], + }) + if (customEndpointResponse) return customEndpointResponse + + switch (slug.length) { + case 1: + // /:collection + res = await endpoints.collection.PATCH.update({ req, collection }) + break + case 2: + // /:collection/:id + res = await endpoints.collection.PATCH.updateByID({ req, id: slug2, collection }) + break + } + } + + if (res instanceof Response) return res + + // root routes + const customEndpointResponse = await handleCustomEndpoints({ + payloadRequest: req, + endpoints: req.payload.config.endpoints, + }) + if (customEndpointResponse) return customEndpointResponse + + return RouteNotFoundResponse(slug) + } catch (error) { + return RouteError({ + req, + collection, + err: error, + }) + } + } diff --git a/packages/next/src/routes/types.ts b/packages/next/src/routes/rest/types.ts similarity index 100% rename from packages/next/src/routes/types.ts rename to packages/next/src/routes/rest/types.ts