diff --git a/packages/next/src/app/api/[collection]/me/route.ts b/packages/dev/src/app/(payload)/api/[...slug]/route.ts similarity index 51% rename from packages/next/src/app/api/[collection]/me/route.ts rename to packages/dev/src/app/(payload)/api/[...slug]/route.ts index 50f0fd4531..3ea1a223a6 100644 --- a/packages/next/src/app/api/[collection]/me/route.ts +++ b/packages/dev/src/app/(payload)/api/[...slug]/route.ts @@ -1,6 +1,4 @@ /* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */ /* DO NOT MODIFY it because it could be re-written at any time. */ -import { me } from '@payloadcms/next/routes/me' -import config from 'payload-config' -export const GET = me({ config }) +export { GET, POST, DELETE, PATCH } from '@payloadcms/next/dist/routes/index' diff --git a/packages/dev/src/app/(payload)/api/[collection]/init/route.ts b/packages/dev/src/app/(payload)/api/[collection]/init/route.ts deleted file mode 100644 index fd1bba2dbc..0000000000 --- a/packages/dev/src/app/(payload)/api/[collection]/init/route.ts +++ /dev/null @@ -1,6 +0,0 @@ -/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */ -/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */ -import { init } from '@payloadcms/next/routes/init' -import config from 'payload-config' - -export const GET = init({ config }) diff --git a/packages/dev/src/app/(payload)/api/[collection]/login/route.ts b/packages/dev/src/app/(payload)/api/[collection]/login/route.ts deleted file mode 100644 index b1be6d4b65..0000000000 --- a/packages/dev/src/app/(payload)/api/[collection]/login/route.ts +++ /dev/null @@ -1,6 +0,0 @@ -/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */ -/* DO NOT MODIFY it because it could be re-written at any time. */ -import { login } from '@payloadcms/next/routes/login' -import config from 'payload-config' - -export const POST = login({ config }) diff --git a/packages/next/.swcrc b/packages/next/.swcrc new file mode 100644 index 0000000000..d46b555fe0 --- /dev/null +++ b/packages/next/.swcrc @@ -0,0 +1,15 @@ +{ + "$schema": "https://json.schemastore.org/swcrc", + "sourceMaps": "inline", + "jsc": { + "target": "esnext", + "parser": { + "syntax": "typescript", + "tsx": true, + "dts": true + } + }, + "module": { + "type": "commonjs" + } +} diff --git a/packages/next/package.json b/packages/next/package.json index f5f0d60eab..b7bc19af55 100644 --- a/packages/next/package.json +++ b/packages/next/package.json @@ -30,18 +30,24 @@ "import": "./src/routes/*.ts", "require": "./src/routes/*.ts", "types": "./src/routes/*.ts" - } + }, + "./dist/routes/*": "./dist/routes/*.js" }, "devDependencies": { - "@payloadcms/ui": "workspace:*", "@payloadcms/eslint-config": "workspace:*", + "@payloadcms/ui": "workspace:*", "payload": "workspace:*", "sass": "^1.69.5" }, + "dependencies": { + "jsonwebtoken": "9.0.1", + "path-to-regexp": "^6.2.1" + }, "peerDependencies": { - "payload": "^2.0.0", + "http-status": "1.6.2", + "i18next": "22.5.1", "next": "^14.0.0", - "i18next": "22.5.1" + "payload": "^2.0.0" }, "publishConfig": { "exports": { @@ -60,6 +66,11 @@ "require": "./dist/pages/*.js", "types": "./dist/pages/*.d.ts" }, + "./app/*": { + "import": "./dist/app/*.js", + "require": "./dist/app/*.js", + "types": "./dist/app/*.d.ts" + }, "./routes/*": { "import": "./dist/routes/*.js", "require": "./dist/routes/*.js", diff --git a/packages/dev/src/app/(payload)/api/[collection]/me/route.ts b/packages/next/src/app/api/[...slug]/route.ts similarity index 51% rename from packages/dev/src/app/(payload)/api/[collection]/me/route.ts rename to packages/next/src/app/api/[...slug]/route.ts index 50f0fd4531..3ea1a223a6 100644 --- a/packages/dev/src/app/(payload)/api/[collection]/me/route.ts +++ b/packages/next/src/app/api/[...slug]/route.ts @@ -1,6 +1,4 @@ /* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */ /* DO NOT MODIFY it because it could be re-written at any time. */ -import { me } from '@payloadcms/next/routes/me' -import config from 'payload-config' -export const GET = me({ config }) +export { GET, POST, DELETE, PATCH } from '@payloadcms/next/dist/routes/index' diff --git a/packages/next/src/app/api/[collection]/init/route.ts b/packages/next/src/app/api/[collection]/init/route.ts deleted file mode 100644 index 7489d6daa4..0000000000 --- a/packages/next/src/app/api/[collection]/init/route.ts +++ /dev/null @@ -1,6 +0,0 @@ -/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */ -/* DO NOT MODIFY it because it could be re-written at any time. */ -import { init } from '@payloadcms/next/routes/init' -import config from 'payload-config' - -export const GET = init({ config }) diff --git a/packages/next/src/app/api/[collection]/login/route.ts b/packages/next/src/app/api/[collection]/login/route.ts deleted file mode 100644 index b1be6d4b65..0000000000 --- a/packages/next/src/app/api/[collection]/login/route.ts +++ /dev/null @@ -1,6 +0,0 @@ -/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */ -/* DO NOT MODIFY it because it could be re-written at any time. */ -import { login } from '@payloadcms/next/routes/login' -import config from 'payload-config' - -export const POST = login({ config }) diff --git a/packages/next/src/auth/authenticate.ts b/packages/next/src/auth/authenticate.ts deleted file mode 100644 index 7dc905810f..0000000000 --- a/packages/next/src/auth/authenticate.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { PayloadRequest, SanitizedConfig } from 'payload/types' - -type Args = { - config: SanitizedConfig - req: PayloadRequest -} -export const authenticate = async ({ config, req }: Args): Promise => { - return req -} diff --git a/packages/next/src/exports/handlers.ts b/packages/next/src/exports/handlers.ts deleted file mode 100644 index df948ed5d3..0000000000 --- a/packages/next/src/exports/handlers.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { init } from '../routes/init' -export { login } from '../routes/login' diff --git a/packages/next/src/routes/auth/access.ts b/packages/next/src/routes/auth/access.ts new file mode 100644 index 0000000000..b1e19375ae --- /dev/null +++ b/packages/next/src/routes/auth/access.ts @@ -0,0 +1,14 @@ +import httpStatus from 'http-status' +import { accessOperation } from 'payload/operations' +import { PayloadRequest } from 'payload/types' + +// TODO(JARROD): pattern to catch errors and return correct Response +export const access = async ({ req }: { req: PayloadRequest }): Promise => { + const results = await accessOperation({ + req, + }) + + return Response.json(results, { + status: httpStatus.OK, + }) +} diff --git a/packages/next/src/routes/auth/forgotPassword.ts b/packages/next/src/routes/auth/forgotPassword.ts new file mode 100644 index 0000000000..f1b62e50fd --- /dev/null +++ b/packages/next/src/routes/auth/forgotPassword.ts @@ -0,0 +1,25 @@ +import { forgotPasswordOperation } from 'payload/operations' +import { PayloadRequest } from 'payload/types' +import httpStatus from 'http-status' + +// TODO(JARROD): pattern to catch errors and return correct Response +export const forgotPassword = async ({ req }: { req: PayloadRequest }): Promise => { + await forgotPasswordOperation({ + collection: req.collection, + data: { + email: req.data.email as string, + }, + disableEmail: Boolean(req.data?.disableEmail), + expiration: typeof req.data.expiration === 'number' ? req.data.expiration : undefined, + req, + }) + + return Response.json( + { + message: 'Success', + }, + { + status: httpStatus.OK, + }, + ) +} diff --git a/packages/next/src/routes/auth/init.ts b/packages/next/src/routes/auth/init.ts new file mode 100644 index 0000000000..402ed6f4c7 --- /dev/null +++ b/packages/next/src/routes/auth/init.ts @@ -0,0 +1,11 @@ +import type { PayloadRequest } from 'payload/types' +import { init as initOperation } from 'payload/operations' + +export const init = async ({ req }: { req: PayloadRequest }): Promise => { + const initialized = await initOperation({ + collection: req.collection.config.slug, + req, + }) + + return Response.json({ initialized }) +} diff --git a/packages/next/src/routes/auth/login.ts b/packages/next/src/routes/auth/login.ts new file mode 100644 index 0000000000..f54832f892 --- /dev/null +++ b/packages/next/src/routes/auth/login.ts @@ -0,0 +1,42 @@ +import httpStatus from 'http-status' +import { loginOperation } from 'payload/operations' +import { PayloadRequest } from 'payload/types' +import { isNumber } from 'payload/utilities' +import { generatePayloadCookie } from '../../utilities/cookies' + +// TODO(JARROD): pattern to catch errors and return correct Response +export const login = async ({ req }: { req: PayloadRequest }): Promise => { + const { searchParams } = new URL(req.url) + const depth = searchParams.get('depth') + + const result = await loginOperation({ + collection: req.collection, + data: { + email: typeof req.data?.email === 'string' ? req.data.email : '', + password: typeof req.data?.password === 'string' ? req.data.password : '', + }, + depth: isNumber(depth) ? Number(depth) : undefined, + req, + }) + + const cookie = generatePayloadCookie({ + token: result.token, + payload: req.payload, + collectionConfig: req.collection.config, + }) + + return Response.json( + { + exp: result.exp, + message: 'Auth Passed', + token: result.token, + user: result.user, + }, + { + headers: new Headers({ + 'Set-Cookie': cookie, + }), + status: httpStatus.OK, + }, + ) +} diff --git a/packages/next/src/routes/auth/logout.ts b/packages/next/src/routes/auth/logout.ts new file mode 100644 index 0000000000..db0b600443 --- /dev/null +++ b/packages/next/src/routes/auth/logout.ts @@ -0,0 +1,40 @@ +import httpStatus from 'http-status' +import { logoutOperation } from 'payload/operations' +import { PayloadRequest } from 'payload/types' +import { generateExpiredPayloadCookie } from '../../utilities/cookies' + +// TODO(JARROD): pattern to catch errors and return correct Response +export const logout = async ({ req }: { req: PayloadRequest }): Promise => { + const result = logoutOperation({ + collection: req.collection, + req, + }) + + if (!result) { + return Response.json( + { + message: 'Logout failed.', + }, + { + status: httpStatus.BAD_REQUEST, + }, + ) + } + + const expiredCookie = generateExpiredPayloadCookie({ + collectionConfig: req.collection.config, + payload: req.payload, + }) + + return Response.json( + { + message: 'Logout successful.', + }, + { + headers: new Headers({ + 'Set-Cookie': expiredCookie, + }), + status: httpStatus.OK, + }, + ) +} diff --git a/packages/next/src/routes/auth/me.ts b/packages/next/src/routes/auth/me.ts new file mode 100644 index 0000000000..7fdc3f786b --- /dev/null +++ b/packages/next/src/routes/auth/me.ts @@ -0,0 +1,25 @@ +import httpStatus from 'http-status' +import type { PayloadRequest } from 'payload/types' +import { meOperation } from 'payload/operations' +import { extractJWT } from '../../utilities/jwt' + +// TODO(JARROD): pattern to catch errors and return correct Response +export const me = async ({ req }: { req: PayloadRequest }): Promise => { + const currentToken = extractJWT(req) + + const result = await meOperation({ + collection: req.collection, + req, + currentToken, + }) + + return Response.json( + { + ...result, + message: 'Successfully retrieved me user.', + }, + { + status: httpStatus.OK, + }, + ) +} diff --git a/packages/next/src/routes/auth/refresh.ts b/packages/next/src/routes/auth/refresh.ts new file mode 100644 index 0000000000..98dab8d66e --- /dev/null +++ b/packages/next/src/routes/auth/refresh.ts @@ -0,0 +1,48 @@ +import { extractJWT } from '../../utilities/jwt' +import { refreshOperation } from 'payload/operations' +import { PayloadRequest } from 'payload/types' +import httpStatus from 'http-status' +import { generatePayloadCookie } from '../../utilities/cookies' + +// TODO(JARROD): pattern to catch errors and return correct Response +export const refresh = async ({ req }: { req: PayloadRequest }): Promise => { + const token = typeof req.data?.token === 'string' ? req.data.token : extractJWT(req) + + if (!token) { + return Response.json( + { + message: 'Token not provided.', + }, + { + status: httpStatus.UNAUTHORIZED, + }, + ) + } + + const result = await refreshOperation({ + token, + req, + collection: req.collection, + }) + + const cookie = generatePayloadCookie({ + token: result.refreshedToken, + payload: req.payload, + collectionConfig: req.collection.config, + }) + + return Response.json( + { + exp: result.exp, + message: 'Token refresh successful', + token: result.refreshedToken, + user: result.user, + }, + { + headers: new Headers({ + 'Set-Cookie': cookie, + }), + status: httpStatus.OK, + }, + ) +} diff --git a/packages/next/src/routes/auth/registerFirstUser.ts b/packages/next/src/routes/auth/registerFirstUser.ts new file mode 100644 index 0000000000..0962950b16 --- /dev/null +++ b/packages/next/src/routes/auth/registerFirstUser.ts @@ -0,0 +1,37 @@ +import httpStatus from 'http-status' +import { registerFirstUserOperation } from 'payload/operations' +import { PayloadRequest } from 'payload/types' +import { generatePayloadCookie } from '../../utilities/cookies' + +// TODO(JARROD): pattern to catch errors and return correct Response +export const registerFirstUser = async ({ req }: { req: PayloadRequest }): Promise => { + const result = await registerFirstUserOperation({ + collection: req.collection, + data: { + email: typeof req.data?.email === 'string' ? req.data.email : '', + password: typeof req.data?.password === 'string' ? req.data.password : '', + }, + req, + }) + + const cookie = generatePayloadCookie({ + token: result.token, + payload: req.payload, + collectionConfig: req.collection.config, + }) + + return Response.json( + { + exp: result.exp, + message: 'Successfully registered first user.', + token: result.token, + user: result.user, + }, + { + headers: new Headers({ + 'Set-Cookie': cookie, + }), + status: httpStatus.OK, + }, + ) +} diff --git a/packages/next/src/routes/auth/resetPassword.ts b/packages/next/src/routes/auth/resetPassword.ts new file mode 100644 index 0000000000..6a78d2c94b --- /dev/null +++ b/packages/next/src/routes/auth/resetPassword.ts @@ -0,0 +1,41 @@ +import httpStatus from 'http-status' + +import { resetPasswordOperation } from 'payload/operations' +import { generatePayloadCookie } from '../../utilities/cookies' +import { PayloadRequest } from 'payload/types' + +// TODO(JARROD): pattern to catch errors and return correct Response +export const resetPassword = async ({ req }: { req: PayloadRequest }): Promise => { + const { searchParams } = new URL(req.url) + const depth = searchParams.get('depth') + + const result = await resetPasswordOperation({ + collection: req.collection, + data: { + password: typeof req.data?.password === 'string' ? req.data.password : '', + token: typeof req.data?.token === 'string' ? req.data.token : '', + }, + depth: depth ? Number(depth) : undefined, + req, + }) + + const cookie = generatePayloadCookie({ + token: result.token, + payload: req.payload, + collectionConfig: req.collection.config, + }) + + return Response.json( + { + message: 'Password reset successfully.', + token: result.token, + user: result.user, + }, + { + headers: new Headers({ + 'Set-Cookie': cookie, + }), + status: httpStatus.OK, + }, + ) +} diff --git a/packages/next/src/routes/auth/unlock.ts b/packages/next/src/routes/auth/unlock.ts new file mode 100644 index 0000000000..588e300eda --- /dev/null +++ b/packages/next/src/routes/auth/unlock.ts @@ -0,0 +1,22 @@ +import httpStatus from 'http-status' + +import { unlockOperation } from 'payload/operations' +import { PayloadRequest } from 'payload/types' + +// TODO(JARROD): pattern to catch errors and return correct Response +export const unlock = async ({ req }: { req: PayloadRequest }): Promise => { + await unlockOperation({ + collection: req.collection, + data: { email: req.data.email as string }, + req, + }) + + return Response.json( + { + message: 'Success', + }, + { + status: httpStatus.OK, + }, + ) +} diff --git a/packages/next/src/routes/auth/verifyEmail.ts b/packages/next/src/routes/auth/verifyEmail.ts new file mode 100644 index 0000000000..ada925a824 --- /dev/null +++ b/packages/next/src/routes/auth/verifyEmail.ts @@ -0,0 +1,27 @@ +import httpStatus from 'http-status' + +import { verifyEmailOperation } from 'payload/operations' +import { PayloadRequest } from 'payload/types' + +export const verifyEmail = async ({ + req, + id, +}: { + req: PayloadRequest + id: string +}): Promise => { + await verifyEmailOperation({ + collection: req.collection, + req, + token: id, + }) + + return Response.json( + { + message: 'Email verified successfully.', + }, + { + status: httpStatus.OK, + }, + ) +} diff --git a/packages/next/src/routes/collections/create.ts b/packages/next/src/routes/collections/create.ts new file mode 100644 index 0000000000..554a171c26 --- /dev/null +++ b/packages/next/src/routes/collections/create.ts @@ -0,0 +1,33 @@ +import httpStatus from 'http-status' + +import type { PayloadRequest } from 'payload/types' + +import { isNumber } from 'payload/utilities' +import { createOperation } from 'payload/operations' + +// TODO(JARROD): pattern to catch errors and return correct Response +export const create = async ({ req }: { req: PayloadRequest }): Promise => { + const { searchParams } = new URL(req.url) + const autosave = searchParams.get('autosave') === 'true' + const draft = searchParams.get('draft') === 'true' + const depth = searchParams.get('depth') + + const doc = await createOperation({ + autosave, + collection: req.collection, + data: req.data, + depth: isNumber(depth) ? depth : undefined, + draft, + req, + }) + + // ...formatSuccessResponse( + // req.t('general:successfullyCreated', { + // label: getTranslation(req.collection.config.labels.singular, req.i18n), + // }), + // 'message', + // ) + return Response.json(doc, { + status: httpStatus.OK, + }) +} diff --git a/packages/next/src/routes/collections/delete.ts b/packages/next/src/routes/collections/delete.ts new file mode 100644 index 0000000000..bba72aab4e --- /dev/null +++ b/packages/next/src/routes/collections/delete.ts @@ -0,0 +1,62 @@ +import httpStatus from 'http-status' + +import type { PayloadRequest, Where } from 'payload/types' + +import { isNumber } from 'payload/utilities' +import { deleteOperation } from 'payload/operations' + +// TODO(JARROD): pattern to catch errors and return correct Response +export const deleteDoc = async ({ req }: { req: PayloadRequest }): Promise => { + const { searchParams } = new URL(req.url) + const depth = searchParams.get('depth') + const where = searchParams.get('where') + + const result = await deleteOperation({ + collection: req.collection, + depth: isNumber(depth) ? depth : undefined, + req, + where: where ? (JSON.parse(where) as Where) : {}, + }) + + if (result.errors.length === 0) { + // const message = req.t('general:deletedCountSuccessfully', { + // count: result.docs.length, + // label: getTranslation( + // req.collection.config.labels[result.docs.length > 1 ? 'plural' : 'singular'], + // req.i18n, + // ), + // }) + + // res.status(httpStatus.OK).json({ + // ...formatSuccessResponse(message, 'message'), + // ...result, + // }) + return Response.json(result, { + status: httpStatus.OK, + }) + } + + // const total = result.docs.length + result.errors.length + // const message = req.t('error:unableToDeleteCount', { + // count: result.errors.length, + // label: getTranslation( + // req.collection.config.labels[total > 1 ? 'plural' : 'singular'], + // req.i18n, + // ), + // total, + // }) + + // res.status(httpStatus.BAD_REQUEST).json({ + // message, + // ...result, + // }) + + return Response.json( + { + ...result, + }, + { + status: httpStatus.BAD_REQUEST, + }, + ) +} diff --git a/packages/next/src/routes/collections/deleteByID.ts b/packages/next/src/routes/collections/deleteByID.ts new file mode 100644 index 0000000000..64ff626d45 --- /dev/null +++ b/packages/next/src/routes/collections/deleteByID.ts @@ -0,0 +1,45 @@ +import httpStatus from 'http-status' + +import type { PayloadRequest } from 'payload/types' + +import { isNumber } from 'payload/utilities' +import { deleteByIDOperation } from 'payload/operations' + +// TODO(JARROD): pattern to catch errors and return correct Response +export const deleteByID = async ({ + req, + id, +}: { + req: PayloadRequest + id: string +}): Promise => { + const { searchParams } = new URL(req.url) + const depth = searchParams.get('depth') + const doc = await deleteByIDOperation({ + id, + collection: req.collection, + depth: isNumber(depth) ? depth : undefined, + req, + }) + + if (!doc) { + return Response.json( + { + message: 'Not Found', + }, + { + status: httpStatus.NOT_FOUND, + }, + ) + } + + return Response.json( + { + ...doc, + // ...formatSuccessResponse(req.t('general:successfullyDeleted'), 'message'), + }, + { + status: httpStatus.OK, + }, + ) +} diff --git a/packages/next/src/routes/collections/docAccess.ts b/packages/next/src/routes/collections/docAccess.ts new file mode 100644 index 0000000000..ce2b2b7073 --- /dev/null +++ b/packages/next/src/routes/collections/docAccess.ts @@ -0,0 +1,23 @@ +import httpStatus from 'http-status' + +import type { PayloadRequest } from 'payload/types' + +import { docAccessOperation } from 'payload/operations' + +// TODO(JARROD): pattern to catch errors and return correct Response +export const docAccess = async ({ + req, + id, +}: { + req: PayloadRequest + id: string +}): Promise => { + const result = await docAccessOperation({ + id, + req, + }) + + return Response.json(result, { + status: httpStatus.OK, + }) +} diff --git a/packages/next/src/routes/collections/find.ts b/packages/next/src/routes/collections/find.ts new file mode 100644 index 0000000000..e9f95bc037 --- /dev/null +++ b/packages/next/src/routes/collections/find.ts @@ -0,0 +1,31 @@ +import httpStatus from 'http-status' + +import type { PayloadRequest } from 'payload/types' + +import { isNumber } from 'payload/utilities' +import { findOperation } from 'payload/operations' + +// TODO(JARROD): pattern to catch errors and return correct Response +export const find = async ({ req }: { req: PayloadRequest }): Promise => { + const { searchParams } = new URL(req.url) + + const depth = searchParams.get('depth') + const limit = searchParams.get('limit') + const page = searchParams.get('page') + const where = searchParams.get('where') + + const result = await findOperation({ + collection: req.collection, + depth: isNumber(depth) ? Number(depth) : undefined, + draft: searchParams.get('draft') === 'true', + limit: isNumber(limit) ? Number(limit) : undefined, + page: isNumber(page) ? Number(page) : undefined, + req, + sort: searchParams.get('sort'), + where: where ? JSON.parse(where) : undefined, + }) + + return Response.json(result, { + status: httpStatus.OK, + }) +} diff --git a/packages/next/src/routes/collections/findByID.ts b/packages/next/src/routes/collections/findByID.ts new file mode 100644 index 0000000000..d66cc19b52 --- /dev/null +++ b/packages/next/src/routes/collections/findByID.ts @@ -0,0 +1,29 @@ +import httpStatus from 'http-status' + +import { PayloadRequest } from 'payload/types' +import { findByIDOperation } from 'payload/operations' +import { isNumber } from 'payload/utilities' + +// TODO(JARROD): pattern to catch errors and return correct Response +export const findByID = async ({ + req, + id, +}: { + req: PayloadRequest + id: string +}): Promise => { + const { searchParams } = new URL(req.url) + const depth = searchParams.get('depth') + + const result = await findByIDOperation({ + id, + collection: req.collection, + depth: isNumber(depth) ? Number(depth) : undefined, + draft: searchParams.get('draft') === 'true', + req, + }) + + return Response.json(result, { + status: httpStatus.OK, + }) +} diff --git a/packages/next/src/routes/collections/findVersionByID.ts b/packages/next/src/routes/collections/findVersionByID.ts new file mode 100644 index 0000000000..6210ed25b8 --- /dev/null +++ b/packages/next/src/routes/collections/findVersionByID.ts @@ -0,0 +1,28 @@ +import httpStatus from 'http-status' + +import { PayloadRequest } from 'payload/types' +import { findVersionByIDOperation } from 'payload/operations' +import { isNumber } from 'payload/utilities' + +// TODO(JARROD): pattern to catch errors and return correct Response +export const findVersionByID = async ({ + req, + id, +}: { + req: PayloadRequest + id: string +}): Promise => { + const { searchParams } = new URL(req.url) + const depth = searchParams.get('depth') + + const result = await findVersionByIDOperation({ + id, + collection: req.collection, + depth: isNumber(depth) ? Number(depth) : undefined, + req, + }) + + return Response.json(result, { + status: httpStatus.OK, + }) +} diff --git a/packages/next/src/routes/collections/findVersions.ts b/packages/next/src/routes/collections/findVersions.ts new file mode 100644 index 0000000000..e91c66b2aa --- /dev/null +++ b/packages/next/src/routes/collections/findVersions.ts @@ -0,0 +1,35 @@ +import httpStatus from 'http-status' + +import { PayloadRequest, Where } from 'payload/types' +import { findVersionsOperation } from 'payload/operations' +import { isNumber } from 'payload/utilities' + +// TODO(JARROD): pattern to catch errors and return correct Response +export const findVersions = async ({ + req, + id, +}: { + req: PayloadRequest + id: string +}): Promise => { + const { searchParams } = new URL(req.url) + const page = searchParams.get('page') + const depth = searchParams.get('depth') + const limit = searchParams.get('limit') + const where = searchParams.get('where') + const sort = searchParams.get('sort') + + const result = await findVersionsOperation({ + collection: req.collection, + depth: isNumber(depth) ? Number(depth) : undefined, + limit: isNumber(limit) ? Number(limit) : undefined, + page: isNumber(page) ? Number(page) : undefined, + req, + sort: sort, + where: where ? (JSON.parse(where) as Where) : undefined, + }) + + return Response.json(result, { + status: httpStatus.OK, + }) +} diff --git a/packages/next/src/routes/collections/restoreVersion.ts b/packages/next/src/routes/collections/restoreVersion.ts new file mode 100644 index 0000000000..72af44c2ca --- /dev/null +++ b/packages/next/src/routes/collections/restoreVersion.ts @@ -0,0 +1,34 @@ +import httpStatus from 'http-status' + +import { PayloadRequest } from 'payload/types' +import { restoreVersionOperation } from 'payload/operations' +import { isNumber } from 'payload/utilities' + +// TODO(JARROD): pattern to catch errors and return correct Response +export const restoreVersion = async ({ + req, + id, +}: { + req: PayloadRequest + id: string +}): Promise => { + const { searchParams } = new URL(req.url) + const depth = searchParams.get('depth') + + const result = await restoreVersionOperation({ + id, + collection: req.collection, + depth: isNumber(depth) ? Number(depth) : undefined, + req, + }) + + return Response.json( + { + ...result, + // ...formatSuccessResponse(req.t('version:restoredSuccessfully'), 'message'), + }, + { + status: httpStatus.OK, + }, + ) +} diff --git a/packages/next/src/routes/collections/update.ts b/packages/next/src/routes/collections/update.ts new file mode 100644 index 0000000000..fde0b45fbd --- /dev/null +++ b/packages/next/src/routes/collections/update.ts @@ -0,0 +1,63 @@ +import httpStatus from 'http-status' + +import type { PayloadRequest, Where } from 'payload/types' + +import { isNumber } from 'payload/utilities' +import { updateOperation } from 'payload/operations' + +// TODO(JARROD): pattern to catch errors and return correct Response +export const update = async ({ req }: { req: PayloadRequest }): Promise => { + const { searchParams } = new URL(req.url) + const depth = searchParams.get('depth') + const where = searchParams.get('where') + + const result = await updateOperation({ + collection: req.collection, + data: req.data, + depth: isNumber(depth) ? Number(depth) : undefined, + draft: searchParams.get('draft') === 'true', + req, + where: where ? (JSON.parse(where) as Where) : undefined, + }) + + if (result.errors.length === 0) { + // const message = req.t('general:updatedCountSuccessfully', { + // count: result.docs.length, + // label: getTranslation( + // req.collection.config.labels[result.docs.length > 1 ? 'plural' : 'singular'], + // req.i18n, + // ), + // }) + + // res.status(httpStatus.OK).json({ + // ...formatSuccessResponse(message, 'message'), + // ...result, + // }) + return Response.json(result, { + status: httpStatus.OK, + }) + } + + // const total = result.docs.length + result.errors.length + // const message = req.t('error:unableToUpdateCount', { + // count: result.errors.length, + // label: getTranslation( + // req.collection.config.labels[total > 1 ? 'plural' : 'singular'], + // req.i18n, + // ), + // total, + // }) + + // res.status(httpStatus.BAD_REQUEST).json({ + // ...formatSuccessResponse(message, 'message'), + // ...result, + // }) + return Response.json( + { + ...result, + }, + { + status: httpStatus.BAD_REQUEST, + }, + ) +} diff --git a/packages/next/src/routes/collections/updateByID.ts b/packages/next/src/routes/collections/updateByID.ts new file mode 100644 index 0000000000..a65eaa2f0a --- /dev/null +++ b/packages/next/src/routes/collections/updateByID.ts @@ -0,0 +1,45 @@ +import httpStatus from 'http-status' + +import type { PayloadRequest } from 'payload/types' + +import { isNumber } from 'payload/utilities' +import { updateByIDOperation } from 'payload/operations' + +// TODO(JARROD): pattern to catch errors and return correct Response +export const updateByID = async ({ + req, + id, +}: { + req: PayloadRequest + id: string +}): Promise => { + const { searchParams } = new URL(req.url) + const depth = searchParams.get('depth') + const autosave = searchParams.get('autosave') === 'true' + const draft = searchParams.get('draft') === 'true' + + const doc = await updateByIDOperation({ + id, + autosave, + collection: req.collection, + data: req.data, + depth: isNumber(depth) ? Number(depth) : undefined, + draft, + req, + }) + + let message = req.t('general:updatedSuccessfully') + + if (draft) message = req.t('version:draftSavedSuccessfully') + if (autosave) message = req.t('version:autosavedSuccessfully') + + return Response.json( + { + // ...formatSuccessResponse(message, 'message'), + doc, + }, + { + status: httpStatus.OK, + }, + ) +} diff --git a/packages/next/src/routes/globals/docAccess.ts b/packages/next/src/routes/globals/docAccess.ts new file mode 100644 index 0000000000..4d5575edbc --- /dev/null +++ b/packages/next/src/routes/globals/docAccess.ts @@ -0,0 +1,23 @@ +import httpStatus from 'http-status' + +import type { PayloadRequest, SanitizedGlobalConfig } from 'payload/types' + +import { docAccessOperationGlobal } from 'payload/operations' + +// TODO(JARROD): pattern to catch errors and return correct Response +export const docAccess = async ({ + req, + globalConfig, +}: { + req: PayloadRequest + globalConfig: SanitizedGlobalConfig +}): Promise => { + const result = await docAccessOperationGlobal({ + globalConfig, + req, + }) + + return Response.json(result, { + status: httpStatus.OK, + }) +} diff --git a/packages/next/src/routes/globals/findOne.ts b/packages/next/src/routes/globals/findOne.ts new file mode 100644 index 0000000000..bef0248dc6 --- /dev/null +++ b/packages/next/src/routes/globals/findOne.ts @@ -0,0 +1,30 @@ +import httpStatus from 'http-status' + +import type { PayloadRequest, SanitizedGlobalConfig } from 'payload/types' + +import { findOneOperation } from 'payload/operations' +import { isNumber } from 'payload/utilities' + +// TODO(JARROD): pattern to catch errors and return correct Response +export const findOne = async ({ + req, + globalConfig, +}: { + req: PayloadRequest + globalConfig: SanitizedGlobalConfig +}): Promise => { + const { searchParams } = new URL(req.url) + const depth = searchParams.get('depth') + + const result = await findOneOperation({ + depth: isNumber(depth) ? Number(depth) : undefined, + draft: searchParams.get('draft') === 'true', + globalConfig, + req, + slug: globalConfig.slug, + }) + + return Response.json(result, { + status: httpStatus.OK, + }) +} diff --git a/packages/next/src/routes/globals/findVersionByID.ts b/packages/next/src/routes/globals/findVersionByID.ts new file mode 100644 index 0000000000..7fc15321fa --- /dev/null +++ b/packages/next/src/routes/globals/findVersionByID.ts @@ -0,0 +1,31 @@ +import httpStatus from 'http-status' + +import type { PayloadRequest, SanitizedGlobalConfig } from 'payload/types' + +import { findVersionByIDOperationGlobal } from 'payload/operations' +import { isNumber } from 'payload/utilities' + +// TODO(JARROD): pattern to catch errors and return correct Response +export const findVersionByID = async ({ + req, + globalConfig, + id, +}: { + req: PayloadRequest + globalConfig: SanitizedGlobalConfig + id: string +}): Promise => { + const { searchParams } = new URL(req.url) + const depth = searchParams.get('depth') + + const result = await findVersionByIDOperationGlobal({ + id, + depth: isNumber(depth) ? Number(depth) : undefined, + globalConfig, + req, + }) + + return Response.json(result, { + status: httpStatus.OK, + }) +} diff --git a/packages/next/src/routes/globals/findVersions.ts b/packages/next/src/routes/globals/findVersions.ts new file mode 100644 index 0000000000..337b6007ca --- /dev/null +++ b/packages/next/src/routes/globals/findVersions.ts @@ -0,0 +1,35 @@ +import httpStatus from 'http-status' + +import type { PayloadRequest, SanitizedGlobalConfig, Where } from 'payload/types' + +import { findVersionsOperationGlobal } from 'payload/operations' +import { isNumber } from 'payload/utilities' + +// TODO(JARROD): pattern to catch errors and return correct Response +export const findVersions = async ({ + req, + globalConfig, +}: { + req: PayloadRequest + globalConfig: SanitizedGlobalConfig +}): Promise => { + const { searchParams } = new URL(req.url) + const page = searchParams.get('page') + const limit = searchParams.get('limit') + const depth = searchParams.get('depth') + const where = searchParams.get('where') + + const result = await findVersionsOperationGlobal({ + depth: isNumber(depth) ? Number(depth) : undefined, + globalConfig, + limit: isNumber(limit) ? Number(limit) : undefined, + page: isNumber(page) ? Number(page) : undefined, + req, + sort: searchParams.get('sort'), + where: where ? (JSON.parse(where) as Where) : undefined, + }) + + return Response.json(result, { + status: httpStatus.OK, + }) +} diff --git a/packages/next/src/routes/globals/restoreVersion.ts b/packages/next/src/routes/globals/restoreVersion.ts new file mode 100644 index 0000000000..089c9a43bd --- /dev/null +++ b/packages/next/src/routes/globals/restoreVersion.ts @@ -0,0 +1,32 @@ +import httpStatus from 'http-status' + +import type { PayloadRequest, SanitizedGlobalConfig } from 'payload/types' + +import { restoreVersionOperationGlobal } from 'payload/operations' +import { isNumber } from 'payload/utilities' + +// TODO(JARROD): pattern to catch errors and return correct Response +export const restoreVersion = async ({ + req, + globalConfig, + id, +}: { + req: PayloadRequest + globalConfig: SanitizedGlobalConfig + id: string +}): Promise => { + const { searchParams } = new URL(req.url) + const depth = searchParams.get('depth') + + const doc = await restoreVersionOperationGlobal({ + id, + depth: isNumber(depth) ? Number(depth) : undefined, + globalConfig, + req, + }) + + // ...formatSuccessResponse(req.t('version:restoredSuccessfully'), 'message'), + return Response.json(doc, { + status: httpStatus.OK, + }) +} diff --git a/packages/next/src/routes/globals/update.ts b/packages/next/src/routes/globals/update.ts new file mode 100644 index 0000000000..00a07ded8f --- /dev/null +++ b/packages/next/src/routes/globals/update.ts @@ -0,0 +1,39 @@ +import httpStatus from 'http-status' + +import type { PayloadRequest, SanitizedGlobalConfig } from 'payload/types' + +import { updateOperationGlobal } from 'payload/operations' +import { isNumber } from 'payload/utilities' + +// TODO(JARROD): pattern to catch errors and return correct Response +export const update = async ({ + req, + globalConfig, +}: { + req: PayloadRequest + globalConfig: SanitizedGlobalConfig +}): Promise => { + const { searchParams } = new URL(req.url) + const depth = searchParams.get('depth') + const draft = searchParams.get('draft') === 'true' + const autosave = searchParams.get('autosave') === 'true' + + const result = await updateOperationGlobal({ + autosave, + data: req.data, + depth: isNumber(depth) ? Number(depth) : undefined, + draft, + globalConfig, + req, + slug: globalConfig.slug, + }) + + let message = req.t('general:updatedSuccessfully') + + if (draft) message = req.t('version:draftSavedSuccessfully') + if (autosave) message = req.t('version:autosavedSuccessfully') + + return Response.json(result, { + status: httpStatus.OK, + }) +} diff --git a/packages/next/src/routes/index.ts b/packages/next/src/routes/index.ts new file mode 100644 index 0000000000..bf9f7a1f19 --- /dev/null +++ b/packages/next/src/routes/index.ts @@ -0,0 +1,258 @@ +import config from 'payload-config' +import { createPayloadRequest } from '../utilities/createPayloadRequest' + +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' + +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, + }, + }, +} + +export const GET = async ( + request: Request, + { params: { slug } }: { params: { slug: string[] } }, +) => { + const [slug1, slug2, slug3, slug4] = slug + + const req = await createPayloadRequest({ + request, + config, + params: { + collection: slug1, + }, + }) + + if (slug.length === 1 && slug1 === 'access') { + return endpoints.root.GET.access({ req }) + } + + if (req?.collection) { + switch (slug.length) { + case 1: + // /:collection + return endpoints.collection.GET.find({ req }) + case 2: + if (slug2 in endpoints.collection.GET) { + // /:collection/init + // /:collection/me + // /:collection/versions + return endpoints.collection.GET?.[slug2]({ req }) + } else if (req.collection.config.endpoints && req.collection.config.endpoints.length > 0) { + // /:collection/:id + } + return endpoints.collection.GET.findByID({ req, id: slug2 }) + case 3: + // /:collection/access/:id + // /:collection/versions/:id + const key = `doc-${slug2}-by-id` + if (key in endpoints.collection.GET) { + return endpoints.collection.GET[key]({ req, id: slug3 }) + } + break + default: + return new Response('Route Not Found', { status: 404 }) + } + } else if (slug1 === 'globals') { + const globalConfig = req.payload.config.globals.find((global) => global.slug === slug2) + + switch (slug.length) { + case 2: + // /globals/:slug + return endpoints.global.GET.findOne({ req, globalConfig }) + case 3: + // /globals/:slug/access + // /globals/:slug/versions + return endpoints.global.GET?.[`doc-${slug3}`]({ req, globalConfig }) + case 4: + // /globals/:slug/versions/:id + return endpoints.global.GET?.[`doc-${slug3}-by-id`]({ req, id: slug4, globalConfig }) + default: + return new Response('Route Not Found', { status: 404 }) + } + } +} + +export const POST = async ( + request: Request, + { params: { slug } }: { params: { slug: string[] } }, +) => { + const [slug1, slug2, slug3, slug4] = slug + + const req = await createPayloadRequest({ request, config, params: { collection: slug1 } }) + + if (req?.collection) { + switch (slug.length) { + case 1: + // /:collection + return endpoints.collection.POST.create({ req }) + 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]({ req }) + } + case 3: + // /: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`]({ req, id: slug3 }) + default: + return new Response('Route Not Found', { status: 404 }) + } + } else if (slug1 === 'globals') { + 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: + // /globals/:slug/access + return endpoints.global.POST?.[`doc-${slug3}`]({ req, globalConfig }) + case 4: + // /globals/:slug/versions/:id + return endpoints.global.POST?.[`doc-${slug3}-by-id`]({ req, id: slug4, globalConfig }) + default: + return new Response('Route Not Found', { status: 404 }) + } + } +} + +export const DELETE = async ( + request: Request, + { params: { slug } }: { params: { slug: string[] } }, +) => { + const [slug1, slug2] = slug + + const req = await createPayloadRequest({ + request, + config, + params: { + collection: slug1, + }, + }) + + if (req?.collection) { + switch (slug.length) { + case 1: + // /:collection + return endpoints.collection.DELETE.delete({ req }) + case 2: + // /:collection/:id + return endpoints.collection.DELETE.deleteByID({ req, id: slug2 }) + default: + return new Response('Route Not Found', { status: 404 }) + } + } +} + +export const PATCH = async ( + request: Request, + { params: { slug } }: { params: { slug: string[] } }, +) => { + const [slug1, slug2] = slug + + const req = await createPayloadRequest({ + request, + config, + params: { + collection: slug1, + }, + }) + + if (req?.collection) { + switch (slug.length) { + case 1: + // /:collection + return endpoints.collection.PATCH.update({ req }) + case 2: + // /:collection/:id + return endpoints.collection.PATCH.updateByID({ req, id: slug2 }) + default: + return new Response('Route Not Found', { status: 404 }) + } + } +} diff --git a/packages/next/src/routes/init.ts b/packages/next/src/routes/init.ts deleted file mode 100644 index 6317edf7ac..0000000000 --- a/packages/next/src/routes/init.ts +++ /dev/null @@ -1,15 +0,0 @@ -import type { SanitizedConfig } from 'payload/types' -import { init as initOperation } from 'payload/operations' -import { createPayloadRequest } from '../createPayloadRequest' - -export const init = ({ config }: { config: Promise }) => - async function (request: Request, { params }: { params: { collection: string } }) { - const req = await createPayloadRequest({ request, config, params }) - - const initialized = await initOperation({ - collection: params.collection, - req, - }) - - return Response.json({ initialized }) - } diff --git a/packages/next/src/routes/login.ts b/packages/next/src/routes/login.ts deleted file mode 100644 index 870a292492..0000000000 --- a/packages/next/src/routes/login.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { login as loginOperation } from 'payload/operations' -import { createPayloadRequest } from '../createPayloadRequest' -import { SanitizedConfig } from 'payload/types' -import { isNumber } from 'payload/utilities' - -export const login = ({ config }: { config: Promise }) => - async function (request: Request, { params }: { params: { collection: string } }) { - const req = await createPayloadRequest({ request, config }) - const collection = req.payload.collections[params.collection] - - const { searchParams } = new URL(request.url) - const depth = searchParams.get('depth') - let responseOptions = { - headers: new Headers(), - } - const result = await loginOperation({ - collection, - data: { - email: typeof req.data?.email === 'string' ? req.data.email : '', - password: typeof req.data?.password === 'string' ? req.data.password : '', - }, - depth: isNumber(depth) ? Number(depth) : undefined, - req, - responseOptions, - }) - - return Response.json( - { - exp: result.exp, - message: 'Auth Passed', - token: result.token, - user: result.user, - }, - responseOptions, - ) - } diff --git a/packages/next/src/routes/me.ts b/packages/next/src/routes/me.ts deleted file mode 100644 index 3981a9cdaf..0000000000 --- a/packages/next/src/routes/me.ts +++ /dev/null @@ -1,15 +0,0 @@ -import type { SanitizedConfig } from 'payload/types' -import { me as meOperation } from 'payload/operations' -import { createPayloadRequest } from '../createPayloadRequest' - -export const me = ({ config }: { config: Promise }) => - async function (request: Request, { params }: { params: { collection: string } }) { - const req = await createPayloadRequest({ request, config }) - const collection = req.payload.collections[params.collection] - const meRes = await meOperation({ - collection, - req, - }) - - return Response.json(meRes) - } diff --git a/packages/next/src/types.ts b/packages/next/src/types.ts deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/packages/next/src/utilities/cookies.ts b/packages/next/src/utilities/cookies.ts new file mode 100644 index 0000000000..db0e461c19 --- /dev/null +++ b/packages/next/src/utilities/cookies.ts @@ -0,0 +1,146 @@ +import type { PayloadT, SanitizedCollectionConfig } from 'payload/types' + +type CookieOptions = { + domain?: string + expires?: Date + httpOnly?: boolean + maxAge?: number + name: string + path?: string + sameSite?: 'Lax' | 'None' | 'Strict' + secure?: boolean + value?: string +} + +export const generateCookies = (cookies: CookieOptions[]): string => { + return cookies.map((options) => generateCookie(options)).join('; ') +} + +export const generateCookie = (args: CookieOptions): string => { + const { name, domain, expires, httpOnly, maxAge, path, sameSite, secure: secureArg, value } = args + + let cookieString = `${name}=${value || ''}` + + const secure = secureArg || sameSite === 'None' + + if (expires) { + cookieString += `; Expires=${expires.toUTCString()}` + } + + if (maxAge) { + cookieString += `; Max-Age=${maxAge}` + } + + if (domain) { + cookieString += `; Domain=${domain}` + } + + if (path) { + cookieString += `; Path=${path}` + } + + if (secure) { + cookieString += '; Secure' + } + + if (httpOnly) { + cookieString += '; HttpOnly' + } + + if (sameSite) { + cookieString += `; SameSite=${sameSite}` + } + + return cookieString +} + +type GetCookieExpirationArgs = { + /* + The number of seconds until the cookie expires + @default 7200 seconds (2 hours) + */ + seconds: number +} +const getCookieExpiration = ({ seconds = 7200 }: GetCookieExpirationArgs) => { + const currentTime = new Date() + currentTime.setSeconds(currentTime.getSeconds() + seconds) + return currentTime +} + +type GeneratePayloadCookieArgs = { + /* The auth collection config */ + collectionConfig: SanitizedCollectionConfig + /* An instance of payload */ + payload: PayloadT + /* The token to be stored in the cookie */ + token: string +} +export const generatePayloadCookie = ({ + collectionConfig, + payload, + token, +}: GeneratePayloadCookieArgs): string => { + const sameSite = + typeof collectionConfig.auth.cookies.sameSite === 'string' + ? collectionConfig.auth.cookies.sameSite + : collectionConfig.auth.cookies.sameSite + ? 'Strict' + : undefined + + return generateCookie({ + name: `${payload.config.cookiePrefix}-token`, + domain: collectionConfig.auth.cookies.domain ?? undefined, + expires: getCookieExpiration({ seconds: collectionConfig.auth.tokenExpiration }), + httpOnly: true, + path: '/', + sameSite, + secure: collectionConfig.auth.cookies.secure, + value: token, + }) +} + +export const generateExpiredPayloadCookie = ({ + collectionConfig, + payload, +}: Omit): string => { + const sameSite = + typeof collectionConfig.auth.cookies.sameSite === 'string' + ? collectionConfig.auth.cookies.sameSite + : collectionConfig.auth.cookies.sameSite + ? 'Strict' + : undefined + + const expires = new Date(Date.now() - 1000) + + return generateCookie({ + name: `${payload.config.cookiePrefix}-token`, + domain: collectionConfig.auth.cookies.domain ?? undefined, + expires, + httpOnly: true, + path: '/', + sameSite, + secure: collectionConfig.auth.cookies.secure, + }) +} + +export const parseCookies = (headers: Request['headers']): Map => { + const cookieMap = new Map() + const cookie = headers.get('Cookie') + + if (cookie) { + cookie.split(';').forEach((cookie) => { + const parts = cookie.split('=') + const key = parts.shift().trim() + const encodedValue = parts.join('=') + + try { + const decodedValue = decodeURI(encodedValue) + cookieMap.set(key, decodedValue) + } catch (e) { + return null + } + }) + } + + return cookieMap +} diff --git a/packages/next/src/createPayloadRequest.ts b/packages/next/src/utilities/createPayloadRequest.ts similarity index 100% rename from packages/next/src/createPayloadRequest.ts rename to packages/next/src/utilities/createPayloadRequest.ts diff --git a/packages/next/src/utilities/jwt.ts b/packages/next/src/utilities/jwt.ts new file mode 100644 index 0000000000..15a1df095e --- /dev/null +++ b/packages/next/src/utilities/jwt.ts @@ -0,0 +1,34 @@ +import { AuthStrategyFunctionArgs } from 'payload/auth' +import { parseCookies } from './cookies' + +export const extractJWT = ( + args: Pick, +): null | string => { + const { headers, payload } = args + + const jwtFromHeader = headers.get('Authorization') + const origin = headers.get('Origin') + + if (jwtFromHeader?.startsWith('JWT ')) { + return jwtFromHeader.replace('JWT ', '') + } + // allow RFC6750 OAuth 2.0 compliant Bearer tokens + // in addition to the payload default JWT format + if (jwtFromHeader?.startsWith('Bearer ')) { + return jwtFromHeader.replace('Bearer ', '') + } + + const cookies = parseCookies(headers) + const tokenCookieName = `${payload.config.cookiePrefix}-token` + const cookieToken = cookies?.get(tokenCookieName) + + if (!cookieToken) { + return null + } + + if (!origin || payload.config.csrf.length === 0 || payload.config.csrf.indexOf(origin) > -1) { + return cookieToken + } + + return null +} diff --git a/packages/payload/src/auth/executeAccess.ts b/packages/payload/src/auth/executeAccess.ts index 0c3e10fa42..222b230008 100644 --- a/packages/payload/src/auth/executeAccess.ts +++ b/packages/payload/src/auth/executeAccess.ts @@ -4,7 +4,7 @@ import type { PayloadRequest } from '../exports/types' import { Forbidden } from '../errors' type OperationArgs = { - data?: Record + data?: any disableErrors?: boolean id?: number | string req: PayloadRequest diff --git a/packages/payload/src/auth/getExtractJWT.ts b/packages/payload/src/auth/getExtractJWT.ts index 1d6036efce..8671443de0 100644 --- a/packages/payload/src/auth/getExtractJWT.ts +++ b/packages/payload/src/auth/getExtractJWT.ts @@ -1,6 +1,6 @@ import type { AuthStrategyFunctionArgs } from '.' -import { parseCookies } from '../utilities/cookies' +import { parseCookies } from '../utilities/parseCookies' export const extractJWT = ( args: Pick, diff --git a/packages/payload/src/auth/operations/getFieldsToSign.ts b/packages/payload/src/auth/getFieldsToSign.ts similarity index 92% rename from packages/payload/src/auth/operations/getFieldsToSign.ts rename to packages/payload/src/auth/getFieldsToSign.ts index 57025dced5..741a0f1524 100644 --- a/packages/payload/src/auth/operations/getFieldsToSign.ts +++ b/packages/payload/src/auth/getFieldsToSign.ts @@ -1,9 +1,9 @@ /* eslint-disable no-param-reassign */ -import type { User } from '..' -import type { CollectionConfig } from '../../collections/config/types' -import type { Field, TabAsField } from '../../fields/config/types' +import type { User } from '.' +import type { CollectionConfig } from '../collections/config/types' +import type { Field, TabAsField } from '../fields/config/types' -import { fieldAffectsData, tabHasName } from '../../fields/config/types' +import { fieldAffectsData, tabHasName } from '../fields/config/types' type TraverseFieldsArgs = { data: Record diff --git a/packages/payload/src/auth/graphql/resolvers/access.ts b/packages/payload/src/auth/graphql/resolvers/access.ts index f58e3c2fb3..ced76945d0 100644 --- a/packages/payload/src/auth/graphql/resolvers/access.ts +++ b/packages/payload/src/auth/graphql/resolvers/access.ts @@ -2,7 +2,7 @@ import type { PayloadT } from '../../..' import formatName from '../../../graphql/utilities/formatName' import isolateTransactionID from '../../../utilities/isolateTransactionID' -import access from '../../operations/access' +import { accessOperation } from '../../operations/access' const formatConfigNames = (results, configs) => { const formattedResults = { ...results } @@ -22,7 +22,7 @@ function accessResolver(payload: PayloadT) { req: isolateTransactionID(context.req), } - const accessResults = await access(options) + const accessResults = await accessOperation(options) return { ...accessResults, diff --git a/packages/payload/src/auth/graphql/resolvers/init.ts b/packages/payload/src/auth/graphql/resolvers/init.ts index 4a42e10b72..a98e896a30 100644 --- a/packages/payload/src/auth/graphql/resolvers/init.ts +++ b/packages/payload/src/auth/graphql/resolvers/init.ts @@ -1,5 +1,5 @@ import isolateTransactionID from '../../../utilities/isolateTransactionID' -import init from '../../operations/init' +import { init } from '../../operations/init' function initResolver(collection: string) { async function resolver(_, args, context) { diff --git a/packages/payload/src/auth/graphql/resolvers/login.ts b/packages/payload/src/auth/graphql/resolvers/login.ts index cb881df5e0..165886fb07 100644 --- a/packages/payload/src/auth/graphql/resolvers/login.ts +++ b/packages/payload/src/auth/graphql/resolvers/login.ts @@ -1,7 +1,7 @@ import type { Collection } from '../../../collections/config/types' import isolateTransactionID from '../../../utilities/isolateTransactionID' -import login from '../../operations/login' +import { loginOperation } from '../../operations/login' function loginResolver(collection: Collection) { async function resolver(_, args, context) { @@ -16,7 +16,7 @@ function loginResolver(collection: Collection) { res: context.res, } - const result = login(options) + const result = loginOperation(options) return result } diff --git a/packages/payload/src/auth/graphql/resolvers/logout.ts b/packages/payload/src/auth/graphql/resolvers/logout.ts index 40b1e18b05..103c7eb65e 100644 --- a/packages/payload/src/auth/graphql/resolvers/logout.ts +++ b/packages/payload/src/auth/graphql/resolvers/logout.ts @@ -1,7 +1,7 @@ import type { Collection } from '../../../collections/config/types' import isolateTransactionID from '../../../utilities/isolateTransactionID' -import logout from '../../operations/logout' +import { logoutOperation } from '../../operations/logout' function logoutResolver(collection: Collection): any { async function resolver(_, args, context) { @@ -11,7 +11,7 @@ function logoutResolver(collection: Collection): any { res: context.res, } - const result = await logout(options) + const result = await logoutOperation(options) return result } diff --git a/packages/payload/src/auth/graphql/resolvers/me.ts b/packages/payload/src/auth/graphql/resolvers/me.ts index 84ce247243..4be39561f3 100644 --- a/packages/payload/src/auth/graphql/resolvers/me.ts +++ b/packages/payload/src/auth/graphql/resolvers/me.ts @@ -1,7 +1,7 @@ import type { Collection } from '../../../collections/config/types' import isolateTransactionID from '../../../utilities/isolateTransactionID' -import me from '../../operations/me' +import { meOperation } from '../../operations/me' function meResolver(collection: Collection): any { async function resolver(_, args, context) { @@ -10,7 +10,7 @@ function meResolver(collection: Collection): any { depth: 0, req: isolateTransactionID(context.req), } - return me(options) + return meOperation(options) } return resolver diff --git a/packages/payload/src/auth/graphql/resolvers/refresh.ts b/packages/payload/src/auth/graphql/resolvers/refresh.ts index dddfc7592e..f809a1b30b 100644 --- a/packages/payload/src/auth/graphql/resolvers/refresh.ts +++ b/packages/payload/src/auth/graphql/resolvers/refresh.ts @@ -2,7 +2,7 @@ import type { Collection } from '../../../collections/config/types' import isolateTransactionID from '../../../utilities/isolateTransactionID' import { extractJWT } from '../../getExtractJWT' -import refresh from '../../operations/refresh' +import { refreshOperation } from '../../operations/refresh' function refreshResolver(collection: Collection) { async function resolver(_, args, context) { @@ -22,7 +22,7 @@ function refreshResolver(collection: Collection) { token, } - const result = await refresh(options) + const result = await refreshOperation(options) return result } diff --git a/packages/payload/src/auth/graphql/resolvers/resetPassword.ts b/packages/payload/src/auth/graphql/resolvers/resetPassword.ts index c8095ceaeb..43a9ee0e9a 100644 --- a/packages/payload/src/auth/graphql/resolvers/resetPassword.ts +++ b/packages/payload/src/auth/graphql/resolvers/resetPassword.ts @@ -2,7 +2,7 @@ import type { Collection } from '../../../collections/config/types' import isolateTransactionID from '../../../utilities/isolateTransactionID' -import resetPassword from '../../operations/resetPassword' +import { resetPasswordOperation } from '../../operations/resetPassword' function resetPasswordResolver(collection: Collection) { async function resolver(_, args, context) { @@ -18,7 +18,7 @@ function resetPasswordResolver(collection: Collection) { res: context.res, } - const result = await resetPassword(options) + const result = await resetPasswordOperation(options) return result } diff --git a/packages/payload/src/auth/graphql/resolvers/unlock.ts b/packages/payload/src/auth/graphql/resolvers/unlock.ts index 4f4274142d..a2b961c5a9 100644 --- a/packages/payload/src/auth/graphql/resolvers/unlock.ts +++ b/packages/payload/src/auth/graphql/resolvers/unlock.ts @@ -1,7 +1,7 @@ import type { Collection } from '../../../collections/config/types' import isolateTransactionID from '../../../utilities/isolateTransactionID' -import unlock from '../../operations/unlock' +import { unlockOperation } from '../../operations/unlock' function unlockResolver(collection: Collection) { async function resolver(_, args, context) { @@ -11,7 +11,7 @@ function unlockResolver(collection: Collection) { req: isolateTransactionID(context.req), } - const result = await unlock(options) + const result = await unlockOperation(options) return result } diff --git a/packages/payload/src/auth/graphql/resolvers/verifyEmail.ts b/packages/payload/src/auth/graphql/resolvers/verifyEmail.ts index 063283b2a3..7eafbbdc2a 100644 --- a/packages/payload/src/auth/graphql/resolvers/verifyEmail.ts +++ b/packages/payload/src/auth/graphql/resolvers/verifyEmail.ts @@ -2,7 +2,7 @@ import type { Collection } from '../../../collections/config/types' import isolateTransactionID from '../../../utilities/isolateTransactionID' -import verifyEmail from '../../operations/verifyEmail' +import { verifyEmailOperation } from '../../operations/verifyEmail' function verifyEmailResolver(collection: Collection) { async function resolver(_, args, context) { @@ -17,7 +17,7 @@ function verifyEmailResolver(collection: Collection) { token: args.token, } - const success = await verifyEmail(options) + const success = await verifyEmailOperation(options) return success } diff --git a/packages/payload/src/auth/handlers/access.ts b/packages/payload/src/auth/handlers/access.ts deleted file mode 100644 index 1b2401c8da..0000000000 --- a/packages/payload/src/auth/handlers/access.ts +++ /dev/null @@ -1,24 +0,0 @@ -import type { NextFunction, Response } from 'express' - -import httpStatus from 'http-status' - -import type { PayloadRequest } from '../../types' -import type { Permissions } from '../types' - -import access from '../operations/access' - -export default async function accessRequestHandler( - req: PayloadRequest, - res: Response, - next: NextFunction, -): Promise | void> { - try { - const accessResults = await access({ - req, - }) - - return res.status(httpStatus.OK).json(accessResults) - } catch (error) { - return next(error) - } -} diff --git a/packages/payload/src/auth/handlers/forgotPassword.ts b/packages/payload/src/auth/handlers/forgotPassword.ts deleted file mode 100644 index 4afb21c27f..0000000000 --- a/packages/payload/src/auth/handlers/forgotPassword.ts +++ /dev/null @@ -1,30 +0,0 @@ -import type { NextFunction, Response } from 'express' - -import httpStatus from 'http-status' - -import type { PayloadRequest } from '../../types' - -import forgotPassword from '../operations/forgotPassword' - -export default async function forgotPasswordHandler( - req: PayloadRequest, - res: Response, - next: NextFunction, -): Promise { - try { - await forgotPassword({ - collection: req.collection, - // TODO(JARROD): remove reliance on express body parsing - data: { email: req.body.email }, - disableEmail: req.body.disableEmail, - expiration: req.body.expiration, - req, - }) - - return res.status(httpStatus.OK).json({ - message: 'Success', - }) - } catch (error) { - return next(error) - } -} diff --git a/packages/payload/src/auth/handlers/init.ts b/packages/payload/src/auth/handlers/init.ts deleted file mode 100644 index ab8f048cb9..0000000000 --- a/packages/payload/src/auth/handlers/init.ts +++ /dev/null @@ -1,17 +0,0 @@ -import type { SanitizedConfig } from '../../exports/config' - -import { getPayload } from '../..' -import init from '../operations/init' - -export const initHandler = ({ config }: { config: Promise }) => - async function (request: Request, { params }: { params: { collection: string } }) { - const payload = await getPayload({ config }) - request.payload = payload - - const initialized = await init({ - collection: params.collection, - req: request, - }) - - return Response.json({ initialized }) - } diff --git a/packages/payload/src/auth/handlers/login.ts b/packages/payload/src/auth/handlers/login.ts deleted file mode 100644 index 81321e6c00..0000000000 --- a/packages/payload/src/auth/handlers/login.ts +++ /dev/null @@ -1,39 +0,0 @@ -import type { NextFunction, Response } from 'express' - -import httpStatus from 'http-status' -import { URL } from 'url' - -import type { PayloadRequest } from '../../types' -import type { Result } from '../operations/login' - -import { isNumber } from '../../utilities/isNumber' -import login from '../operations/login' - -export default async function loginHandler( - req: PayloadRequest, - res: Response, - next: NextFunction, -): Promise | void> { - try { - const searchParams = new URL(req.url).searchParams - const depth = searchParams.get('depth') - - const result = await login({ - collection: req.collection, - // TODO(JARROD): remove reliance on express body parsing - data: req.body, - depth: isNumber(depth) ? depth : undefined, - req, - res, - }) - - res.status(httpStatus.OK).json({ - exp: result.exp, - message: 'Auth Passed', - token: result.token, - user: result.user, - }) - } catch (error) { - next(error) - } -} diff --git a/packages/payload/src/auth/handlers/logout.ts b/packages/payload/src/auth/handlers/logout.ts deleted file mode 100644 index 217d19c7ea..0000000000 --- a/packages/payload/src/auth/handlers/logout.ts +++ /dev/null @@ -1,25 +0,0 @@ -import type { NextFunction, Response } from 'express' - -import httpStatus from 'http-status' - -import type { PayloadRequest } from '../../types' - -import logout from '../operations/logout' - -export default async function logoutHandler( - req: PayloadRequest, - res: Response, - next: NextFunction, -): Promise | void> { - try { - const message = await logout({ - collection: req.collection, - req, - res, - }) - - return res.status(httpStatus.OK).json({ message }) - } catch (error) { - return next(error) - } -} diff --git a/packages/payload/src/auth/handlers/me.ts b/packages/payload/src/auth/handlers/me.ts deleted file mode 100644 index bc43cecbc7..0000000000 --- a/packages/payload/src/auth/handlers/me.ts +++ /dev/null @@ -1,21 +0,0 @@ -import type { NextFunction, Response } from 'express' - -import type { PayloadRequest } from '../../types' - -import me from '../operations/me' - -export default async function meHandler( - req: PayloadRequest, - res: Response, - next: NextFunction, -): Promise { - try { - const response = await me({ - collection: req.collection, - req, - }) - return res.status(200).json(response) - } catch (err) { - return next(err) - } -} diff --git a/packages/payload/src/auth/handlers/refresh.ts b/packages/payload/src/auth/handlers/refresh.ts deleted file mode 100644 index 42921edfe9..0000000000 --- a/packages/payload/src/auth/handlers/refresh.ts +++ /dev/null @@ -1,36 +0,0 @@ -import type { NextFunction, Response } from 'express' - -import type { PayloadRequest } from '../../types' - -import { extractJWT } from '../getExtractJWT' -import refresh from '../operations/refresh' - -export default async function refreshHandler( - req: PayloadRequest, - res: Response, - next: NextFunction, -): Promise { - try { - let token - - token = extractJWT(req) - - if (req.body) { - token = req.body.data - } - - const result = await refresh({ - collection: req.collection, - req, - res, - token, - }) - - return res.status(200).json({ - message: 'Token refresh successful', - ...result, - }) - } catch (error) { - return next(error) - } -} diff --git a/packages/payload/src/auth/handlers/registerFirstUser.ts b/packages/payload/src/auth/handlers/registerFirstUser.ts deleted file mode 100644 index c1705a3be6..0000000000 --- a/packages/payload/src/auth/handlers/registerFirstUser.ts +++ /dev/null @@ -1,25 +0,0 @@ -import type { NextFunction, Response } from 'express' - -import type { PayloadRequest } from '../../types' - -import registerFirstUser from '../operations/registerFirstUser' - -export default async function registerFirstUserHandler( - req: PayloadRequest, - res: Response, - next: NextFunction, -): Promise { - try { - const firstUser = await registerFirstUser({ - collection: req.collection, - // TODO(JARROD): remove reliance on express body parsing - data: req.body, - req, - res, - }) - - return res.status(201).json(firstUser) - } catch (error) { - return next(error) - } -} diff --git a/packages/payload/src/auth/handlers/resetPassword.ts b/packages/payload/src/auth/handlers/resetPassword.ts deleted file mode 100644 index c1700037e7..0000000000 --- a/packages/payload/src/auth/handlers/resetPassword.ts +++ /dev/null @@ -1,33 +0,0 @@ -import type { NextFunction, Response } from 'express' - -import httpStatus from 'http-status' - -import type { PayloadRequest } from '../../types' - -import resetPassword from '../operations/resetPassword' - -async function resetPasswordHandler( - req: PayloadRequest, - res: Response, - next: NextFunction, -): Promise { - try { - const result = await resetPassword({ - collection: req.collection, - // TODO(JARROD): remove reliance on express body parsing - data: req.body, - req, - res, - }) - - return res.status(httpStatus.OK).json({ - message: 'Password reset successfully.', - token: result.token, - user: result.user, - }) - } catch (error) { - return next(error) - } -} - -export default resetPasswordHandler diff --git a/packages/payload/src/auth/handlers/unlock.ts b/packages/payload/src/auth/handlers/unlock.ts deleted file mode 100644 index d7be3884f5..0000000000 --- a/packages/payload/src/auth/handlers/unlock.ts +++ /dev/null @@ -1,28 +0,0 @@ -import type { NextFunction, Response } from 'express' - -import httpStatus from 'http-status' - -import type { PayloadRequest } from '../../types' - -import unlock from '../operations/unlock' - -export default async function unlockHandler( - req: PayloadRequest, - res: Response, - next: NextFunction, -): Promise { - try { - await unlock({ - collection: req.collection, - // TODO(JARROD): remove reliance on express body parsing - data: { email: req.body.email }, - req, - }) - - return res.status(httpStatus.OK).json({ - message: 'Success', - }) - } catch (error) { - return next(error) - } -} diff --git a/packages/payload/src/auth/handlers/verifyEmail.ts b/packages/payload/src/auth/handlers/verifyEmail.ts deleted file mode 100644 index 2ba374a239..0000000000 --- a/packages/payload/src/auth/handlers/verifyEmail.ts +++ /dev/null @@ -1,30 +0,0 @@ -// TODO(JARROD): remove reliance on express -import type { NextFunction, Response } from 'express' - -import httpStatus from 'http-status' - -import type { PayloadRequest } from '../../types' - -import verifyEmail from '../operations/verifyEmail' - -async function verifyEmailHandler( - req: PayloadRequest, - res: Response, - next: NextFunction, -): Promise { - try { - await verifyEmail({ - collection: req.collection, - req, - token: req.params.token, - }) - - return res.status(httpStatus.OK).json({ - message: 'Email verified successfully.', - }) - } catch (error) { - return next(error) - } -} - -export default verifyEmailHandler diff --git a/packages/payload/src/auth/init.ts b/packages/payload/src/auth/init.ts deleted file mode 100644 index ee4cdbb4c7..0000000000 --- a/packages/payload/src/auth/init.ts +++ /dev/null @@ -1,13 +0,0 @@ -import passport from 'passport' -import AnonymousStrategy from 'passport-anonymous' - -import type { Payload } from '../payload' - -import jwtStrategy from './strategies/jwt' - -function initAuth(ctx: Payload): void { - passport.use(new AnonymousStrategy.Strategy()) - passport.use('jwt', jwtStrategy(ctx)) -} - -export default initAuth diff --git a/packages/payload/src/auth/operations/access.ts b/packages/payload/src/auth/operations/access.ts index 1b216ec9d4..19b87f90af 100644 --- a/packages/payload/src/auth/operations/access.ts +++ b/packages/payload/src/auth/operations/access.ts @@ -11,7 +11,7 @@ type Arguments = { req: PayloadRequest } -async function accessOperation(args: Arguments): Promise { +export const accessOperation = async (args: Arguments): Promise => { const { req, req: { payload, user }, @@ -40,5 +40,3 @@ async function accessOperation(args: Arguments): Promise { throw e } } - -export default accessOperation diff --git a/packages/payload/src/auth/operations/forgotPassword.ts b/packages/payload/src/auth/operations/forgotPassword.ts index b02caf32cd..6d95a6e4db 100644 --- a/packages/payload/src/auth/operations/forgotPassword.ts +++ b/packages/payload/src/auth/operations/forgotPassword.ts @@ -23,7 +23,7 @@ export type Arguments = { export type Result = string -async function forgotPassword(incomingArgs: Arguments): Promise { +export const forgotPasswordOperation = async (incomingArgs: Arguments): Promise => { if (!Object.prototype.hasOwnProperty.call(incomingArgs.data, 'email')) { throw new APIError('Missing email.', 400) } @@ -66,8 +66,7 @@ async function forgotPassword(incomingArgs: Arguments): Promise { // Forget password // ///////////////////////////////////// - let token: Buffer | string = crypto.randomBytes(20) - token = token.toString('hex') + let token: string = crypto.randomBytes(20).toString('hex') type UserDoc = { id: number | string @@ -165,5 +164,3 @@ async function forgotPassword(incomingArgs: Arguments): Promise { throw error } } - -export default forgotPassword diff --git a/packages/payload/src/auth/operations/init.ts b/packages/payload/src/auth/operations/init.ts index 4e7f664375..24989db747 100644 --- a/packages/payload/src/auth/operations/init.ts +++ b/packages/payload/src/auth/operations/init.ts @@ -1,6 +1,9 @@ import type { PayloadRequest } from '../../types' -async function init(args: { collection: string; req: PayloadRequest }): Promise { +export const initOperation = async (args: { + collection: string + req: PayloadRequest +}): Promise => { const { collection: slug, req } = args const doc = await req.payload.db.findOne({ @@ -10,5 +13,3 @@ async function init(args: { collection: string; req: PayloadRequest }): Promise< return !!doc } - -export default init diff --git a/packages/payload/src/auth/operations/local/forgotPassword.ts b/packages/payload/src/auth/operations/local/forgotPassword.ts index a4ed4e851e..f5d9d2db96 100644 --- a/packages/payload/src/auth/operations/local/forgotPassword.ts +++ b/packages/payload/src/auth/operations/local/forgotPassword.ts @@ -6,7 +6,7 @@ import { getDataLoader } from '../../../collections/dataloader' import { APIError } from '../../../errors' import { i18nInit } from '../../../translations/init' import { setRequestContext } from '../../../utilities/setRequestContext' -import forgotPassword from '../forgotPassword' +import { forgotPasswordOperation } from '../forgotPassword' export type Options = { collection: T @@ -50,7 +50,7 @@ async function localForgotPassword = { collection: TSlug @@ -23,7 +21,6 @@ export type Options = { locale?: string overrideAccess?: boolean req?: PayloadRequest - res?: Response showHiddenFields?: boolean } @@ -40,7 +37,6 @@ async function localLogin( locale, overrideAccess = true, req = {} as PayloadRequest, - res, showHiddenFields, } = options setRequestContext(req, context) @@ -68,14 +64,13 @@ async function localLogin( depth, overrideAccess, req, - res, showHiddenFields, } if (locale) args.req.locale = locale if (fallbackLocale) args.req.fallbackLocale = fallbackLocale - return login(args) + return loginOperation(args) } export default localLogin diff --git a/packages/payload/src/auth/operations/local/resetPassword.ts b/packages/payload/src/auth/operations/local/resetPassword.ts index 5e285fdd2a..5fee6b162b 100644 --- a/packages/payload/src/auth/operations/local/resetPassword.ts +++ b/packages/payload/src/auth/operations/local/resetPassword.ts @@ -7,7 +7,7 @@ import { getDataLoader } from '../../../collections/dataloader' import { APIError } from '../../../errors' import { i18nInit } from '../../../translations/init' import { setRequestContext } from '../../../utilities/setRequestContext' -import resetPassword from '../resetPassword' +import { resetPasswordOperation } from '../resetPassword' export type Options = { collection: T @@ -51,7 +51,7 @@ async function localResetPassword if (!req.t) req.t = req.i18n.t if (!req.payloadDataLoader) req.payloadDataLoader = getDataLoader(req) - return resetPassword({ + return resetPasswordOperation({ collection, data, overrideAccess, diff --git a/packages/payload/src/auth/operations/local/unlock.ts b/packages/payload/src/auth/operations/local/unlock.ts index ee79a34664..d8ba1be51c 100644 --- a/packages/payload/src/auth/operations/local/unlock.ts +++ b/packages/payload/src/auth/operations/local/unlock.ts @@ -6,7 +6,7 @@ import { getDataLoader } from '../../../collections/dataloader' import { APIError } from '../../../errors' import { i18nInit } from '../../../translations/init' import { setRequestContext } from '../../../utilities/setRequestContext' -import unlock from '../unlock' +import { unlockOperation } from '../unlock' export type Options = { collection: T @@ -46,7 +46,7 @@ async function localUnlock( if (!req.t) req.t = req.i18n.t if (!req.payloadDataLoader) req.payloadDataLoader = getDataLoader(req) - return unlock({ + return unlockOperation({ collection, data, overrideAccess, diff --git a/packages/payload/src/auth/operations/local/verifyEmail.ts b/packages/payload/src/auth/operations/local/verifyEmail.ts index 23ff3f5013..b63e44301f 100644 --- a/packages/payload/src/auth/operations/local/verifyEmail.ts +++ b/packages/payload/src/auth/operations/local/verifyEmail.ts @@ -5,7 +5,7 @@ import type { PayloadRequest } from '../../../types' import { APIError } from '../../../errors' import { i18nInit } from '../../../translations/init' import { setRequestContext } from '../../../utilities/setRequestContext' -import verifyEmail from '../verifyEmail' +import { verifyEmailOperation } from '../verifyEmail' export type Options = { collection: T @@ -33,7 +33,7 @@ async function localVerifyEmail( req.payloadAPI = req.payloadAPI || 'local' req.i18n = i18nInit(payload.config.i18n) - return verifyEmail({ + return verifyEmailOperation({ collection, req, token, diff --git a/packages/payload/src/auth/operations/login.ts b/packages/payload/src/auth/operations/login.ts index 7fdeee5786..f51b85501b 100644 --- a/packages/payload/src/auth/operations/login.ts +++ b/packages/payload/src/auth/operations/login.ts @@ -9,16 +9,14 @@ import { buildAfterOperation } from '../../collections/operations/utils' import { AuthenticationError, LockedAuth } from '../../errors' import { afterRead } from '../../fields/hooks/afterRead' import { commitTransaction } from '../../utilities/commitTransaction' -import { generateCookie } from '../../utilities/cookies' -import getCookieExpiration from '../../utilities/getCookieExpiration' import { initTransaction } from '../../utilities/initTransaction' import { killTransaction } from '../../utilities/killTransaction' import sanitizeInternalFields from '../../utilities/sanitizeInternalFields' +import { getFieldsToSign } from '../getFieldsToSign' import isLocked from '../isLocked' import { authenticateLocalStrategy } from '../strategies/local/authenticate' import { incrementLoginAttempts } from '../strategies/local/incrementLoginAttempts' -import { getFieldsToSign } from './getFieldsToSign' -import unlock from './unlock' +import { unlockOperation } from './unlock' export type Result = { exp?: number @@ -35,15 +33,12 @@ export type Arguments = { depth?: number overrideAccess?: boolean req: PayloadRequest - responseOptions?: ResponseInit & { - headers: Headers - } showHiddenFields?: boolean } -async function login( +export const loginOperation = async ( incomingArgs: Arguments, -): Promise { +): Promise => { let args = incomingArgs // ///////////////////////////////////// @@ -70,7 +65,7 @@ async function login( req, req: { payload, - payload: { config, secret }, + payload: { secret }, }, showHiddenFields, } = args @@ -120,7 +115,7 @@ async function login( } if (maxLoginAttemptsEnabled) { - await unlock({ + await unlockOperation({ collection: { config: collectionConfig, }, @@ -152,28 +147,6 @@ async function login( expiresIn: collectionConfig.auth.tokenExpiration, }) - if (args.responseOptions) { - const sameSite = - typeof collectionConfig.auth.cookies.sameSite === 'string' - ? collectionConfig.auth.cookies.sameSite - : collectionConfig.auth.cookies.sameSite - ? 'Strict' - : undefined - - const cookie = generateCookie({ - name: `${config.cookiePrefix}-token`, - domain: collectionConfig.auth.cookies.domain ?? undefined, - expires: getCookieExpiration(collectionConfig.auth.tokenExpiration), - httpOnly: true, - path: '/', - sameSite, - secure: collectionConfig.auth.cookies.secure, - value: token, - }) - - args.responseOptions.headers.set('Set-Cookie', cookie) - } - req.user = user // ///////////////////////////////////// @@ -273,5 +246,3 @@ async function login( throw error } } - -export default login diff --git a/packages/payload/src/auth/operations/logout.ts b/packages/payload/src/auth/operations/logout.ts index 00daae15b8..8aa0fe2387 100644 --- a/packages/payload/src/auth/operations/logout.ts +++ b/packages/payload/src/auth/operations/logout.ts @@ -1,6 +1,3 @@ -// TODO(JARROD): remove express Response -import type { Response } from 'express' - import httpStatus from 'http-status' import type { Collection } from '../../collections/config/types' @@ -11,37 +8,20 @@ import { APIError } from '../../errors' export type Arguments = { collection: Collection req: PayloadRequest - res: Response } -async function logout(incomingArgs: Arguments): Promise { +export const logoutOperation = async (incomingArgs: Arguments): Promise => { let args = incomingArgs const { - collection, collection: { config: collectionConfig }, + req: { collection, user }, req, - req: { - payload: { config }, - user, - }, - res, } = incomingArgs if (!user) throw new APIError('No User', httpStatus.BAD_REQUEST) if (user.collection !== collectionConfig.slug) throw new APIError('Incorrect collection', httpStatus.FORBIDDEN) - const cookieOptions = { - domain: undefined, - httpOnly: true, - path: '/', - sameSite: collectionConfig.auth.cookies.sameSite, - secure: collectionConfig.auth.cookies.secure, - } - - if (collectionConfig.auth.cookies.domain) - cookieOptions.domain = collectionConfig.auth.cookies.domain - await collection.config.hooks.afterLogout.reduce(async (priorHook, hook) => { await priorHook @@ -50,13 +30,8 @@ async function logout(incomingArgs: Arguments): Promise { collection: args.collection?.config, context: req.context, req, - res, })) || args }, Promise.resolve()) - res.clearCookie(`${config.cookiePrefix}-token`, cookieOptions) - - return req.t('authentication:loggedOutSuccessfully') + return true } - -export default logout diff --git a/packages/payload/src/auth/operations/me.ts b/packages/payload/src/auth/operations/me.ts index 6374524017..c77973ab97 100644 --- a/packages/payload/src/auth/operations/me.ts +++ b/packages/payload/src/auth/operations/me.ts @@ -5,8 +5,6 @@ import type { Collection } from '../../collections/config/types' import type { PayloadRequest } from '../../types' import type { User } from '../types' -import { extractJWT } from '../getExtractJWT' - export type Result = { collection?: string exp?: number @@ -16,11 +14,16 @@ export type Result = { export type Arguments = { collection: Collection + currentToken?: string req: PayloadRequest } -async function me({ collection, req }: Arguments): Promise { - let response: Result = { +export const meOperation = async ({ + collection, + currentToken, + req, +}: Arguments): Promise => { + let result: Result = { user: null, } @@ -45,17 +48,15 @@ async function me({ collection, req }: Arguments): Promise { delete user.collection - response = { + result = { collection: req.user.collection, user, } - const token = extractJWT(req) - - if (token) { - const decoded = jwt.decode(token) as jwt.JwtPayload - if (decoded) response.exp = decoded.exp - if (!collection.config.auth.removeTokenFromResponses) response.token = token + if (currentToken) { + const decoded = jwt.decode(currentToken) as jwt.JwtPayload + if (decoded) result.exp = decoded.exp + if (!collection.config.auth.removeTokenFromResponses) result.token = currentToken } } @@ -66,16 +67,14 @@ async function me({ collection, req }: Arguments): Promise { await collection.config.hooks.afterMe.reduce(async (priorHook, hook) => { await priorHook - response = + result = (await hook({ collection: collection?.config, context: req.context, req, - response, - })) || response + response: result, + })) || result }, Promise.resolve()) - return response + return result } - -export default me diff --git a/packages/payload/src/auth/operations/refresh.ts b/packages/payload/src/auth/operations/refresh.ts index b656fdc77f..f28de0a5e4 100644 --- a/packages/payload/src/auth/operations/refresh.ts +++ b/packages/payload/src/auth/operations/refresh.ts @@ -1,6 +1,3 @@ -// TODO(JARROD): remove express Response -import type { Response } from 'express' - import jwt from 'jsonwebtoken' import url from 'url' @@ -10,8 +7,7 @@ import type { Document } from '../../types' import { buildAfterOperation } from '../../collections/operations/utils' import { Forbidden } from '../../errors' -import getCookieExpiration from '../../utilities/getCookieExpiration' -import { getFieldsToSign } from './getFieldsToSign' +import { getFieldsToSign } from '../getFieldsToSign' export type Result = { exp: number @@ -22,11 +18,10 @@ export type Result = { export type Arguments = { collection: Collection req: PayloadRequest - res?: Response token: string } -async function refresh(incomingArgs: Arguments): Promise { +export const refreshOperation = async (incomingArgs: Arguments): Promise => { let args = incomingArgs // ///////////////////////////////////// @@ -83,22 +78,6 @@ async function refresh(incomingArgs: Arguments): Promise { const exp = (jwt.decode(refreshedToken) as Record).exp as number - if (args.res) { - const cookieOptions = { - domain: undefined, - expires: getCookieExpiration(collectionConfig.auth.tokenExpiration), - httpOnly: true, - path: '/', - sameSite: collectionConfig.auth.cookies.sameSite, - secure: collectionConfig.auth.cookies.secure, - } - - if (collectionConfig.auth.cookies.domain) - cookieOptions.domain = collectionConfig.auth.cookies.domain - - args.res.cookie(`${config.cookiePrefix}-token`, refreshedToken, cookieOptions) - } - let result: Result = { exp, refreshedToken, @@ -118,7 +97,6 @@ async function refresh(incomingArgs: Arguments): Promise { context: args.req.context, exp, req: args.req, - res: args.res, token: refreshedToken, })) || result }, Promise.resolve()) @@ -144,5 +122,3 @@ async function refresh(incomingArgs: Arguments): Promise { return result } - -export default refresh diff --git a/packages/payload/src/auth/operations/registerFirstUser.ts b/packages/payload/src/auth/operations/registerFirstUser.ts index 4527bd3a2d..c8c8a7ce2e 100644 --- a/packages/payload/src/auth/operations/registerFirstUser.ts +++ b/packages/payload/src/auth/operations/registerFirstUser.ts @@ -1,5 +1,3 @@ -// TODO(JARROD): remove express Response -import type { Response } from 'express' import type { MarkOptional } from 'ts-essentials' import type { GeneratedTypes } from '../../' @@ -18,18 +16,17 @@ export type Arguments password: string } req: PayloadRequest - // TODO(JARROD): remove express Response - res: Response } export type Result = { - message: string - user: T + exp?: number + token?: string + user?: T } -async function registerFirstUser( +export const registerFirstUserOperation = async ( args: Arguments, -): Promise> { +): Promise> => { const { collection: { config, @@ -80,27 +77,21 @@ async function registerFirstUser { +export const resetPasswordOperation = async (args: Arguments): Promise => { if ( !Object.prototype.hasOwnProperty.call(args.data, 'token') || !Object.prototype.hasOwnProperty.call(args.data, 'password') @@ -47,7 +41,7 @@ async function resetPassword(args: Arguments): Promise { depth, overrideAccess, req: { - payload: { config, secret }, + payload: { secret }, payload, }, req, @@ -102,22 +96,6 @@ async function resetPassword(args: Arguments): Promise { expiresIn: collectionConfig.auth.tokenExpiration, }) - if (args.res) { - const cookieOptions = { - domain: undefined, - expires: getCookieExpiration(collectionConfig.auth.tokenExpiration), - httpOnly: true, - path: '/', - sameSite: collectionConfig.auth.cookies.sameSite, - secure: collectionConfig.auth.cookies.secure, - } - - if (collectionConfig.auth.cookies.domain) - cookieOptions.domain = collectionConfig.auth.cookies.domain - - args.res.cookie(`${config.cookiePrefix}-token`, token, cookieOptions) - } - const fullUser = await payload.findByID({ id: user.id, collection: collectionConfig.slug, @@ -137,4 +115,4 @@ async function resetPassword(args: Arguments): Promise { } } -export default resetPassword +export default resetPasswordOperation diff --git a/packages/payload/src/auth/operations/unlock.ts b/packages/payload/src/auth/operations/unlock.ts index d529084ac4..dd674a769e 100644 --- a/packages/payload/src/auth/operations/unlock.ts +++ b/packages/payload/src/auth/operations/unlock.ts @@ -17,7 +17,7 @@ export type Args = { req: PayloadRequest } -async function unlock(args: Args): Promise { +export const unlockOperation = async (args: Args): Promise => { if (!Object.prototype.hasOwnProperty.call(args.data, 'email')) { throw new APIError('Missing email.') } @@ -25,7 +25,7 @@ async function unlock(args: Args): Promise { const { collection: { config: collectionConfig }, overrideAccess, - req: { locale, payload }, + req: { locale }, req, } = args @@ -82,4 +82,4 @@ async function unlock(args: Args): Promise { } } -export default unlock +export default unlockOperation diff --git a/packages/payload/src/auth/operations/verifyEmail.ts b/packages/payload/src/auth/operations/verifyEmail.ts index 73b7de4ec4..9bcb9a8153 100644 --- a/packages/payload/src/auth/operations/verifyEmail.ts +++ b/packages/payload/src/auth/operations/verifyEmail.ts @@ -14,7 +14,7 @@ export type Args = { token: string } -async function verifyEmail(args: Args): Promise { +export const verifyEmailOperation = async (args: Args): Promise => { const { collection, req, token } = args if (!Object.prototype.hasOwnProperty.call(args, 'token')) { throw new APIError('Missing required data.', httpStatus.BAD_REQUEST) @@ -55,4 +55,4 @@ async function verifyEmail(args: Args): Promise { } } -export default verifyEmail +export default verifyEmailOperation diff --git a/packages/payload/src/collections/bindCollection.ts b/packages/payload/src/collections/bindCollection.ts deleted file mode 100644 index e3f648fd01..0000000000 --- a/packages/payload/src/collections/bindCollection.ts +++ /dev/null @@ -1,12 +0,0 @@ -import type { NextFunction, Request, Response } from 'express' - -import type { Collection } from './config/types' - -const bindCollectionMiddleware = - (collection: Collection) => - (req: Request & { collection: Collection }, res: Response, next: NextFunction): void => { - req.collection = collection - next() - } - -export default bindCollectionMiddleware diff --git a/packages/payload/src/collections/buildEndpoints.ts b/packages/payload/src/collections/buildEndpoints.ts deleted file mode 100644 index 11297b94cd..0000000000 --- a/packages/payload/src/collections/buildEndpoints.ts +++ /dev/null @@ -1,177 +0,0 @@ -import type { Endpoint } from '../config/types' -import type { SanitizedCollectionConfig } from './config/types' - -import forgotPasswordHandler from '../auth/handlers/forgotPassword' -import initHandler from '../auth/handlers/init' -import loginHandler from '../auth/handlers/login' -import logoutHandler from '../auth/handlers/logout' -import meHandler from '../auth/handlers/me' -import refreshHandler from '../auth/handlers/refresh' -import registerFirstUserHandler from '../auth/handlers/registerFirstUser' -import resetPassword from '../auth/handlers/resetPassword' -import unlock from '../auth/handlers/unlock' -import verifyEmail from '../auth/handlers/verifyEmail' -import create from './requestHandlers/create' -import deleteHandler from './requestHandlers/delete' -import deleteByID from './requestHandlers/deleteByID' -import docAccessRequestHandler from './requestHandlers/docAccess' -import find from './requestHandlers/find' -import findByID from './requestHandlers/findByID' -import findVersionByID from './requestHandlers/findVersionByID' -import findVersions from './requestHandlers/findVersions' -import restoreVersion from './requestHandlers/restoreVersion' -import update from './requestHandlers/update' -import updateByID, { deprecatedUpdate } from './requestHandlers/updateByID' - -const buildEndpoints = (collection: SanitizedCollectionConfig): Endpoint[] => { - if (!collection.endpoints) return [] - const endpoints = [...collection.endpoints] - - if (collection.auth) { - if (!collection.auth.disableLocalStrategy) { - if (collection.auth.verify) { - endpoints.push({ - handler: verifyEmail, - method: 'post', - path: '/verify/:token', - }) - } - - if (collection.auth.maxLoginAttempts > 0) { - endpoints.push({ - handler: unlock, - method: 'post', - path: '/unlock', - }) - } - - endpoints.push( - { - handler: loginHandler, - method: 'post', - path: '/login', - }, - { - handler: registerFirstUserHandler, - method: 'post', - path: '/first-register', - }, - { - handler: forgotPasswordHandler, - method: 'post', - path: '/forgot-password', - }, - { - handler: resetPassword, - method: 'post', - path: '/reset-password', - }, - ) - } - - endpoints.push( - { - handler: initHandler, - method: 'get', - path: '/init', - }, - { - handler: meHandler, - method: 'get', - path: '/me', - }, - { - handler: logoutHandler, - method: 'post', - path: '/logout', - }, - { - handler: refreshHandler, - method: 'post', - path: '/refresh-token', - }, - ) - } - - if (collection.versions) { - endpoints.push( - { - handler: findVersions, - method: 'get', - path: '/versions', - }, - { - handler: findVersionByID, - method: 'get', - path: '/versions/:id', - }, - { - handler: restoreVersion, - method: 'post', - path: '/versions/:id', - }, - ) - } - - endpoints.push( - { - handler: find, - method: 'get', - path: '/', - }, - { - handler: create, - method: 'post', - path: '/', - }, - { - handler: docAccessRequestHandler, - method: 'get', - path: '/access/:id', - }, - { - handler: docAccessRequestHandler, - method: 'post', - path: '/access/:id', - }, - { - handler: docAccessRequestHandler, - method: 'post', - path: '/access', - }, - { - handler: deprecatedUpdate, - method: 'put', - path: '/:id', - }, - { - handler: update, - method: 'patch', - path: '/', - }, - { - handler: updateByID, - method: 'patch', - path: '/:id', - }, - { - handler: findByID, - method: 'get', - path: '/:id', - }, - { - handler: deleteByID, - method: 'delete', - path: '/:id', - }, - { - handler: deleteHandler, - method: 'delete', - path: '/', - }, - ) - - return endpoints -} - -export default buildEndpoints diff --git a/packages/payload/src/collections/config/types.ts b/packages/payload/src/collections/config/types.ts index 44de85db7a..8b4456131b 100644 --- a/packages/payload/src/collections/config/types.ts +++ b/packages/payload/src/collections/config/types.ts @@ -1,5 +1,3 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import type { Response } from 'express' import type { GraphQLInputObjectType, GraphQLNonNull, GraphQLObjectType } from 'graphql' import type { DeepRequired } from 'ts-essentials' @@ -169,7 +167,6 @@ export type AfterLogoutHook = (args: { collection: SanitizedCollectionConfig context: RequestContext req: PayloadRequest - res: Response }) => any export type AfterMeHook = (args: { @@ -186,7 +183,6 @@ export type AfterRefreshHook = (args: { context: RequestContext exp: number req: PayloadRequest - res: Response token: string }) => any diff --git a/packages/payload/src/collections/graphql/init.ts b/packages/payload/src/collections/graphql/init.ts index c98a10b923..7c7c0ba8ba 100644 --- a/packages/payload/src/collections/graphql/init.ts +++ b/packages/payload/src/collections/graphql/init.ts @@ -7,9 +7,9 @@ import { GraphQLString, } from 'graphql' +import type { PayloadT } from '../..' import type { Field } from '../../fields/config/types' import type { ObjectTypeConfig } from '../../graphql/schema/buildObjectType' -import type { Payload } from '../../payload' import type { Collection, SanitizedCollectionConfig } from '../config/types' import forgotPassword from '../../auth/graphql/resolvers/forgotPassword' @@ -42,7 +42,7 @@ import findVersionsResolver from './resolvers/findVersions' import restoreVersionResolver from './resolvers/restoreVersion' import updateResolver from './resolvers/update' -function initCollectionsGraphQL(payload: Payload): void { +function initCollectionsGraphQL(payload: PayloadT): void { Object.keys(payload.collections).forEach((slug) => { const collection: Collection = payload.collections[slug] const { diff --git a/packages/payload/src/collections/graphql/resolvers/create.ts b/packages/payload/src/collections/graphql/resolvers/create.ts index 859de93433..57aa426b08 100644 --- a/packages/payload/src/collections/graphql/resolvers/create.ts +++ b/packages/payload/src/collections/graphql/resolvers/create.ts @@ -7,7 +7,7 @@ import type { PayloadRequest } from '../../../types' import type { Collection } from '../../config/types' import isolateTransactionID from '../../../utilities/isolateTransactionID' -import create from '../../operations/create' +import { createOperation } from '../../operations/create' export type Resolver = ( _: unknown, @@ -21,7 +21,6 @@ export type Resolver = ( }, context: { req: PayloadRequest - res: Response }, ) => Promise @@ -41,7 +40,7 @@ export default function createResolver = ( _: unknown, @@ -34,7 +34,7 @@ export default function getDeleteResolver = ( _: unknown, @@ -35,7 +35,7 @@ export default function findByIDResolver = ( _: unknown, @@ -35,7 +35,7 @@ export default function findVersionByIDResolver(collection: Collection): Resolve req: isolateTransactionID(context.req), } - const result = await findVersionByID(options) + const result = await findVersionByIDOperation(options) return result } diff --git a/packages/payload/src/collections/graphql/resolvers/findVersions.ts b/packages/payload/src/collections/graphql/resolvers/findVersions.ts index 86bfd323ba..a976f7c464 100644 --- a/packages/payload/src/collections/graphql/resolvers/findVersions.ts +++ b/packages/payload/src/collections/graphql/resolvers/findVersions.ts @@ -8,7 +8,7 @@ import type { Where } from '../../../types' import type { Collection } from '../../config/types' import isolateTransactionID from '../../../utilities/isolateTransactionID' -import findVersions from '../../operations/findVersions' +import { findVersionsOperation } from '../../operations/findVersions' export type Resolver = ( _: unknown, @@ -41,7 +41,7 @@ export default function findVersionsResolver(collection: Collection): Resolver { where: args.where, } - const result = await findVersions(options) + const result = await findVersionsOperation(options) return result } diff --git a/packages/payload/src/collections/graphql/resolvers/restoreVersion.ts b/packages/payload/src/collections/graphql/resolvers/restoreVersion.ts index 4c2f1cd4b8..4e88544752 100644 --- a/packages/payload/src/collections/graphql/resolvers/restoreVersion.ts +++ b/packages/payload/src/collections/graphql/resolvers/restoreVersion.ts @@ -5,7 +5,7 @@ import type { PayloadRequest } from '../../../types' import type { Collection } from '../../config/types' import isolateTransactionID from '../../../utilities/isolateTransactionID' -import restoreVersion from '../../operations/restoreVersion' +import { restoreVersionOperation } from '../../operations/restoreVersion' export type Resolver = ( _: unknown, @@ -27,7 +27,7 @@ export default function restoreVersionResolver(collection: Collection): Resolver req: isolateTransactionID(context.req), } - const result = await restoreVersion(options) + const result = await restoreVersionOperation(options) return result } diff --git a/packages/payload/src/collections/graphql/resolvers/update.ts b/packages/payload/src/collections/graphql/resolvers/update.ts index 0e971e7050..c5e9c72634 100644 --- a/packages/payload/src/collections/graphql/resolvers/update.ts +++ b/packages/payload/src/collections/graphql/resolvers/update.ts @@ -6,7 +6,7 @@ import type { PayloadRequest } from '../../../types' import type { Collection } from '../../config/types' import isolateTransactionID from '../../../utilities/isolateTransactionID' -import updateByID from '../../operations/updateByID' +import { updateByIDOperation } from '../../operations/updateByID' export type Resolver = ( _: unknown, @@ -40,7 +40,7 @@ export default function updateResolver(options) + const result = await updateByIDOperation(options) return result } diff --git a/packages/payload/src/collections/initHTTP.ts b/packages/payload/src/collections/initHTTP.ts deleted file mode 100644 index d4613488d5..0000000000 --- a/packages/payload/src/collections/initHTTP.ts +++ /dev/null @@ -1,43 +0,0 @@ -import express from 'express' -import passport from 'passport' - -import type { Payload } from '../payload' -import type { SanitizedCollectionConfig } from './config/types' - -import apiKeyStrategy from '../auth/strategies/apiKey' -import mountEndpoints from '../express/mountEndpoints' -import bindCollectionMiddleware from './bindCollection' -import buildEndpoints from './buildEndpoints' - -export default function initCollectionsHTTP(ctx: Payload): void { - ctx.config.collections = ctx.config.collections.map((collection: SanitizedCollectionConfig) => { - const formattedCollection = collection - - const router = express.Router() - const { slug } = collection - - router.all('*', bindCollectionMiddleware(ctx.collections[formattedCollection.slug])) - - if (collection.auth) { - const { config } = ctx.collections[formattedCollection.slug] - - if (collection.auth.useAPIKey) { - passport.use(`${config.slug}-api-key`, apiKeyStrategy(ctx, config)) - } - - if (Array.isArray(collection.auth.strategies)) { - collection.auth.strategies.forEach(({ name, strategy }, index) => { - const passportStrategy = typeof strategy === 'object' ? strategy : strategy(ctx) - passport.use(`${config.slug}-${name ?? index}`, passportStrategy) - }) - } - } - - const endpoints = buildEndpoints(collection) - mountEndpoints(ctx.express, router, endpoints) - - ctx.router.use(`/${slug}`, router) - - return formattedCollection - }) -} diff --git a/packages/payload/src/collections/initLocal.ts b/packages/payload/src/collections/initLocal.ts deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/packages/payload/src/collections/operations/create.ts b/packages/payload/src/collections/operations/create.ts index a9de9a88eb..03a68c88d4 100644 --- a/packages/payload/src/collections/operations/create.ts +++ b/packages/payload/src/collections/operations/create.ts @@ -49,9 +49,9 @@ export type Arguments = { showHiddenFields?: boolean } -async function create( +export const createOperation = async ( incomingArgs: Arguments, -): Promise { +): Promise => { let args = incomingArgs // ///////////////////////////////////// @@ -371,5 +371,3 @@ async function create( throw error } } - -export default create diff --git a/packages/payload/src/collections/operations/delete.ts b/packages/payload/src/collections/operations/delete.ts index 80aab93d18..7992cffefc 100644 --- a/packages/payload/src/collections/operations/delete.ts +++ b/packages/payload/src/collections/operations/delete.ts @@ -28,7 +28,7 @@ export type Arguments = { where: Where } -async function deleteOperation( +export const deleteOperation = async ( incomingArgs: Arguments, ): Promise<{ docs: GeneratedTypes['collections'][TSlug][] @@ -36,7 +36,7 @@ async function deleteOperation { +}> => { let args = incomingArgs // ///////////////////////////////////// @@ -268,5 +268,3 @@ async function deleteOperation( +export const deleteByIDOperation = async ( incomingArgs: Arguments, -): Promise { +): Promise => { let args = incomingArgs // ///////////////////////////////////// @@ -217,5 +217,3 @@ async function deleteByID( throw error } } - -export default deleteByID diff --git a/packages/payload/src/collections/operations/docAccess.ts b/packages/payload/src/collections/operations/docAccess.ts index 5ef440f259..4970d3fa14 100644 --- a/packages/payload/src/collections/operations/docAccess.ts +++ b/packages/payload/src/collections/operations/docAccess.ts @@ -14,7 +14,7 @@ type Arguments = { req: PayloadRequest } -export async function docAccess(args: Arguments): Promise { +export async function docAccessOperation(args: Arguments): Promise { const { id, req, diff --git a/packages/payload/src/collections/operations/find.ts b/packages/payload/src/collections/operations/find.ts index 422a382ce5..2810de6f27 100644 --- a/packages/payload/src/collections/operations/find.ts +++ b/packages/payload/src/collections/operations/find.ts @@ -32,9 +32,9 @@ export type Arguments = { where?: Where } -async function find>( +export const findOperation = async >( incomingArgs: Arguments, -): Promise> { +): Promise> => { let args = incomingArgs // ///////////////////////////////////// @@ -257,5 +257,3 @@ async function find>( throw error } } - -export default find diff --git a/packages/payload/src/collections/operations/findByID.ts b/packages/payload/src/collections/operations/findByID.ts index 05e28cc5cc..3097097787 100644 --- a/packages/payload/src/collections/operations/findByID.ts +++ b/packages/payload/src/collections/operations/findByID.ts @@ -27,7 +27,9 @@ export type Arguments = { showHiddenFields?: boolean } -async function findByID(incomingArgs: Arguments): Promise { +export const findByIDOperation = async ( + incomingArgs: Arguments, +): Promise => { let args = incomingArgs // ///////////////////////////////////// @@ -208,5 +210,3 @@ async function findByID(incomingArgs: Arguments): Promise< throw error } } - -export default findByID diff --git a/packages/payload/src/collections/operations/findVersionByID.ts b/packages/payload/src/collections/operations/findVersionByID.ts index f49a754c1f..bb8e3a0f73 100644 --- a/packages/payload/src/collections/operations/findVersionByID.ts +++ b/packages/payload/src/collections/operations/findVersionByID.ts @@ -24,9 +24,9 @@ export type Arguments = { showHiddenFields?: boolean } -async function findVersionByID( +export const findVersionByIDOperation = async ( args: Arguments, -): Promise> { +): Promise> => { const { id, collection: { config: collectionConfig }, @@ -147,5 +147,3 @@ async function findVersionByID( throw error } } - -export default findVersionByID diff --git a/packages/payload/src/collections/operations/findVersions.ts b/packages/payload/src/collections/operations/findVersions.ts index f28f03f058..fb767ae8a2 100644 --- a/packages/payload/src/collections/operations/findVersions.ts +++ b/packages/payload/src/collections/operations/findVersions.ts @@ -27,9 +27,9 @@ export type Arguments = { where?: Where } -async function findVersions>( +export const findVersionsOperation = async >( args: Arguments, -): Promise> { +): Promise> => { const { collection: { config: collectionConfig }, depth, @@ -181,5 +181,3 @@ async function findVersions>( throw error } } - -export default findVersions diff --git a/packages/payload/src/collections/operations/local/create.ts b/packages/payload/src/collections/operations/local/create.ts index 083e331f8b..d01cb48fb7 100644 --- a/packages/payload/src/collections/operations/local/create.ts +++ b/packages/payload/src/collections/operations/local/create.ts @@ -12,7 +12,7 @@ import { i18nInit } from '../../../translations/init' import getFileByPath from '../../../uploads/getFileByPath' import { setRequestContext } from '../../../utilities/setRequestContext' import { getDataLoader } from '../../dataloader' -import create from '../create' +import { createOperation } from '../create' export type Options = { collection: TSlug @@ -86,7 +86,7 @@ export default async function createLocal({ + return createOperation({ collection, data, depth, diff --git a/packages/payload/src/collections/operations/local/delete.ts b/packages/payload/src/collections/operations/local/delete.ts index 77a04582ea..06a5e66f8a 100644 --- a/packages/payload/src/collections/operations/local/delete.ts +++ b/packages/payload/src/collections/operations/local/delete.ts @@ -8,8 +8,8 @@ import { APIError } from '../../../errors' import { i18nInit } from '../../../translations/init' import { setRequestContext } from '../../../utilities/setRequestContext' import { getDataLoader } from '../../dataloader' -import deleteOperation from '../delete' -import deleteByID from '../deleteByID' +import { deleteOperation } from '../delete' +import { deleteByIDOperation } from '../deleteByID' export type BaseOptions = { collection: T @@ -106,7 +106,7 @@ async function deleteLocal( } if (options.id) { - return deleteByID(args) + return deleteByIDOperation(args) } return deleteOperation(args) } diff --git a/packages/payload/src/collections/operations/local/find.ts b/packages/payload/src/collections/operations/local/find.ts index 179577ad86..e0f88c61cd 100644 --- a/packages/payload/src/collections/operations/local/find.ts +++ b/packages/payload/src/collections/operations/local/find.ts @@ -7,7 +7,7 @@ import { APIError } from '../../../errors' import { i18nInit } from '../../../translations/init' import { setRequestContext } from '../../../utilities/setRequestContext' import { getDataLoader } from '../../dataloader' -import find from '../find' +import { findOperation } from '../find' export type Options = { collection: T @@ -89,7 +89,7 @@ export default async function findLocal({ + return findOperation({ collection, currentDepth, depth, diff --git a/packages/payload/src/collections/operations/local/findByID.ts b/packages/payload/src/collections/operations/local/findByID.ts index f2c32ce3e1..2c570dab83 100644 --- a/packages/payload/src/collections/operations/local/findByID.ts +++ b/packages/payload/src/collections/operations/local/findByID.ts @@ -7,7 +7,7 @@ import { APIError } from '../../../errors' import { i18nInit } from '../../../translations/init' import { setRequestContext } from '../../../utilities/setRequestContext' import { getDataLoader } from '../../dataloader' -import findByID from '../findByID' +import { findByIDOperation } from '../findByID' export type Options = { collection: T @@ -81,7 +81,7 @@ export default async function findByIDLocal({ + return findByIDOperation({ id, collection, currentDepth, diff --git a/packages/payload/src/collections/operations/local/findVersionByID.ts b/packages/payload/src/collections/operations/local/findVersionByID.ts index d244b4bcd8..55a05760eb 100644 --- a/packages/payload/src/collections/operations/local/findVersionByID.ts +++ b/packages/payload/src/collections/operations/local/findVersionByID.ts @@ -7,7 +7,7 @@ import { APIError } from '../../../errors' import { i18nInit } from '../../../translations/init' import { setRequestContext } from '../../../utilities/setRequestContext' import { getDataLoader } from '../../dataloader' -import findVersionByID from '../findVersionByID' +import { findVersionByIDOperation } from '../findVersionByID' export type Options = { collection: T @@ -77,7 +77,7 @@ export default async function findVersionByIDLocal = { collection: T @@ -76,7 +76,7 @@ export default async function findVersionsLocal = { collection: T @@ -77,5 +77,5 @@ export default async function restoreVersionLocal = { autosave?: boolean @@ -130,9 +130,9 @@ async function updateLocal( } if (options.id) { - return updateByID(args) + return updateByIDOperation(args) } - return update(args) + return updateOperation(args) } export default updateLocal diff --git a/packages/payload/src/collections/operations/restoreVersion.ts b/packages/payload/src/collections/operations/restoreVersion.ts index d85a06f3c1..2859dd8be3 100644 --- a/packages/payload/src/collections/operations/restoreVersion.ts +++ b/packages/payload/src/collections/operations/restoreVersion.ts @@ -27,7 +27,9 @@ export type Arguments = { showHiddenFields?: boolean } -async function restoreVersion(args: Arguments): Promise { +export const restoreVersionOperation = async ( + args: Arguments, +): Promise => { const { id, collection: { config: collectionConfig }, @@ -203,5 +205,3 @@ async function restoreVersion(args: Arguments): Prom throw error } } - -export default restoreVersion diff --git a/packages/payload/src/collections/operations/update.ts b/packages/payload/src/collections/operations/update.ts index 614019b3d7..cf3fc90044 100644 --- a/packages/payload/src/collections/operations/update.ts +++ b/packages/payload/src/collections/operations/update.ts @@ -42,9 +42,9 @@ export type Arguments = { where: Where } -async function update( +export const updateOperation = async ( incomingArgs: Arguments, -): Promise> { +): Promise> => { let args = incomingArgs // ///////////////////////////////////// @@ -408,5 +408,3 @@ async function update( throw error } } - -export default update diff --git a/packages/payload/src/collections/operations/updateByID.ts b/packages/payload/src/collections/operations/updateByID.ts index ce06f7df73..bfc96655a7 100644 --- a/packages/payload/src/collections/operations/updateByID.ts +++ b/packages/payload/src/collections/operations/updateByID.ts @@ -41,9 +41,9 @@ export type Arguments showHiddenFields?: boolean } -async function updateByID( +export const updateByIDOperation = async ( incomingArgs: Arguments, -): Promise { +): Promise => { let args = incomingArgs // ///////////////////////////////////// @@ -380,5 +380,3 @@ async function updateByID( throw error } } - -export default updateByID diff --git a/packages/payload/src/collections/operations/utils.ts b/packages/payload/src/collections/operations/utils.ts index 1abda80a30..2ad3c9e2a6 100644 --- a/packages/payload/src/collections/operations/utils.ts +++ b/packages/payload/src/collections/operations/utils.ts @@ -1,26 +1,26 @@ -import type forgotPassword from '../../auth/operations/forgotPassword' -import type login from '../../auth/operations/login' -import type refresh from '../../auth/operations/refresh' +import type { forgotPasswordOperation } from '../../auth/operations/forgotPassword' +import type { loginOperation } from '../../auth/operations/login' +import type { refreshOperation } from '../../auth/operations/refresh' import type { AfterOperationHook, SanitizedCollectionConfig, TypeWithID } from '../config/types' -import type create from './create' -import type deleteOperation from './delete' -import type deleteByID from './deleteByID' -import type find from './find' -import type findByID from './findByID' -import type update from './update' -import type updateByID from './updateByID' +import type { createOperation } from './create' +import type { deleteOperation } from './delete' +import type { deleteByIDOperation } from './deleteByID' +import type { findOperation } from './find' +import type { findByIDOperation } from './findByID' +import type { updateOperation } from './update' +import type { updateByIDOperation } from './updateByID' export type AfterOperationMap = { - create: typeof create // todo: pass correct generic + create: typeof createOperation // todo: pass correct generic delete: typeof deleteOperation // todo: pass correct generic - deleteByID: typeof deleteByID // todo: pass correct generic - find: typeof find - findByID: typeof findByID - forgotPassword: typeof forgotPassword - login: typeof login - refresh: typeof refresh - update: typeof update // todo: pass correct generic - updateByID: typeof updateByID // todo: pass correct generic + deleteByID: typeof deleteByIDOperation // todo: pass correct generic + find: typeof findOperation + findByID: typeof findByIDOperation + forgotPassword: typeof forgotPasswordOperation + login: typeof loginOperation + refresh: typeof refreshOperation + update: typeof updateOperation // todo: pass correct generic + updateByID: typeof updateByIDOperation // todo: pass correct generic } export type AfterOperationArg = | { diff --git a/packages/payload/src/collections/requestHandlers/create.ts b/packages/payload/src/collections/requestHandlers/create.ts deleted file mode 100644 index 76c16d1904..0000000000 --- a/packages/payload/src/collections/requestHandlers/create.ts +++ /dev/null @@ -1,52 +0,0 @@ -import type { NextFunction, Response } from 'express' - -import httpStatus from 'http-status' -import { URL } from 'url' - -import type { PayloadRequest } from '../../types' -import type { Document } from '../../types' - -import formatSuccessResponse from '../../express/responses/formatSuccess' -import { getTranslation } from '../../utilities/getTranslation' -import { isNumber } from '../../utilities/isNumber' -import create from '../operations/create' - -export type CreateResult = { - doc: Document - message: string -} - -export default async function createHandler( - req: PayloadRequest, - res: Response, - next: NextFunction, -): Promise | void> { - try { - const { searchParams } = new URL(req.url) - const autosave = searchParams.get('autosave') === 'true' - const draft = searchParams.get('draft') === 'true' - const depth = searchParams.get('depth') - - const doc = await create({ - autosave, - collection: req.collection, - // TODO(JARROD): remove reliance on express body parsing - data: req.body, - depth: isNumber(depth) ? depth : undefined, - draft, - req, - }) - - res.status(httpStatus.CREATED).json({ - ...formatSuccessResponse( - req.t('general:successfullyCreated', { - label: getTranslation(req.collection.config.labels.singular, req.i18n), - }), - 'message', - ), - doc, - }) - } catch (error) { - next(error) - } -} diff --git a/packages/payload/src/collections/requestHandlers/delete.ts b/packages/payload/src/collections/requestHandlers/delete.ts deleted file mode 100644 index 9d7d9f6c77..0000000000 --- a/packages/payload/src/collections/requestHandlers/delete.ts +++ /dev/null @@ -1,69 +0,0 @@ -import type { NextFunction, Response } from 'express' - -import httpStatus from 'http-status' -import { URL } from 'url' - -import type { PayloadRequest } from '../../types' -import type { Document, Where } from '../../types' - -import formatSuccessResponse from '../../express/responses/formatSuccess' -import { getTranslation } from '../../utilities/getTranslation' -import { isNumber } from '../../utilities/isNumber' -import deleteOperation from '../operations/delete' - -export type DeleteResult = { - doc: Document - message: string -} - -export default async function deleteHandler( - req: PayloadRequest, - res: Response, - next: NextFunction, -): Promise | void> { - try { - const { searchParams } = new URL(req.url) - const depth = searchParams.get('depth') - const where = searchParams.get('where') - - const result = await deleteOperation({ - collection: req.collection, - depth: isNumber(depth) ? depth : undefined, - req, - where: where ? (JSON.parse(where) as Where) : {}, - }) - - if (result.errors.length === 0) { - const message = req.t('general:deletedCountSuccessfully', { - count: result.docs.length, - label: getTranslation( - req.collection.config.labels[result.docs.length > 1 ? 'plural' : 'singular'], - req.i18n, - ), - }) - - res.status(httpStatus.OK).json({ - ...formatSuccessResponse(message, 'message'), - ...result, - }) - return - } - - const total = result.docs.length + result.errors.length - const message = req.t('error:unableToDeleteCount', { - count: result.errors.length, - label: getTranslation( - req.collection.config.labels[total > 1 ? 'plural' : 'singular'], - req.i18n, - ), - total, - }) - - res.status(httpStatus.BAD_REQUEST).json({ - message, - ...result, - }) - } catch (error) { - next(error) - } -} diff --git a/packages/payload/src/collections/requestHandlers/deleteByID.ts b/packages/payload/src/collections/requestHandlers/deleteByID.ts deleted file mode 100644 index 748625f813..0000000000 --- a/packages/payload/src/collections/requestHandlers/deleteByID.ts +++ /dev/null @@ -1,41 +0,0 @@ -import type { NextFunction, Response } from 'express' - -import httpStatus from 'http-status' -import { URL } from 'url' - -import type { PayloadRequest } from '../../types' -import type { Document } from '../../types' - -import { NotFound } from '../../errors' -import { isNumber } from '../../utilities/isNumber' -import deleteByID from '../operations/deleteByID' - -export type DeleteResult = { - doc: Document - message: string -} - -export default async function deleteByIDHandler( - req: PayloadRequest, - res: Response, - next: NextFunction, -): Promise | void> { - try { - const { searchParams } = new URL(req.url) - const depth = searchParams.get('depth') - const doc = await deleteByID({ - id: req.params.id, - collection: req.collection, - depth: isNumber(depth) ? depth : undefined, - req, - }) - - if (!doc) { - res.status(httpStatus.NOT_FOUND).json(new NotFound(req.t)) - } - - res.status(httpStatus.OK).send(doc) - } catch (error) { - next(error) - } -} diff --git a/packages/payload/src/collections/requestHandlers/docAccess.ts b/packages/payload/src/collections/requestHandlers/docAccess.ts deleted file mode 100644 index 4d253a97f8..0000000000 --- a/packages/payload/src/collections/requestHandlers/docAccess.ts +++ /dev/null @@ -1,25 +0,0 @@ -import type { NextFunction, Response } from 'express' - -import httpStatus from 'http-status' - -import type { CollectionPermission, GlobalPermission } from '../../auth' -import type { PayloadRequest } from '../../types' - -import { docAccess } from '../operations/docAccess' - -export default async function docAccessRequestHandler( - req: PayloadRequest, - res: Response, - next: NextFunction, -): Promise | void> { - try { - const accessResults = await docAccess({ - id: req.params.id, - req, - }) - - return res.status(httpStatus.OK).json(accessResults) - } catch (error) { - return next(error) - } -} diff --git a/packages/payload/src/collections/requestHandlers/find.ts b/packages/payload/src/collections/requestHandlers/find.ts deleted file mode 100644 index 3bfb380aea..0000000000 --- a/packages/payload/src/collections/requestHandlers/find.ts +++ /dev/null @@ -1,41 +0,0 @@ -import type { NextFunction, Response } from 'express' - -import httpStatus from 'http-status' -import { URL } from 'url' - -import type { PaginatedDocs } from '../../database/types' -import type { PayloadRequest } from '../../types' -import type { TypeWithID } from '../config/types' - -import { isNumber } from '../../utilities/isNumber' -import find from '../operations/find' - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export default async function findHandler( - req: PayloadRequest, - res: Response, - next: NextFunction, -): Promise> | void> { - try { - const { searchParams } = new URL(req.url) - const depth = searchParams.get('depth') - const limit = searchParams.get('limit') - const page = searchParams.get('page') - const where = searchParams.get('where') - - const result = await find({ - collection: req.collection, - depth: isNumber(depth) ? Number(depth) : undefined, - draft: searchParams.get('draft') === 'true', - limit: isNumber(limit) ? Number(limit) : undefined, - page: isNumber(page) ? Number(page) : undefined, - req, - sort: searchParams.get('sort'), - where: where ? JSON.parse(where) : undefined, - }) - - return res.status(httpStatus.OK).json(result) - } catch (error) { - return next(error) - } -} diff --git a/packages/payload/src/collections/requestHandlers/findByID.ts b/packages/payload/src/collections/requestHandlers/findByID.ts deleted file mode 100644 index ae71750c7e..0000000000 --- a/packages/payload/src/collections/requestHandlers/findByID.ts +++ /dev/null @@ -1,36 +0,0 @@ -import type { NextFunction, Response } from 'express' - -import { URL } from 'url' - -import type { PayloadRequest } from '../../types' -import type { Document } from '../../types' - -import { isNumber } from '../../utilities/isNumber' -import findByID from '../operations/findByID' - -export type FindByIDResult = { - doc: Document - message: string -} - -export default async function findByIDHandler( - req: PayloadRequest, - res: Response, - next: NextFunction, -): Promise | void> { - try { - const { searchParams } = new URL(req.url) - const depth = searchParams.get('depth') - - const doc = await findByID({ - id: req.params.id, - collection: req.collection, - depth: isNumber(depth) ? Number(depth) : undefined, - draft: searchParams.get('draft') === 'true', - req, - }) - return res.json(doc) - } catch (error) { - return next(error) - } -} diff --git a/packages/payload/src/collections/requestHandlers/findVersionByID.ts b/packages/payload/src/collections/requestHandlers/findVersionByID.ts deleted file mode 100644 index 7afa7e3f05..0000000000 --- a/packages/payload/src/collections/requestHandlers/findVersionByID.ts +++ /dev/null @@ -1,38 +0,0 @@ -import type { NextFunction, Response } from 'express' - -import { URL } from 'url' - -import type { PayloadRequest } from '../../types' -import type { Document } from '../../types' - -import { isNumber } from '../../utilities/isNumber' -import findVersionByID from '../operations/findVersionByID' - -export type FindByIDResult = { - doc: Document - message: string -} - -export default async function findVersionByIDHandler( - req: PayloadRequest, - res: Response, - next: NextFunction, -): Promise | void> { - const { searchParams } = new URL(req.url) - const depth = searchParams.get('depth') - - const options = { - id: req.params.id, - collection: req.collection, - depth: isNumber(depth) ? Number(depth) : undefined, - payload: req.payload, - req, - } - - try { - const doc = await findVersionByID(options) - return res.json(doc) - } catch (error) { - return next(error) - } -} diff --git a/packages/payload/src/collections/requestHandlers/findVersions.ts b/packages/payload/src/collections/requestHandlers/findVersions.ts deleted file mode 100644 index 05ec48b8fe..0000000000 --- a/packages/payload/src/collections/requestHandlers/findVersions.ts +++ /dev/null @@ -1,44 +0,0 @@ -import type { NextFunction, Response } from 'express' - -import httpStatus from 'http-status' -import { URL } from 'url' - -import type { PaginatedDocs } from '../../database/types' -import type { PayloadRequest } from '../../types' -import type { Where } from '../../types' -import type { TypeWithID } from '../config/types' - -import { isNumber } from '../../utilities/isNumber' -import findVersions from '../operations/findVersions' - -export default async function findVersionsHandler( - req: PayloadRequest, - res: Response, - next: NextFunction, -): Promise> | void> { - try { - const { searchParams } = new URL(req.url) - const page = searchParams.get('page') - const depth = searchParams.get('depth') - const limit = searchParams.get('limit') - const where = searchParams.get('where') - const sort = searchParams.get('sort') - - const options = { - collection: req.collection, - depth: isNumber(depth) ? Number(depth) : undefined, - limit: isNumber(limit) ? Number(limit) : undefined, - page: isNumber(page) ? Number(page) : undefined, - payload: req.payload, - req, - sort: sort, - where: where ? (JSON.parse(where) as Where) : undefined, - } - - const result = await findVersions(options) - - return res.status(httpStatus.OK).json(result) - } catch (error) { - return next(error) - } -} diff --git a/packages/payload/src/collections/requestHandlers/restoreVersion.ts b/packages/payload/src/collections/requestHandlers/restoreVersion.ts deleted file mode 100644 index 835c15604a..0000000000 --- a/packages/payload/src/collections/requestHandlers/restoreVersion.ts +++ /dev/null @@ -1,43 +0,0 @@ -import type { NextFunction, Response } from 'express' - -import httpStatus from 'http-status' -import { URL } from 'url' - -import type { PayloadRequest } from '../../types' -import type { Document } from '../../types' - -import formatSuccessResponse from '../../express/responses/formatSuccess' -import { isNumber } from '../../utilities/isNumber' -import restoreVersion from '../operations/restoreVersion' - -export type RestoreResult = { - doc: Document - message: string -} - -export default async function restoreVersionHandler( - req: PayloadRequest, - res: Response, - next: NextFunction, -): Promise | void> { - const { searchParams } = new URL(req.url) - const depth = searchParams.get('depth') - - const options = { - id: req.params.id, - collection: req.collection, - depth: isNumber(depth) ? Number(depth) : undefined, - payload: req.payload, - req, - } - - try { - const doc = await restoreVersion(options) - res.status(httpStatus.OK).json({ - ...formatSuccessResponse(req.t('version:restoredSuccessfully'), 'message'), - doc, - }) - } catch (error) { - next(error) - } -} diff --git a/packages/payload/src/collections/requestHandlers/update.ts b/packages/payload/src/collections/requestHandlers/update.ts deleted file mode 100644 index bb4d4b10da..0000000000 --- a/packages/payload/src/collections/requestHandlers/update.ts +++ /dev/null @@ -1,71 +0,0 @@ -import type { NextFunction, Response } from 'express' - -import httpStatus from 'http-status' -import { URL } from 'url' - -import type { PayloadRequest } from '../../types' -import type { Document, Where } from '../../types' - -import formatSuccessResponse from '../../express/responses/formatSuccess' -import { getTranslation } from '../../utilities/getTranslation' -import { isNumber } from '../../utilities/isNumber' -import update from '../operations/update' - -export type UpdateResult = { - doc: Document - message: string -} - -export default async function updateHandler( - req: PayloadRequest, - res: Response, - next: NextFunction, -): Promise | void> { - try { - const { searchParams } = new URL(req.url) - const depth = searchParams.get('depth') - const where = searchParams.get('where') - - const result = await update({ - collection: req.collection, - data: req.body, - depth: isNumber(depth) ? Number(depth) : undefined, - draft: searchParams.get('draft') === 'true', - req, - where: where ? (JSON.parse(where) as Where) : undefined, - }) - - if (result.errors.length === 0) { - const message = req.t('general:updatedCountSuccessfully', { - count: result.docs.length, - label: getTranslation( - req.collection.config.labels[result.docs.length > 1 ? 'plural' : 'singular'], - req.i18n, - ), - }) - - res.status(httpStatus.OK).json({ - ...formatSuccessResponse(message, 'message'), - ...result, - }) - return - } - - const total = result.docs.length + result.errors.length - const message = req.t('error:unableToUpdateCount', { - count: result.errors.length, - label: getTranslation( - req.collection.config.labels[total > 1 ? 'plural' : 'singular'], - req.i18n, - ), - total, - }) - - res.status(httpStatus.BAD_REQUEST).json({ - ...formatSuccessResponse(message, 'message'), - ...result, - }) - } catch (error) { - next(error) - } -} diff --git a/packages/payload/src/collections/requestHandlers/updateByID.ts b/packages/payload/src/collections/requestHandlers/updateByID.ts deleted file mode 100644 index fca6318366..0000000000 --- a/packages/payload/src/collections/requestHandlers/updateByID.ts +++ /dev/null @@ -1,62 +0,0 @@ -import type { NextFunction, Response } from 'express' - -import httpStatus from 'http-status' -import { URL } from 'url' - -import type { PayloadRequest } from '../../types' - -import formatSuccessResponse from '../../express/responses/formatSuccess' -import { isNumber } from '../../utilities/isNumber' -import updateByID from '../operations/updateByID' - -export type UpdateResult = { - doc: Document - message: string -} - -export async function deprecatedUpdate( - req: PayloadRequest, - res: Response, - next: NextFunction, -): Promise | void> { - req.payload.logger.warn( - 'The PUT method is deprecated and will no longer be supported in a future release. Please use the PATCH method for update requests.', - ) - - return updateByIDHandler(req, res, next) -} - -export default async function updateByIDHandler( - req: PayloadRequest, - res: Response, - next: NextFunction, -): Promise | void> { - try { - const { searchParams } = new URL(req.url) - const depth = searchParams.get('depth') - const autosave = searchParams.get('autosave') === 'true' - const draft = searchParams.get('draft') === 'true' - - const doc = await updateByID({ - id: req.params.id, - autosave, - collection: req.collection, - data: req.body, - depth: isNumber(depth) ? Number(depth) : undefined, - draft, - req, - }) - - let message = req.t('general:updatedSuccessfully') - - if (draft) message = req.t('version:draftSavedSuccessfully') - if (autosave) message = req.t('version:autosavedSuccessfully') - - res.status(httpStatus.OK).json({ - ...formatSuccessResponse(message, 'message'), - doc, - }) - } catch (error) { - next(error) - } -} diff --git a/packages/payload/src/exports/auth.ts b/packages/payload/src/exports/auth.ts index c4738fc5a9..4161dac297 100644 --- a/packages/payload/src/exports/auth.ts +++ b/packages/payload/src/exports/auth.ts @@ -1,6 +1,7 @@ export { getAccessResults } from '../auth/getAccessResults' export { getAuthenticatedUser } from '../auth/getAuthenticatedUser' export type { + AuthStrategyFunction, AuthStrategyFunctionArgs, CollectionPermission, FieldPermissions, diff --git a/packages/payload/src/exports/operations.ts b/packages/payload/src/exports/operations.ts index d97a51ba3a..5d901122ed 100644 --- a/packages/payload/src/exports/operations.ts +++ b/packages/payload/src/exports/operations.ts @@ -1,3 +1,30 @@ -export { default as init } from '../auth/operations/init' -export { default as login } from '../auth/operations/login' -export { default as me } from '../auth/operations/me' +export { accessOperation } from '../auth/operations/access' +export { forgotPasswordOperation } from '../auth/operations/forgotPassword' +export { initOperation } from '../auth/operations/init' +export { loginOperation } from '../auth/operations/login' +export { logoutOperation } from '../auth/operations/logout' +export { meOperation } from '../auth/operations/me' +export { refreshOperation } from '../auth/operations/refresh' +export { registerFirstUserOperation } from '../auth/operations/registerFirstUser' +export { resetPasswordOperation } from '../auth/operations/resetPassword' +export { unlockOperation } from '../auth/operations/unlock' +export { verifyEmailOperation } from '../auth/operations/verifyEmail' + +export { createOperation } from '../collections/operations/create' +export { deleteOperation } from '../collections/operations/delete' +export { deleteByIDOperation } from '../collections/operations/deleteByID' +export { docAccessOperation } from '../collections/operations/docAccess' +export { findOperation } from '../collections/operations/find' +export { findByIDOperation } from '../collections/operations/findByID' +export { findVersionByIDOperation } from '../collections/operations/findVersionByID' +export { findVersionsOperation } from '../collections/operations/findVersions' +export { restoreVersionOperation } from '../collections/operations/restoreVersion' +export { updateOperation } from '../collections/operations/update' +export { updateByIDOperation } from '../collections/operations/updateByID' + +export { docAccessOperation as docAccessOperationGlobal } from '../globals/operations/docAccess' +export { findOneOperation } from '../globals/operations/findOne' +export { findVersionByIDOperation as findVersionByIDOperationGlobal } from '../globals/operations/findVersionByID' +export { findVersionsOperation as findVersionsOperationGlobal } from '../globals/operations/findVersions' +export { restoreVersionOperation as restoreVersionOperationGlobal } from '../globals/operations/restoreVersion' +export { updateOperation as updateOperationGlobal } from '../globals/operations/update' diff --git a/packages/payload/src/exports/utilities.ts b/packages/payload/src/exports/utilities.ts index 59d1132108..da5d69a461 100644 --- a/packages/payload/src/exports/utilities.ts +++ b/packages/payload/src/exports/utilities.ts @@ -11,15 +11,16 @@ export { entityToJSONSchema, withNullableJSONSchemaType, } from '../utilities/configToJSONSchema' -export { createArrayFromCommaDelineated } from '../utilities/createArrayFromCommaDelineated' +export { createArrayFromCommaDelineated } from '../utilities/createArrayFromCommaDelineated' export { deepCopyObject } from '../utilities/deepCopyObject' export { deepMerge } from '../utilities/deepMerge' export { fieldSchemaToJSON } from '../utilities/fieldSchemaToJSON' export { default as flattenTopLevelFields } from '../utilities/flattenTopLevelFields' export { formatLabels, formatNames, toWords } from '../utilities/formatLabels' export { getIDType } from '../utilities/getIDType' -export { getTranslation } from '../utilities/getTranslation' +export { getTranslation } from '../utilities/getTranslation' export { isNumber } from '../utilities/isNumber' + export { isValidID } from '../utilities/isValidID' diff --git a/packages/payload/src/globals/buildEndpoints.ts b/packages/payload/src/globals/buildEndpoints.ts deleted file mode 100644 index fb850d962b..0000000000 --- a/packages/payload/src/globals/buildEndpoints.ts +++ /dev/null @@ -1,61 +0,0 @@ -import type { Endpoint } from '../config/types' -import type { SanitizedGlobalConfig } from './config/types' - -import docAccessRequestHandler from './requestHandlers/docAccess' -import findOne from './requestHandlers/findOne' -import findVersionByID from './requestHandlers/findVersionByID' -import findVersions from './requestHandlers/findVersions' -import restoreVersion from './requestHandlers/restoreVersion' -import update from './requestHandlers/update' - -const buildEndpoints = (global: SanitizedGlobalConfig): Endpoint[] => { - if (!global.endpoints) return [] - const endpoints = [...global.endpoints] - - if (global.versions) { - endpoints.push( - { - handler: findVersions(global), - method: 'get', - path: '/versions', - }, - { - handler: findVersionByID(global), - method: 'get', - path: '/versions/:id', - }, - { - handler: restoreVersion(global), - method: 'post', - path: '/versions/:id', - }, - ) - } - - endpoints.push( - { - handler: async (req, res, next) => docAccessRequestHandler(req, res, next, global), - method: 'get', - path: '/access', - }, - { - handler: async (req, res, next) => docAccessRequestHandler(req, res, next, global), - method: 'post', - path: '/access', - }, - { - handler: findOne(global), - method: 'get', - path: '/', - }, - { - handler: update(global), - method: 'post', - path: '/', - }, - ) - - return endpoints -} - -export default buildEndpoints diff --git a/packages/payload/src/globals/graphql/resolvers/docAccess.ts b/packages/payload/src/globals/graphql/resolvers/docAccess.ts index 1a004b5ef4..6b42ebbc58 100644 --- a/packages/payload/src/globals/graphql/resolvers/docAccess.ts +++ b/packages/payload/src/globals/graphql/resolvers/docAccess.ts @@ -3,7 +3,7 @@ import type { PayloadRequest } from '../../../types' import type { SanitizedGlobalConfig } from '../../config/types' import isolateTransactionID from '../../../utilities/isolateTransactionID' -import { docAccess } from '../../operations/docAccess' +import { docAccessOperation } from '../../operations/docAccess' export type Resolver = ( _: unknown, @@ -15,7 +15,7 @@ export type Resolver = ( export function docAccessResolver(global: SanitizedGlobalConfig): Resolver { async function resolver(_, context) { - return docAccess({ + return docAccessOperation({ globalConfig: global, req: isolateTransactionID(context.req), }) diff --git a/packages/payload/src/globals/graphql/resolvers/findOne.ts b/packages/payload/src/globals/graphql/resolvers/findOne.ts index 1ac402c463..a1c64bc28f 100644 --- a/packages/payload/src/globals/graphql/resolvers/findOne.ts +++ b/packages/payload/src/globals/graphql/resolvers/findOne.ts @@ -4,7 +4,7 @@ import type { Document } from '../../../types' import type { SanitizedGlobalConfig } from '../../config/types' import isolateTransactionID from '../../../utilities/isolateTransactionID' -import findOne from '../../operations/findOne' +import { findOneOperation } from '../../operations/findOne' export default function findOneResolver(globalConfig: SanitizedGlobalConfig): Document { return async function resolver(_, args, context) { @@ -21,7 +21,7 @@ export default function findOneResolver(globalConfig: SanitizedGlobalConfig): Do slug, } - const result = await findOne(options) + const result = await findOneOperation(options) return result } } diff --git a/packages/payload/src/globals/graphql/resolvers/findVersionByID.ts b/packages/payload/src/globals/graphql/resolvers/findVersionByID.ts index 86f6e99f51..0478201744 100644 --- a/packages/payload/src/globals/graphql/resolvers/findVersionByID.ts +++ b/packages/payload/src/globals/graphql/resolvers/findVersionByID.ts @@ -6,7 +6,7 @@ import type { Document } from '../../../types' import type { SanitizedGlobalConfig } from '../../config/types' import isolateTransactionID from '../../../utilities/isolateTransactionID' -import findVersionByID from '../../operations/findVersionByID' +import { findVersionByIDOperation } from '../../operations/findVersionByID' export type Resolver = ( _: unknown, @@ -35,7 +35,7 @@ export default function findVersionByIDResolver(globalConfig: SanitizedGlobalCon req: isolateTransactionID(context.req), } - const result = await findVersionByID(options) + const result = await findVersionByIDOperation(options) return result } } diff --git a/packages/payload/src/globals/graphql/resolvers/findVersions.ts b/packages/payload/src/globals/graphql/resolvers/findVersions.ts index 758da27c1b..cd18926620 100644 --- a/packages/payload/src/globals/graphql/resolvers/findVersions.ts +++ b/packages/payload/src/globals/graphql/resolvers/findVersions.ts @@ -5,7 +5,7 @@ import type { Document, Where } from '../../../types' import type { SanitizedGlobalConfig } from '../../config/types' import isolateTransactionID from '../../../utilities/isolateTransactionID' -import findVersions from '../../operations/findVersions' +import { findVersionsOperation } from '../../operations/findVersions' export type Resolver = ( _: unknown, @@ -35,7 +35,7 @@ export default function findVersionsResolver(globalConfig: SanitizedGlobalConfig where: args.where, } - const result = await findVersions(options) + const result = await findVersionsOperation(options) return result } diff --git a/packages/payload/src/globals/graphql/resolvers/restoreVersion.ts b/packages/payload/src/globals/graphql/resolvers/restoreVersion.ts index 3b9a943e09..c0efe47b57 100644 --- a/packages/payload/src/globals/graphql/resolvers/restoreVersion.ts +++ b/packages/payload/src/globals/graphql/resolvers/restoreVersion.ts @@ -5,7 +5,7 @@ import type { Document } from '../../../types' import type { SanitizedGlobalConfig } from '../../config/types' import isolateTransactionID from '../../../utilities/isolateTransactionID' -import restoreVersion from '../../operations/restoreVersion' +import { restoreVersionOperation } from '../../operations/restoreVersion' type Resolver = ( _: unknown, @@ -26,7 +26,7 @@ export default function restoreVersionResolver(globalConfig: SanitizedGlobalConf req: isolateTransactionID(context.req), } - const result = await restoreVersion(options) + const result = await restoreVersionOperation(options) return result } } diff --git a/packages/payload/src/globals/graphql/resolvers/update.ts b/packages/payload/src/globals/graphql/resolvers/update.ts index 0a7457a519..569fdcbac4 100644 --- a/packages/payload/src/globals/graphql/resolvers/update.ts +++ b/packages/payload/src/globals/graphql/resolvers/update.ts @@ -6,7 +6,7 @@ import type { PayloadRequest } from '../../../types' import type { SanitizedGlobalConfig } from '../../config/types' import isolateTransactionID from '../../../utilities/isolateTransactionID' -import update from '../../operations/update' +import { updateOperation } from '../../operations/update' type Resolver = ( _: unknown, @@ -40,7 +40,7 @@ export default function updateResolver(options) + const result = await updateOperation(options) return result } } diff --git a/packages/payload/src/globals/initHTTP.ts b/packages/payload/src/globals/initHTTP.ts deleted file mode 100644 index 4d5d9d518a..0000000000 --- a/packages/payload/src/globals/initHTTP.ts +++ /dev/null @@ -1,21 +0,0 @@ -import express from 'express' - -import type { Payload } from '../payload' -import type { SanitizedGlobalConfig } from './config/types' - -import mountEndpoints from '../express/mountEndpoints' -import buildEndpoints from './buildEndpoints' - -export default function initGlobals(ctx: Payload): void { - if (ctx.config.globals) { - ctx.config.globals.forEach((global: SanitizedGlobalConfig) => { - const router = express.Router() - const { slug } = global - - const endpoints = buildEndpoints(global) - mountEndpoints(ctx.express, router, endpoints) - - ctx.router.use(`/globals/${slug}`, router) - }) - } -} diff --git a/packages/payload/src/globals/initLocal.ts b/packages/payload/src/globals/initLocal.ts deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/packages/payload/src/globals/operations/docAccess.ts b/packages/payload/src/globals/operations/docAccess.ts index 9edcf5fc7e..7bd64b6f2f 100644 --- a/packages/payload/src/globals/operations/docAccess.ts +++ b/packages/payload/src/globals/operations/docAccess.ts @@ -13,7 +13,7 @@ type Arguments = { req: PayloadRequest } -export async function docAccess(args: Arguments): Promise { +export const docAccessOperation = async (args: Arguments): Promise => { const { globalConfig, req } = args const globalOperations: AllOperations[] = ['read', 'update'] diff --git a/packages/payload/src/globals/operations/findOne.ts b/packages/payload/src/globals/operations/findOne.ts index e904ed11e5..74fbe27471 100644 --- a/packages/payload/src/globals/operations/findOne.ts +++ b/packages/payload/src/globals/operations/findOne.ts @@ -21,7 +21,9 @@ type Args = { slug: string } -async function findOne>(args: Args): Promise { +export const findOneOperation = async >( + args: Args, +): Promise => { const { depth, draft: draftEnabled = false, @@ -138,5 +140,3 @@ async function findOne>(args: Args): Promise = any>(args: Arguments): Promise { +export const findVersionByIDOperation = async = any>( + args: Arguments, +): Promise => { const { id, currentDepth, @@ -143,5 +145,3 @@ async function findVersionByID = any>(args: Argumen throw error } } - -export default findVersionByID diff --git a/packages/payload/src/globals/operations/findVersions.ts b/packages/payload/src/globals/operations/findVersions.ts index a533a97502..f4c5f297b1 100644 --- a/packages/payload/src/globals/operations/findVersions.ts +++ b/packages/payload/src/globals/operations/findVersions.ts @@ -26,9 +26,9 @@ export type Arguments = { where?: Where } -async function findVersions>( +export const findVersionsOperation = async >( args: Arguments, -): Promise> { +): Promise> => { const { depth, globalConfig, @@ -153,5 +153,3 @@ async function findVersions>( throw error } } - -export default findVersions diff --git a/packages/payload/src/globals/operations/local/findOne.ts b/packages/payload/src/globals/operations/local/findOne.ts index 292d7fdbed..810a35b3e7 100644 --- a/packages/payload/src/globals/operations/local/findOne.ts +++ b/packages/payload/src/globals/operations/local/findOne.ts @@ -7,7 +7,7 @@ import { getDataLoader } from '../../../collections/dataloader' import { APIError } from '../../../errors' import { i18nInit } from '../../../translations/init' import { setRequestContext } from '../../../utilities/setRequestContext' -import findOne from '../findOne' +import { findOneOperation } from '../findOne' export type Options = { context?: RequestContext @@ -62,7 +62,7 @@ export default async function findOneLocal = { context?: RequestContext @@ -63,7 +63,7 @@ export default async function findVersionByIDLocal = { context?: RequestContext @@ -68,7 +68,7 @@ export default async function findVersionsLocal = { context?: RequestContext @@ -60,7 +60,7 @@ export default async function restoreVersionLocal = { context?: RequestContext @@ -64,7 +64,7 @@ export default async function updateLocal({ + return updateOperation({ data, depth, draft, diff --git a/packages/payload/src/globals/operations/restoreVersion.ts b/packages/payload/src/globals/operations/restoreVersion.ts index 4d566a5616..c7430cd84a 100644 --- a/packages/payload/src/globals/operations/restoreVersion.ts +++ b/packages/payload/src/globals/operations/restoreVersion.ts @@ -19,7 +19,9 @@ export type Arguments = { showHiddenFields?: boolean } -async function restoreVersion = any>(args: Arguments): Promise { +export const restoreVersionOperation = async = any>( + args: Arguments, +): Promise => { const { id, depth, @@ -167,5 +169,3 @@ async function restoreVersion = any>(args: Argument throw error } } - -export default restoreVersion diff --git a/packages/payload/src/globals/operations/update.ts b/packages/payload/src/globals/operations/update.ts index 8201a4a216..b8ca1abb51 100644 --- a/packages/payload/src/globals/operations/update.ts +++ b/packages/payload/src/globals/operations/update.ts @@ -28,9 +28,9 @@ type Args = { slug: string } -async function update( +export const updateOperation = async ( args: Args, -): Promise { +): Promise => { const { autosave, depth, @@ -284,5 +284,3 @@ async function update( throw error } } - -export default update diff --git a/packages/payload/src/globals/requestHandlers/docAccess.ts b/packages/payload/src/globals/requestHandlers/docAccess.ts deleted file mode 100644 index 0a1bad1560..0000000000 --- a/packages/payload/src/globals/requestHandlers/docAccess.ts +++ /dev/null @@ -1,27 +0,0 @@ -import type { NextFunction, Response } from 'express' - -import httpStatus from 'http-status' - -import type { CollectionPermission, GlobalPermission } from '../../auth' -import type { PayloadRequest } from '../../types' -import type { SanitizedGlobalConfig } from '../config/types' - -import { docAccess } from '../operations/docAccess' - -export default async function docAccessRequestHandler( - req: PayloadRequest, - res: Response, - next: NextFunction, - globalConfig: SanitizedGlobalConfig, -): Promise | void> { - try { - const accessResults = await docAccess({ - globalConfig, - req, - }) - - return res.status(httpStatus.OK).json(accessResults) - } catch (error) { - return next(error) - } -} diff --git a/packages/payload/src/globals/requestHandlers/findOne.ts b/packages/payload/src/globals/requestHandlers/findOne.ts deleted file mode 100644 index a54c3b34e5..0000000000 --- a/packages/payload/src/globals/requestHandlers/findOne.ts +++ /dev/null @@ -1,41 +0,0 @@ -import type { NextFunction, Response } from 'express' - -import httpStatus from 'http-status' - -import type { PayloadRequest } from '../../types' -import type { Document } from '../../types' -import type { SanitizedGlobalConfig } from '../config/types' - -import { isNumber } from '../../utilities/isNumber' -import findOne from '../operations/findOne' - -export type FindOneGlobalResult = Promise | void> -export type FindOneGlobalResponse = ( - req: PayloadRequest, - res: Response, - next: NextFunction, -) => FindOneGlobalResult - -export default function findOneHandler(globalConfig: SanitizedGlobalConfig): FindOneGlobalResponse { - return async function handler( - req: PayloadRequest, - res: Response, - next: NextFunction, - ): FindOneGlobalResult { - try { - const { slug } = globalConfig - - const result = await findOne({ - depth: isNumber(req.query?.depth) ? Number(req.query.depth) : undefined, - draft: req.query.draft === 'true', - globalConfig, - req, - slug, - }) - - return res.status(httpStatus.OK).json(result) - } catch (error) { - return next(error) - } - } -} diff --git a/packages/payload/src/globals/requestHandlers/findVersionByID.ts b/packages/payload/src/globals/requestHandlers/findVersionByID.ts deleted file mode 100644 index b68ab87d7e..0000000000 --- a/packages/payload/src/globals/requestHandlers/findVersionByID.ts +++ /dev/null @@ -1,35 +0,0 @@ -import type { NextFunction, Response } from 'express' - -import { URL } from 'url' - -import type { PayloadRequest } from '../../types' -import type { Document } from '../../types' -import type { SanitizedGlobalConfig } from '../config/types' - -import { isNumber } from '../../utilities/isNumber' -import findVersionByID from '../operations/findVersionByID' - -export default function findVersionByIDHandler(globalConfig: SanitizedGlobalConfig): Document { - return async function handler( - req: PayloadRequest, - res: Response, - next: NextFunction, - ): Promise | void> { - const { searchParams } = new URL(req.url) - const depth = searchParams.get('depth') - - const options = { - id: req.params.id, - depth: isNumber(depth) ? Number(depth) : undefined, - globalConfig, - req, - } - - try { - const doc = await findVersionByID(options) - return res.json(doc) - } catch (error) { - return next(error) - } - } -} diff --git a/packages/payload/src/globals/requestHandlers/findVersions.ts b/packages/payload/src/globals/requestHandlers/findVersions.ts deleted file mode 100644 index 849d1bcf2a..0000000000 --- a/packages/payload/src/globals/requestHandlers/findVersions.ts +++ /dev/null @@ -1,45 +0,0 @@ -import type { NextFunction, Response } from 'express' - -import httpStatus from 'http-status' -import { URL } from 'url' - -import type { TypeWithID } from '../../collections/config/types' -import type { PaginatedDocs } from '../../database/types' -import type { PayloadRequest } from '../../types' -import type { Where } from '../../types' -import type { SanitizedGlobalConfig } from '../config/types' - -import { isNumber } from '../../utilities/isNumber' -import findVersions from '../operations/findVersions' - -export default function findVersionsHandler(global: SanitizedGlobalConfig) { - return async function handler( - req: PayloadRequest, - res: Response, - next: NextFunction, - ): Promise> | void> { - try { - const { searchParams } = new URL(req.url) - const page = searchParams.get('page') - const limit = searchParams.get('limit') - const depth = searchParams.get('depth') - const where = searchParams.get('where') - - const options = { - depth: isNumber(depth) ? Number(depth) : undefined, - globalConfig: global, - limit: isNumber(limit) ? Number(limit) : undefined, - page: isNumber(page) ? Number(page) : undefined, - req, - sort: searchParams.get('sort'), - where: where ? (JSON.parse(where) as Where) : undefined, - } - - const result = await findVersions(options) - - return res.status(httpStatus.OK).json(result) - } catch (error) { - return next(error) - } - } -} diff --git a/packages/payload/src/globals/requestHandlers/restoreVersion.ts b/packages/payload/src/globals/requestHandlers/restoreVersion.ts deleted file mode 100644 index 33c36536be..0000000000 --- a/packages/payload/src/globals/requestHandlers/restoreVersion.ts +++ /dev/null @@ -1,40 +0,0 @@ -import type { NextFunction, Response } from 'express' - -import httpStatus from 'http-status' -import { URL } from 'url' - -import type { PayloadRequest } from '../../types' -import type { Document } from '../../types' -import type { SanitizedGlobalConfig } from '../config/types' - -import formatSuccessResponse from '../../express/responses/formatSuccess' -import { isNumber } from '../../utilities/isNumber' -import restoreVersion from '../operations/restoreVersion' - -export default function restoreVersionHandler(globalConfig: SanitizedGlobalConfig) { - return async function handler( - req: PayloadRequest, - res: Response, - next: NextFunction, - ): Promise | void> { - const { searchParams } = new URL(req.url) - const depth = searchParams.get('depth') - - const options = { - id: req.params.id, - depth: isNumber(depth) ? Number(depth) : undefined, - globalConfig, - req, - } - - try { - const doc = await restoreVersion(options) - return res.status(httpStatus.OK).json({ - ...formatSuccessResponse(req.t('version:restoredSuccessfully'), 'message'), - doc, - }) - } catch (error) { - return next(error) - } - } -} diff --git a/packages/payload/src/globals/requestHandlers/update.ts b/packages/payload/src/globals/requestHandlers/update.ts deleted file mode 100644 index 9cc2b6de95..0000000000 --- a/packages/payload/src/globals/requestHandlers/update.ts +++ /dev/null @@ -1,49 +0,0 @@ -import type { NextFunction, Response } from 'express' - -import httpStatus from 'http-status' -import { URL } from 'url' - -import type { PayloadRequest } from '../../types' -import type { Document } from '../../types' -import type { SanitizedGlobalConfig } from '../config/types' - -import { isNumber } from '../../utilities/isNumber' -import update from '../operations/update' - -export type UpdateGlobalResult = Promise | void> -export type UpdateGlobalResponse = ( - req: PayloadRequest, - res: Response, - next: NextFunction, -) => UpdateGlobalResult - -export default function updateHandler(globalConfig: SanitizedGlobalConfig): UpdateGlobalResponse { - return async function handler(req: PayloadRequest, res: Response, next: NextFunction) { - try { - const { slug } = globalConfig - const { searchParams } = new URL(req.url) - const depth = searchParams.get('depth') - const draft = searchParams.get('draft') === 'true' - const autosave = searchParams.get('autosave') === 'true' - - const result = await update({ - autosave, - data: req.body, - depth: isNumber(depth) ? Number(depth) : undefined, - draft, - globalConfig, - req, - slug, - }) - - let message = req.t('general:updatedSuccessfully') - - if (draft) message = req.t('version:draftSavedSuccessfully') - if (autosave) message = req.t('version:autosavedSuccessfully') - - res.status(httpStatus.OK).json({ message, result }) - } catch (error) { - next(error) - } - } -} diff --git a/packages/payload/src/index.ts b/packages/payload/src/index.ts index 34ca506728..c50b38eae1 100644 --- a/packages/payload/src/index.ts +++ b/packages/payload/src/index.ts @@ -1,4 +1,3 @@ -import type { Express, Router } from 'express' import type { ExecutionResult, GraphQLSchema, ValidationRule } from 'graphql' import type { OperationArgs, Request as graphQLRequest } from 'graphql-http/lib/handler' import type { SendMailOptions } from 'nodemailer' @@ -96,8 +95,6 @@ export class Payload { errorHandler: ErrorHandler - express?: Express - extensions: (args: { args: OperationArgs req: graphQLRequest @@ -239,8 +236,6 @@ export class Payload { return restoreVersion(this, options) } - router?: Router - schema: GraphQLSchema secret: string @@ -463,7 +458,7 @@ type GeneratedTypes = { } } -type PayloadT = Payload +export type PayloadT = Payload interface RequestContext { [key: string]: unknown diff --git a/packages/payload/src/types/index.ts b/packages/payload/src/types/index.ts index a554e20944..63160d16a5 100644 --- a/packages/payload/src/types/index.ts +++ b/packages/payload/src/types/index.ts @@ -6,6 +6,7 @@ import type { User } from '../auth/types' import type { Collection, TypeWithID, TypeWithTimestamps } from '../collections/config/types' import type { FindOneArgs } from '../database/types' import type { validOperators } from './constants' +export type { PayloadT } from '../' export type CustomPayloadRequest = { /** diff --git a/packages/payload/src/utilities/cookies.ts b/packages/payload/src/utilities/cookies.ts deleted file mode 100644 index 40f3c47760..0000000000 --- a/packages/payload/src/utilities/cookies.ts +++ /dev/null @@ -1,77 +0,0 @@ -type CookieOptions = { - domain?: string - expires?: Date - httpOnly?: boolean - maxAge?: number - name: string - path?: string - sameSite?: 'Lax' | 'None' | 'Strict' - secure?: boolean - value: string -} - -export const generateCookies = (cookies: CookieOptions[]): string => { - return cookies.map((options) => generateCookie(options)).join('; ') -} - -export const generateCookie = (args: CookieOptions): string => { - const { name, domain, expires, httpOnly, maxAge, path, sameSite, secure: secureArg, value } = args - - let cookieString = `${name}=${value}` - - const secure = secureArg || sameSite === 'None' - - if (expires) { - cookieString += `; Expires=${expires.toUTCString()}` - } - - if (maxAge) { - cookieString += `; Max-Age=${maxAge}` - } - - if (domain) { - cookieString += `; Domain=${domain}` - } - - if (path) { - cookieString += `; Path=${path}` - } - - if (secure) { - cookieString += '; Secure' - } - - if (httpOnly) { - cookieString += '; HttpOnly' - } - - if (sameSite) { - cookieString += `; SameSite=${sameSite}` - } - - return cookieString -} - -import { APIError } from '../errors' - -export const parseCookies = (headers: Request['headers']): Map => { - const list = new Map() - const rc = headers.get('Cookie') - - if (rc) { - rc.split(';').forEach((cookie) => { - const parts = cookie.split('=') - const key = parts.shift().trim() - const encodedValue = parts.join('=') - - try { - const decodedValue = decodeURI(encodedValue) - list.set(key, decodedValue) - } catch (e) { - throw new APIError(`Error decoding cookie value for key ${key}: ${e.message}`) - } - }) - } - - return list -} diff --git a/packages/payload/src/utilities/getCookieExpiration.ts b/packages/payload/src/utilities/getCookieExpiration.ts deleted file mode 100644 index b35aa94aa6..0000000000 --- a/packages/payload/src/utilities/getCookieExpiration.ts +++ /dev/null @@ -1,7 +0,0 @@ -const getCookieExpiration = (seconds = 7200) => { - const currentTime = new Date() - currentTime.setSeconds(currentTime.getSeconds() + seconds) - return currentTime -} - -export default getCookieExpiration diff --git a/packages/payload/src/utilities/parseCookies.ts b/packages/payload/src/utilities/parseCookies.ts index a28925411b..f907efb81d 100644 --- a/packages/payload/src/utilities/parseCookies.ts +++ b/packages/payload/src/utilities/parseCookies.ts @@ -1,6 +1,6 @@ import { APIError } from '../errors' -export default function parseCookies(headers: Request['headers']): Map { +export const parseCookies = (headers: Request['headers']): Map => { const list = new Map() const rc = headers.get('Cookie') diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 46e99c5cdd..bbf3971c85 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -625,12 +625,21 @@ importers: packages/next: dependencies: + http-status: + specifier: 1.6.2 + version: 1.6.2 i18next: specifier: 22.5.1 version: 22.5.1 + jsonwebtoken: + specifier: 9.0.1 + version: 9.0.1 next: specifier: ^14.0.0 version: 14.0.3(@babel/core@7.22.20)(react-dom@18.2.0)(react@18.2.0)(sass@1.69.5) + path-to-regexp: + specifier: ^6.2.1 + version: 6.2.1 devDependencies: '@payloadcms/eslint-config': specifier: workspace:* @@ -14524,6 +14533,10 @@ packages: isarray: 0.0.1 dev: false + /path-to-regexp@6.2.1: + resolution: {integrity: sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw==} + dev: false + /path-type@3.0.0: resolution: {integrity: sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==} engines: {node: '>=4'}