Compare commits
306 Commits
v3.0.0-bet
...
templates/
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
73148ca819 | ||
|
|
00d438e91f | ||
|
|
b1d92c2bad | ||
|
|
5c2f72d70e | ||
|
|
e78b542ebc | ||
|
|
45d20643df | ||
|
|
91e8acc871 | ||
|
|
b83ea8494e | ||
|
|
b73fc586b8 | ||
|
|
0303b78d62 | ||
|
|
a0f0316534 | ||
|
|
522399095c | ||
|
|
306b5d2300 | ||
|
|
ca52a50dd9 | ||
|
|
5bfc92d71d | ||
|
|
b1ef28dd39 | ||
|
|
09246a45e0 | ||
|
|
da6bc55b19 | ||
|
|
f7172b5b2c | ||
|
|
563694d930 | ||
|
|
fee17448e7 | ||
|
|
76428373e4 | ||
|
|
254d888b73 | ||
|
|
36c2714251 | ||
|
|
43a0ce7445 | ||
|
|
d3b8d0c263 | ||
|
|
1e5364ff34 | ||
|
|
e095222a9c | ||
|
|
84abfdf84a | ||
|
|
e746d7afd8 | ||
|
|
d791db24bd | ||
|
|
b0c9b41a5a | ||
|
|
60ceeb0dc1 | ||
|
|
dc741bb140 | ||
|
|
7599ede41d | ||
|
|
e236c28b9c | ||
|
|
f09ee0b84b | ||
|
|
1fdc7cc70d | ||
|
|
2c0bea8c3e | ||
|
|
67a35d35e5 | ||
|
|
a80de3f0c0 | ||
|
|
ff62017cf2 | ||
|
|
8f3f44922e | ||
|
|
1aa23d3ea3 | ||
|
|
a1a0a07ff0 | ||
|
|
afd0b54d1b | ||
|
|
62fc2f552f | ||
|
|
10eab87653 | ||
|
|
bbf35a62d8 | ||
|
|
a108986f1b | ||
|
|
4cc6f4cee4 | ||
|
|
cb691e0642 | ||
|
|
c9ce3501a0 | ||
|
|
ef8d3c9bf4 | ||
|
|
d3232b947d | ||
|
|
28c6b2a66b | ||
|
|
a11243e288 | ||
|
|
19ddd3cfc6 | ||
|
|
1ab3be65f8 | ||
|
|
de53f2a826 | ||
|
|
7def6b7ddf | ||
|
|
840dde2b17 | ||
|
|
c2ff9b1dd8 | ||
|
|
97aca5fde7 | ||
|
|
97d3bb1c11 | ||
|
|
8f785e1fde | ||
|
|
89db8fb7f2 | ||
|
|
3d1305de5c | ||
|
|
0829a350ce | ||
|
|
1fc9c47f20 | ||
|
|
61a4656ef5 | ||
|
|
d8f7034ab8 | ||
|
|
fa39b37a44 | ||
|
|
fa7ed3f621 | ||
|
|
2321970fcc | ||
|
|
84a5b4066d | ||
|
|
f12b4dc6b0 | ||
|
|
8e26824bf8 | ||
|
|
12a8bba852 | ||
|
|
dff71eedaa | ||
|
|
4a324a9de7 | ||
|
|
5fec816ad6 | ||
|
|
0ca473e6ca | ||
|
|
c7218c0bd7 | ||
|
|
340bc85560 | ||
|
|
9bffa098b9 | ||
|
|
d118544b44 | ||
|
|
50823be9e5 | ||
|
|
f5aad49ba7 | ||
|
|
32f0f340ac | ||
|
|
2ef7de57a9 | ||
|
|
0f2feacc19 | ||
|
|
a53c1d5517 | ||
|
|
fce210b9b1 | ||
|
|
1bd689b7e1 | ||
|
|
fd9a0073f1 | ||
|
|
67179a7fb8 | ||
|
|
fbb59bab0a | ||
|
|
01d574624c | ||
|
|
4bb5e4ecbc | ||
|
|
45bc33ba5f | ||
|
|
8beb7c1636 | ||
|
|
188466d842 | ||
|
|
3f3222404f | ||
|
|
0c34f27291 | ||
|
|
18f9f35692 | ||
|
|
7bd1a1632b | ||
|
|
d2007b1670 | ||
|
|
643c92dde7 | ||
|
|
92a01c26e1 | ||
|
|
d047f01bfa | ||
|
|
a6ba9e3a41 | ||
|
|
d1de106944 | ||
|
|
6104fe5011 | ||
|
|
40f5c72e8d | ||
|
|
cf34d3aec4 | ||
|
|
5b3079a88e | ||
|
|
0dbfc237d3 | ||
|
|
a89d54454a | ||
|
|
e4c3c5b1d2 | ||
|
|
58b7415385 | ||
|
|
631edd4c17 | ||
|
|
c5fe021570 | ||
|
|
edc04aec56 | ||
|
|
d04cea123c | ||
|
|
24c75b09c0 | ||
|
|
877b89962e | ||
|
|
71ba4a8bce | ||
|
|
9d724086c7 | ||
|
|
0b445c8013 | ||
|
|
73e0e25e14 | ||
|
|
f07679745a | ||
|
|
963387f7b0 | ||
|
|
4cd31ce343 | ||
|
|
f151723745 | ||
|
|
2b4522b1db | ||
|
|
e6ea68e848 | ||
|
|
1e8c9d3a09 | ||
|
|
fdfa07b863 | ||
|
|
519752f152 | ||
|
|
2d2e7d50f0 | ||
|
|
030b759529 | ||
|
|
3d1a0656d2 | ||
|
|
9892303f1f | ||
|
|
0716128b3b | ||
|
|
bc2d7c91e0 | ||
|
|
b8965077b8 | ||
|
|
aa5dd8a77f | ||
|
|
702df1f5da | ||
|
|
f150a68f56 | ||
|
|
07e40d37ac | ||
|
|
c4327f2294 | ||
|
|
61a51ca02a | ||
|
|
27eeac2568 | ||
|
|
3e4f7dbae2 | ||
|
|
36c9a19b0e | ||
|
|
b5ddc328ef | ||
|
|
36a6a19de8 | ||
|
|
3da9be00bc | ||
|
|
4b302f22a0 | ||
|
|
c7138b9aab | ||
|
|
3c35d81fe5 | ||
|
|
3961223cc1 | ||
|
|
303227363b | ||
|
|
ca07c9f0d7 | ||
|
|
17c79454e3 | ||
|
|
4a854f86ae | ||
|
|
519bb790d8 | ||
|
|
b47ebb6550 | ||
|
|
be59d52ac7 | ||
|
|
6af4deefc2 | ||
|
|
072f1efb41 | ||
|
|
c8bee29920 | ||
|
|
2248be4a2e | ||
|
|
21db058e2e | ||
|
|
90f893a558 | ||
|
|
5d18a5288e | ||
|
|
defa13e4fe | ||
|
|
bffd98f019 | ||
|
|
601759d967 | ||
|
|
61d6614746 | ||
|
|
f8b7e3eb14 | ||
|
|
1a8ac74df7 | ||
|
|
fd0ff51296 | ||
|
|
67a9d669b6 | ||
|
|
f19053e049 | ||
|
|
b61622019e | ||
|
|
bb648b3b5f | ||
|
|
dac42ff1bd | ||
|
|
a9f511d540 | ||
|
|
44c0cdb75f | ||
|
|
910b68154e | ||
|
|
4c4eb2ae2b | ||
|
|
71c2f63722 | ||
|
|
4e3be4414b | ||
|
|
d0af8e8d06 | ||
|
|
82145f7bb0 | ||
|
|
0757e06e71 | ||
|
|
058bd02ebd | ||
|
|
aa26312b96 | ||
|
|
b5f89d5199 | ||
|
|
c8589a640c | ||
|
|
da788413eb | ||
|
|
0c7e418dbc | ||
|
|
8383426313 | ||
|
|
af096a374a | ||
|
|
6ffd4c7825 | ||
|
|
08270426ba | ||
|
|
f136a7db2a | ||
|
|
e176b8b764 | ||
|
|
3c8f042d1d | ||
|
|
e5cc9153aa | ||
|
|
b96475b7b9 | ||
|
|
cae300e8e3 | ||
|
|
8658945d7b | ||
|
|
aa1d300062 | ||
|
|
150c55de79 | ||
|
|
4b4cfbeca7 | ||
|
|
7eb388d403 | ||
|
|
07c76aa3b9 | ||
|
|
9c59359da6 | ||
|
|
3c0e832a9a | ||
|
|
13fc94dc4d | ||
|
|
23d54a76c1 | ||
|
|
f155e00f00 | ||
|
|
09abebd58c | ||
|
|
90fedbc16b | ||
|
|
322738fca3 | ||
|
|
be8cd7f4d9 | ||
|
|
b9d02aa3a8 | ||
|
|
65ac739da9 | ||
|
|
ae6c71b3b5 | ||
|
|
27acdaee7a | ||
|
|
fb3242df0a | ||
|
|
251c4c30d7 | ||
|
|
9e31e17e31 | ||
|
|
b9cc4d4083 | ||
|
|
a891e98bfc | ||
|
|
38de760ca5 | ||
|
|
18b139b9de | ||
|
|
3b68671348 | ||
|
|
91dcf6dc08 | ||
|
|
f5683b0a64 | ||
|
|
2036a566fd | ||
|
|
0b4e5a6ece | ||
|
|
e3866c4035 | ||
|
|
f338c5c40c | ||
|
|
118b442883 | ||
|
|
ee1a91ee7c | ||
|
|
304ecd29ac | ||
|
|
204b08bc25 | ||
|
|
f2205d1694 | ||
|
|
082e546159 | ||
|
|
d7ad1733f2 | ||
|
|
d499de1e0f | ||
|
|
3d0424bc77 | ||
|
|
0960290558 | ||
|
|
48b60fc905 | ||
|
|
90e37fe78d | ||
|
|
9470f9b01f | ||
|
|
1f92b5b412 | ||
|
|
025d917fa0 | ||
|
|
c67291d538 | ||
|
|
5db7e1e864 | ||
|
|
07a9125f9c | ||
|
|
ef3748319e | ||
|
|
439dcd493e | ||
|
|
ff0386f276 | ||
|
|
568e1a274b | ||
|
|
9e85be0006 | ||
|
|
4030e212f5 | ||
|
|
646a5345a8 | ||
|
|
1dc8094905 | ||
|
|
9ea26638e9 | ||
|
|
4f1a4a28a3 | ||
|
|
41d2e64a3a | ||
|
|
661f450c61 | ||
|
|
69b7a11a28 | ||
|
|
2c7ea6362a | ||
|
|
077d3e7d16 | ||
|
|
188baec34c | ||
|
|
d9c6288cb2 | ||
|
|
037662d9f5 | ||
|
|
389ef16a5f | ||
|
|
cb3d7b37ef | ||
|
|
d542bd774d | ||
|
|
7f65c83a98 | ||
|
|
0f3f6e73da | ||
|
|
a50029f659 | ||
|
|
197a22fb28 | ||
|
|
6d74fbc6cb | ||
|
|
f42b1e1e05 | ||
|
|
bb4f69fb0c | ||
|
|
26cb1e1546 | ||
|
|
5d2b0b30b0 | ||
|
|
1425d58b57 | ||
|
|
d3b0a045be | ||
|
|
7e41f17ec2 | ||
|
|
763ccdcf00 | ||
|
|
76286136ba | ||
|
|
6145accb83 | ||
|
|
6407e577d3 | ||
|
|
7fe207937f | ||
|
|
cc04396a4f | ||
|
|
0d63dc9f9c | ||
|
|
f7fc8a2ea0 |
6
.github/ISSUE_TEMPLATE/1.bug_report_v3.yml
vendored
6
.github/ISSUE_TEMPLATE/1.bug_report_v3.yml
vendored
@@ -1,6 +1,6 @@
|
||||
name: Functionality Bug
|
||||
description: '[REPRODUCTION REQUIRED] - Create a bug report'
|
||||
labels: ['status: needs-triage', 'v3', 'validate-reproduction']
|
||||
labels: ['status: needs-triage', 'validate-reproduction']
|
||||
body:
|
||||
- type: textarea
|
||||
attributes:
|
||||
@@ -14,7 +14,7 @@ body:
|
||||
label: Link to the code that reproduces this issue
|
||||
description: >-
|
||||
_REQUIRED_: Please provide a link to your reproduction. Note, if the URL is invalid (404 or a private repository), we may close the issue.
|
||||
Either use `npx create-payload-app@beta -t blank` then push to a repo or follow the [reproduction-guide](https://github.com/payloadcms/payload/blob/main/.github/reproduction-guide.md) for more information.
|
||||
Either use `pnpx create-payload-app@latest -t blank` then push to a repo or follow the [reproduction-guide](https://github.com/payloadcms/payload/blob/main/.github/reproduction-guide.md) for more information.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
@@ -57,7 +57,7 @@ body:
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Environment Info
|
||||
description: Paste output from `pnpm payload info` (>= beta.92) _or_ Payload, Node.js, and Next.js versions.
|
||||
description: Paste output from `pnpm payload info` _or_ Payload, Node.js, and Next.js versions.
|
||||
render: text
|
||||
placeholder: |
|
||||
Payload:
|
||||
|
||||
2
.github/ISSUE_TEMPLATE/2.design_issue.yml
vendored
2
.github/ISSUE_TEMPLATE/2.design_issue.yml
vendored
@@ -20,7 +20,7 @@ body:
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Environment Info
|
||||
description: Paste output from `pnpm payload info` (>= beta.92) _or_ Payload, Node.js, and Next.js versions.
|
||||
description: Paste output from `pnpm payload info` _or_ Payload, Node.js, and Next.js versions.
|
||||
render: text
|
||||
placeholder: |
|
||||
Payload:
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@octokit/webhooks-types": "^7.5.1",
|
||||
"@swc/jest": "^0.2.36",
|
||||
"@swc/jest": "^0.2.37",
|
||||
"@types/jest": "^27.5.2",
|
||||
"@types/node": "^20.16.5",
|
||||
"@typescript-eslint/eslint-plugin": "^4.33.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"target": "ES2022",
|
||||
"lib": ["es2020.string"],
|
||||
"noEmit": true,
|
||||
"strict": true,
|
||||
|
||||
48
.github/actions/setup/action.yml
vendored
48
.github/actions/setup/action.yml
vendored
@@ -1,15 +1,33 @@
|
||||
name: Setup node and pnpm
|
||||
description: Configure the Node.js and pnpm versions
|
||||
description: |
|
||||
Configures Node, pnpm, cache, performs pnpm install
|
||||
|
||||
inputs:
|
||||
node-version:
|
||||
description: 'The Node.js version to use'
|
||||
description: Node.js version
|
||||
required: true
|
||||
default: 22.6.2
|
||||
default: 22.6.0
|
||||
pnpm-version:
|
||||
description: 'The pnpm version to use'
|
||||
description: Pnpm version
|
||||
required: true
|
||||
default: 9.7.1
|
||||
pnpm-run-install:
|
||||
description: Whether to run pnpm install
|
||||
required: false
|
||||
default: true
|
||||
pnpm-restore-cache:
|
||||
description: Whether to restore cache
|
||||
required: false
|
||||
default: true
|
||||
pnpm-install-cache-key:
|
||||
description: The cache key for the pnpm install cache
|
||||
default: pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
|
||||
outputs:
|
||||
pnpm-store-path:
|
||||
description: The resolved pnpm store path
|
||||
pnpm-install-cache-key:
|
||||
description: The cache key used for pnpm install cache
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
@@ -30,19 +48,29 @@ runs:
|
||||
version: ${{ inputs.pnpm-version }}
|
||||
run_install: false
|
||||
|
||||
- name: Get pnpm store directory
|
||||
- name: Get pnpm store path
|
||||
shell: bash
|
||||
run: |
|
||||
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
|
||||
STORE_PATH=$(pnpm store path --silent)
|
||||
echo "STORE_PATH=$STORE_PATH" >> $GITHUB_ENV
|
||||
echo "Pnpm store path resolved to: $STORE_PATH"
|
||||
|
||||
- name: Setup pnpm cache
|
||||
- name: Restore pnpm install cache
|
||||
if: ${{ inputs.pnpm-restore-cache == 'true' }}
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{ env.STORE_PATH }}
|
||||
key: pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
key: ${{ inputs.pnpm-install-cache-key }}
|
||||
restore-keys: |
|
||||
pnpm-store-${{ inputs.pnpm-version }}-
|
||||
pnpm-store-
|
||||
pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
|
||||
- shell: bash
|
||||
- name: Run pnpm install
|
||||
if: ${{ inputs.pnpm-run-install == 'true' }}
|
||||
shell: bash
|
||||
run: pnpm install
|
||||
|
||||
# Set the cache key output
|
||||
- run: |
|
||||
echo "pnpm-install-cache-key=${{ inputs.pnpm-install-cache-key }}" >> $GITHUB_ENV
|
||||
shell: bash
|
||||
|
||||
2
.github/actions/triage/tsconfig.json
vendored
2
.github/actions/triage/tsconfig.json
vendored
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"target": "ES2022",
|
||||
"lib": ["es2020.string"],
|
||||
"noEmit": true,
|
||||
"strict": true,
|
||||
|
||||
9
.github/dependabot.yml
vendored
9
.github/dependabot.yml
vendored
@@ -21,6 +21,7 @@ updates:
|
||||
- package-ecosystem: npm
|
||||
directory: /
|
||||
target-branch: main
|
||||
open-pull-requests-limit: 0 # Only allow security updates
|
||||
schedule:
|
||||
interval: weekly
|
||||
day: sunday
|
||||
@@ -38,8 +39,6 @@ updates:
|
||||
- patch
|
||||
patterns:
|
||||
- '*'
|
||||
exclude-patterns:
|
||||
- 'drizzle*'
|
||||
dev-deps:
|
||||
dependency-type: development
|
||||
update-types:
|
||||
@@ -47,13 +46,11 @@ updates:
|
||||
- patch
|
||||
patterns:
|
||||
- '*'
|
||||
exclude-patterns:
|
||||
- 'drizzle*'
|
||||
|
||||
# Only bump patch versions for 2.x
|
||||
- package-ecosystem: npm
|
||||
directory: /
|
||||
target-branch: 2.x
|
||||
open-pull-requests-limit: 0 # Only allow security updates
|
||||
schedule:
|
||||
interval: weekly
|
||||
day: sunday
|
||||
@@ -70,5 +67,3 @@ updates:
|
||||
- patch
|
||||
patterns:
|
||||
- '*'
|
||||
exclude-patterns:
|
||||
- 'drizzle*'
|
||||
|
||||
12
.github/workflows/label-on-change.yml
vendored
12
.github/workflows/label-on-change.yml
vendored
@@ -13,7 +13,7 @@ on:
|
||||
|
||||
jobs:
|
||||
on-labeled-ensure-one-status:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-24.04
|
||||
permissions:
|
||||
issues: write
|
||||
# Only run on issue labeled and if label starts with 'status:'
|
||||
@@ -49,7 +49,7 @@ jobs:
|
||||
}
|
||||
|
||||
on-issue-close:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-24.04
|
||||
permissions:
|
||||
issues: write
|
||||
if: github.event.action == 'closed'
|
||||
@@ -82,7 +82,7 @@ jobs:
|
||||
}
|
||||
|
||||
on-issue-reopen:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-24.04
|
||||
permissions:
|
||||
issues: write
|
||||
if: github.event.action == 'reopened'
|
||||
@@ -93,7 +93,7 @@ jobs:
|
||||
labels: 'status: needs-triage'
|
||||
|
||||
on-issue-assigned:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-24.04
|
||||
permissions:
|
||||
issues: write
|
||||
if: >
|
||||
@@ -106,11 +106,11 @@ jobs:
|
||||
labels: 'status: needs-triage'
|
||||
|
||||
# on-pr-merge:
|
||||
# runs-on: ubuntu-latest
|
||||
# runs-on: ubuntu-24.04
|
||||
# if: github.event.pull_request.merged == true
|
||||
# steps:
|
||||
|
||||
# on-pr-close:
|
||||
# runs-on: ubuntu-latest
|
||||
# runs-on: ubuntu-24.04
|
||||
# if: github.event_name == 'pull_request_target' && github.event.pull_request.merged == false
|
||||
# steps:
|
||||
|
||||
2
.github/workflows/lock-issues.yml
vendored
2
.github/workflows/lock-issues.yml
vendored
@@ -11,7 +11,7 @@ permissions:
|
||||
|
||||
jobs:
|
||||
lock_issues:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Lock issues
|
||||
uses: dessant/lock-threads@v5
|
||||
|
||||
257
.github/workflows/main.yml
vendored
257
.github/workflows/main.yml
vendored
@@ -23,11 +23,12 @@ env:
|
||||
|
||||
jobs:
|
||||
changes:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-24.04
|
||||
permissions:
|
||||
pull-requests: read
|
||||
outputs:
|
||||
needs_build: ${{ steps.filter.outputs.needs_build }}
|
||||
needs_tests: ${{ steps.filter.outputs.needs_tests }}
|
||||
templates: ${{ steps.filter.outputs.templates }}
|
||||
steps:
|
||||
# https://github.com/actions/virtual-environments/issues/1187
|
||||
@@ -35,8 +36,6 @@ jobs:
|
||||
run: sudo ethtool -K eth0 tx off rx off
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 25
|
||||
- uses: dorny/paths-filter@v3
|
||||
id: filter
|
||||
with:
|
||||
@@ -48,53 +47,36 @@ jobs:
|
||||
- 'pnpm-lock.yaml'
|
||||
- 'package.json'
|
||||
- 'templates/**'
|
||||
needs_tests:
|
||||
- '.github/workflows/**'
|
||||
- 'packages/**'
|
||||
- 'test/**'
|
||||
- 'pnpm-lock.yaml'
|
||||
- 'package.json'
|
||||
templates:
|
||||
- 'templates/**'
|
||||
- name: Log all filter results
|
||||
run: |
|
||||
echo "needs_build: ${{ steps.filter.outputs.needs_build }}"
|
||||
echo "needs_tests: ${{ steps.filter.outputs.needs_tests }}"
|
||||
echo "templates: ${{ steps.filter.outputs.templates }}"
|
||||
|
||||
lint:
|
||||
if: >
|
||||
github.event_name == 'pull_request' && !contains(github.event.pull_request.title, 'no-lint') && !contains(github.event.pull_request.title, 'skip-lint')
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
# https://github.com/actions/virtual-environments/issues/1187
|
||||
- name: tune linux network
|
||||
run: sudo ethtool -K eth0 tx off rx off
|
||||
|
||||
- name: Setup Node@${{ env.NODE_VERSION }}
|
||||
uses: actions/setup-node@v4
|
||||
- name: Node setup
|
||||
uses: ./.github/actions/setup
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
pnpm-version: ${{ env.PNPM_VERSION }}
|
||||
pnpm-install-cache-key: pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: ${{ env.PNPM_VERSION }}
|
||||
run_install: false
|
||||
|
||||
- name: Get pnpm store directory
|
||||
shell: bash
|
||||
run: |
|
||||
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
|
||||
|
||||
- name: Setup pnpm cache
|
||||
uses: actions/cache@v4
|
||||
timeout-minutes: 720
|
||||
with:
|
||||
path: ${{ env.STORE_PATH }}
|
||||
key: pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
restore-keys: |
|
||||
pnpm-store-
|
||||
pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
|
||||
- run: pnpm install
|
||||
- name: Lint staged
|
||||
run: |
|
||||
git diff --name-only --diff-filter=d origin/${GITHUB_BASE_REF}...${GITHUB_SHA}
|
||||
@@ -103,78 +85,46 @@ jobs:
|
||||
build:
|
||||
needs: changes
|
||||
if: ${{ needs.changes.outputs.needs_build == 'true' }}
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-24.04
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 25
|
||||
|
||||
# https://github.com/actions/virtual-environments/issues/1187
|
||||
- name: tune linux network
|
||||
run: sudo ethtool -K eth0 tx off rx off
|
||||
|
||||
- name: Setup Node@${{ env.NODE_VERSION }}
|
||||
uses: actions/setup-node@v4
|
||||
- name: Node setup
|
||||
uses: ./.github/actions/setup
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
pnpm-version: ${{ env.PNPM_VERSION }}
|
||||
pnpm-install-cache-key: pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: ${{ env.PNPM_VERSION }}
|
||||
run_install: false
|
||||
|
||||
- name: Get pnpm store directory
|
||||
shell: bash
|
||||
run: |
|
||||
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
|
||||
|
||||
- name: Setup pnpm cache
|
||||
uses: actions/cache@v4
|
||||
timeout-minutes: 720
|
||||
with:
|
||||
path: ${{ env.STORE_PATH }}
|
||||
key: pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
restore-keys: |
|
||||
pnpm-store-
|
||||
pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
|
||||
- run: pnpm install
|
||||
- run: pnpm run build:all
|
||||
env:
|
||||
DO_NOT_TRACK: 1 # Disable Turbopack telemetry
|
||||
|
||||
- name: Cache build
|
||||
uses: actions/cache@v4
|
||||
timeout-minutes: 10
|
||||
with:
|
||||
path: ./*
|
||||
key: ${{ github.sha }}-${{ github.run_number }}
|
||||
|
||||
tests-unit:
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
|
||||
runs-on: ubuntu-24.04
|
||||
needs: [changes, build]
|
||||
if: ${{ needs.changes.outputs.needs_tests == 'true' }}
|
||||
steps:
|
||||
# https://github.com/actions/virtual-environments/issues/1187
|
||||
- name: tune linux network
|
||||
run: sudo ethtool -K eth0 tx off rx off
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node@${{ env.NODE_VERSION }}
|
||||
uses: actions/setup-node@v4
|
||||
- name: Node setup
|
||||
uses: ./.github/actions/setup
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: ${{ env.PNPM_VERSION }}
|
||||
run_install: false
|
||||
pnpm-version: ${{ env.PNPM_VERSION }}
|
||||
pnpm-run-install: false
|
||||
pnpm-restore-cache: false # Full build is restored below
|
||||
pnpm-install-cache-key: pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
|
||||
- name: Restore build
|
||||
uses: actions/cache@v4
|
||||
timeout-minutes: 10
|
||||
with:
|
||||
path: ./*
|
||||
key: ${{ github.sha }}-${{ github.run_number }}
|
||||
@@ -184,9 +134,37 @@ jobs:
|
||||
env:
|
||||
NODE_OPTIONS: --max-old-space-size=8096
|
||||
|
||||
tests-types:
|
||||
runs-on: ubuntu-24.04
|
||||
needs: [changes, build]
|
||||
if: ${{ needs.changes.outputs.needs_tests == 'true' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Node setup
|
||||
uses: ./.github/actions/setup
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
pnpm-version: ${{ env.PNPM_VERSION }}
|
||||
pnpm-run-install: false
|
||||
pnpm-restore-cache: false # Full build is restored below
|
||||
pnpm-install-cache-key: pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
|
||||
- name: Restore build
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ./*
|
||||
key: ${{ github.sha }}-${{ github.run_number }}
|
||||
|
||||
- name: Types Tests
|
||||
run: pnpm test:types --target '>=5.7'
|
||||
env:
|
||||
NODE_OPTIONS: --max-old-space-size=8096
|
||||
|
||||
tests-int:
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
runs-on: ubuntu-24.04
|
||||
needs: [changes, build]
|
||||
if: ${{ needs.changes.outputs.needs_tests == 'true' }}
|
||||
name: int-${{ matrix.database }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
@@ -222,24 +200,21 @@ jobs:
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 25
|
||||
# https://github.com/actions/virtual-environments/issues/1187
|
||||
- name: tune linux network
|
||||
run: sudo ethtool -K eth0 tx off rx off
|
||||
|
||||
- name: Setup Node@${{ env.NODE_VERSION }}
|
||||
uses: actions/setup-node@v4
|
||||
- name: Node setup
|
||||
uses: ./.github/actions/setup
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
pnpm-version: ${{ env.PNPM_VERSION }}
|
||||
pnpm-run-install: false
|
||||
pnpm-restore-cache: false # Full build is restored below
|
||||
pnpm-install-cache-key: pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
- name: Restore build
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
version: ${{ env.PNPM_VERSION }}
|
||||
run_install: false
|
||||
|
||||
- run: pnpm install
|
||||
path: ./*
|
||||
key: ${{ github.sha }}-${{ github.run_number }}
|
||||
|
||||
- name: Start LocalStack
|
||||
run: pnpm docker:start
|
||||
@@ -274,15 +249,22 @@ jobs:
|
||||
if: matrix.database == 'supabase'
|
||||
|
||||
- name: Integration Tests
|
||||
run: pnpm test:int
|
||||
uses: nick-fields/retry@v3
|
||||
env:
|
||||
NODE_OPTIONS: --max-old-space-size=8096
|
||||
PAYLOAD_DATABASE: ${{ matrix.database }}
|
||||
POSTGRES_URL: ${{ env.POSTGRES_URL }}
|
||||
with:
|
||||
retry_on: any
|
||||
max_attempts: 5
|
||||
timeout_minutes: 15
|
||||
command: pnpm test:int
|
||||
on_retry_command: pnpm clean:build && pnpm install --no-frozen-lockfile
|
||||
|
||||
tests-e2e:
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
runs-on: ubuntu-24.04
|
||||
needs: [changes, build]
|
||||
if: ${{ needs.changes.outputs.needs_tests == 'true' }}
|
||||
name: e2e-${{ matrix.suite }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
@@ -296,6 +278,7 @@ jobs:
|
||||
- admin__e2e__3
|
||||
- admin-root
|
||||
- auth
|
||||
- auth-basic
|
||||
- field-error-states
|
||||
- fields-relationship
|
||||
- fields
|
||||
@@ -324,24 +307,19 @@ jobs:
|
||||
env:
|
||||
SUITE_NAME: ${{ matrix.suite }}
|
||||
steps:
|
||||
# https://github.com/actions/virtual-environments/issues/1187
|
||||
- name: tune linux network
|
||||
run: sudo ethtool -K eth0 tx off rx off
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node@${{ env.NODE_VERSION }}
|
||||
uses: actions/setup-node@v4
|
||||
- name: Node setup
|
||||
uses: ./.github/actions/setup
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: ${{ env.PNPM_VERSION }}
|
||||
run_install: false
|
||||
pnpm-version: ${{ env.PNPM_VERSION }}
|
||||
pnpm-run-install: false
|
||||
pnpm-restore-cache: false # Full build is restored below
|
||||
pnpm-install-cache-key: pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
|
||||
- name: Restore build
|
||||
uses: actions/cache@v4
|
||||
timeout-minutes: 10
|
||||
with:
|
||||
path: ./*
|
||||
key: ${{ github.sha }}-${{ github.run_number }}
|
||||
@@ -373,7 +351,13 @@ jobs:
|
||||
run: pnpm exec playwright install-deps chromium
|
||||
|
||||
- name: E2E Tests
|
||||
run: PLAYWRIGHT_JSON_OUTPUT_NAME=results_${{ matrix.suite }}.json pnpm test:e2e:prod:ci ${{ matrix.suite }}
|
||||
uses: nick-fields/retry@v3
|
||||
with:
|
||||
retry_on: any
|
||||
max_attempts: 5
|
||||
timeout_minutes: 20
|
||||
command: PLAYWRIGHT_JSON_OUTPUT_NAME=results_${{ matrix.suite }}.json pnpm test:e2e:prod:ci ${{ matrix.suite }}
|
||||
on_retry_command: pnpm clean:build && pnpm install --no-frozen-lockfile && pnpm build:all
|
||||
env:
|
||||
PLAYWRIGHT_JSON_OUTPUT_NAME: results_${{ matrix.suite }}.json
|
||||
NEXT_TELEMETRY_DISABLED: 1
|
||||
@@ -395,7 +379,7 @@ jobs:
|
||||
|
||||
# Build listed templates with packed local packages
|
||||
build-templates:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-24.04
|
||||
needs: build
|
||||
strategy:
|
||||
matrix:
|
||||
@@ -426,24 +410,19 @@ jobs:
|
||||
POSTGRES_DB: payloadtests
|
||||
|
||||
steps:
|
||||
# https://github.com/actions/virtual-environments/issues/1187
|
||||
- name: tune linux network
|
||||
run: sudo ethtool -K eth0 tx off rx off
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node@${{ env.NODE_VERSION }}
|
||||
uses: actions/setup-node@v4
|
||||
- name: Node setup
|
||||
uses: ./.github/actions/setup
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: ${{ env.PNPM_VERSION }}
|
||||
run_install: false
|
||||
pnpm-version: ${{ env.PNPM_VERSION }}
|
||||
pnpm-run-install: false
|
||||
pnpm-restore-cache: false # Full build is restored below
|
||||
pnpm-install-cache-key: pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
|
||||
- name: Restore build
|
||||
uses: actions/cache@v4
|
||||
timeout-minutes: 10
|
||||
with:
|
||||
path: ./*
|
||||
key: ${{ github.sha }}-${{ github.run_number }}
|
||||
@@ -479,28 +458,23 @@ jobs:
|
||||
pnpm runts scripts/build-template-with-local-pkgs.ts ${{ matrix.template }} $POSTGRES_URL
|
||||
|
||||
tests-type-generation:
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
|
||||
runs-on: ubuntu-24.04
|
||||
needs: [changes, build]
|
||||
if: ${{ needs.changes.outputs.needs_tests == 'true' }}
|
||||
steps:
|
||||
# https://github.com/actions/virtual-environments/issues/1187
|
||||
- name: tune linux network
|
||||
run: sudo ethtool -K eth0 tx off rx off
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node@${{ env.NODE_VERSION }}
|
||||
uses: actions/setup-node@v4
|
||||
- name: Node setup
|
||||
uses: ./.github/actions/setup
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: ${{ env.PNPM_VERSION }}
|
||||
run_install: false
|
||||
pnpm-version: ${{ env.PNPM_VERSION }}
|
||||
pnpm-run-install: false
|
||||
pnpm-restore-cache: false # Full build is restored below
|
||||
pnpm-install-cache-key: pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
|
||||
- name: Restore build
|
||||
uses: actions/cache@v4
|
||||
timeout-minutes: 10
|
||||
with:
|
||||
path: ./*
|
||||
key: ${{ github.sha }}-${{ github.run_number }}
|
||||
@@ -514,13 +488,16 @@ jobs:
|
||||
all-green:
|
||||
name: All Green
|
||||
if: always()
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-24.04
|
||||
needs:
|
||||
- lint
|
||||
- build
|
||||
- build-templates
|
||||
- tests-unit
|
||||
- tests-int
|
||||
- tests-e2e
|
||||
- tests-types
|
||||
- tests-type-generation
|
||||
|
||||
steps:
|
||||
- if: ${{ always() && (contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled')) }}
|
||||
@@ -528,7 +505,7 @@ jobs:
|
||||
|
||||
publish-canary:
|
||||
name: Publish Canary
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-24.04
|
||||
if: ${{ needs.all-green.result == 'success' && github.ref_name == 'main' }}
|
||||
needs:
|
||||
- all-green
|
||||
|
||||
105
.github/workflows/post-release-templates.yml
vendored
Normal file
105
.github/workflows/post-release-templates.yml
vendored
Normal file
@@ -0,0 +1,105 @@
|
||||
name: post-release-templates
|
||||
|
||||
on:
|
||||
release:
|
||||
types:
|
||||
- published
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
NODE_VERSION: 22.6.0
|
||||
PNPM_VERSION: 9.7.1
|
||||
DO_NOT_TRACK: 1 # Disable Turbopack telemetry
|
||||
NEXT_TELEMETRY_DISABLED: 1 # Disable Next telemetry
|
||||
|
||||
jobs:
|
||||
update_templates:
|
||||
runs-on: ubuntu-24.04
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
env:
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
POSTGRES_DB: payloadtests
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0 # Needed for tags
|
||||
|
||||
- name: Setup
|
||||
uses: ./.github/actions/setup
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
pnpm-version: ${{ env.PNPM_VERSION }}
|
||||
|
||||
- name: Start PostgreSQL
|
||||
uses: CasperWA/postgresql-action@v1.2
|
||||
with:
|
||||
postgresql version: '14' # See https://hub.docker.com/_/postgres for available versions
|
||||
postgresql db: ${{ env.POSTGRES_DB }}
|
||||
postgresql user: ${{ env.POSTGRES_USER }}
|
||||
postgresql password: ${{ env.POSTGRES_PASSWORD }}
|
||||
|
||||
- name: Wait for PostgreSQL
|
||||
run: sleep 30
|
||||
|
||||
- name: Configure PostgreSQL
|
||||
run: |
|
||||
psql "postgresql://$POSTGRES_USER:$POSTGRES_PASSWORD@localhost:5432/$POSTGRES_DB" -c "CREATE ROLE runner SUPERUSER LOGIN;"
|
||||
psql "postgresql://$POSTGRES_USER:$POSTGRES_PASSWORD@localhost:5432/$POSTGRES_DB" -c "SELECT version();"
|
||||
echo "POSTGRES_URL=postgresql://$POSTGRES_USER:$POSTGRES_PASSWORD@localhost:5432/$POSTGRES_DB" >> $GITHUB_ENV
|
||||
echo "DATABASE_URI=postgresql://$POSTGRES_USER:$POSTGRES_PASSWORD@localhost:5432/$POSTGRES_DB" >> $GITHUB_ENV
|
||||
|
||||
- name: Start MongoDB
|
||||
uses: supercharge/mongodb-github-action@1.11.0
|
||||
with:
|
||||
mongodb-version: 6.0
|
||||
|
||||
- name: Update template lockfiles and migrations
|
||||
run: pnpm script:gen-templates
|
||||
|
||||
- name: Determine Release Tag
|
||||
id: determine_tag
|
||||
run: |
|
||||
if [ "${{ github.event_name }}" == "release" ]; then
|
||||
echo "Using tag from release event: ${{ github.event.release.tag_name }}"
|
||||
echo "release_tag=${{ github.event.release.tag_name }}" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
# pull latest tag from github, must match any version except v2. Should match v3, v4, v99, etc.
|
||||
echo "Fetching latest tag from github..."
|
||||
LATEST_TAG=$(git describe --tags --abbrev=0 --match 'v[0-9]*' --exclude 'v2*')
|
||||
echo "Latest tag: $LATEST_TAG"
|
||||
echo "release_tag=$LATEST_TAG" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
- name: Commit and push changes
|
||||
id: commit
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
set -ex
|
||||
git config --global user.name "github-actions[bot]"
|
||||
git config --global user.email "github-actions[bot]@users.noreply.github.com"
|
||||
|
||||
git diff --name-only
|
||||
|
||||
export BRANCH_NAME=templates/bump-${{ steps.determine_tag.outputs.release_tag }}-$(date +%s)
|
||||
echo "branch=$BRANCH_NAME" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Create pull request
|
||||
uses: peter-evans/create-pull-request@v7
|
||||
with:
|
||||
token: ${{ secrets.GH_TOKEN_POST_RELEASE_TEMPLATES }}
|
||||
labels: 'area: templates'
|
||||
author: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
|
||||
commit-message: 'templates: bump templates for ${{ steps.determine_tag.outputs.release_tag }}'
|
||||
branch: ${{ steps.commit.outputs.branch }}
|
||||
base: main
|
||||
assignees: ${{ github.actor }}
|
||||
title: 'templates: bump for ${{ steps.determine_tag.outputs.release_tag }}'
|
||||
body: |
|
||||
🤖 Automated bump of templates for ${{ steps.determine_tag.outputs.release_tag }}
|
||||
|
||||
Triggered by user: @${{ github.actor }}
|
||||
75
.github/workflows/post-release.yml
vendored
75
.github/workflows/post-release.yml
vendored
@@ -19,12 +19,10 @@ env:
|
||||
|
||||
jobs:
|
||||
post_release:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-24.04
|
||||
if: ${{ github.event_name != 'workflow_dispatch' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: ./.github/actions/release-commenter
|
||||
continue-on-error: true
|
||||
env:
|
||||
@@ -39,69 +37,16 @@ jobs:
|
||||
comment-template: |
|
||||
🚀 This is included in version {release_link}
|
||||
|
||||
update_templates:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
github-releases-to-discord:
|
||||
runs-on: ubuntu-24.04
|
||||
if: ${{ github.event_name != 'workflow_dispatch' }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup
|
||||
uses: ./.github/actions/setup
|
||||
- name: Github Releases To Discord
|
||||
uses: SethCohen/github-releases-to-discord@v1.16.2
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
pnpm-version: ${{ env.PNPM_VERSION }}
|
||||
|
||||
- name: Update template lockfiles and migrations
|
||||
run: pnpm script:gen-templates
|
||||
|
||||
- name: Determine Release Tag
|
||||
id: determine_tag
|
||||
run: |
|
||||
if [ "${{ github.event.inputs.tag }}" != "" ]; then
|
||||
echo "Using tag from input: ${{ github.event.inputs.tag }}"
|
||||
echo "release_tag=${{ github.event.inputs.tag }}" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "Using tag from release event: ${{ github.event.release.tag_name }}"
|
||||
echo "release_tag=${{ github.event.release.tag_name }}" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
- name: Commit and push changes
|
||||
id: commit
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
set -ex
|
||||
git config --global user.name "github-actions[bot]"
|
||||
git config --global user.email "github-actions[bot]@users.noreply.github.com"
|
||||
export BRANCH_NAME=chore/templates-${{ steps.determine_tag.outputs.release_tag }}
|
||||
git checkout -b $BRANCH_NAME
|
||||
git add -A
|
||||
|
||||
# If no files have changed, exit early with success
|
||||
git diff --cached --quiet --exit-code && exit 0
|
||||
|
||||
git commit -m "chore(templates): bump lockfiles after ${{ steps.determine_tag.outputs.release_tag }}"
|
||||
git push origin $BRANCH_NAME
|
||||
echo "committed=true" >> "$GITHUB_OUTPUT"
|
||||
echo "branch=$BRANCH_NAME" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Debug Branches
|
||||
run: |
|
||||
echo "Target Commitish: ${{ github.event.release.target_commitish }}"
|
||||
echo "Branch: ${{ steps.commit.outputs.branch }}"
|
||||
echo "Ref: ${{ github.ref }}"
|
||||
|
||||
- name: Create pull request
|
||||
uses: peter-evans/create-pull-request@v7
|
||||
if: steps.commit.outputs.committed == 'true'
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
labels: 'area: templates'
|
||||
author: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
|
||||
commit-message: 'Automated update after release'
|
||||
branch: ${{ steps.commit.outputs.branch }}
|
||||
base: ${{ github.event_name != 'workflow_dispatch' && github.event.release.target_commitish || github.ref }}
|
||||
title: 'chore(templates): bump lockfiles after ${{ steps.determine_tag.outputs.release_tag }}'
|
||||
body: 'Automated bump of template lockfiles after release ${{ steps.determine_tag.outputs.release_tag }}'
|
||||
webhook_url: ${{ secrets.DISCORD_RELEASES_WEBHOOK_URL }}
|
||||
color: '16777215'
|
||||
username: 'Payload Releases'
|
||||
avatar_url: 'https://l4wlsi8vxy8hre4v.public.blob.vercel-storage.com/discord-bot-logo.png'
|
||||
|
||||
14
.github/workflows/pr-title.yml
vendored
14
.github/workflows/pr-title.yml
vendored
@@ -1,7 +1,7 @@
|
||||
name: pr-title
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
pull_request_target:
|
||||
types:
|
||||
- opened
|
||||
- edited
|
||||
@@ -12,7 +12,7 @@ permissions:
|
||||
jobs:
|
||||
main:
|
||||
name: lint-pr-title
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: amannn/action-semantic-pull-request@v5
|
||||
id: lint_pr_title
|
||||
@@ -24,14 +24,15 @@ jobs:
|
||||
chore
|
||||
ci
|
||||
docs
|
||||
examples
|
||||
feat
|
||||
fix
|
||||
perf
|
||||
refactor
|
||||
revert
|
||||
style
|
||||
templates
|
||||
test
|
||||
types
|
||||
scopes: |
|
||||
cpa
|
||||
db-\*
|
||||
@@ -106,7 +107,7 @@ jobs:
|
||||
|
||||
label-pr-on-open:
|
||||
name: label-pr-on-open
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-24.04
|
||||
if: github.event.action == 'opened'
|
||||
steps:
|
||||
- name: Tag with 2.x branch with v2
|
||||
@@ -114,8 +115,3 @@ jobs:
|
||||
uses: actions-ecosystem/action-add-labels@v1
|
||||
with:
|
||||
labels: v2
|
||||
- name: Tag with main branch with v3
|
||||
if: github.event.pull_request.base.ref == 'main'
|
||||
uses: actions-ecosystem/action-add-labels@v1
|
||||
with:
|
||||
labels: v3
|
||||
|
||||
5
.github/workflows/release-canary.yml
vendored
5
.github/workflows/release-canary.yml
vendored
@@ -2,8 +2,6 @@ name: release-canary
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
branches:
|
||||
- main
|
||||
|
||||
env:
|
||||
NODE_VERSION: 22.6.0
|
||||
@@ -13,9 +11,10 @@ env:
|
||||
|
||||
jobs:
|
||||
release:
|
||||
name: release-canary-${{ github.ref_name }}-${{ github.sha }}
|
||||
permissions:
|
||||
id-token: write
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
47
.github/workflows/stale.yml
vendored
47
.github/workflows/stale.yml
vendored
@@ -1,11 +1,21 @@
|
||||
name: stale
|
||||
|
||||
on:
|
||||
schedule:
|
||||
# Run nightly at 1am EST
|
||||
- cron: '0 5 * * *'
|
||||
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
dry-run:
|
||||
description: Run the stale action in debug-only mode
|
||||
default: false
|
||||
required: false
|
||||
type: boolean
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-24.04
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
@@ -13,30 +23,35 @@ jobs:
|
||||
- uses: actions/stale@v9
|
||||
id: stale
|
||||
with:
|
||||
debug-only: true
|
||||
days-before-stale: 90
|
||||
debug-only: ${{ inputs.dry-run || false }}
|
||||
|
||||
days-before-stale: 23
|
||||
days-before-close: 7
|
||||
days-before-pr-close: -1 # Never close PRs
|
||||
ascending: true
|
||||
operations-per-run: 300
|
||||
|
||||
# Ignore all assigned
|
||||
exempt-all-assignees: false
|
||||
|
||||
# Issues
|
||||
stale-issue-label: 'stale'
|
||||
exempt-issue-labels: 'blocked,must,should,keep,created-by: Payload team,created-by: Contributor'
|
||||
stale-issue-message: >
|
||||
This issue has been marked as stale due to lack of activity. To keep the ticket open, please indicate that it is still relevant in a comment below.
|
||||
close-issue-message: >
|
||||
stale-issue-label: stale
|
||||
exempt-issue-labels: 'prioritized,keep,created-by: Payload team,created-by: Contributor,status: verified'
|
||||
stale-issue-message: |
|
||||
This issue has been marked as stale due to lack of activity.
|
||||
|
||||
To keep this issue open, please indicate that it is still relevant in a comment below.
|
||||
|
||||
close-issue-message: |
|
||||
This issue was automatically closed due to lack of activity.
|
||||
|
||||
# Pull Requests
|
||||
stale-pr-label: 'stale'
|
||||
exempt-pr-labels: 'blocked,must,should,keep,created-by: Payload team,created-by: Contributor'
|
||||
stale-pr-message: >
|
||||
This PR is stale due to lack of activity. To keep the PR open, please indicate that it is still relevant in a comment below.
|
||||
close-pr-message: >
|
||||
stale-pr-label: stale
|
||||
exempt-pr-labels: 'prioritized,keep,created-by: Payload team,created-by: Contributor'
|
||||
stale-pr-message: |
|
||||
This PR is stale due to lack of activity.
|
||||
|
||||
To keep the PR open, please indicate that it is still relevant in a comment below.
|
||||
close-pr-message: |
|
||||
This pull request was automatically closed due to lack of activity.
|
||||
|
||||
# TODO: Add a step to notify team
|
||||
- name: Print outputs
|
||||
run: echo ${{ format('{0},{1}', toJSON(steps.stale.outputs.staled-issues-prs), toJSON(steps.stale.outputs.closed-issues-prs)) }}
|
||||
|
||||
10
.github/workflows/triage.yml
vendored
10
.github/workflows/triage.yml
vendored
@@ -1,7 +1,7 @@
|
||||
name: triage
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
pull_request_target:
|
||||
types:
|
||||
- opened
|
||||
issues:
|
||||
@@ -18,7 +18,7 @@ permissions:
|
||||
|
||||
jobs:
|
||||
debug-context:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: View context attributes
|
||||
uses: actions/github-script@v7
|
||||
@@ -27,11 +27,10 @@ jobs:
|
||||
|
||||
label-created-by:
|
||||
name: label-on-open
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Tag with 'created-by'
|
||||
uses: actions/github-script@v7
|
||||
if: github.event.action == 'opened'
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
@@ -89,10 +88,11 @@ jobs:
|
||||
triage:
|
||||
name: initial-triage
|
||||
if: github.event_name == 'issues'
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.base.ref }}
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
- uses: ./.github/actions/triage
|
||||
with:
|
||||
|
||||
4463
CHANGELOG.md
4463
CHANGELOG.md
File diff suppressed because it is too large
Load Diff
42
README.md
42
README.md
@@ -1,4 +1,4 @@
|
||||
<a href="https://payloadcms.com"><img width="100%" src="https://github.com/payloadcms/payload/blob/main/packages/payload/src/assets/images/github-banner-nextjs-native.jpg" alt="Payload headless CMS Admin panel built with React" /></a>
|
||||
<a href="https://payloadcms.com"><img width="100%" src="https://l4wlsi8vxy8hre4v.public.blob.vercel-storage.com/github-banner-new-logo.jpg" alt="Payload headless CMS Admin panel built with React" /></a>
|
||||
<br />
|
||||
<br />
|
||||
|
||||
@@ -9,18 +9,20 @@
|
||||
|
||||
<a href="https://www.npmjs.com/package/payload"><img alt="npm" src="https://img.shields.io/npm/dw/payload?style=flat-square" /></a>
|
||||
|
||||
<a href="https://github.com/payloadcms/payload/graphs/contributors"><img alt="npm" src="https://img.shields.io/github/contributors-anon/payloadcms/payload?color=yellow&style=flat-square" /></a>
|
||||
|
||||
<a href="https://www.npmjs.com/package/payload"><img alt="npm" src="https://img.shields.io/npm/v/payload?style=flat-square" /></a>
|
||||
|
||||
<a href="https://twitter.com/payloadcms"><img src="https://img.shields.io/badge/follow-payloadcms-1DA1F2?logo=twitter&style=flat-square" alt="Payload Twitter" /></a>
|
||||
</p>
|
||||
<hr/>
|
||||
<h4>
|
||||
<a target="_blank" href="https://payloadcms.com/docs/beta/getting-started/what-is-payload" rel="dofollow"><strong>Explore the Docs</strong></a> · <a target="_blank" href="https://payloadcms.com/community-help" rel="dofollow"><strong>Community Help</strong></a> · <a target="_blank" href="https://github.com/payloadcms/payload/discussions/1539" rel="dofollow"><strong>Roadmap</strong></a> · <a target="_blank" href="https://www.g2.com/products/payload-cms/reviews#reviews" rel="dofollow"><strong>View G2 Reviews</strong></a>
|
||||
<a target="_blank" href="https://payloadcms.com/docs/getting-started/what-is-payload" rel="dofollow"><strong>Explore the Docs</strong></a> · <a target="_blank" href="https://payloadcms.com/community-help" rel="dofollow"><strong>Community Help</strong></a> · <a target="_blank" href="https://github.com/payloadcms/payload/discussions/1539" rel="dofollow"><strong>Roadmap</strong></a> · <a target="_blank" href="https://www.g2.com/products/payload-cms/reviews#reviews" rel="dofollow"><strong>View G2 Reviews</strong></a>
|
||||
</h4>
|
||||
<hr/>
|
||||
|
||||
> [!IMPORTANT]
|
||||
> 🚨 <strong>We're about to release 3.0 stable.</strong> Star this repo or keep an eye on it to follow along.
|
||||
> 🎉 <strong>We've released 3.0!</strong> Star this repo or keep an eye on it to follow along.
|
||||
|
||||
Payload is the first-ever Next.js native CMS that can install directly in your existing `/app` folder. It's the start of a new era for headless CMS.
|
||||
|
||||
@@ -38,25 +40,25 @@ Payload is the first-ever Next.js native CMS that can install directly in your e
|
||||
|
||||
## Quickstart
|
||||
|
||||
Before beginning to work with Payload, make sure you have all of the [required software](https://payloadcms.com/docs/beta/getting-started/installation).
|
||||
Before beginning to work with Payload, make sure you have all of the [required software](https://payloadcms.com/docs/getting-started/installation).
|
||||
|
||||
```text
|
||||
pnpx create-payload-app@beta
|
||||
pnpx create-payload-app@latest
|
||||
```
|
||||
|
||||
**If you're new to Payload, you should start with the 3.0 beta website template** (`pnpx create-payload-app@beta -t website`). It shows how to do _everything_ - including custom Rich Text blocks, on-demand revalidation, live preview, and more. It comes with a frontend built with Tailwind all in one `/app` folder.
|
||||
**If you're new to Payload, you should start with the website template** (`pnpx create-payload-app@latest -t website`). It shows how to do _everything_ - including custom Rich Text blocks, on-demand revalidation, live preview, and more. It comes with a frontend built with Tailwind all in one `/app` folder.
|
||||
|
||||
## One-click templates
|
||||
|
||||
Jumpstart your next project by starting with a pre-made template. These are production-ready, end-to-end solutions designed to get you to market as fast as possible.
|
||||
|
||||
### [🌐 Website](https://github.com/payloadcms/payload/tree/beta/templates/website)
|
||||
### [🌐 Website](https://github.com/payloadcms/payload/tree/main/templates/website)
|
||||
|
||||
Build any kind of website, blog, or portfolio from small to enterprise. Comes with a fully functional front-end built with RSCs and Tailwind.
|
||||
|
||||
We're constantly adding more templates to our [Templates Directory](https://github.com/payloadcms/payload/tree/beta/templates). If you maintain your own template, consider adding the `payload-template` topic to your GitHub repository for others to find.
|
||||
We're constantly adding more templates to our [Templates Directory](https://github.com/payloadcms/payload/tree/main/templates). If you maintain your own template, consider adding the `payload-template` topic to your GitHub repository for others to find.
|
||||
|
||||
- [Official Templates](https://github.com/payloadcms/payload/tree/beta/templates)
|
||||
- [Official Templates](https://github.com/payloadcms/payload/tree/main/templates)
|
||||
- [Community Templates](https://github.com/topics/payload-template)
|
||||
|
||||
## ✨ Features
|
||||
@@ -66,15 +68,15 @@ We're constantly adding more templates to our [Templates Directory](https://gith
|
||||
- Use server components to extend Payload UI
|
||||
- Query your database directly in server components, no need for REST / GraphQL
|
||||
- Fully TypeScript with automatic types for your data
|
||||
- [Auth out of the box](https://payloadcms.com/docs/beta/authentication/overview)
|
||||
- [Versions and drafts](https://payloadcms.com/docs/beta/versions/overview)
|
||||
- [Localization](https://payloadcms.com/docs/beta/configuration/localization)
|
||||
- [Block-based kayout builder](https://payloadcms.com/docs/beta/fields/blocks)
|
||||
- [Customizable React admin](https://payloadcms.com/docs/beta/admin/overview)
|
||||
- [Lexical rich text editor](https://payloadcms.com/docs/beta/fields/rich-text)
|
||||
- [Conditional field logic](https://payloadcms.com/docs/beta/fields/overview#conditional-logic)
|
||||
- Extremely granular [Access Control](https://payloadcms.com/docs/beta/access-control/overview)
|
||||
- [Document and field-level hooks](https://payloadcms.com/docs/beta/hooks/overview) for every action Payload provides
|
||||
- [Auth out of the box](https://payloadcms.com/docs/authentication/overview)
|
||||
- [Versions and drafts](https://payloadcms.com/docs/versions/overview)
|
||||
- [Localization](https://payloadcms.com/docs/configuration/localization)
|
||||
- [Block-based layout builder](https://payloadcms.com/docs/fields/blocks)
|
||||
- [Customizable React admin](https://payloadcms.com/docs/admin/overview)
|
||||
- [Lexical rich text editor](https://payloadcms.com/docs/fields/rich-text)
|
||||
- [Conditional field logic](https://payloadcms.com/docs/fields/overview#conditional-logic)
|
||||
- Extremely granular [Access Control](https://payloadcms.com/docs/access-control/overview)
|
||||
- [Document and field-level hooks](https://payloadcms.com/docs/hooks/overview) for every action Payload provides
|
||||
- Intensely fast API
|
||||
- Highly secure thanks to HTTP-only cookies, CSRF protection, and more
|
||||
|
||||
@@ -82,9 +84,9 @@ We're constantly adding more templates to our [Templates Directory](https://gith
|
||||
|
||||
## 🗒️ Documentation
|
||||
|
||||
Check out the [Payload website](https://payloadcms.com/docs/beta/getting-started/what-is-payload) to find in-depth documentation for everything that Payload offers.
|
||||
Check out the [Payload website](https://payloadcms.com/docs/getting-started/what-is-payload) to find in-depth documentation for everything that Payload offers.
|
||||
|
||||
Migrating from v1 to v2? Check out the [2.0 Release Notes](https://github.com/payloadcms/payload/releases/tag/v2.0.0) on how to do it.
|
||||
Migrating from v2 to v3? Check out the [3.0 Migration Guide](https://github.com/payloadcms/payload/blob/main/docs/migration-guide/overview.mdx) on how to do it.
|
||||
|
||||
## 🙋 Contributing
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ desc: With Collection-level Access Control you can define which users can create
|
||||
keywords: collections, access control, permissions, documentation, Content Management System, cms, headless, javascript, node, react, nextjs
|
||||
---
|
||||
|
||||
Collection Access Control is [Access Control](../access-control) used to restrict access to Documents within a [Collection](../collections/overview), as well as what they can and cannot see within the [Admin Panel](../admin/overview) as it relates to that Collection.
|
||||
Collection Access Control is [Access Control](../access-control/overview) used to restrict access to Documents within a [Collection](../getting-started/concepts#collections), as well as what they can and cannot see within the [Admin Panel](../admin/overview) as it relates to that Collection.
|
||||
|
||||
To add Access Control to a Collection, use the `access` property in your [Collection Config](../configuration/collections):
|
||||
|
||||
@@ -259,7 +259,7 @@ The following arguments are provided to the `delete` function:
|
||||
|
||||
### Admin
|
||||
|
||||
If the Collection is use to access the [Admin Panel](../admin/overview#the-admin-user-collection), the `Admin` Access Control function determines whether or not the currently logged in user can access the admin UI.
|
||||
If the Collection is used to access the [Admin Panel](../admin/overview#the-admin-user-collection), the `Admin` Access Control function determines whether or not the currently logged in user can access the admin UI.
|
||||
|
||||
To add Admin Access Control to a Collection, use the `admin` property in the [Collection Config](../collections/overview):
|
||||
|
||||
@@ -315,7 +315,7 @@ The following arguments are provided to the `unlock` function:
|
||||
|
||||
If the Collection has [Versions](../versions/overview) enabled, the `readVersions` Access Control function determines whether or not the currently logged in user can access the version history of a Document.
|
||||
|
||||
To add Read Versions Access Control to a Collection, use the `readVersions` property in the [Collection Config](../collections/overview):
|
||||
To add Read Versions Access Control to a Collection, use the `readVersions` property in the [Collection Config](../configuration/collections):
|
||||
|
||||
```ts
|
||||
import type { CollectionConfig } from 'payload'
|
||||
|
||||
@@ -6,7 +6,7 @@ desc: Field-level Access Control is specified within a field's config, and allow
|
||||
keywords: fields, access control, permissions, documentation, Content Management System, cms, headless, javascript, node, react, nextjs
|
||||
---
|
||||
|
||||
Field Access Control is [Access Control](../access-control) used to restrict access to specific [Fields](../fields/overview) within a Document.
|
||||
Field Access Control is [Access Control](../overview) used to restrict access to specific [Fields](../fields/overview) within a Document.
|
||||
|
||||
To add Access Control to a Field, use the `access` property in your [Field Config](../fields/overview):
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ desc: Global-level Access Control is specified within each Global's `access` pro
|
||||
keywords: globals, access control, permissions, documentation, Content Management System, cms, headless, javascript, node, react, nextjs
|
||||
---
|
||||
|
||||
Global Access Control is [Access Control](../access-control) used to restrict access to [Global](../globals/overview) Documents, as well as what they can and cannot see within the [Admin Panel](../admin/overview) as it relates to that Global.
|
||||
Global Access Control is [Access Control](../overview) used to restrict access to [Global](../globals/overview) Documents, as well as what they can and cannot see within the [Admin Panel](../admin/overview) as it relates to that Global.
|
||||
|
||||
To add Access Control to a Global, use the `access` property in your [Global Config](../configuration/globals):
|
||||
|
||||
|
||||
@@ -25,24 +25,24 @@ export const MyCollection: CollectionConfig = {
|
||||
|
||||
The following options are available:
|
||||
|
||||
| Option | Description |
|
||||
| -------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`group`** | Text used as a label for grouping Collection and Global links together in the navigation. |
|
||||
| **`hidden`** | Set to true or a function, called with the current user, returning true to exclude this Collection from navigation and admin routing. |
|
||||
| **`hooks`** | Admin-specific hooks for this Collection. [More details](../hooks/collections). |
|
||||
| **`useAsTitle`** | Specify a top-level field to use for a document title throughout the Admin Panel. If no field is defined, the ID of the document is used as the title. A field with `virtual: true` cannot be used as the title. |
|
||||
| Option | Description |
|
||||
| -------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`group`** | Text or localization object used to group Collection and Global links in the admin navigation. Set to `false` to hide the link from the navigation while keeping its routes accessible. |
|
||||
| **`hidden`** | Set to true or a function, called with the current user, returning true to exclude this Collection from navigation and admin routing. |
|
||||
| **`hooks`** | Admin-specific hooks for this Collection. [More details](../hooks/collections). |
|
||||
| **`useAsTitle`** | Specify a top-level field to use for a document title throughout the Admin Panel. If no field is defined, the ID of the document is used as the title. A field with `virtual: true` cannot be used as the title. |
|
||||
| **`description`** | Text to display below the Collection label in the List View to give editors more information. Alternatively, you can use the `admin.components.Description` to render a React component. [More details](#custom-components). |
|
||||
| **`defaultColumns`** | Array of field names that correspond to which columns to show by default in this Collection's List View. |
|
||||
| **`hideAPIURL`** | Hides the "API URL" meta field while editing documents within this Collection. |
|
||||
| **`enableRichTextLink`** | The [Rich Text](../fields/rich-text) field features a `Link` element which allows for users to automatically reference related documents within their rich text. Set to `true` by default. |
|
||||
| **`enableRichTextRelationship`** | The [Rich Text](../fields/rich-text) field features a `Relationship` element which allows for users to automatically reference related documents within their rich text. Set to `true` by default. |
|
||||
| **`meta`** | Page metadata overrides to apply to this Collection within the Admin Panel. [More details](./metadata). |
|
||||
| **`preview`** | Function to generate preview URLs within the Admin Panel that can point to your app. [More details](#preview). |
|
||||
| **`livePreview`** | Enable real-time editing for instant visual feedback of your front-end application. [More details](../live-preview/overview). |
|
||||
| **`defaultColumns`** | Array of field names that correspond to which columns to show by default in this Collection's List View. |
|
||||
| **`hideAPIURL`** | Hides the "API URL" meta field while editing documents within this Collection. |
|
||||
| **`enableRichTextLink`** | The [Rich Text](../fields/rich-text) field features a `Link` element which allows for users to automatically reference related documents within their rich text. Set to `true` by default. |
|
||||
| **`enableRichTextRelationship`** | The [Rich Text](../fields/rich-text) field features a `Relationship` element which allows for users to automatically reference related documents within their rich text. Set to `true` by default. |
|
||||
| **`meta`** | Page metadata overrides to apply to this Collection within the Admin Panel. [More details](./metadata). |
|
||||
| **`preview`** | Function to generate preview URLs within the Admin Panel that can point to your app. [More details](#preview). |
|
||||
| **`livePreview`** | Enable real-time editing for instant visual feedback of your front-end application. [More details](../live-preview/overview). |
|
||||
| **`components`** | Swap in your own React components to be used within this Collection. [More details](#custom-components). |
|
||||
| **`listSearchableFields`** | Specify which fields should be searched in the List search view. [More details](#list-searchable-fields). |
|
||||
| **`pagination`** | Set pagination-specific options for this Collection. [More details](#pagination). |
|
||||
| **`baseListFilter`** | You can define a default base filter for this collection's List view, which will be merged into any filters that the user performs. |
|
||||
| **`listSearchableFields`** | Specify which fields should be searched in the List search view. [More details](#list-searchable-fields). |
|
||||
| **`pagination`** | Set pagination-specific options for this Collection. [More details](#pagination). |
|
||||
| **`baseListFilter`** | You can define a default base filter for this collection's List view, which will be merged into any filters that the user performs. |
|
||||
|
||||
### Custom Components
|
||||
|
||||
@@ -108,12 +108,20 @@ export const Posts: CollectionConfig = {
|
||||
}
|
||||
```
|
||||
|
||||
The `preview` property resolves to a string that points to your front-end application with additional URL parameters. This can be an absolute URL or a relative path.
|
||||
|
||||
The preview function receives two arguments:
|
||||
|
||||
| Argument | Description |
|
||||
| --- | --- |
|
||||
| **`doc`** | The Document being edited. |
|
||||
| **`ctx`** | An object containing `locale` and `token` properties. The `token` is the currently logged-in user's JWT. |
|
||||
| **`ctx`** | An object containing `locale`, `token`, and `req` properties. The `token` is the currently logged-in user's JWT. |
|
||||
|
||||
If your application requires a fully qualified URL, such as within deploying to Vercel Preview Deployments, you can use the `req` property to build this URL:
|
||||
|
||||
```ts
|
||||
preview: (doc, { req }) => `${req.protocol}//${req.host}/${doc.slug}` // highlight-line
|
||||
```
|
||||
|
||||
<Banner type="success">
|
||||
<strong>Note:</strong>
|
||||
|
||||
@@ -8,7 +8,7 @@ keywords: admin, components, custom, documentation, Content Management System, c
|
||||
|
||||
The Payload [Admin Panel](./overview) is designed to be as minimal and straightforward as possible to allow for easy customization and full control over the UI. In order for Payload to support this level of customization, Payload provides a pattern for you to supply your own React components through your [Payload Config](../configuration/overview).
|
||||
|
||||
All Custom Components in Payload are [React Server Components](https://react.dev/reference/rsc/server-components) by default, with the exception of [Custom Providers](#custom-providers). This enables the use of the [Local API](../local-api/overview) directly on the front-end. Custom Components are available for nearly every part of the Admin Panel for extreme granularity and control.
|
||||
All Custom Components in Payload are [React Server Components](https://react.dev/reference/rsc/server-components) by default. This enables the use of the [Local API](../local-api/overview) directly on the front-end. Custom Components are available for nearly every part of the Admin Panel for extreme granularity and control.
|
||||
|
||||
<Banner type="success">
|
||||
<strong>Note:</strong>
|
||||
@@ -151,7 +151,7 @@ export default buildConfig({
|
||||
|
||||
## Building Custom Components
|
||||
|
||||
All Custom Components in Payload are [React Server Components](https://react.dev/reference/rsc/server-components) by default, with the exception of [Custom Providers](#custom-providers). This enables the use of the [Local API](../local-api/overview) directly on the front-end, among other things.
|
||||
All Custom Components in Payload are [React Server Components](https://react.dev/reference/rsc/server-components) by default. This enables the use of the [Local API](../local-api/overview) directly on the front-end, among other things.
|
||||
|
||||
### Default Props
|
||||
|
||||
@@ -185,7 +185,7 @@ Each Custom Component receives the following props by default:
|
||||
|
||||
<Banner type="warning">
|
||||
<strong>Reminder:</strong>
|
||||
All Custom Components also receive various other props that are specific component being rendered. See [Root Components](#root-components), [Collection Components](#collection-components), [Global Components](#global-components), or [Field Components](#custom-field-components) for a complete list of all default props per component.
|
||||
All Custom Components also receive various other props that are specific component being rendered. See [Root Components](#root-components), [Collection Components](./collections#custom-components), [Global Components](./globals#custom-components), or [Field Components](./fields#custom-components) for a complete list of all default props per component.
|
||||
</Banner>
|
||||
|
||||
### Custom Props
|
||||
@@ -445,7 +445,7 @@ Then to colorize your Custom Component's background, for example, you can use th
|
||||
Payload also exports its [SCSS](https://sass-lang.com) library for reuse which includes mixins, etc. To use this, simply import it as follows into your `.scss` file:
|
||||
|
||||
```scss
|
||||
@import '~payload/scss';
|
||||
@import '~@payloadcms/ui/scss';
|
||||
|
||||
.my-component {
|
||||
@include mid-break {
|
||||
@@ -546,5 +546,5 @@ export const useMyCustomContext = () => useContext(MyCustomContext)
|
||||
```
|
||||
|
||||
<Banner type="warning">
|
||||
<strong>Reminder:</strong> Custom Providers are by definition Client Components. This means they must include the `use client` directive at the top of their files and cannot use server-only code.
|
||||
<strong>Reminder:</strong>React Context exists only within Client Components. This means they must include the `use client` directive at the top of their files and cannot contain server-only code. To use a Server Component here, simply _wrap_ your Client Component with it.
|
||||
</Banner>
|
||||
|
||||
@@ -8,7 +8,7 @@ keywords: admin, css, scss, documentation, Content Management System, cms, headl
|
||||
|
||||
Customizing the Payload [Admin Panel](./overview) through CSS alone is one of the easiest and most powerful ways to customize the look and feel of the dashboard. To allow for this level of customization, Payload:
|
||||
|
||||
1. Exposes a [root-level stylesheet](#global-css) for you to easily to inject custom selectors
|
||||
1. Exposes a [root-level stylesheet](#global-css) for you to inject custom selectors
|
||||
1. Provides a [CSS library](#css-library) that can be easily overridden or extended
|
||||
1. Uses [BEM naming conventions](http://getbem.com) so that class names are globally accessible
|
||||
|
||||
@@ -30,7 +30,7 @@ Here is an example of how you might target the Dashboard View and change the bac
|
||||
|
||||
<Banner type="warning">
|
||||
<strong>Note:</strong>
|
||||
If you are building [Custom Components](./overview), it is best to import your own stylesheets directly into your components, rather than using the global stylesheet. You can continue to use the [CSS library](#css-library) as needed.
|
||||
If you are building [Custom Components](./components), it is best to import your own stylesheets directly into your components, rather than using the global stylesheet. You can continue to use the [CSS library](#css-library) as needed.
|
||||
</Banner>
|
||||
|
||||
### Specificity rules
|
||||
|
||||
@@ -50,7 +50,7 @@ The following options are available:
|
||||
| **`style`** | [CSS Properties](https://developer.mozilla.org/en-US/docs/Web/CSS) to inject into the root element of the field. |
|
||||
| **`className`** | Attach a [CSS class attribute](https://developer.mozilla.org/en-US/docs/Web/CSS/Class_selectors) to the root DOM element of a field. |
|
||||
| **`readOnly`** | Setting a field to `readOnly` has no effect on the API whatsoever but disables the admin component's editability to prevent editors from modifying the field's value. |
|
||||
| **`disabled`** | If a field is `disabled`, it is completely omitted from the [Admin Panel](../admin/overview). |
|
||||
| **`disabled`** | If a field is `disabled`, it is completely omitted from the [Admin Panel](../admin/overview) entirely. |
|
||||
| **`disableBulkEdit`** | Set `disableBulkEdit` to `true` to prevent fields from appearing in the select options when making edits for multiple documents. Defaults to `true` for UI fields. |
|
||||
| **`disableListColumn`** | Set `disableListColumn` to `true` to prevent fields from appearing in the list view column selector. |
|
||||
| **`disableListFilter`** | Set `disableListFilter` to `true` to prevent fields from appearing in the list view filter options. |
|
||||
@@ -66,7 +66,7 @@ A description can be configured in three ways:
|
||||
- As a function which returns a string. [More details](#description-functions).
|
||||
- As a React component. [More details](#description).
|
||||
|
||||
To easily add a Custom Description to a field, use the `admin.description` property in your [Field Config](../fields/overview):
|
||||
To add a Custom Description to a field, use the `admin.description` property in your [Field Config](../fields/overview):
|
||||
|
||||
```ts
|
||||
import type { SanitizedCollectionConfig } from 'payload'
|
||||
@@ -95,7 +95,7 @@ export const MyCollectionConfig: SanitizedCollectionConfig = {
|
||||
|
||||
Custom Descriptions can also be defined as a function. Description Functions are executed on the server and can be used to format simple descriptions based on the user's current [Locale](../configuration/localization).
|
||||
|
||||
To easily add a Description Function to a field, set the `admin.description` property to a _function_ in your [Field Config](../fields/overview):
|
||||
To add a Description Function to a field, set the `admin.description` property to a _function_ in your [Field Config](../fields/overview):
|
||||
|
||||
```ts
|
||||
import type { SanitizedCollectionConfig } from 'payload'
|
||||
@@ -173,7 +173,7 @@ Within the [Admin Panel](./overview), fields are represented in three distinct p
|
||||
- [Cell](#cell) - The table cell component rendered in the List View.
|
||||
- [Filter](#filter) - The filter component rendered in the List View.
|
||||
|
||||
To easily swap in Field Components with your own, use the `admin.components` property in your [Field Config](../fields/overview):
|
||||
To swap in Field Components with your own, use the `admin.components` property in your [Field Config](../fields/overview):
|
||||
|
||||
```ts
|
||||
import type { CollectionConfig } from 'payload'
|
||||
@@ -211,7 +211,7 @@ The following options are available:
|
||||
|
||||
The Field Component is the actual form field rendered in the Edit View. This is the input that user's will interact with when editing a document.
|
||||
|
||||
To easily swap in your own Field Component, use the `admin.components.Field` property in your [Field Config](../fields/overview):
|
||||
To swap in your own Field Component, use the `admin.components.Field` property in your [Field Config](../fields/overview):
|
||||
|
||||
```ts
|
||||
import type { CollectionConfig } from 'payload'
|
||||
@@ -312,7 +312,7 @@ import type {
|
||||
|
||||
The Cell Component is rendered in the table of the List View. It represents the value of the field when displayed in a table cell.
|
||||
|
||||
To easily swap in your own Cell Component, use the `admin.components.Cell` property in your [Field Config](../fields/overview):
|
||||
To swap in your own Cell Component, use the `admin.components.Cell` property in your [Field Config](../fields/overview):
|
||||
|
||||
```ts
|
||||
import type { Field } from 'payload'
|
||||
@@ -337,11 +337,35 @@ All Cell Components receive the same [Default Field Component Props](#field), pl
|
||||
|
||||
For details on how to build Custom Components themselves, see [Building Custom Components](./components#building-custom-components).
|
||||
|
||||
### Filter
|
||||
|
||||
The Filter Component is the actual input element rendered within the "Filter By" dropdown of the List View used to represent this field when building filters.
|
||||
|
||||
To swap in your own Filter Component, use the `admin.components.Filter` property in your [Field Config](../fields/overview):
|
||||
|
||||
```ts
|
||||
import type { Field } from 'payload'
|
||||
|
||||
export const myField: Field = {
|
||||
name: 'myField',
|
||||
type: 'text',
|
||||
admin: {
|
||||
components: {
|
||||
Filter: '/path/to/MyCustomFilterComponent', // highlight-line
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
All Custom Filter Components receive the same [Default Field Component Props](#field).
|
||||
|
||||
For details on how to build Custom Components themselves, see [Building Custom Components](./components#building-custom-components).
|
||||
|
||||
### Label
|
||||
|
||||
The Label Component is rendered anywhere a field needs to be represented by a label. This is typically used in the Edit View, but can also be used in the List View and elsewhere.
|
||||
|
||||
To easily swap in your own Label Component, use the `admin.components.Label` property in your [Field Config](../fields/overview):
|
||||
To swap in your own Label Component, use the `admin.components.Label` property in your [Field Config](../fields/overview):
|
||||
|
||||
```ts
|
||||
import type { Field } from 'payload'
|
||||
@@ -377,7 +401,7 @@ import type {
|
||||
|
||||
Alternatively to the [Description Property](#the-description-property), you can also use a [Custom Component](./components) as the Field Description. This can be useful when you need to provide more complex feedback to the user, such as rendering dynamic field values or other interactive elements.
|
||||
|
||||
To easily add a Description Component to a field, use the `admin.components.Description` property in your [Field Config](../fields/overview):
|
||||
To add a Description Component to a field, use the `admin.components.Description` property in your [Field Config](../fields/overview):
|
||||
|
||||
```ts
|
||||
import type { SanitizedCollectionConfig } from 'payload'
|
||||
@@ -419,7 +443,7 @@ import type {
|
||||
|
||||
The Error Component is rendered when a field fails validation. It is typically displayed beneath the field input in a visually-compelling style.
|
||||
|
||||
To easily swap in your own Error Component, use the `admin.components.Error` property in your [Field Config](../fields/overview):
|
||||
To swap in your own Error Component, use the `admin.components.Error` property in your [Field Config](../fields/overview):
|
||||
|
||||
```ts
|
||||
import type { Field } from 'payload'
|
||||
|
||||
@@ -25,9 +25,9 @@ export const MyGlobal: GlobalConfig = {
|
||||
|
||||
The following options are available:
|
||||
|
||||
| Option | Description |
|
||||
| ------------- | --------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`group`** | Text used as a label for grouping Collection and Global links together in the navigation. |
|
||||
| Option | Description |
|
||||
| ----------------- | --------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`group`** | Text or localization object used to group Collection and Global links in the admin navigation. Set to `false` to hide the link from the navigation while keeping its routes accessible. |
|
||||
| **`hidden`** | Set to true or a function, called with the current user, returning true to exclude this Global from navigation and admin routing. |
|
||||
| **`components`** | Swap in your own React components to be used within this Global. [More details](#custom-components). |
|
||||
| **`preview`** | Function to generate a preview URL within the Admin Panel for this Global that can point to your app. [More details](#preview). |
|
||||
|
||||
@@ -785,7 +785,7 @@ const Greeting: React.FC = () => {
|
||||
|
||||
## useConfig
|
||||
|
||||
Used to easily retrieve the Payload [Client Config](./components#accessing-the-payload-config).
|
||||
Used to retrieve the Payload [Client Config](./components#accessing-the-payload-config).
|
||||
|
||||
```tsx
|
||||
'use client'
|
||||
|
||||
@@ -184,7 +184,7 @@ export const MyGlobal: GlobalConfig = {
|
||||
meta: {
|
||||
// highlight-end
|
||||
title: 'My Global',
|
||||
description: 'The best
|
||||
description: 'The best admin panel in the world',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -86,20 +86,21 @@ const config = buildConfig({
|
||||
|
||||
The following options are available:
|
||||
|
||||
| Option | Description |
|
||||
|---------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| **`avatar`** | Set account profile picture. Options: `gravatar`, `default` or a custom React component. |
|
||||
| **`autoLogin`** | Used to automate log-in for dev and demonstration convenience. [More details](../authentication/overview). |
|
||||
| **`buildPath`** | Specify an absolute path for where to store the built Admin bundle used in production. Defaults to `path.resolve(process.cwd(), 'build')`. |
|
||||
| **`components`** | Component overrides that affect the entirety of the Admin Panel. [More details](./components). |
|
||||
| **`custom`** | Any custom properties you wish to pass to the Admin Panel. |
|
||||
| **`dateFormat`** | The date format that will be used for all dates within the Admin Panel. Any valid [date-fns](https://date-fns.org/) format pattern can be used. |
|
||||
| **`disable`** | If set to `true`, the entire Admin Panel will be disabled. |
|
||||
| **`livePreview`** | Enable real-time editing for instant visual feedback of your front-end application. [More details](../live-preview/overview). |
|
||||
| **`meta`** | Base metadata to use for the Admin Panel. [More details](./metadata). |
|
||||
| **`routes`** | Replace built-in Admin Panel routes with your own custom routes. [More details](#customizing-routes). |
|
||||
| **`theme`** | Restrict the Admin Panel theme to use only one of your choice. Default is `all`.
|
||||
| **`user`** | The `slug` of the Collection that you want to allow to login to the Admin Panel. [More details](#the-admin-user-collection). |
|
||||
| Option | Description |
|
||||
|--------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| **`avatar`** | Set account profile picture. Options: `gravatar`, `default` or a custom React component. |
|
||||
| **`autoLogin`** | Used to automate log-in for dev and demonstration convenience. [More details](../authentication/overview). |
|
||||
| **`buildPath`** | Specify an absolute path for where to store the built Admin bundle used in production. Defaults to `path.resolve(process.cwd(), 'build')`. |
|
||||
| **`components`** | Component overrides that affect the entirety of the Admin Panel. [More details](./components). |
|
||||
| **`custom`** | Any custom properties you wish to pass to the Admin Panel. |
|
||||
| **`dateFormat`** | The date format that will be used for all dates within the Admin Panel. Any valid [date-fns](https://date-fns.org/) format pattern can be used. |
|
||||
| **`disable`** | If set to `true`, the entire Admin Panel will be disabled. |
|
||||
| **`livePreview`** | Enable real-time editing for instant visual feedback of your front-end application. [More details](../live-preview/overview). |
|
||||
| **`meta`** | Base metadata to use for the Admin Panel. [More details](./metadata). |
|
||||
| **`routes`** | Replace built-in Admin Panel routes with your own custom routes. [More details](#customizing-routes). |
|
||||
| **`suppressHydrationWarning`** | If set to `true`, suppresses React hydration mismatch warnings during the hydration of the root <html> tag. Defaults to `false`. |
|
||||
| **`theme`** | Restrict the Admin Panel theme to use only one of your choice. Default is `all`. |
|
||||
| **`user`** | The `slug` of the Collection that you want to allow to login to the Admin Panel. [More details](#the-admin-user-collection). |
|
||||
|
||||
<Banner type="success">
|
||||
<strong>Reminder:</strong>
|
||||
@@ -143,7 +144,7 @@ It is also possible to allow multiple user types into the Admin Panel with limit
|
||||
- `super-admin` - full access to the Admin Panel to perform any action
|
||||
- `editor` - limited access to the Admin Panel to only manage content
|
||||
|
||||
To do this, add a `roles` or similar field to your auth-enabled Collection, then use the `access.admin` property to grant or deny access based on the value of that field. See [Access Control](/docs/access-control/overview) for full details. For a complete, working example of role-based access control, check out the official [Auth Example](https://github.com/payloadcms/payload/tree/main/examples/auth/payload).
|
||||
To do this, add a `roles` or similar field to your auth-enabled Collection, then use the `access.admin` property to grant or deny access based on the value of that field. See [Access Control](/docs/access-control/overview) for full details. For a complete, working example of role-based access control, check out the official [Auth Example](https://github.com/payloadcms/payload/tree/main/examples/auth).
|
||||
|
||||
## Customizing Routes
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ Payload automatically creates an internally used `payload-preferences` Collectio
|
||||
|
||||
## APIs
|
||||
|
||||
Preferences are available to both [GraphQL](/docs/graphql/overview#preferences) and [REST](/docs/rest-api/overview#) APIs.
|
||||
Preferences are available to both [GraphQL](/docs/graphql/overview#preferences) and [REST](/docs/rest-api/overview#preferences) APIs.
|
||||
|
||||
## Adding or reading Preferences in your own components
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ To swap in your own Custom View, first consult the list of available components,
|
||||
|
||||
Root Views are the main views of the [Admin Panel](./overview). These are views that are scoped directly under the `/admin` route, such as the Dashboard or Account views.
|
||||
|
||||
To easily swap Root Views with your own, or to [create entirely new ones](#adding-new-root-views), use the `admin.components.views` property of your root [Payload Config](../configuration/overview):
|
||||
To swap Root Views with your own, or to [create entirely new ones](#adding-new-root-views), use the `admin.components.views` property of your root [Payload Config](../configuration/overview):
|
||||
|
||||
```ts
|
||||
import { buildConfig } from 'payload'
|
||||
@@ -143,7 +143,7 @@ The above example shows how to add a new [Root View](#root-views), but the patte
|
||||
|
||||
Collection Views are views that are scoped under the `/collections` route, such as the Collection List and Document Edit views.
|
||||
|
||||
To easily swap out Collection Views with your own, or to [create entirely new ones](#adding-new-views), use the `admin.components.views` property of your [Collection Config](../collections/overview):
|
||||
To swap out Collection Views with your own, or to [create entirely new ones](#adding-new-views), use the `admin.components.views` property of your [Collection Config](../collections/overview):
|
||||
|
||||
```ts
|
||||
import type { SanitizedCollectionConfig } from 'payload'
|
||||
@@ -198,7 +198,7 @@ The following options are available:
|
||||
|
||||
Global Views are views that are scoped under the `/globals` route, such as the Document Edit View.
|
||||
|
||||
To easily swap out Global Views with your own or [create entirely new ones](#adding-new-views), use the `admin.components.views` property in your [Global Config](../globals/overview):
|
||||
To swap out Global Views with your own or [create entirely new ones](#adding-new-views), use the `admin.components.views` property in your [Global Config](../globals/overview):
|
||||
|
||||
```ts
|
||||
import type { SanitizedGlobalConfig } from 'payload'
|
||||
@@ -248,7 +248,7 @@ The following options are available:
|
||||
|
||||
Document Views are views that are scoped under the `/collections/:collectionSlug/:id` or the `/globals/:globalSlug` route, such as the Edit View or the API View. All Document Views keep their overall structure across navigation changes, such as their title and tabs, and replace only the content below.
|
||||
|
||||
To easily swap out Document Views with your own, or to [create entirely new ones](#adding-new-document-views), use the `admin.components.views.Edit[key]` property in your [Collection Config](../collections/overview) or [Global Config](../globals/overview):
|
||||
To swap out Document Views with your own, or to [create entirely new ones](#adding-new-document-views), use the `admin.components.views.Edit[key]` property in your [Collection Config](../collections/overview) or [Global Config](../globals/overview):
|
||||
|
||||
```ts
|
||||
import type { SanitizedCollectionConfig } from 'payload'
|
||||
|
||||
@@ -34,8 +34,8 @@ The following options are available:
|
||||
|
||||
| Option | Description |
|
||||
|----------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| **`generateEmailHTML`** | Allows for overriding the HTML within emails that are sent to users indicating how to validate their account. [More details](#generateEmailHTML). |
|
||||
| **`generateEmailSubject`** | Allows for overriding the subject of the email that is sent to users indicating how to validate their account. [More details](#generateEmailSubject). |
|
||||
| **`generateEmailHTML`** | Allows for overriding the HTML within emails that are sent to users indicating how to validate their account. [More details](#generateemailhtml). |
|
||||
| **`generateEmailSubject`** | Allows for overriding the subject of the email that is sent to users indicating how to validate their account. [More details](#generateemailsubject). |
|
||||
|
||||
#### generateEmailHTML
|
||||
|
||||
@@ -111,6 +111,7 @@ The following options are available:
|
||||
|
||||
| Option | Description |
|
||||
|----------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| **`expiration`** | Configure how long password reset tokens remain valid, specified in milliseconds. |
|
||||
| **`generateEmailHTML`** | Allows for overriding the HTML within emails that are sent to users attempting to reset their password. [More details](#generateEmailHTML). |
|
||||
| **`generateEmailSubject`** | Allows for overriding the subject of the email that is sent to users attempting to reset their password. [More details](#generateEmailSubject). |
|
||||
|
||||
|
||||
@@ -273,7 +273,7 @@ const result = await payload.verifyEmail({
|
||||
|
||||
If a user locks themselves out and you wish to deliberately unlock them, you can utilize the Unlock operation. The [Admin Panel](../admin/overview) features an Unlock control automatically for all collections that feature max login attempts, but you can programmatically unlock users as well by using the Unlock operation.
|
||||
|
||||
To restrict who is allowed to unlock users, you can utilize the [`unlock`](../access-control/overview#unlock) access control function.
|
||||
To restrict who is allowed to unlock users, you can utilize the [`unlock`](../access-control/collections#unlock) access control function.
|
||||
|
||||
**Example REST API unlock**:
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ Here are some common use cases of Authentication in your own applications:
|
||||
|
||||
When Authentication is enabled on a [Collection](../configuration/collections), Payload injects all necessary functionality to support the entire user flow. This includes all [auth-related operations](./operations) like account creation, logging in and out, and resetting passwords, all [auth-related emails](./email) like email verification and password reset, as well as any necessary UI to manage users from the Admin Panel.
|
||||
|
||||
To enable Authentication on a Collection, use the `auth` property in the [Collection Config](../configuration/collection#auth):
|
||||
To enable Authentication on a Collection, use the `auth` property in the [Collection Config](../configuration/collections#config-options):
|
||||
|
||||
```ts
|
||||
import type { CollectionConfig } from 'payload'
|
||||
|
||||
@@ -46,6 +46,6 @@ _Creating a new project from an existing repository._
|
||||
|
||||
<Banner type="warning">
|
||||
<strong>Note:</strong> In order to make use of the features of Payload Cloud in your own codebase,
|
||||
you will need to add the [Cloud Plugin](https://github.com/payloadcms/plugin-cloud) to your
|
||||
you will need to add the [Cloud Plugin](https://github.com/payloadcms/payload/tree/main/packages/payload-cloud) to your
|
||||
Payload app.
|
||||
</Banner>
|
||||
|
||||
@@ -28,7 +28,7 @@ Your Payload Cloud project comes with a MongoDB serverless Atlas DB instance or
|
||||
|
||||
Payload Cloud gives you S3 file storage backed by Cloudflare as a CDN, and this plugin extends Payload so that all of your media will be stored in S3 rather than locally.
|
||||
|
||||
AWS Cognito is used for authentication to your S3 bucket. The [Payload Cloud Plugin](https://github.com/payloadcms/plugin-cloud) will automatically pick up these values. These values are only if you'd like to access your files directly, outside of Payload Cloud.
|
||||
AWS Cognito is used for authentication to your S3 bucket. The [Payload Cloud Plugin](https://github.com/payloadcms/payload/tree/main/packages/payload-cloud) will automatically pick up these values. These values are only if you'd like to access your files directly, outside of Payload Cloud.
|
||||
|
||||
### Accessing Files Outside of Payload Cloud
|
||||
|
||||
@@ -98,7 +98,7 @@ From there, you are ready to make updates to your project. When you are ready to
|
||||
|
||||
Projects generated from a template will come pre-configured with the official Cloud Plugin, but if you are using your own repository you will need to add this into your project. To do so, add the plugin to your Payload Config:
|
||||
|
||||
`yarn add @payloadcms/payload-cloud`
|
||||
`pnpm add @payloadcms/payload-cloud`
|
||||
|
||||
```js
|
||||
import { payloadCloudPlugin } from '@payloadcms/payload-cloud'
|
||||
|
||||
@@ -52,7 +52,7 @@ export const Posts: CollectionConfig = {
|
||||
|
||||
<Banner type="success">
|
||||
<strong>Reminder:</strong>
|
||||
For a more complex example, see the [Public Demo](https://github.com/payloadcms/public-demo) source code on GitHub, or the [Templates](https://github.com/payloadcms/payload/tree/main/templates) and [Examples](https://github.com/payloadcms/payload/tree/main/examples) directories in the Payload repository.
|
||||
For more complex examples, see the [Templates](https://github.com/payloadcms/payload/tree/main/templates) and [Examples](https://github.com/payloadcms/payload/tree/main/examples) directories in the Payload repository.
|
||||
</Banner>
|
||||
|
||||
The following options are available:
|
||||
|
||||
@@ -6,7 +6,7 @@ desc: Set up your Global config for your needs by defining fields, adding slugs
|
||||
keywords: globals, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, nextjs
|
||||
---
|
||||
|
||||
Globals are in many ways similar to [Collections](../configuration/collections), except they correspond to only a single Document. You can define as many Globals as your application needs. Each Global Document is stored in the [Database](../database/overview) based on the [Fields](../fields/overview) that you define, and automatically generates a [Local API](../local-api/overview), [REST API](../rest-api/overview), and [GraphQL API](../graphql/overview) used to manage your Documents.
|
||||
Globals are in many ways similar to [Collections](../configuration/collections), except that they correspond to only a single Document. You can define as many Globals as your application needs. Each Global Document is stored in the [Database](../database/overview) based on the [Fields](../fields/overview) that you define, and automatically generates a [Local API](../local-api/overview), [REST API](../rest-api/overview), and [GraphQL API](../graphql/overview) used to manage your Documents.
|
||||
|
||||
Globals are the primary way to structure singletons in Payload, such as a header navigation, site-wide banner alerts, or app-wide localized strings. Each Global can have its own unique [Access Control](../access-control/overview), [Hooks](../hooks/overview), [Admin Options](#admin-options), and more.
|
||||
|
||||
@@ -60,7 +60,7 @@ export const Nav: GlobalConfig = {
|
||||
|
||||
<Banner type="success">
|
||||
<strong>Reminder:</strong>
|
||||
For a more complex example, see the [Public Demo](https://github.com/payloadcms/public-demo) source code on GitHub, or the [Templates](https://github.com/payloadcms/payload/tree/main/templates) and [Examples](https://github.com/payloadcms/payload/tree/main/examples) directories in the Payload repository.
|
||||
For more complex examples, see the [Templates](https://github.com/payloadcms/payload/tree/main/templates) and [Examples](https://github.com/payloadcms/payload/tree/main/examples) directories in the Payload repository.
|
||||
</Banner>
|
||||
|
||||
The following options are available:
|
||||
|
||||
@@ -8,7 +8,7 @@ keywords: internationalization, i18n, config, configuration, documentation, Cont
|
||||
|
||||
The [Admin Panel](../admin/overview) is translated in over [30 languages and counting](https://github.com/payloadcms/payload/tree/main/packages/translations). With I18n, editors can navigate the interface and read API error messages in their preferred language. This is similar to [Localization](./localization), but instead of managing translations for the data itself, you are managing translations for your application's interface.
|
||||
|
||||
By default, Payload comes with preinstalled with English, but you can easily load other languages into your own application. Languages are automatically detected based on the request. If no language was detected, or if the user's language is not yet supported by your application, English will be chosen.
|
||||
By default, Payload comes preinstalled with English, but you can easily load other languages into your own application. Languages are automatically detected based on the request. If no language is detected, or if the user's language is not yet supported by your application, English will be chosen.
|
||||
|
||||
To configure I18n, use the `i18n` key in your [Payload Config](./overview):
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ import { buildConfig } from 'payload'
|
||||
export default buildConfig({
|
||||
// ...
|
||||
localization: {
|
||||
locales: ['en', 'es', 'de'] // required
|
||||
locales: ['en', 'es', 'de'], // required
|
||||
defaultLocale: 'en', // required
|
||||
},
|
||||
})
|
||||
|
||||
@@ -58,7 +58,7 @@ export default buildConfig({
|
||||
|
||||
<Banner type="success">
|
||||
<strong>Note:</strong>
|
||||
For a more complex example, see the [Public Demo](https://github.com/payloadcms/public-demo) source code on GitHub, or the [Templates](https://github.com/payloadcms/payload/tree/main/templates) and [Examples](https://github.com/payloadcms/payload/tree/main/examples) directories in the Payload repository.
|
||||
For more complex examples, see the [Templates](https://github.com/payloadcms/payload/tree/main/templates) and [Examples](https://github.com/payloadcms/payload/tree/main/examples) directories in the Payload repository.
|
||||
</Banner>
|
||||
|
||||
The following options are available:
|
||||
|
||||
@@ -51,12 +51,44 @@ export async function down({ payload, req }: MigrateDownArgs): Promise<void> {
|
||||
|
||||
## Using Transactions
|
||||
|
||||
When migrations are run, each migration is performed in a new [transactions](/docs/database/transactions) for you. All
|
||||
When migrations are run, each migration is performed in a new [transaction](/docs/database/transactions) for you. All
|
||||
you need to do is pass the `req` object to any [local API](/docs/local-api/overview) or direct database calls, such as
|
||||
`payload.db.updateMany()`, to make database changes inside the transaction. Assuming no errors were thrown, the transaction is committed
|
||||
after your `up` or `down` function runs. If the migration errors at any point or fails to commit, it is caught and the
|
||||
transaction gets aborted. This way no change is made to the database if the migration fails.
|
||||
|
||||
### Using database directly with the transaction
|
||||
|
||||
Additionally, you can bypass Payload's layer entirely and perform operations directly on your underlying database within the active transaction:
|
||||
|
||||
### MongoDB:
|
||||
```ts
|
||||
import { type MigrateUpArgs } from '@payloadcms/db-mongodb'
|
||||
|
||||
export async function up({ session, payload, req }: MigrateUpArgs): Promise<void> {
|
||||
const posts = await payload.db.collections.posts.collection.find({ session }).toArray()
|
||||
}
|
||||
```
|
||||
|
||||
### Postgres:
|
||||
```ts
|
||||
import { type MigrateUpArgs, sql } from '@payloadcms/db-postgres'
|
||||
|
||||
export async function up({ db, payload, req }: MigrateUpArgs): Promise<void> {
|
||||
const { rows: posts } = await db.execute(sql`SELECT * from posts`)
|
||||
}
|
||||
```
|
||||
|
||||
### SQLite:
|
||||
In SQLite, transactions are disabled by default. [More](./transactions).
|
||||
```ts
|
||||
import { type MigrateUpArgs, sql } from '@payloadcms/db-sqlite'
|
||||
|
||||
export async function up({ db, payload, req }: MigrateUpArgs): Promise<void> {
|
||||
const { rows: posts } = await db.run(sql`SELECT * from posts`)
|
||||
}
|
||||
```
|
||||
|
||||
## Migrations Directory
|
||||
|
||||
Each DB adapter has an optional property `migrationDir` where you can override where you want your migrations to be
|
||||
|
||||
@@ -60,7 +60,7 @@ export default buildConfig({
|
||||
| `schemaName` (experimental) | A string for the postgres schema to use, defaults to 'public'. |
|
||||
| `idType` | A string of 'serial', or 'uuid' that is used for the data type given to id columns. |
|
||||
| `transactionOptions` | A PgTransactionConfig object for transactions, or set to `false` to disable using transactions. [More details](https://orm.drizzle.team/docs/transactions) |
|
||||
| `disableCreateDatabase` | Pass `true` to disale auto database creation if it doesn't exist. Defaults to `false`. |
|
||||
| `disableCreateDatabase` | Pass `true` to disable auto database creation if it doesn't exist. Defaults to `false`. |
|
||||
| `localesSuffix` | A string appended to the end of table names for storing localized fields. Default is '_locales'. |
|
||||
| `relationshipsSuffix` | A string appended to the end of table names for storing relationships. Default is '_rels'. |
|
||||
| `versionsSuffix` | A string appended to the end of table names for storing versions. Defaults to '_v'. |
|
||||
|
||||
@@ -16,6 +16,12 @@ By default, Payload will use transactions for all data changing operations, as l
|
||||
MongoDB requires a connection to a replicaset in order to make use of transactions.
|
||||
</Banner>
|
||||
|
||||
<Banner type="info">
|
||||
<strong>Note:</strong>
|
||||
<br />
|
||||
Transactions in SQLite are disabled by default. You need to pass `transactionOptions: {}` to enable them.
|
||||
</Banner>
|
||||
|
||||
The initial request made to Payload will begin a new transaction and attach it to the `req.transactionID`. If you have a `hook` that interacts with the database, you can opt in to using the same transaction by passing the `req` in the arguments. For example:
|
||||
|
||||
```ts
|
||||
|
||||
@@ -6,9 +6,7 @@ desc:
|
||||
keywords: example, examples, starter, boilerplate, template, templates
|
||||
---
|
||||
|
||||
Payload provides a vast array of examples to help you get started with your project no matter what you are working on. These examples are designed to be easy to get up and running, and to be easy to understand. They showcase nothing more than the specific features being demonstrated so you can easily decipher what is going on.
|
||||
|
||||
Examples are changing every day, so be sure to check back often to see what new examples have been added. If you have a specific example you would like to see, please feel free to start a new [Discussion](https://github.com/payloadcms/payload/discussions) or open a new [PR](https://github.com/payloadcms/payload/pulls) to add it yourself.
|
||||
Payload provides a vast array of examples to help you get started with your project no matter what you are working on. These examples are designed to be easy to get up and running, and to be easy to understand. They showcase nothing more than the specific features being demonstrated so you can easily decipher precisely what is going on.
|
||||
|
||||
- [Auth](https://github.com/payloadcms/payload/tree/main/examples/auth)
|
||||
- [Custom Components](https://github.com/payloadcms/payload/tree/main/examples/custom-components)
|
||||
@@ -21,16 +19,4 @@ Examples are changing every day, so be sure to check back often to see what new
|
||||
- [Tests](https://github.com/payloadcms/payload/tree/main/examples/testing)
|
||||
- [White-label Admin UI](https://github.com/payloadcms/payload/tree/main/examples/whitelabel)
|
||||
|
||||
When necessary, some examples include a front-end. Examples that require a front-end share this folder structure:
|
||||
|
||||
```plaintext
|
||||
example/
|
||||
├── payload/
|
||||
├── next-app/
|
||||
├── next-pages/
|
||||
├── react-router/
|
||||
├── vue/
|
||||
├── svelte/
|
||||
```
|
||||
|
||||
Where `payload` is your Payload project, and the other directories are dedicated to their respective front-end framework. We are adding new examples every day, so if your framework of choice is not yet supported in any particular example, please feel free to start a new [Discussion](https://github.com/payloadcms/payload/discussions) or open a new [PR](https://github.com/payloadcms/payload/pulls) to add it yourself.
|
||||
We are adding new examples every day, so if your particular use case is not demonstrated in any existing example, please feel free to start a new [Discussion](https://github.com/payloadcms/payload/discussions) or open a new [PR](https://github.com/payloadcms/payload/pulls) to add it yourself.
|
||||
|
||||
@@ -108,7 +108,7 @@ called with an argument object with the following properties:
|
||||
| `id` | The `id` of the current document being edited. `id` is `undefined` during the `create` operation |
|
||||
| `user` | An object containing the currently authenticated user |
|
||||
|
||||
## Example
|
||||
### Example#filter-options-example
|
||||
|
||||
```ts
|
||||
const uploadField = {
|
||||
|
||||
@@ -34,7 +34,7 @@ Hooks allow you to execute your own side effects during specific events of the D
|
||||
|
||||
## Authentication
|
||||
|
||||
Payload provides a secure, portable way to manage user accounts out of the box. Payload Authentication is designed to be used in both the Admin Panel, all well as your own external applications. [More details](../authentication/overview).
|
||||
Payload provides a secure, portable way to manage user accounts out of the box. Payload Authentication is designed to be used in both the Admin Panel, as well as your own external applications. [More details](../authentication/overview).
|
||||
|
||||
## Access Control
|
||||
|
||||
@@ -151,7 +151,7 @@ Whereas Payload itself is responsible for direct database access, and control ov
|
||||
|
||||
`@payloadcms/graphql`
|
||||
|
||||
All of Payload's GraphQL functionality is abstracted into a separate package. Payload, its Admin UI, and REST API have absolutely no overlap with GraphQL, and you will incur no performance overhead from GraphQL if you are not using it. However, it's installed within in the `@payloadcms/next` package so you don't have to install it manually. You do, however, need to have GraphQL installed separately in your `package.json` if you are using GraphQL.
|
||||
All of Payload's GraphQL functionality is abstracted into a separate package. Payload, its Admin UI, and REST API have absolutely no overlap with GraphQL, and you will incur no performance overhead from GraphQL if you are not using it. However, it's installed within the `@payloadcms/next` package so you don't have to install it manually. You do, however, need to have GraphQL installed separately in your `package.json` if you are using GraphQL.
|
||||
|
||||
`@payloadcms/ui`
|
||||
|
||||
|
||||
@@ -10,9 +10,9 @@ keywords: documentation, getting started, guide, Content Management System, cms,
|
||||
|
||||
Payload requires the following software:
|
||||
|
||||
- Any JavaScript package manager (Yarn, NPM, or pnpm - pnpm is preferred)
|
||||
- Any JavaScript package manager (pnpm, npm, or yarn - pnpm is preferred)
|
||||
- Node.js version 20.9.0+
|
||||
- Any [compatible database](/docs/database/overview) (MongoDB or Postgres)
|
||||
- Any [compatible database](/docs/database/overview) (MongoDB, Postgres or SQLite)
|
||||
|
||||
<Banner type="warning">
|
||||
<strong>Important:</strong>
|
||||
@@ -49,7 +49,7 @@ pnpm i payload @payloadcms/next @payloadcms/richtext-lexical sharp graphql
|
||||
|
||||
<Banner type="warning">
|
||||
<strong>Note:</strong>
|
||||
Swap out `pnpm` for your package manager. If you are using NPM, you might need to install using legacy peer deps: `npm i --legacy-peer-deps`.
|
||||
Swap out `pnpm` for your package manager. If you are using npm, you might need to install using legacy peer deps: `npm i --legacy-peer-deps`.
|
||||
</Banner>
|
||||
|
||||
Next, install a [Database Adapter](/docs/database/overview). Payload requires a Database Adapter to establish a database connection. Payload works with all types of databases, but the most common are MongoDB and Postgres.
|
||||
@@ -181,6 +181,6 @@ Once you have a Payload Config, update your `tsconfig` to include a `path` that
|
||||
|
||||
#### 5. Fire it up!
|
||||
|
||||
After you've gotten this far, it's time to boot up Payload. Start your project in your application's folder to get going. By default, the Next.js dev script is `pnpm dev` (or `npm run dev` if using NPM).
|
||||
After you've reached this point, it's time to boot up Payload. Start your project in your application's folder to get going. By default, the Next.js dev script is `pnpm dev` (or `npm run dev` if using npm).
|
||||
|
||||
After it starts, you can go to `http://localhost:3000/admin` to create your first Payload user!
|
||||
|
||||
@@ -24,7 +24,7 @@ keywords: documentation, getting started, guide, Content Management System, cms,
|
||||
|
||||
### Instant backend superpowers
|
||||
|
||||
No matter what you're building, Payload will give you backend superpowers. It can be installed in one line into any existing Next.js app, and is designed to catapult your development process. Payload takes the most complex and time-consuming parts of any modern web app and makes them simple.
|
||||
No matter what you're building, Payload will give you backend superpowers. Your entire Payload config can be installed in one line into any existing Next.js app, and is designed to catapult your development process. Payload takes the most complex and time-consuming parts of any modern web app and makes them simple.
|
||||
|
||||
### Open source - deploy anywhere, including Vercel
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ type Collection1 {
|
||||
|
||||
The above example outputs all your definitions to a file relative from your payload config as `./graphql/schema.graphql`. By default, the file will be output to your current working directory as `schema.graphql`.
|
||||
|
||||
### Adding an NPM script
|
||||
### Adding an npm script
|
||||
|
||||
<Banner type="warning">
|
||||
<strong>Important</strong>
|
||||
@@ -72,7 +72,7 @@ The above example outputs all your definitions to a file relative from your payl
|
||||
|
||||
Payload will automatically try and locate your config, but might not always be able to find it. For example, if you are working in a `/src` directory or similar, you need to tell Payload where to find your config manually by using an environment variable.
|
||||
|
||||
If this applies to you, create an NPM script to make generating types easier:
|
||||
If this applies to you, create an npm script to make generating types easier:
|
||||
|
||||
```json
|
||||
// package.json
|
||||
|
||||
@@ -28,6 +28,8 @@ To pass data between hooks, you can assign values to context in an earlier hook
|
||||
For example:
|
||||
|
||||
```ts
|
||||
import type { CollectionConfig } from 'payload'
|
||||
|
||||
const Customer: CollectionConfig = {
|
||||
slug: 'customers',
|
||||
hooks: {
|
||||
@@ -43,7 +45,6 @@ const Customer: CollectionConfig = {
|
||||
},
|
||||
],
|
||||
afterChange: [
|
||||
|
||||
async ({ context, doc, req }) => {
|
||||
// use context.customerData without needing to fetch it again
|
||||
if (context.customerData.contacted === false) {
|
||||
@@ -65,6 +66,8 @@ Let's say you have an `afterChange` hook, and you want to do a calculation insid
|
||||
Bad example:
|
||||
|
||||
```ts
|
||||
import type { CollectionConfig } from 'payload'
|
||||
|
||||
const Customer: CollectionConfig = {
|
||||
slug: 'customers',
|
||||
hooks: {
|
||||
@@ -92,6 +95,8 @@ Instead of the above, we need to tell the `afterChange` hook to not run again if
|
||||
Fixed example:
|
||||
|
||||
```ts
|
||||
import type { CollectionConfig } from 'payload'
|
||||
|
||||
const MyCollection: CollectionConfig = {
|
||||
slug: 'slug',
|
||||
hooks: {
|
||||
@@ -125,7 +130,7 @@ const MyCollection: CollectionConfig = {
|
||||
|
||||
The default TypeScript interface for `context` is `{ [key: string]: unknown }`. If you prefer a more strict typing in your project or when authoring plugins for others, you can override this using the `declare` syntax.
|
||||
|
||||
This is known as "type augmentation", a TypeScript feature which allows us to add types to existing objects. Simply put this in any `.ts` or `.d.ts` file:
|
||||
This is known as "type augmentation", a TypeScript feature which allows us to add types to existing types. Simply put this in any `.ts` or `.d.ts` file:
|
||||
|
||||
```ts
|
||||
import { RequestContext as OriginalRequestContext } from 'payload'
|
||||
|
||||
@@ -37,7 +37,7 @@ Root Hooks are not associated with any specific Collection, Global, or Field. Th
|
||||
To add Root Hooks, use the `hooks` property in your [Payload Config](/docs/configuration/config):
|
||||
|
||||
```ts
|
||||
import { buildConfig } from 'payload'
|
||||
import { buildConfig } from 'payload'
|
||||
|
||||
export default buildConfig({
|
||||
// ...
|
||||
@@ -60,7 +60,7 @@ The following options are available:
|
||||
The `afterError` Hook is triggered when an error occurs in the Payload application. This can be useful for logging errors to a third-party service, sending an email to the development team, logging the error to Sentry or DataDog, etc. The output can be used to transform the result object / status code.
|
||||
|
||||
```ts
|
||||
import { buildConfig } from 'payload'
|
||||
import { buildConfig } from 'payload'
|
||||
|
||||
export default buildConfig({
|
||||
// ...
|
||||
|
||||
@@ -42,7 +42,7 @@ Examples:
|
||||
|
||||
**Offloading complex operations**
|
||||
|
||||
You may run into the need to perform computationally expensive functions which might slow down your main Payload API server(s). The Jobs Queue allows you to offload these tasks a separate compute resource rather than slowing down the server(s) that run your Payload APIs. With Payload Task definitions, you can even keep large dependencies out of your main Next.js bundle by dynamically importing them only when they are used. This keeps your Next.js + Payload compilation fast and ensures large dependencies do not get bundled into your Payload production build.
|
||||
You may run into the need to perform computationally expensive functions which might slow down your main Payload API server(s). The Jobs Queue allows you to offload these tasks to a separate compute resource rather than slowing down the server(s) that run your Payload APIs. With Payload Task definitions, you can even keep large dependencies out of your main Next.js bundle by dynamically importing them only when they are used. This keeps your Next.js + Payload compilation fast and ensures large dependencies do not get bundled into your Payload production build.
|
||||
|
||||
Examples:
|
||||
|
||||
|
||||
@@ -98,11 +98,24 @@ After the project is deployed to Vercel, the Vercel Cron job will automatically
|
||||
|
||||
If you want to process jobs programmatically from your server-side code, you can use the Local API:
|
||||
|
||||
**Run all jobs:**
|
||||
|
||||
```ts
|
||||
const results = await payload.jobs.run()
|
||||
|
||||
// You can customize the queue name and limit by passing them as arguments:
|
||||
await payload.jobs.run({ queue: 'nightly', limit: 100 })
|
||||
|
||||
// You can provide a where clause to filter the jobs that should be run:
|
||||
await payload.jobs.run({ where: { 'input.message': { equals: 'secret' } } })
|
||||
```
|
||||
|
||||
**Run a single job:**
|
||||
|
||||
```ts
|
||||
const results = await payload.jobs.runByID({
|
||||
id: myJobID
|
||||
})
|
||||
```
|
||||
|
||||
#### Bin script
|
||||
|
||||
@@ -23,14 +23,14 @@ Simply add a task to the `jobs.tasks` array in your Payload config. A task consi
|
||||
| Option | Description |
|
||||
| --------------------------- | -------------------------------------------------------------------------------- |
|
||||
| `slug` | Define a slug-based name for this job. This slug needs to be unique among both tasks and workflows.|
|
||||
| `handler` | The function that should be responsible for running the job. You can either pass a string-based path to the job function file, or the job function itself. If you are using large dependencies within your job, you might prefer to pass the string path because that will avoid bundling large dependencies in your Next.js app. |
|
||||
| `handler` | The function that should be responsible for running the job. You can either pass a string-based path to the job function file, or the job function itself. If you are using large dependencies within your job, you might prefer to pass the string path because that will avoid bundling large dependencies in your Next.js app. Passing a string path is an advanced feature that may require a sophisticated build pipeline in order to work. |
|
||||
| `inputSchema` | Define the input field schema - payload will generate a type for this schema. |
|
||||
| `interfaceName` | You can use interfaceName to change the name of the interface that is generated for this task. By default, this is "Task" + the capitalized task slug. |
|
||||
| `outputSchema` | Define the output field schema - payload will generate a type for this schema. |
|
||||
| `label` | Define a human-friendly label for this task. |
|
||||
| `onFail` | Function to be executed if the task fails. |
|
||||
| `onSuccess` | Function to be executed if the task succeeds. |
|
||||
| `retries` | Specify the number of times that this step should be retried if it fails. |
|
||||
| `retries` | Specify the number of times that this step should be retried if it fails. If this is undefined, the task will either inherit the retries from the workflow or have no retries. If this is 0, the task will not be retried. By default, this is undefined. |
|
||||
|
||||
The logic for the Task is defined in the `handler` - which can be defined as a function, or a path to a function. The `handler` will run once a worker picks picks up a Job that includes this task.
|
||||
|
||||
@@ -93,6 +93,8 @@ export default buildConfig({
|
||||
|
||||
In addition to defining handlers as functions directly provided to your Payload config, you can also pass an _absolute path_ to where the handler is defined. If your task has large dependencies, and you are planning on executing your jobs in a separate process that has access to the filesystem, this could be a handy way to make sure that your Payload + Next.js app remains quick to compile and has minimal dependencies.
|
||||
|
||||
Keep in mind that this is an advanced feature that may require a sophisticated build pipeline, especially when using it in production or within Next.js, e.g. by calling opening the `/api/payload-jobs/run` endpoint. You will have to transpile the handler files separately and ensure they are available in the same location when the job is run. If you're using an endpoint to execute your jobs, it's recommended to define your handlers as functions directly in your Payload Config, or use import paths handlers outside of Next.js.
|
||||
|
||||
In general, this is an advanced use case. Here's how this would look:
|
||||
|
||||
`payload.config.ts:`
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
title: Workflows
|
||||
label: Workflows
|
||||
order: 30
|
||||
desc: A Task is a distinct function declaration that can be run within Payload's Jobs Queue.
|
||||
desc: A Task is a distinct function declaration that can be run within Payload's Jobs Queue.
|
||||
keywords: jobs queue, application framework, typescript, node, react, nextjs
|
||||
---
|
||||
|
||||
@@ -25,11 +25,12 @@ To define a JS-based workflow, simply add a workflow to the `jobs.wokflows` arra
|
||||
| Option | Description |
|
||||
| --------------------------- | -------------------------------------------------------------------------------- |
|
||||
| `slug` | Define a slug-based name for this workflow. This slug needs to be unique among both tasks and workflows.|
|
||||
| `handler` | The function that should be responsible for running the workflow. You can either pass a string-based path to the workflow function file, or workflow job function itself. If you are using large dependencies within your workflow, you might prefer to pass the string path because that will avoid bundling large dependencies in your Next.js app. |
|
||||
| `handler` | The function that should be responsible for running the workflow. You can either pass a string-based path to the workflow function file, or workflow job function itself. If you are using large dependencies within your workflow, you might prefer to pass the string path because that will avoid bundling large dependencies in your Next.js app. Passing a string path is an advanced feature that may require a sophisticated build pipeline in order to work. |
|
||||
| `inputSchema` | Define the input field schema - payload will generate a type for this schema. |
|
||||
| `interfaceName` | You can use interfaceName to change the name of the interface that is generated for this workflow. By default, this is "Workflow" + the capitalized workflow slug. |
|
||||
| `label` | Define a human-friendly label for this workflow. |
|
||||
| `queue` | Optionally, define the queue name that this workflow should be tied to. Defaults to "default". |
|
||||
| `retries` | You can define `retries` on the workflow level, which will enforce that the workflow can only fail up to that number of retries. If a task does not have retries specified, it will inherit the retry count as specified on the workflow. You can specify `0` as `workflow` retries, which will disregard all `task` retry specifications and fail the entire workflow on any task failure. You can leave `workflow` retries as undefined, in which case, the workflow will respect what each task dictates as their own retry count. By default this is undefined, meaning workflows retries are defined by their tasks |
|
||||
|
||||
Example:
|
||||
|
||||
|
||||
@@ -77,7 +77,7 @@ export const MyFeature = createServerFeature({
|
||||
|
||||
This allows you to add i18n translations scoped to your feature. This specific example translation will be available under `lexical:myFeature:label` - `myFeature` being your feature key.
|
||||
|
||||
### Markdown Transformers
|
||||
### Markdown Transformers#server-feature-markdown-transformers
|
||||
|
||||
The Server Feature, just like the Client Feature, allows you to add markdown transformers. Markdown transformers on the server are used when [converting the editor from or to markdown](/docs/lexical/converters#markdown-lexical).
|
||||
|
||||
@@ -120,7 +120,7 @@ export const MyFeature = createServerFeature({
|
||||
|
||||
In this example, the node will be outputted as `+++` in Markdown, and the markdown `+++` will be converted to a `MyNode` node in the editor.
|
||||
|
||||
### Nodes
|
||||
### Nodes#server-feature-nodes
|
||||
|
||||
While nodes added to the server feature do not control how the node is rendered in the editor, they control other aspects of the node:
|
||||
- HTML conversion
|
||||
@@ -266,7 +266,7 @@ export const MyClientFeature = createClientFeature({
|
||||
|
||||
Explore the APIs available through ClientFeature to add the specific functionality you need. Remember, do not import directly from `'@payloadcms/richtext-lexical'` when working on the client-side, as it will cause errors with webpack or turbopack. Instead, use `'@payloadcms/richtext-lexical/client'` for all client-side imports. Type-imports are excluded from this rule and can always be imported.
|
||||
|
||||
### Nodes
|
||||
### Nodes#client-feature-nodes
|
||||
|
||||
Add nodes to the `nodes` array in **both** your client & server feature. On the server side, nodes are utilized for backend operations like HTML conversion in a headless editor. On the client side, these nodes are integral to how content is displayed and managed in the editor, influencing how they are rendered, behave, and saved in the database.
|
||||
|
||||
@@ -705,7 +705,7 @@ export const MyClientFeature = createClientFeature({
|
||||
| **`keywords`** | Keywords are used to match the item for different texts typed after the '/'. E.g. you might want to show a horizontal rule item if you type both /hr, /separator, /horizontal etc. In addition to the keywords, the label and key will be used to find the right slash menu item. |
|
||||
|
||||
|
||||
### Markdown Transformers
|
||||
### Markdown Transformers#client-feature-markdown-transformers
|
||||
|
||||
The Client Feature, just like the Server Feature, allows you to add markdown transformers. Markdown transformers on the client are used to create new nodes when a certain markdown pattern is typed in the editor.
|
||||
|
||||
|
||||
@@ -6,14 +6,67 @@ desc: Conversion between lexical, markdown and html
|
||||
keywords: lexical, rich text, editor, headless cms, convert, html, mdx, markdown, md, conversion, export
|
||||
---
|
||||
|
||||
Lexical saves data in JSON - this is great for storage and flexibility and allows you to easily to convert it to other formats like JSX, HTML or Markdown.
|
||||
|
||||
## Lexical => JSX
|
||||
|
||||
If you have a React-based frontend, converting lexical to JSX is the recommended way to render rich text content in your frontend. To do that, import the `RichText` component from `@payloadcms/richtext-lexical/react` and pass the lexical content to it:
|
||||
|
||||
```tsx
|
||||
import React from 'react'
|
||||
import { RichText } from '@payloadcms/richtext-lexical/react'
|
||||
|
||||
export const MyComponent = ({ lexicalData }) => {
|
||||
return (
|
||||
<RichText data={lexicalData} />
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
The `RichText` component will come with the most common serializers built-in, though you can also pass in your own serializers if you need to.
|
||||
|
||||
<Banner type="default">
|
||||
The JSX converter expects the input data to be fully populated. When fetching data, ensure the `depth` setting is high enough, to ensure that lexical nodes such as uploads are populated.
|
||||
</Banner>
|
||||
|
||||
### Converting Lexical Blocks to JSX
|
||||
|
||||
In order to convert lexical blocks or inline blocks to JSX, you will have to pass the converter for your block to the RichText component. This converter is not included by default, as Payload doesn't know how to render your custom blocks.
|
||||
|
||||
```tsx
|
||||
import React from 'react'
|
||||
import {
|
||||
type JSXConvertersFunction,
|
||||
RichText,
|
||||
} from '@payloadcms/richtext-lexical/react'
|
||||
import type { SerializedEditorState } from '@payloadcms/richtext-lexical/lexical'
|
||||
|
||||
const jsxConverters: JSXConvertersFunction = ({ defaultConverters }) => ({
|
||||
...defaultConverters,
|
||||
blocks: {
|
||||
// myTextBlock is the slug of the block
|
||||
myTextBlock: ({ node }) => <div style={{ backgroundColor: 'red' }}>{node.fields.text}</div>,
|
||||
},
|
||||
})
|
||||
|
||||
export const MyComponent = ({ lexicalData }) => {
|
||||
return (
|
||||
<RichText
|
||||
converters={jsxConverters}
|
||||
data={lexicalData.lexicalWithBlocks as SerializedEditorState}
|
||||
/>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
## Lexical => HTML
|
||||
|
||||
Lexical saves data in JSON, but can also generate its HTML representation via two main methods:
|
||||
If you don't have a React-based frontend, or if you need to send the content to a third-party service, you can convert lexical to HTML. There are two ways to do this:
|
||||
|
||||
1. **Outputting HTML from the Collection:** Create a new field in your collection to convert saved JSON content to HTML. Payload generates and outputs the HTML for use in your frontend.
|
||||
2. **Generating HTML on any server** Convert JSON to HTML on-demand on the server.
|
||||
|
||||
The editor comes with built-in HTML serializers, simplifying the process of converting JSON to HTML.
|
||||
In both cases, the conversion needs to happen on a server, as the HTML converter will automatically fetch data for nodes that require it (e.g. uploads and internal links). The editor comes with built-in HTML serializers, simplifying the process of converting JSON to HTML.
|
||||
|
||||
### Outputting HTML from the Collection
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ _\* An asterisk denotes that a property is required._
|
||||
|
||||
### URL
|
||||
|
||||
The `url` property is a string that points to your front-end application. This value is used as the `src` attribute of the iframe rendering your front-end. Once loaded, the Admin Panel will communicate directly with your app through `window.postMessage` events.
|
||||
The `url` property resolves to a string that points to your front-end application. This value is used as the `src` attribute of the iframe rendering your front-end. Once loaded, the Admin Panel will communicate directly with your app through `window.postMessage` events.
|
||||
|
||||
To set the URL, use the `admin.livePreview.url` property in your [Payload Config](../configuration/overview):
|
||||
|
||||
@@ -105,8 +105,16 @@ The following arguments are provided to the `url` function:
|
||||
| Path | Description |
|
||||
| ------------------ | ----------------------------------------------------------------------------------------------------------------- |
|
||||
| **`data`** | The data of the Document being edited. This includes changes that have not yet been saved. |
|
||||
| **`documentInfo`** | Information about the Document being edited like collection slug. [More details](../admin/hooks#usedocumentinfo). |
|
||||
| **`locale`** | The locale currently being edited (if applicable). [More details](../configuration/localization). |
|
||||
| **`collectionConfig`** | The Collection Admin Config of the Document being edited. [More details](../admin/collections). |
|
||||
| **`globalConfig`** | The Global Admin Config of the Document being edited. [More details](../admin/globals). |
|
||||
| **`req`** | The Payload Request object. |
|
||||
|
||||
If your application requires a fully qualified URL, such as within deploying to Vercel Preview Deployments, you can use the `req` property to build this URL:
|
||||
|
||||
```ts
|
||||
url: (doc, { req }) => `${req.protocol}//${req.host}/${doc.slug}` // highlight-line
|
||||
```
|
||||
|
||||
### Breakpoints
|
||||
|
||||
|
||||
@@ -102,7 +102,7 @@ const post = await payload.find({
|
||||
|
||||
The following Collection operations are available through the Local API:
|
||||
|
||||
### Create
|
||||
### Create#collection-create
|
||||
|
||||
```js
|
||||
// The created Post document is returned
|
||||
@@ -131,10 +131,13 @@ const post = await payload.create({
|
||||
// Alternatively, you can directly pass a File,
|
||||
// if file is provided, filePath will be omitted
|
||||
file: uploadedFile,
|
||||
|
||||
// If you want to create a document that is a duplicate of another document
|
||||
duplicateFromID: 'document-id-to-duplicate',
|
||||
})
|
||||
```
|
||||
|
||||
### Find
|
||||
### Find#collection-find
|
||||
|
||||
```js
|
||||
// Result will be a paginated set of Posts.
|
||||
@@ -155,7 +158,7 @@ const result = await payload.find({
|
||||
})
|
||||
```
|
||||
|
||||
### Find by ID
|
||||
### Find by ID#collection-find-by-id
|
||||
|
||||
```js
|
||||
// Result will be a Post document.
|
||||
@@ -171,7 +174,7 @@ const result = await payload.findByID({
|
||||
})
|
||||
```
|
||||
|
||||
### Count
|
||||
### Count#collection-count
|
||||
|
||||
```js
|
||||
// Result will be an object with:
|
||||
@@ -187,7 +190,7 @@ const result = await payload.count({
|
||||
})
|
||||
```
|
||||
|
||||
### Update by ID
|
||||
### Update by ID#collection-update-by-id
|
||||
|
||||
```js
|
||||
// Result will be the updated Post document.
|
||||
@@ -219,7 +222,7 @@ const result = await payload.update({
|
||||
})
|
||||
```
|
||||
|
||||
### Update Many
|
||||
### Update Many#collection-update-many
|
||||
|
||||
```js
|
||||
// Result will be an object with:
|
||||
@@ -258,7 +261,7 @@ const result = await payload.update({
|
||||
})
|
||||
```
|
||||
|
||||
### Delete
|
||||
### Delete#collection-delete
|
||||
|
||||
```js
|
||||
// Result will be the now-deleted Post document.
|
||||
@@ -275,7 +278,7 @@ const result = await payload.delete({
|
||||
})
|
||||
```
|
||||
|
||||
### Delete Many
|
||||
### Delete Many#collection-delete-many
|
||||
|
||||
```js
|
||||
// Result will be an object with:
|
||||
@@ -304,6 +307,27 @@ const result = await payload.delete({
|
||||
If a collection has [`Authentication`](/docs/authentication/overview) enabled, additional Local API operations will be
|
||||
available:
|
||||
|
||||
### Auth
|
||||
|
||||
```js
|
||||
// If you're using Next.js, you'll have to import headers from next/headers, like so:
|
||||
// import { headers as nextHeaders } from 'next/headers'
|
||||
|
||||
// you'll also have to await headers inside your function, or component, like so:
|
||||
// const headers = await nextHeaders()
|
||||
|
||||
// If you're using payload outside of Next.js, you'll have to provide headers accordingly.
|
||||
|
||||
// result will be formatted as follows:
|
||||
// {
|
||||
// permissions: { ... }, // object containing current user's permissions
|
||||
// user: { ... }, // currently logged in user's document
|
||||
// responseHeaders: { ... } // returned headers from the response
|
||||
// }
|
||||
|
||||
const result = await payload.auth({headers})
|
||||
```
|
||||
|
||||
### Login
|
||||
|
||||
```js
|
||||
@@ -394,7 +418,7 @@ const result = await payload.verifyEmail({
|
||||
|
||||
The following Global operations are available through the Local API:
|
||||
|
||||
### Find
|
||||
### Find#global-find
|
||||
|
||||
```js
|
||||
// Result will be the Header Global.
|
||||
@@ -409,7 +433,7 @@ const result = await payload.findGlobal({
|
||||
})
|
||||
```
|
||||
|
||||
### Update
|
||||
### Update#global-update
|
||||
|
||||
```js
|
||||
// Result will be the updated Header Global.
|
||||
|
||||
@@ -10,6 +10,8 @@ keywords: local api, config, configuration, documentation, Content Management Sy
|
||||
|
||||
Payload 3.0 completely replatforms the Admin Panel from a React Router single-page application onto the Next.js App Router with full support for React Server Components. This change completely separates Payload "core" from its rendering and HTTP layers, making it truly Node-safe and portable.
|
||||
|
||||
If you are upgrading from a _previous beta_, please see the [Upgrade From Previous Beta](#upgrade-from-previous-beta) section.
|
||||
|
||||
## What has changed?
|
||||
|
||||
The core logic and principles of Payload remain the same from 2.0 to 3.0, with the majority of changes affecting specifically the HTTP layer and the Admin Panel, which is now built upon Next.js. With this change, your entire application can be served within a single repo, with Payload endpoints are now opened within your own Next.js application, directly alongside your frontend. Payload is still headless, you will still be able to leverage it completely headlessly just as you do now with Sveltekit, etc. All Payload APIs remain exactly the same (with a few new features), and the Payload Config is generally the same, with the breaking changes detailed below.
|
||||
@@ -26,6 +28,7 @@ All breaking changes are listed below. If you encounter changes that are not exp
|
||||
- [Types](#types)
|
||||
- [Email Adapters](#email-adapters)
|
||||
- [Plugins](#plugins)
|
||||
- [Upgrade From Previous Beta](#upgrade-from-previous-beta)
|
||||
|
||||
## Installation
|
||||
|
||||
@@ -37,7 +40,7 @@ Payload 3.0 requires a set of auto-generated files that you will need to bring i
|
||||
|
||||
For more details, see the [Documentation](https://payloadcms.com/docs/getting-started/installation).
|
||||
|
||||
1. **Install new dependencies of payload, next.js and react**:
|
||||
1. **Install new dependencies of Payload, Next.js and React**:
|
||||
|
||||
Refer to the package.json file made in the create-payload-app, including peerDependencies, devDependencies, and dependencies. The core package and plugins require all versions to be synced. Previously, on 2.x it was possible to be running the latest version of payload 2.x with an older version of db-mongodb for example. This is no longer the case.
|
||||
|
||||
@@ -66,7 +69,14 @@ For more details, see the [Documentation](https://payloadcms.com/docs/getting-st
|
||||
pnpm remove express nodemon @payloadcms/bundler-webpack @payloadcms/bundler-vite
|
||||
```
|
||||
|
||||
1. For Payload Cloud users, the plugin has changed.
|
||||
1. Database Adapter Migrations
|
||||
|
||||
_If you have existing data_ and are using the MongoDB or Postgres adapters, you will need to run the database migrations to ensure your database schema is up-to-date.
|
||||
|
||||
- [postgres](https://github.com/payloadcms/payload/releases/tag/v3.0.0-beta.39)
|
||||
- [mongodb](https://github.com/payloadcms/payload/releases/tag/v3.0.0-beta.131)
|
||||
|
||||
2. For Payload Cloud users, the plugin has changed.
|
||||
|
||||
Uninstall the old package:
|
||||
|
||||
@@ -94,7 +104,7 @@ For more details, see the [Documentation](https://payloadcms.com/docs/getting-st
|
||||
})
|
||||
```
|
||||
|
||||
1. **Optional** sharp dependency
|
||||
3. **Optional** sharp dependency
|
||||
|
||||
If you have upload enabled collections that use `formatOptions`, `imageSizes`, or `resizeOptions`—payload expects to have `sharp` installed. In 2.0 this was a dependency was installed for you. Now it is only installed if needed. If you have any of these options set, you will need to install `sharp` and add it to your payload.config.ts:
|
||||
|
||||
@@ -111,10 +121,6 @@ For more details, see the [Documentation](https://payloadcms.com/docs/getting-st
|
||||
})
|
||||
```
|
||||
|
||||
1. Database Adapter Migrations
|
||||
|
||||
If you have existing data and are using the MongoDB or Postgres adapters, you will need to run the database migrations to ensure your database schema is up-to-date. Follow the instructions from the release notes for [postgres](https://github.com/payloadcms/payload/releases/edit/v3.0.0-beta.39) or [mongodb](https://github.com/payloadcms/payload/releases/edit/v3.0.0-beta.131) depending on your chosen adapter.
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
1. Delete the `admin.bundler` property from your Payload Config. Payload no longer bundles the Admin Panel. Instead, we rely directly on Next.js for bundling.
|
||||
@@ -406,6 +412,7 @@ For more details, see the [Documentation](https://payloadcms.com/docs/getting-st
|
||||
}
|
||||
})
|
||||
```
|
||||
1. The `./src/public` directory is now located directly at root level `./public` [see Next.js docs for details](https://nextjs.org/docs/pages/building-your-application/optimizing/static-assets)
|
||||
|
||||
## Custom Components
|
||||
|
||||
@@ -656,8 +663,8 @@ For more details, see the [Documentation](https://payloadcms.com/docs/getting-st
|
||||
+ edit: {
|
||||
+ tab: {
|
||||
+ isActive: ({ href }) => true,
|
||||
+ href: ({ href }) => ''
|
||||
+ Component: './path/to/CustomComponent.tsx', // Or use a Custom Component
|
||||
+ href: ({ href }) => '' // or use a Custom Component (see below)
|
||||
+ // Component: './path/to/CustomComponent.tsx'
|
||||
+ }
|
||||
+ },
|
||||
},
|
||||
@@ -979,7 +986,7 @@ export default buildConfig({
|
||||
- If you have created a custom adapter, the type must now provide a `name` property.
|
||||
|
||||
| Service | Package |
|
||||
| -------------------- |------------------------------------------------------------------------------|
|
||||
| -------------------- | ---------------------------------------------------------------------------- |
|
||||
| Vercel Blob | https://github.com/payloadcms/payload/tree/main/packages/storage-vercel-blob |
|
||||
| AWS S3 | https://github.com/payloadcms/payload/tree/main/packages/storage-s3 |
|
||||
| Azure | https://github.com/payloadcms/payload/tree/main/packages/storage-azure |
|
||||
@@ -1096,3 +1103,9 @@ plugins: [
|
||||
## `@payloadcms/richtext-lexical`
|
||||
|
||||
If you have custom features for `@payloadcms/richtext-lexical` you will need to migrate your code to the new API. Read more about the new API in the [documentation](https://payloadcms.com/docs/lexical/building-custom-features).
|
||||
|
||||
## Upgrade from previous beta
|
||||
|
||||
Reference this [community-made site](https://payload-releases-filter.vercel.app/?version=3&from=152429656&to=188243150&sort=asc&breaking=on). Set your version, sort by oldest first, enable breaking changes only.
|
||||
|
||||
Then go through each one of the breaking changes and make the adjustments. You can optionally reference the [blank template](https://github.com/payloadcms/payload/tree/main/templates/blank) for how things should end up.
|
||||
|
||||
@@ -84,7 +84,7 @@ cd dev
|
||||
npx create-payload-app@latest
|
||||
```
|
||||
|
||||
If you're using the plugin template, the dev folder is built out for you and the `samplePlugin` has already been installed in `dev/payload.config()`.
|
||||
If you're using the plugin template, the dev folder is built out for you and the `samplePlugin` has already been installed in `dev/payload.config.ts`.
|
||||
|
||||
```
|
||||
plugins: [
|
||||
@@ -95,11 +95,11 @@ If you're using the plugin template, the dev folder is built out for you an
|
||||
]
|
||||
```
|
||||
|
||||
You can add to the `dev/payload.config` and build out the dev project as needed to test your plugin.
|
||||
You can add to the `dev/payload.config.ts` and build out the dev project as needed to test your plugin.
|
||||
|
||||
When you're ready to start development, navigate into this folder with `cd dev`
|
||||
|
||||
And then start the project with `yarn dev` and pull up `http://localhost:3000` in your browser.
|
||||
And then start the project with `pnpm dev` and pull up `http://localhost:3000` in your browser.
|
||||
|
||||
## Testing
|
||||
|
||||
@@ -112,7 +112,7 @@ Jest organizes tests into test suites and cases. We recommend creating tests bas
|
||||
The plugin template provides a stubbed out test suite at `dev/plugin.spec.ts` which is ready to go - just add in your own test conditions and you're all set!
|
||||
|
||||
```
|
||||
import payload from 'payload'
|
||||
let payload: Payload
|
||||
|
||||
describe('Plugin tests', () => {
|
||||
// Example test to check for seeded data
|
||||
@@ -245,7 +245,7 @@ config.hooks = {
|
||||
```
|
||||
|
||||
### Extending functions
|
||||
Function properties cannot use spread syntax. The way to extend them is to execute the existing function if it exists and then run your additional functionality.
|
||||
Function properties cannot use spread syntax. The way to extend them is to execute the existing function if it exists and then run your additional functionality.
|
||||
|
||||
Here is an example extending the `onInit` property:
|
||||
|
||||
@@ -285,11 +285,11 @@ For a better user experience, provide a way to disable the plugin without uninst
|
||||
|
||||
### Include tests in your GitHub CI workflow
|
||||
|
||||
If you've configured tests for your package, integrate them into your workflow to run the tests each time you commit to the plugin repository. Learn more about [how to configure tests into your GitHub CI workflow.](https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs)
|
||||
If you've configured tests for your package, integrate them into your workflow to run the tests each time you commit to the plugin repository. Learn more about [how to configure tests into your GitHub CI workflow.](https://docs.github.com/en/actions/use-cases-and-examples/building-and-testing/building-and-testing-nodejs)
|
||||
|
||||
### Publish your finished plugin to NPM
|
||||
### Publish your finished plugin to npm
|
||||
|
||||
The best way to share and allow others to use your plugin once it is complete is to publish an NPM package. This process is straightforward and well documented, find out more about [creating and publishing a NPM package here](https://docs.npmjs.com/creating-and-publishing-scoped-public-packages/).
|
||||
The best way to share and allow others to use your plugin once it is complete is to publish an npm package. This process is straightforward and well documented, find out more about [creating and publishing a npm package here](https://docs.npmjs.com/creating-and-publishing-scoped-public-packages/).
|
||||
|
||||
### Add payload-plugin topic tag
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ desc: Easily build and manage forms from the Admin Panel. Send dynamic, personal
|
||||
keywords: plugins, plugin, form, forms, form builder
|
||||
---
|
||||
|
||||
[](https://www.npmjs.com/package/@payloadcms/plugin-form-builder)
|
||||
[](https://www.npmjs.com/package/@payloadcms/plugin-form-builder)
|
||||
|
||||
This plugin allows you to build and manage custom forms directly within the [Admin Panel](../admin/overview). Instead of hard-coding a new form into your website or application every time you need one, admins can simply define the schema for each form they need on-the-fly, and your front-end can map over this schema, render its own UI components, and match your brand's design system.
|
||||
|
||||
@@ -33,7 +33,7 @@ Forms can be as simple or complex as you need, from a basic contact form, to a m
|
||||
|
||||
## Installation
|
||||
|
||||
Install the plugin using any JavaScript package manager like [Yarn](https://yarnpkg.com), [NPM](https://npmjs.com), or [PNPM](https://pnpm.io):
|
||||
Install the plugin using any JavaScript package manager like [pnpm](https://pnpm.io), [npm](https://npmjs.com), or [Yarn](https://yarnpkg.com):
|
||||
|
||||
```bash
|
||||
pnpm add @payloadcms/plugin-form-builder
|
||||
@@ -72,7 +72,7 @@ The `fields` property is an object of field types to allow your admin editors to
|
||||
|
||||
```ts
|
||||
// payload.config.ts
|
||||
formBuilder({
|
||||
formBuilderPlugin({
|
||||
// ...
|
||||
fields: {
|
||||
text: true,
|
||||
@@ -95,7 +95,7 @@ The `redirectRelationships` property is an array of collection slugs that, when
|
||||
|
||||
```ts
|
||||
// payload.config.ts
|
||||
formBuilder({
|
||||
formBuilderPlugin({
|
||||
// ...
|
||||
redirectRelationships: ['pages'],
|
||||
})
|
||||
@@ -103,11 +103,11 @@ formBuilder({
|
||||
|
||||
### `beforeEmail`
|
||||
|
||||
The `beforeEmail` property is a [beforeChange](<[beforeChange](https://payloadcms.com/docs/hooks/globals#beforechange)>) hook that is called just after emails are prepared, but before they are sent. This is a great place to inject your own HTML template to add custom styles.
|
||||
The `beforeEmail` property is a [beforeChange](https://payloadcms.com/docs/hooks/globals#beforechange) hook that is called just after emails are prepared, but before they are sent. This is a great place to inject your own HTML template to add custom styles.
|
||||
|
||||
```ts
|
||||
// payload.config.ts
|
||||
formBuilder({
|
||||
formBuilderPlugin({
|
||||
// ...
|
||||
beforeEmail: (emailsToSend, beforeChangeParams) => {
|
||||
// modify the emails in any way before they are sent
|
||||
@@ -142,7 +142,7 @@ Provide a fallback for the email address to send form submissions to. If the ema
|
||||
|
||||
```ts
|
||||
// payload.config.ts
|
||||
formBuilder({
|
||||
formBuilderPlugin({
|
||||
// ...
|
||||
defaultToEmail: 'test@example.com',
|
||||
})
|
||||
@@ -160,7 +160,7 @@ Good to know: The form collection is publicly available to read by default. The
|
||||
|
||||
```ts
|
||||
// payload.config.ts
|
||||
formBuilder({
|
||||
formBuilderPlugin({
|
||||
// ...
|
||||
formOverrides: {
|
||||
slug: 'contact-forms',
|
||||
@@ -196,7 +196,7 @@ Override anything on the `form-submissions` collection by sending a [Payload Col
|
||||
|
||||
```ts
|
||||
// payload.config.ts
|
||||
formBuilder({
|
||||
formBuilderPlugin({
|
||||
// ...
|
||||
formSubmissionOverrides: {
|
||||
slug: 'leads',
|
||||
@@ -215,7 +215,7 @@ formBuilder({
|
||||
|
||||
### `handlePayment`
|
||||
|
||||
The `handlePayment` property is a [beforeChange](<[beforeChange](https://payloadcms.com/docs/hooks/globals#beforechange)>) hook that is called upon form submission. You can integrate into any third-party payment processing API here to accept payment based on form input. You can use the `getPaymentTotal` function to calculate the total cost after all conditions have been applied. This is only applicable if the form has enabled the `payment` field.
|
||||
The `handlePayment` property is a [beforeChange](https://payloadcms.com/docs/hooks/globals#beforechange) hook that is called upon form submission. You can integrate into any third-party payment processing API here to accept payment based on form input. You can use the `getPaymentTotal` function to calculate the total cost after all conditions have been applied. This is only applicable if the form has enabled the `payment` field.
|
||||
|
||||
First import the utility function. This will execute all of the price conditions that you have set in your form's `payment` field and returns the total price.
|
||||
|
||||
@@ -228,7 +228,7 @@ Then in your plugin's config:
|
||||
|
||||
```ts
|
||||
// payload.config.ts
|
||||
formBuilder({
|
||||
formBuilderPlugin({
|
||||
// ...
|
||||
handlePayment: async ({ form, submissionData }) => {
|
||||
// first calculate the price
|
||||
@@ -396,7 +396,7 @@ Then merging it into your own custom field:
|
||||
|
||||
```ts
|
||||
// payload.config.ts
|
||||
formBuilder({
|
||||
formBuilderPlugin({
|
||||
// ...
|
||||
fields: {
|
||||
text: {
|
||||
|
||||
@@ -6,7 +6,7 @@ desc: Nested documents in a parent, child, and sibling relationship.
|
||||
keywords: plugins, nested, documents, parent, child, sibling, relationship
|
||||
---
|
||||
|
||||
[](https://www.npmjs.com/package/@payloadcms/plugin-nested-docs)
|
||||
[](https://www.npmjs.com/package/@payloadcms/plugin-nested-docs)
|
||||
|
||||
This plugin allows you to easily nest the documents of your application inside of one another. It does so by adding a
|
||||
new `parent` field onto each of your documents that, when selected, attaches itself to the parent's tree. When you edit
|
||||
@@ -44,8 +44,7 @@ but different parents.
|
||||
|
||||
## Installation
|
||||
|
||||
Install the plugin using any JavaScript package manager like [Yarn](https://yarnpkg.com), [NPM](https://npmjs.com),
|
||||
or [PNPM](https://pnpm.io):
|
||||
Install the plugin using any JavaScript package manager like [pnpm](https://pnpm.io), [npm](https://npmjs.com), or [Yarn](https://yarnpkg.com):
|
||||
|
||||
```bash
|
||||
pnpm add @payloadcms/plugin-nested-docs
|
||||
@@ -102,8 +101,8 @@ level and stores the following fields.
|
||||
|
||||
| Field | Description |
|
||||
| ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `label` | The label of the breadcrumb. This field is automatically set to either the `collection.admin.useAsTitle` (if defined) or is set to the `ID` of the document. You can also dynamically define the `label` by passing a function to the options property of [`generateLabel`](#generateLabel). |
|
||||
| `url` | The URL of the breadcrumb. By default, this field is undefined. You can manually define this field by passing a property called function to the plugin options property of [`generateURL`](#generateURL). |
|
||||
| `label` | The label of the breadcrumb. This field is automatically set to either the `collection.admin.useAsTitle` (if defined) or is set to the `ID` of the document. You can also dynamically define the `label` by passing a function to the options property of [`generateLabel`](#generatelabel). |
|
||||
| `url` | The URL of the breadcrumb. By default, this field is undefined. You can manually define this field by passing a property called function to the plugin options property of [`generateURL`](#generateurl). |
|
||||
|
||||
### Options
|
||||
|
||||
@@ -120,7 +119,7 @@ You can also pass a function to dynamically set the `label` of your breadcrumb.
|
||||
|
||||
```ts
|
||||
// payload.config.ts
|
||||
nestedDocs({
|
||||
nestedDocsPlugin({
|
||||
//...
|
||||
generateLabel: (_, doc) => doc.title, // NOTE: 'title' is a hypothetical field
|
||||
})
|
||||
@@ -141,7 +140,7 @@ that point, like `/about-us/company/our-team`.
|
||||
|
||||
```ts
|
||||
// payload.config.ts
|
||||
nestedDocs({
|
||||
nestedDocsPlugin({
|
||||
//...
|
||||
generateURL: (docs) => docs.reduce((url, doc) => `${url}/${doc.slug}`, ''), // NOTE: 'slug' is a hypothetical field
|
||||
})
|
||||
@@ -226,7 +225,7 @@ const examplePageConfig: CollectionConfig = {
|
||||
|
||||
This plugin supports localization by default. If the `localization` property is set in your Payload Config,
|
||||
the `breadcrumbs` field is automatically localized. For more details on how localization works in Payload, see
|
||||
the [Localization](https://payloadcms.com/docs/localization/overview) docs.
|
||||
the [Localization](https://payloadcms.com/docs/configuration/localization) docs.
|
||||
|
||||
## TypeScript
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ keywords: plugins, config, configuration, extensions, custom, documentation, Con
|
||||
|
||||
Payload Plugins take full advantage of the modularity of the [Payload Config](../configuration/overview), allowing developers developers to easily inject custom—sometimes complex—functionality into Payload apps from a very small touch-point. This is especially useful is sharing your work across multiple projects or with the greater Payload community.
|
||||
|
||||
There are many [Official Plugins](#official-plugins) available that solve for some of the most common uses cases, such as the [Form Builder Plugin](./seo) or [SEO Plugin](./seo). There are also [Community Plugins](#community-plugins) available, maintained entirely by contributing members. To extend Payload's functionality in some other way, you can easily [build your own plugin](./build-your-own).
|
||||
There are many [Official Plugins](#official-plugins) available that solve for some of the most common uses cases, such as the [Form Builder Plugin](./form-builder) or [SEO Plugin](./seo). There are also [Community Plugins](#community-plugins) available, maintained entirely by contributing members. To extend Payload's functionality in some other way, you can easily [build your own plugin](./build-your-own).
|
||||
|
||||
To configure Plugins, use the `plugins` property in your [Payload Config](../configuration/overview):
|
||||
|
||||
@@ -68,7 +68,7 @@ Plugins are changing every day, so be sure to check back often to see what new p
|
||||
|
||||
Community Plugins are those that are maintained entirely by outside contributors. They are a great way to share your work across the ecosystem for others to use. You can discover Community Plugins by browsing the `payload-plugin` topic on [GitHub](https://github.com/topics/payload-plugin).
|
||||
|
||||
Some plugins have become so widely used that they are adopted as an [Official Plugin](#official-plugin), such as the [Lexical Plugin](https://github.com/AlessioGr/payload-plugin-lexical). If you have a plugin that you think should be an Official Plugin, please feel free to start a new [Discussion](https://github.com/payloadcms/payload/discussions).
|
||||
Some plugins have become so widely used that they are adopted as an [Official Plugin](#official-plugins), such as the [Lexical Plugin](https://github.com/AlessioGr/payload-plugin-lexical). If you have a plugin that you think should be an Official Plugin, please feel free to start a new [Discussion](https://github.com/payloadcms/payload/discussions).
|
||||
|
||||
<Banner type="warning">
|
||||
For maintainers building plugins for others to use, please add the `payload-plugin` topic on [GitHub](https://github.com/topics/payload-plugin) to help others find it.
|
||||
|
||||
@@ -6,7 +6,7 @@ desc: Automatically create redirects for your Payload application
|
||||
keywords: plugins, redirects, redirect, plugin, payload, cms, seo, indexing, search, search engine
|
||||
---
|
||||
|
||||
[](https://www.npmjs.com/package/@payloadcms/plugin-redirects)
|
||||
[](https://www.npmjs.com/package/@payloadcms/plugin-redirects)
|
||||
|
||||
This plugin allows you to easily manage redirects for your application from within your [Admin Panel](../admin/overview). It does so by adding a `redirects` collection to your config that allows you specify a redirect from one URL to another. Your front-end application can use this data to automatically redirect users to the correct page using proper HTTP status codes. This is useful for SEO, indexing, and search engine ranking when re-platforming or when changing your URL structure.
|
||||
|
||||
@@ -29,7 +29,7 @@ For example, if you have a page at `/about` and you want to change it to `/about
|
||||
|
||||
## Installation
|
||||
|
||||
Install the plugin using any JavaScript package manager like [Yarn](https://yarnpkg.com), [NPM](https://npmjs.com), or [PNPM](https://pnpm.io):
|
||||
Install the plugin using any JavaScript package manager like [pnpm](https://pnpm.io), [npm](https://npmjs.com), or [Yarn](https://yarnpkg.com):
|
||||
|
||||
```bash
|
||||
pnpm add @payloadcms/plugin-redirects
|
||||
|
||||
@@ -6,7 +6,7 @@ desc: Generates records of your documents that are extremely fast to search on.
|
||||
keywords: plugins, search, search plugin, search engine, search index, search results, search bar, search box, search field, search form, search input
|
||||
---
|
||||
|
||||
[](https://www.npmjs.com/package/@payloadcms/plugin-search)
|
||||
[](https://www.npmjs.com/package/@payloadcms/plugin-search)
|
||||
|
||||
This plugin generates records of your documents that are extremely fast to search on. It does so by creating a new `search` collection that is indexed in the database then saving a static copy of each of your documents using only search-critical data. Search records are automatically created, synced, and deleted behind-the-scenes as you manage your application's documents.
|
||||
|
||||
@@ -33,10 +33,11 @@ This plugin is a great way to implement a fast, immersive search experience such
|
||||
- Allows you to query search results using first-party Payload APIs
|
||||
- Allows you to query documents without triggering any of their underlying hooks
|
||||
- Allows you to easily prioritize search results by collection or document
|
||||
- Allows you to reindex search results by collection on demand
|
||||
|
||||
## Installation
|
||||
|
||||
Install the plugin using any JavaScript package manager like [Yarn](https://yarnpkg.com), [NPM](https://npmjs.com), or [PNPM](https://pnpm.io):
|
||||
Install the plugin using any JavaScript package manager like [pnpm](https://pnpm.io), [npm](https://npmjs.com), or [Yarn](https://yarnpkg.com):
|
||||
|
||||
```bash
|
||||
pnpm add @payloadcms/plugin-search
|
||||
@@ -81,7 +82,7 @@ export default config
|
||||
|
||||
The `collections` property is an array of collection slugs to enable syncing to search. Enabled collections receive a `beforeChange` and `afterDelete` hook that creates, updates, and deletes its respective search record as it changes over time.
|
||||
|
||||
### `localize`
|
||||
#### `localize`
|
||||
|
||||
By default, the search plugin will add `localization: true` to the `title` field of the newly added `search` collection if you have localization enabled. If you would like to disable this behavior, you can set this to `false`.
|
||||
|
||||
@@ -134,7 +135,7 @@ Note that the `fields` property is a function that receives an object with a `de
|
||||
|
||||
#### `beforeSync`
|
||||
|
||||
Before creating or updating a search record, the `beforeSync` function runs. This is an [afterChange](<[afterChange](https://payloadcms.com/docs/hooks/globals#afterchange)>) hook that allows you to modify the data or provide fallbacks before its search record is created or updated.
|
||||
Before creating or updating a search record, the `beforeSync` function runs. This is an [afterChange](https://payloadcms.com/docs/hooks/globals#afterchange) hook that allows you to modify the data or provide fallbacks before its search record is created or updated.
|
||||
|
||||
```ts
|
||||
// payload.config.ts
|
||||
@@ -159,6 +160,14 @@ When `syncDrafts` is true, draft documents will be synced to search. This is fal
|
||||
|
||||
If true, will delete documents from search whose status changes to draft. This is true by default. You must have [Payload Drafts](https://payloadcms.com/docs/versions/drafts) enabled for this to apply.
|
||||
|
||||
#### `reindexBatchSize`
|
||||
|
||||
A number that, when specified, will be used as the value to determine how many search documents to fetch for reindexing at a time in each batch. If not set, this will default to `50`.
|
||||
|
||||
### Collection reindexing
|
||||
|
||||
Collection reindexing allows you to recreate search documents from your search-enabled collections on demand. This is useful if you have existing documents that don't already have search indexes, commonly when adding `plugin-search` to an existing project. To get started, navigate to your search collection and click the pill in the top right actions slot of the list view labelled `Reindex`. This will open a popup with options to select one of your search-enabled collections, or all, for reindexing.
|
||||
|
||||
## TypeScript
|
||||
|
||||
All types can be directly imported:
|
||||
|
||||
@@ -6,7 +6,7 @@ desc: Integrate Sentry error tracking into your Payload application
|
||||
keywords: plugins, sentry, error, tracking, monitoring, logging, bug, reporting, performance
|
||||
---
|
||||
|
||||
[](https://www.npmjs.com/package/@payloadcms/plugin-sentry)
|
||||
[](https://www.npmjs.com/package/@payloadcms/plugin-sentry)
|
||||
|
||||
This plugin allows you to integrate [Sentry](https://sentry.io/) seamlessly with your [Payload](https://github.com/payloadcms/payload) application.
|
||||
|
||||
@@ -36,7 +36,7 @@ This multi-faceted software offers a range of features that will help you manage
|
||||
|
||||
## Installation
|
||||
|
||||
Install the plugin using any JavaScript package manager like [Yarn](https://yarnpkg.com), [NPM](https://npmjs.com), or [PNPM](https://pnpm.io):
|
||||
Install the plugin using any JavaScript package manager like [pnpm](https://pnpm.io), [npm](https://npmjs.com), or [Yarn](https://yarnpkg.com):
|
||||
|
||||
```bash
|
||||
pnpm add @payloadcms/plugin-sentry
|
||||
|
||||
@@ -6,7 +6,7 @@ desc: Manage SEO metadata from your Payload admin
|
||||
keywords: plugins, seo, meta, search, engine, ranking, google
|
||||
---
|
||||
|
||||
[](https://www.npmjs.com/package/@payloadcms/plugin-seo)
|
||||
[](https://www.npmjs.com/package/@payloadcms/plugin-seo)
|
||||
|
||||
This plugin allows you to easily manage SEO metadata for your application from within your [Admin Panel](../admin/overview). When enabled on your [Collections](../configuration/collections) and [Globals](../configuration/globals), it adds a new `meta` field group containing `title`, `description`, and `image` by default. Your front-end application can then use this data to render meta tags however your application requires. For example, you would inject a `title` tag into the `<head>` of your page using `meta.title` as its content.
|
||||
|
||||
@@ -34,7 +34,7 @@ To help you visualize what your page might look like in a search engine, a previ
|
||||
|
||||
## Installation
|
||||
|
||||
Install the plugin using any JavaScript package manager like [Yarn](https://yarnpkg.com), [NPM](https://npmjs.com), or [PNPM](https://pnpm.io):
|
||||
Install the plugin using any JavaScript package manager like [pnpm](https://pnpm.io), [npm](https://npmjs.com), or [Yarn](https://yarnpkg.com):
|
||||
|
||||
```bash
|
||||
pnpm add @payloadcms/plugin-seo
|
||||
@@ -171,7 +171,7 @@ A function that allows you to return any meta description, including from docume
|
||||
}
|
||||
```
|
||||
|
||||
For a full list of arguments, see the [`generateTitle`](#generateTitle) function.
|
||||
For a full list of arguments, see the [`generateTitle`](#generatetitle) function.
|
||||
|
||||
##### `generateImage`
|
||||
|
||||
@@ -187,7 +187,7 @@ A function that allows you to return any meta image, including from document's c
|
||||
}
|
||||
```
|
||||
|
||||
For a full list of arguments, see the [`generateTitle`](#generateTitle) function.
|
||||
For a full list of arguments, see the [`generateTitle`](#generatetitle) function.
|
||||
|
||||
##### `generateURL`
|
||||
|
||||
@@ -204,7 +204,7 @@ A function called by the search preview component to display the actual URL of y
|
||||
}
|
||||
```
|
||||
|
||||
For a full list of arguments, see the [`generateTitle`](#generateTitle) function.
|
||||
For a full list of arguments, see the [`generateTitle`](#generatetitle) function.
|
||||
|
||||
#### `interfaceName`
|
||||
|
||||
@@ -277,7 +277,7 @@ Tip: You can override the length rules by changing the minLength and maxLength p
|
||||
All types can be directly imported:
|
||||
|
||||
```ts
|
||||
import {
|
||||
import type {
|
||||
PluginConfig,
|
||||
GenerateTitle,
|
||||
GenerateDescription
|
||||
@@ -288,9 +288,9 @@ import {
|
||||
You can then pass the collections from your generated Payload types into the generation types, for example:
|
||||
|
||||
```ts
|
||||
import { Page } from './payload-types.ts';
|
||||
import type { Page } from './payload-types.ts';
|
||||
|
||||
import { GenerateTitle } from '@payloadcms/plugin-seo/types';
|
||||
import type { GenerateTitle } from '@payloadcms/plugin-seo/types';
|
||||
|
||||
const generateTitle: GenerateTitle<Page> = async ({ doc, locale }) => {
|
||||
return `Website.com — ${doc?.title}`
|
||||
|
||||
@@ -6,9 +6,9 @@ desc: Easily accept payments with Stripe
|
||||
keywords: plugins, stripe, payments, ecommerce
|
||||
---
|
||||
|
||||
[](https://www.npmjs.com/package/@payloadcms/plugin-stripe)
|
||||
[](https://www.npmjs.com/package/@payloadcms/plugin-stripe)
|
||||
|
||||
With this plugin you can easily integrate [Stripe](https://stripe.com) into Payload. Simply provide your Stripe credentials and this plugin will open up a two-way communication channel between the two platforms. This enables you to easily sync data back and forth, as well as proxy the Stripe REST API through Payload's [Access Control](../access-control). Use this plugin to completely offload billing to Stripe and retain full control over your application's data.
|
||||
With this plugin you can easily integrate [Stripe](https://stripe.com) into Payload. Simply provide your Stripe credentials and this plugin will open up a two-way communication channel between the two platforms. This enables you to easily sync data back and forth, as well as proxy the Stripe REST API through Payload's [Access Control](../access-control/overview). Use this plugin to completely offload billing to Stripe and retain full control over your application's data.
|
||||
|
||||
For example, you might be building an e-commerce or SaaS application, where you have a `products` or a `plans` collection that requires either a one-time payment or a subscription. You can to tie each of these products to Stripe, then easily subscribe to billing-related events to perform your application's business logic, such as active purchases or subscription cancellations.
|
||||
|
||||
@@ -36,7 +36,7 @@ The beauty of this plugin is the entirety of your application's content and busi
|
||||
|
||||
## Installation
|
||||
|
||||
Install the plugin using any JavaScript package manager like [Yarn](https://yarnpkg.com), [NPM](https://npmjs.com), or [PNPM](https://pnpm.io):
|
||||
Install the plugin using any JavaScript package manager like [pnpm](https://pnpm.io), [npm](https://npmjs.com), or [Yarn](https://yarnpkg.com):
|
||||
|
||||
```bash
|
||||
pnpm add @payloadcms/plugin-stripe
|
||||
|
||||
@@ -160,7 +160,19 @@ Follow the docs to configure any one of these storage providers. For local devel
|
||||
This is an example of a multi-stage docker build of Payload for production. Ensure you are setting your environment
|
||||
variables on deployment, like `PAYLOAD_SECRET`, `PAYLOAD_CONFIG_PATH`, and `DATABASE_URI` if needed.
|
||||
|
||||
In your Next.js config, set the `output` property `standalone`.
|
||||
|
||||
```js
|
||||
// next.config.js
|
||||
const nextConfig = {
|
||||
output: 'standalone',
|
||||
}
|
||||
```
|
||||
|
||||
Dockerfile
|
||||
|
||||
```dockerfile
|
||||
# Dockerfile
|
||||
# From https://github.com/vercel/next.js/blob/canary/examples/with-docker/Dockerfile
|
||||
|
||||
FROM node:18-alpine AS base
|
||||
|
||||
@@ -43,7 +43,9 @@ But with a `depth` of `1`, the response might look like this:
|
||||
To specify depth in the [Local API](../local-api/overview), you can use the `depth` option in your query:
|
||||
|
||||
```ts
|
||||
const getPosts = async () => {
|
||||
import type { Payload } from 'payload'
|
||||
|
||||
const getPosts = async (payload: Payload) => {
|
||||
const posts = await payload.find({
|
||||
collection: 'posts',
|
||||
depth: 2, // highlight-line
|
||||
|
||||
@@ -19,7 +19,9 @@ Each of these APIs share the same underlying querying language, and fully suppor
|
||||
To query your Documents, you can send any number of [Operators](#operators) through your request:
|
||||
|
||||
```ts
|
||||
const query = {
|
||||
import type { Where } from 'payload'
|
||||
|
||||
const query: Where = {
|
||||
color: {
|
||||
equals: 'blue',
|
||||
},
|
||||
@@ -67,7 +69,9 @@ In addition to defining simple queries, you can join multiple queries together u
|
||||
To join queries, use the `and` or `or` keys in your query object:
|
||||
|
||||
```ts
|
||||
const query = {
|
||||
import type { Where } from 'payload'
|
||||
|
||||
const query: Where = {
|
||||
or: [ // highlight-line
|
||||
{
|
||||
color: {
|
||||
@@ -99,7 +103,9 @@ Written in plain English, if the above query were passed to a `find` operation,
|
||||
When working with nested properties, which can happen when using relational fields, it is possible to use the dot notation to access the nested property. For example, when working with a `Song` collection that has a `artists` field which is related to an `Artists` collection using the `name: 'artists'`. You can access a property within the collection `Artists` like so:
|
||||
|
||||
```js
|
||||
const query = {
|
||||
import type { Where } from 'payload'
|
||||
|
||||
const query: Where = {
|
||||
'artists.featured': {
|
||||
// nested property name to filter on
|
||||
exists: true, // operator to use and boolean value that needs to be true
|
||||
@@ -116,7 +122,9 @@ Writing queries in Payload is simple and consistent across all APIs, with only m
|
||||
The [Local API](../local-api/overview) supports the `find` operation that accepts a raw query object:
|
||||
|
||||
```ts
|
||||
const getPosts = async () => {
|
||||
import type { Payload } from 'payload'
|
||||
|
||||
const getPosts = async (payload: Payload) => {
|
||||
const posts = await payload.find({
|
||||
collection: 'posts',
|
||||
where: {
|
||||
@@ -157,19 +165,20 @@ For this reason, we recommend to use the extremely helpful and ubiquitous [`qs-e
|
||||
|
||||
```ts
|
||||
import { stringify } from 'qs-esm'
|
||||
import type { Where } from 'payload'
|
||||
|
||||
const query = {
|
||||
const query: Where = {
|
||||
color: {
|
||||
equals: 'mint',
|
||||
},
|
||||
// This query could be much more complex
|
||||
// and QS would handle it beautifully
|
||||
// and qs-esm would handle it beautifully
|
||||
}
|
||||
|
||||
const getPosts = async () => {
|
||||
const stringifiedQuery = stringify(
|
||||
{
|
||||
where: query, // ensure that `qs` adds the `where` property, too!
|
||||
where: query, // ensure that `qs-esm` adds the `where` property, too!
|
||||
},
|
||||
{ addQueryPrefix: true },
|
||||
)
|
||||
|
||||
@@ -15,8 +15,10 @@ This is where Payload's `select` feature comes in. Here, you can define exactly
|
||||
To specify `select` in the [Local API](../local-api/overview), you can use the `select` option in your query:
|
||||
|
||||
```ts
|
||||
import type { Payload } from 'payload'
|
||||
|
||||
// Include mode
|
||||
const getPosts = async () => {
|
||||
const getPosts = async (payload: Payload) => {
|
||||
const posts = await payload.find({
|
||||
collection: 'posts',
|
||||
select: {
|
||||
@@ -34,7 +36,7 @@ const getPosts = async () => {
|
||||
}
|
||||
|
||||
// Exclude mode
|
||||
const getPosts = async () => {
|
||||
const getPosts = async (payload: Payload) => {
|
||||
const posts = await payload.find({
|
||||
collection: 'posts',
|
||||
// Select everything except for array and group.number
|
||||
@@ -73,8 +75,9 @@ For this reason, we recommend to use the extremely helpful and ubiquitous [`qs-e
|
||||
|
||||
```ts
|
||||
import { stringify } from 'qs-esm'
|
||||
import type { Where } from 'payload'
|
||||
|
||||
const select = {
|
||||
const select: Where = {
|
||||
text: true,
|
||||
group: {
|
||||
number: true
|
||||
@@ -116,9 +119,6 @@ Loading all of the page content, its related links, and everything else is going
|
||||
```ts
|
||||
import type { CollectionConfig } from 'payload'
|
||||
|
||||
import { lexicalEditor, LinkFeature } from '@payloadcms/richtext-lexical'
|
||||
import { slateEditor } from '@payloadcms/richtext-slate'
|
||||
|
||||
// The TSlug generic can be passed to have type safety for `defaultPopulate`.
|
||||
// If avoided, the `defaultPopulate` type resolves to `SelectType`.
|
||||
export const Pages: CollectionConfig<'pages'> = {
|
||||
@@ -144,7 +144,9 @@ Setting `defaultPopulate` will enforce that each time Payload performs a "popula
|
||||
**Local API:**
|
||||
|
||||
```ts
|
||||
const getPosts = async () => {
|
||||
import type { Payload } from 'payload'
|
||||
|
||||
const getPosts = async (payload: Payload) => {
|
||||
const posts = await payload.find({
|
||||
collection: 'posts',
|
||||
populate: {
|
||||
|
||||
@@ -20,7 +20,9 @@ Because sorting is handled by the database, the field cannot be a [Virtual Field
|
||||
To sort Documents in the [Local API](../local-api/overview), you can use the `sort` option in your query:
|
||||
|
||||
```ts
|
||||
const getPosts = async () => {
|
||||
import type { Payload } from 'payload'
|
||||
|
||||
const getPosts = async (payload: Payload) => {
|
||||
const posts = await payload.find({
|
||||
collection: 'posts',
|
||||
sort: '-createdAt', // highlight-line
|
||||
@@ -33,7 +35,9 @@ const getPosts = async () => {
|
||||
To sort by multiple fields, you can use the `sort` option with fields in an array:
|
||||
|
||||
```ts
|
||||
const getPosts = async () => {
|
||||
import type { Payload } from 'payload'
|
||||
|
||||
const getPosts = async (payload: Payload) => {
|
||||
const posts = await payload.find({
|
||||
collection: 'posts',
|
||||
sort: ['priority', '-createdAt'], // highlight-line
|
||||
|
||||
@@ -501,7 +501,7 @@ Globals cannot be created or deleted, so there are only two REST endpoints opene
|
||||
|
||||
## Preferences
|
||||
|
||||
In addition to the dynamically generated endpoints above Payload also has REST endpoints to manage the admin user [preferences](/docs/admin/overview#preferences) for data specific to the authenticated user.
|
||||
In addition to the dynamically generated endpoints above Payload also has REST endpoints to manage the admin user [preferences](/docs/admin/preferences) for data specific to the authenticated user.
|
||||
|
||||
<RestExamples
|
||||
data={[
|
||||
|
||||
@@ -213,7 +213,7 @@ export interface Collection1 {
|
||||
|
||||
Now that your types have been generated, payloads local API will now be typed. It is common for users to want to use this in their frontend code, we recommend generating them with Payload and then copying the file over to your frontend codebase. This is the simplest way to get your types into your frontend codebase.
|
||||
|
||||
### Adding an NPM script
|
||||
### Adding an npm script
|
||||
|
||||
<Banner type="warning">
|
||||
<strong>Important</strong>
|
||||
@@ -221,9 +221,9 @@ Now that your types have been generated, payloads local API will now be typed. I
|
||||
Payload needs to be able to find your config to generate your types.
|
||||
</Banner>
|
||||
|
||||
Payload will automatically try and locate your config, but might not always be able to find it. For example, if you are working in a `/src` directory or similar, you need to tell Payload where to find your config manually by using an environment variable. If this applies to you, you can create an NPM script to make generating your types easier.
|
||||
Payload will automatically try and locate your config, but might not always be able to find it. For example, if you are working in a `/src` directory or similar, you need to tell Payload where to find your config manually by using an environment variable. If this applies to you, you can create an npm script to make generating your types easier.
|
||||
|
||||
To add an NPM script to generate your types and show Payload where to find your config, open your `package.json` and update the `scripts` property to the following:
|
||||
To add an npm script to generate your types and show Payload where to find your config, open your `package.json` and update the `scripts` property to the following:
|
||||
|
||||
```
|
||||
{
|
||||
@@ -233,4 +233,4 @@ To add an NPM script to generate your types and show Payload where to find your
|
||||
}
|
||||
```
|
||||
|
||||
Now you can run `yarn generate:types` to easily generate your types.
|
||||
Now you can run `pnpm generate:types` to easily generate your types.
|
||||
|
||||
@@ -14,18 +14,18 @@ Payload offers additional storage adapters to handle file uploads. These adapter
|
||||
| AWS S3 | [`@payloadcms/storage-s3`](https://github.com/payloadcms/payload/tree/main/packages/storage-s3) |
|
||||
| Azure | [`@payloadcms/storage-azure`](https://github.com/payloadcms/payload/tree/main/packages/storage-azure) |
|
||||
| Google Cloud Storage | [`@payloadcms/storage-gcs`](https://github.com/payloadcms/payload/tree/main/packages/storage-gcs) |
|
||||
| Uploadthing | [`@payloadcms/storage-uploadthing`](https://github.com/payloadcms/payload/tree/main/packages/uploadthing) |
|
||||
| Uploadthing | [`@payloadcms/storage-uploadthing`](https://github.com/payloadcms/payload/tree/main/packages/storage-uploadthing) |
|
||||
|
||||
## Vercel Blob Storage
|
||||
[`@payloadcms/storage-vercel-blob`](https://www.npmjs.com/package/@payloadcms/storage-vercel-blob)
|
||||
|
||||
### Installation
|
||||
### Installation#vercel-blob-installation
|
||||
|
||||
```sh
|
||||
pnpm add @payloadcms/storage-vercel-blob
|
||||
```
|
||||
|
||||
### Usage
|
||||
### Usage#vercel-blob-usage
|
||||
|
||||
- Configure the `collections` object to specify which collections should use the Vercel Blob adapter. The slug _must_ match one of your existing collection slugs.
|
||||
- Ensure you have `BLOB_READ_WRITE_TOKEN` set in your Vercel environment variables. This is usually set by Vercel automatically after adding blob storage to your project.
|
||||
@@ -55,7 +55,7 @@ export default buildConfig({
|
||||
})
|
||||
```
|
||||
|
||||
### Configuration Options
|
||||
### Configuration Options#vercel-blob-configuration
|
||||
|
||||
| Option | Description | Default |
|
||||
| -------------------- | -------------------------------------------------------------------- | ----------------------------- |
|
||||
@@ -68,13 +68,13 @@ export default buildConfig({
|
||||
## S3 Storage
|
||||
[`@payloadcms/storage-s3`](https://www.npmjs.com/package/@payloadcms/storage-s3)
|
||||
|
||||
### Installation
|
||||
### Installation#s3-installation
|
||||
|
||||
```sh
|
||||
pnpm add @payloadcms/storage-s3
|
||||
```
|
||||
|
||||
### Usage
|
||||
### Usage#s3-usage
|
||||
|
||||
- Configure the `collections` object to specify which collections should use the S3 Storage adapter. The slug _must_ match one of your existing collection slugs.
|
||||
- The `config` object can be any [`S3ClientConfig`](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/client/s3) object (from [`@aws-sdk/client-s3`](https://github.com/aws/aws-sdk-js-v3)). _This is highly dependent on your AWS setup_. Check the AWS documentation for more information.
|
||||
@@ -109,20 +109,20 @@ export default buildConfig({
|
||||
})
|
||||
```
|
||||
|
||||
#### Configuration Options
|
||||
### Configuration Options#s3-configuration
|
||||
|
||||
See the the [AWS SDK Package](https://github.com/aws/aws-sdk-js-v3) and [`S3ClientConfig`](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/client/s3) object for guidance on AWS S3 configuration.
|
||||
|
||||
## Azure Blob Storage
|
||||
[`@payloadcms/storage-azure`](https://www.npmjs.com/package/@payloadcms/storage-azure)
|
||||
|
||||
### Installation
|
||||
### Installation#azure-installation
|
||||
|
||||
```sh
|
||||
pnpm add @payloadcms/storage-azure
|
||||
```
|
||||
|
||||
### Usage
|
||||
### Usage#azure-usage
|
||||
|
||||
- Configure the `collections` object to specify which collections should use the Azure Blob adapter. The slug _must_ match one of your existing collection slugs.
|
||||
- When enabled, this package will automatically set `disableLocalStorage` to `true` for each collection.
|
||||
@@ -151,7 +151,7 @@ export default buildConfig({
|
||||
})
|
||||
```
|
||||
|
||||
### Configuration Options
|
||||
### Configuration Options#azure-configuration
|
||||
|
||||
| Option | Description | Default |
|
||||
| ---------------------- | ------------------------------------------------------------------------ | ------- |
|
||||
@@ -165,13 +165,13 @@ export default buildConfig({
|
||||
## Google Cloud Storage
|
||||
[`@payloadcms/storage-gcs`](https://www.npmjs.com/package/@payloadcms/storage-gcs)
|
||||
|
||||
### Installation
|
||||
### Installation#gcs-installation
|
||||
|
||||
```sh
|
||||
pnpm add @payloadcms/storage-gcs
|
||||
```
|
||||
|
||||
### Usage
|
||||
### Usage#gcs-usage
|
||||
|
||||
- Configure the `collections` object to specify which collections should use the Google Cloud Storage adapter. The slug _must_ match one of your existing collection slugs.
|
||||
- When enabled, this package will automatically set `disableLocalStorage` to `true` for each collection.
|
||||
@@ -201,7 +201,7 @@ export default buildConfig({
|
||||
})
|
||||
```
|
||||
|
||||
### Configuration Options
|
||||
### Configuration Options#gcs-configuration
|
||||
|
||||
| Option | Description | Default |
|
||||
| ------------- | --------------------------------------------------------------------------------------------------- | --------- |
|
||||
@@ -215,13 +215,13 @@ export default buildConfig({
|
||||
## Uploadthing Storage
|
||||
[`@payloadcms/storage-uploadthing`](https://www.npmjs.com/package/@payloadcms/storage-uploadthing)
|
||||
|
||||
### Installation
|
||||
### Installation#uploadthing-installation
|
||||
|
||||
```sh
|
||||
pnpm add @payloadcms/storage-uploadthing
|
||||
```
|
||||
|
||||
### Usage
|
||||
### Usage#uploadthing-usage
|
||||
|
||||
- Configure the `collections` object to specify which collections should use uploadthing. The slug _must_ match one of your existing collection slugs and be an `upload` type.
|
||||
- Get a token from Uploadthing and set it as `token` in the `options` object.
|
||||
@@ -244,7 +244,7 @@ export default buildConfig({
|
||||
})
|
||||
```
|
||||
|
||||
### Configuration Options
|
||||
### Configuration Options#uploadthing-configuration
|
||||
|
||||
| Option | Description | Default |
|
||||
| ---------------- | ----------------------------------------------- | ------------- |
|
||||
@@ -259,11 +259,11 @@ export default buildConfig({
|
||||
|
||||
If you need to create a custom storage adapter, you can use the [`@payloadcms/plugin-cloud-storage`](https://www.npmjs.com/package/@payloadcms/plugin-cloud-storage) package. This package is used internally by the storage adapters mentioned above.
|
||||
|
||||
### Installation
|
||||
### Installation#custom-installation
|
||||
|
||||
`pnpm add @payloadcms/plugin-cloud-storage`
|
||||
|
||||
### Usage
|
||||
### Usage#custom-usage
|
||||
|
||||
Reference any of the existing storage adapters for guidance on how this should be structured. Create an adapter following the `GeneratedAdapter` interface. Then, pass the adapter to the `cloudStorage` plugin.
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@ export const rootEslintConfig = [
|
||||
'test/live-preview/next-app',
|
||||
'packages/**/*.spec.ts',
|
||||
'templates/**',
|
||||
'examples/**',
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
@@ -2,14 +2,7 @@
|
||||
|
||||
This [Payload Auth Example](https://github.com/payloadcms/payload/tree/main/examples/auth) demonstrates how to implement [Payload Authentication](https://payloadcms.com/docs/authentication/overview) into all types of applications. Follow the [Quick Start](#quick-start) to get up and running quickly.
|
||||
|
||||
**IMPORTANT—This example includes a fully integrated Next.js App Router front-end that runs on the same server as Payload.** If you are working on an application running on an entirely separate server, there are various fully working, separately running front-ends made explicitly for this example, including:
|
||||
|
||||
- [Next.js App Router](../next-app)
|
||||
- [Next.js Pages Router](../next-pages)
|
||||
|
||||
Those applications run directly alongside this one. Follow the instructions in each respective README to get started. If you are setting up authentication for another front-end, please consider contributing to this repo with your own example!
|
||||
|
||||
To learn more about this, [check out how Payload can be used in its various headless capacities](https://payloadcms.com/blog/the-ultimate-guide-to-using-nextjs-with-payload).
|
||||
**IMPORTANT—This example includes a fully integrated Next.js App Router front-end that runs on the same server as Payload.** If you are working on an application running on an entirely separate server, the principals are generally the same. To learn more about this, [check out how Payload can be used in its various headless capacities](https://payloadcms.com/blog/the-ultimate-guide-to-using-nextjs-with-payload).
|
||||
|
||||
## Quick Start
|
||||
|
||||
@@ -132,7 +125,3 @@ If you are using an integrated Next.js setup, the easiest way to deploy your Nex
|
||||
## Questions
|
||||
|
||||
If you have any issues or questions, reach out to us on [Discord](https://discord.com/invite/payload) or start a [GitHub discussion](https://github.com/payloadcms/payload/discussions).
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
@@ -1 +0,0 @@
|
||||
NEXT_PUBLIC_PAYLOAD_URL=http://localhost:3000
|
||||
@@ -1,7 +0,0 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
extends: ['plugin:@next/next/recommended', '@payloadcms'],
|
||||
rules: {
|
||||
'import/extensions': 'off',
|
||||
},
|
||||
}
|
||||
6
examples/auth/next-app/.gitignore
vendored
6
examples/auth/next-app/.gitignore
vendored
@@ -1,6 +0,0 @@
|
||||
.next
|
||||
dist
|
||||
build
|
||||
node_modules
|
||||
.env
|
||||
package-lock.json
|
||||
@@ -1,44 +0,0 @@
|
||||
# Payload Auth Example Front-End
|
||||
|
||||
This is a [Next.js](https://nextjs.org) [App Router](https://nextjs.org/docs/app) front-end made explicitly for the [Payload Auth Example](https://github.com/payloadcms/payload/tree/main/examples/auth). This example demonstrates how to authenticate your Next.js app using [Payload Authentication](https://payloadcms.com/docs/authentication/overview).
|
||||
|
||||
> This example uses the App Router, the latest API of Next.js. If your app is using the legacy [Pages Router](https://nextjs.org/docs/pages), check out the official [Pages Router Example](https://github.com/payloadcms/payload/tree/main/examples/auth/next-pages).
|
||||
|
||||
**IMPORTANT—This application runs on a different server as Payload and establishes a connection from another domain or port over HTTP.** For an integrated setup that runs on a single server and uses the [Local API](https://payloadcms.com/docs/local-api/overview#local-api), check out [how to serve Payload alongside Next.js](https://github.com/payloadcms/payload/tree/main/examples/auth/payload). To learn more about this, check out [how Payload can be used in its various headless capacities](https://payloadcms.com/blog/the-ultimate-guide-to-using-nextjs-with-payload).
|
||||
|
||||
## Getting Started
|
||||
|
||||
### Payload
|
||||
|
||||
First you'll need a running Payload app. There is one made explicitly for this example and [can be found here](https://github.com/payloadcms/payload/tree/main/examples/auth/payload). If you have not done so already, clone it down and follow the setup instructions there. This will provide all the necessary APIs that your Next.js app requires for authentication.
|
||||
|
||||
### Next.js
|
||||
|
||||
1. Clone this repo
|
||||
2. `cd` into this directory and run `pnpm i --ignore-workspace`\*, `yarn`, or `npm install`
|
||||
> \*If you are running using pnpm within the Payload Monorepo, the `--ignore-workspace` flag is needed so that pnpm generates a lockfile in this example's directory despite the fact that one exists in root.
|
||||
3. `cp .env.example .env` to copy the example environment variables
|
||||
4. `pnpm dev`, `yarn dev`, or `npm run dev` to start the server
|
||||
5. `open http://localhost:3001` to see the result
|
||||
|
||||
Once running, a user is automatically seeded in your local environment with some basic instructions. See the [Payload Auth Example](https://github.com/payloadcms/payload/tree/main/examples/auth) for full details.
|
||||
|
||||
## Learn More
|
||||
|
||||
To learn more about Payload and Next.js, take a look at the following resources:
|
||||
|
||||
- [Payload Documentation](https://payloadcms.com/docs) - learn about Payload features and API.
|
||||
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
|
||||
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
|
||||
|
||||
You can check out [the Payload GitHub repository](https://github.com/payloadcms/payload) as well as [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
|
||||
|
||||
## Deployment
|
||||
|
||||
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new) from the creators of Next.js. You could also combine this app into a [single Express server](https://github.com/payloadcms/payload/tree/main/examples/custom-server) and deploy in to [Payload Cloud](https://payloadcms.com/new/import).
|
||||
|
||||
Check out our [Payload deployment documentation](https://payloadcms.com/docs/production/deployment) or the [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
|
||||
|
||||
## Questions
|
||||
|
||||
If you have any issues or questions, reach out to us on [Discord](https://discord.com/invite/payload) or start a [GitHub discussion](https://github.com/payloadcms/payload/discussions).
|
||||
@@ -1,75 +0,0 @@
|
||||
'use client'
|
||||
|
||||
import React, { ElementType } from 'react'
|
||||
import Link from 'next/link'
|
||||
|
||||
import classes from './index.module.scss'
|
||||
|
||||
export type Props = {
|
||||
label?: string
|
||||
appearance?: 'default' | 'primary' | 'secondary'
|
||||
el?: 'button' | 'link' | 'a'
|
||||
onClick?: () => void
|
||||
href?: string
|
||||
newTab?: boolean
|
||||
className?: string
|
||||
type?: 'submit' | 'button'
|
||||
disabled?: boolean
|
||||
invert?: boolean
|
||||
}
|
||||
|
||||
export const Button: React.FC<Props> = ({
|
||||
el: elFromProps = 'link',
|
||||
label,
|
||||
newTab,
|
||||
href,
|
||||
appearance,
|
||||
className: classNameFromProps,
|
||||
onClick,
|
||||
type = 'button',
|
||||
disabled,
|
||||
invert,
|
||||
}) => {
|
||||
let el = elFromProps
|
||||
const newTabProps = newTab ? { target: '_blank', rel: 'noopener noreferrer' } : {}
|
||||
|
||||
const className = [
|
||||
classes.button,
|
||||
classNameFromProps,
|
||||
classes[`appearance--${appearance}`],
|
||||
invert && classes[`${appearance}--invert`],
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(' ')
|
||||
|
||||
const content = (
|
||||
<div className={classes.content}>
|
||||
<span className={classes.label}>{label}</span>
|
||||
</div>
|
||||
)
|
||||
|
||||
if (onClick || type === 'submit') el = 'button'
|
||||
|
||||
if (el === 'link') {
|
||||
return (
|
||||
<Link href={href || ''} className={className} {...newTabProps} onClick={onClick}>
|
||||
{content}
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
|
||||
const Element: ElementType = el
|
||||
|
||||
return (
|
||||
<Element
|
||||
href={href}
|
||||
className={className}
|
||||
type={type}
|
||||
{...newTabProps}
|
||||
onClick={onClick}
|
||||
disabled={disabled}
|
||||
>
|
||||
{content}
|
||||
</Element>
|
||||
)
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
import React, { forwardRef, Ref } from 'react'
|
||||
|
||||
import classes from './index.module.scss'
|
||||
|
||||
type Props = {
|
||||
left?: boolean
|
||||
right?: boolean
|
||||
className?: string
|
||||
children: React.ReactNode
|
||||
ref?: Ref<HTMLDivElement>
|
||||
}
|
||||
|
||||
export const Gutter: React.FC<Props> = forwardRef<HTMLDivElement, Props>((props, ref) => {
|
||||
const { left = true, right = true, className, children } = props
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
className={[
|
||||
classes.gutter,
|
||||
left && classes.gutterLeft,
|
||||
right && classes.gutterRight,
|
||||
className,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(' ')}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
})
|
||||
|
||||
Gutter.displayName = 'Gutter'
|
||||
@@ -1,38 +0,0 @@
|
||||
'use client'
|
||||
|
||||
import React from 'react'
|
||||
import Link from 'next/link'
|
||||
|
||||
import { useAuth } from '../../../_providers/Auth'
|
||||
|
||||
import classes from './index.module.scss'
|
||||
|
||||
export const HeaderNav: React.FC = () => {
|
||||
const { user } = useAuth()
|
||||
|
||||
return (
|
||||
<nav
|
||||
className={[
|
||||
classes.nav,
|
||||
// fade the nav in on user load to avoid flash of content and layout shift
|
||||
// Vercel also does this in their own website header, see https://vercel.com
|
||||
user === undefined && classes.hide,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(' ')}
|
||||
>
|
||||
{user && (
|
||||
<React.Fragment>
|
||||
<Link href="/account">Account</Link>
|
||||
<Link href="/logout">Logout</Link>
|
||||
</React.Fragment>
|
||||
)}
|
||||
{!user && (
|
||||
<React.Fragment>
|
||||
<Link href="/login">Login</Link>
|
||||
<Link href="/create-account">Create Account</Link>
|
||||
</React.Fragment>
|
||||
)}
|
||||
</nav>
|
||||
)
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
import Image from 'next/image'
|
||||
import Link from 'next/link'
|
||||
import React from 'react'
|
||||
|
||||
import { Gutter } from '../Gutter'
|
||||
import classes from './index.module.scss'
|
||||
import { HeaderNav } from './Nav'
|
||||
|
||||
export function Header() {
|
||||
return (
|
||||
<header className={classes.header}>
|
||||
<Gutter className={classes.wrap}>
|
||||
<Link className={classes.logo} href="/">
|
||||
<picture>
|
||||
<source
|
||||
media="(prefers-color-scheme: dark)"
|
||||
srcSet="https://raw.githubusercontent.com/payloadcms/payload/main/packages/ui/src/assets/payload-logo-light.svg"
|
||||
/>
|
||||
<Image
|
||||
alt="Payload Logo"
|
||||
height={30}
|
||||
src="https://raw.githubusercontent.com/payloadcms/payload/main/packages/ui/src/assets/payload-logo-dark.svg"
|
||||
width={150}
|
||||
/>
|
||||
</picture>
|
||||
</Link>
|
||||
<HeaderNav />
|
||||
</Gutter>
|
||||
</header>
|
||||
)
|
||||
}
|
||||
|
||||
export default Header
|
||||
@@ -1,55 +0,0 @@
|
||||
import React from 'react'
|
||||
import { FieldValues, UseFormRegister, Validate } from 'react-hook-form'
|
||||
|
||||
import classes from './index.module.scss'
|
||||
|
||||
type Props = {
|
||||
name: string
|
||||
label: string
|
||||
register: UseFormRegister<FieldValues & any>
|
||||
required?: boolean
|
||||
error: any
|
||||
type?: 'text' | 'number' | 'password' | 'email'
|
||||
validate?: (value: string) => boolean | string
|
||||
}
|
||||
|
||||
export const Input: React.FC<Props> = ({
|
||||
name,
|
||||
label,
|
||||
required,
|
||||
register,
|
||||
error,
|
||||
type = 'text',
|
||||
validate,
|
||||
}) => {
|
||||
return (
|
||||
<div className={classes.inputWrap}>
|
||||
<label htmlFor="name" className={classes.label}>
|
||||
{`${label} ${required ? '*' : ''}`}
|
||||
</label>
|
||||
<input
|
||||
className={[classes.input, error && classes.error].filter(Boolean).join(' ')}
|
||||
{...{ type }}
|
||||
{...register(name, {
|
||||
required,
|
||||
validate,
|
||||
...(type === 'email'
|
||||
? {
|
||||
pattern: {
|
||||
value: /\S+@\S+\.\S+/,
|
||||
message: 'Please enter a valid email',
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
})}
|
||||
/>
|
||||
{error && (
|
||||
<div className={classes.errorMessage}>
|
||||
{!error?.message && error?.type === 'required'
|
||||
? 'This field is required'
|
||||
: error?.message}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
import React from 'react'
|
||||
|
||||
import classes from './index.module.scss'
|
||||
|
||||
export const Message: React.FC<{
|
||||
message?: React.ReactNode
|
||||
error?: React.ReactNode
|
||||
success?: React.ReactNode
|
||||
warning?: React.ReactNode
|
||||
className?: string
|
||||
}> = ({ message, error, success, warning, className }) => {
|
||||
const messageToRender = message || error || success || warning
|
||||
|
||||
if (messageToRender) {
|
||||
return (
|
||||
<div
|
||||
className={[
|
||||
classes.message,
|
||||
className,
|
||||
error && classes.error,
|
||||
success && classes.success,
|
||||
warning && classes.warning,
|
||||
!error && !success && !warning && classes.default,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(' ')}
|
||||
>
|
||||
{messageToRender}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
return null
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
'use client'
|
||||
|
||||
import { useSearchParams } from 'next/navigation'
|
||||
|
||||
import { Message } from '../Message'
|
||||
|
||||
export const RenderParams: React.FC<{
|
||||
params?: string[]
|
||||
message?: string
|
||||
className?: string
|
||||
}> = ({ params = ['error', 'message', 'success'], message, className }) => {
|
||||
const searchParams = useSearchParams()
|
||||
const paramValues = params.map((param) => searchParams.get(param)).filter(Boolean)
|
||||
|
||||
if (paramValues.length) {
|
||||
return (
|
||||
<div className={className}>
|
||||
{paramValues.map((paramValue) => (
|
||||
<Message
|
||||
key={paramValue}
|
||||
message={(message || 'PARAM')?.replace('PARAM', paramValue || '')}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
import React from 'react'
|
||||
|
||||
import serialize from './serialize'
|
||||
|
||||
import classes from './index.module.scss'
|
||||
|
||||
const RichText: React.FC<{ className?: string; content: any }> = ({ className, content }) => {
|
||||
if (!content) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={[classes.richText, className].filter(Boolean).join(' ')}>
|
||||
{serialize(content)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default RichText
|
||||
@@ -1,92 +0,0 @@
|
||||
import React, { Fragment } from 'react'
|
||||
import escapeHTML from 'escape-html'
|
||||
import { Text } from 'slate'
|
||||
|
||||
// eslint-disable-next-line no-use-before-define
|
||||
type Children = Leaf[]
|
||||
|
||||
type Leaf = {
|
||||
type: string
|
||||
value?: {
|
||||
url: string
|
||||
alt: string
|
||||
}
|
||||
children: Children
|
||||
url?: string
|
||||
[key: string]: unknown
|
||||
}
|
||||
|
||||
const serialize = (children: Children): React.ReactNode[] =>
|
||||
children.map((node, i) => {
|
||||
if (Text.isText(node)) {
|
||||
let text = <span dangerouslySetInnerHTML={{ __html: escapeHTML(node.text) }} />
|
||||
|
||||
if (node.bold) {
|
||||
text = <strong key={i}>{text}</strong>
|
||||
}
|
||||
|
||||
if (node.code) {
|
||||
text = <code key={i}>{text}</code>
|
||||
}
|
||||
|
||||
if (node.italic) {
|
||||
text = <em key={i}>{text}</em>
|
||||
}
|
||||
|
||||
if (node.underline) {
|
||||
text = (
|
||||
<span style={{ textDecoration: 'underline' }} key={i}>
|
||||
{text}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
if (node.strikethrough) {
|
||||
text = (
|
||||
<span style={{ textDecoration: 'line-through' }} key={i}>
|
||||
{text}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
return <Fragment key={i}>{text}</Fragment>
|
||||
}
|
||||
|
||||
if (!node) {
|
||||
return null
|
||||
}
|
||||
|
||||
switch (node.type) {
|
||||
case 'h1':
|
||||
return <h1 key={i}>{serialize(node.children)}</h1>
|
||||
case 'h2':
|
||||
return <h2 key={i}>{serialize(node.children)}</h2>
|
||||
case 'h3':
|
||||
return <h3 key={i}>{serialize(node.children)}</h3>
|
||||
case 'h4':
|
||||
return <h4 key={i}>{serialize(node.children)}</h4>
|
||||
case 'h5':
|
||||
return <h5 key={i}>{serialize(node.children)}</h5>
|
||||
case 'h6':
|
||||
return <h6 key={i}>{serialize(node.children)}</h6>
|
||||
case 'blockquote':
|
||||
return <blockquote key={i}>{serialize(node.children)}</blockquote>
|
||||
case 'ul':
|
||||
return <ul key={i}>{serialize(node.children)}</ul>
|
||||
case 'ol':
|
||||
return <ol key={i}>{serialize(node.children)}</ol>
|
||||
case 'li':
|
||||
return <li key={i}>{serialize(node.children)}</li>
|
||||
case 'link':
|
||||
return (
|
||||
<a href={escapeHTML(node.url)} key={i}>
|
||||
{serialize(node.children)}
|
||||
</a>
|
||||
)
|
||||
|
||||
default:
|
||||
return <p key={i}>{serialize(node.children)}</p>
|
||||
}
|
||||
})
|
||||
|
||||
export default serialize
|
||||
@@ -1,34 +0,0 @@
|
||||
export const USER = `
|
||||
id
|
||||
email
|
||||
firstName
|
||||
lastName
|
||||
`
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export const gql = async (query: string): Promise<any> => {
|
||||
try {
|
||||
const res = await fetch(`${process.env.NEXT_PUBLIC_PAYLOAD_URL}/api/graphql`, {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
query,
|
||||
}),
|
||||
})
|
||||
|
||||
const { data, errors } = await res.json()
|
||||
|
||||
if (errors) {
|
||||
throw new Error(errors[0].message)
|
||||
}
|
||||
|
||||
if (res.ok && data) {
|
||||
return data
|
||||
}
|
||||
} catch (e: unknown) {
|
||||
throw new Error(e as string)
|
||||
}
|
||||
}
|
||||
@@ -1,180 +0,0 @@
|
||||
'use client'
|
||||
|
||||
import React, { createContext, useCallback, useContext, useEffect, useState } from 'react'
|
||||
|
||||
import { User } from '../../payload-types'
|
||||
import { gql, USER } from './gql'
|
||||
import { rest } from './rest'
|
||||
import { AuthContext, Create, ForgotPassword, Login, Logout, ResetPassword } from './types'
|
||||
|
||||
const Context = createContext({} as AuthContext)
|
||||
|
||||
export const AuthProvider: React.FC<{ children: React.ReactNode; api?: 'rest' | 'gql' }> = ({
|
||||
children,
|
||||
api = 'rest',
|
||||
}) => {
|
||||
const [user, setUser] = useState<User | null>()
|
||||
|
||||
const create = useCallback<Create>(
|
||||
async (args) => {
|
||||
if (api === 'rest') {
|
||||
const user = await rest(`${process.env.NEXT_PUBLIC_PAYLOAD_URL}/api/users`, args)
|
||||
setUser(user)
|
||||
return user
|
||||
}
|
||||
|
||||
if (api === 'gql') {
|
||||
const { createUser: user } = await gql(`mutation {
|
||||
createUser(data: { email: "${args.email}", password: "${args.password}", firstName: "${args.firstName}", lastName: "${args.lastName}" }) {
|
||||
${USER}
|
||||
}
|
||||
}`)
|
||||
|
||||
setUser(user)
|
||||
return user
|
||||
}
|
||||
},
|
||||
[api],
|
||||
)
|
||||
|
||||
const login = useCallback<Login>(
|
||||
async (args) => {
|
||||
if (api === 'rest') {
|
||||
const user = await rest(`${process.env.NEXT_PUBLIC_PAYLOAD_URL}/api/users/login`, args)
|
||||
setUser(user)
|
||||
return user
|
||||
}
|
||||
|
||||
if (api === 'gql') {
|
||||
const { loginUser } = await gql(`mutation {
|
||||
loginUser(email: "${args.email}", password: "${args.password}") {
|
||||
user {
|
||||
${USER}
|
||||
}
|
||||
exp
|
||||
}
|
||||
}`)
|
||||
|
||||
setUser(loginUser?.user)
|
||||
return loginUser?.user
|
||||
}
|
||||
},
|
||||
[api],
|
||||
)
|
||||
|
||||
const logout = useCallback<Logout>(async () => {
|
||||
if (api === 'rest') {
|
||||
await rest(`${process.env.NEXT_PUBLIC_PAYLOAD_URL}/api/users/logout`)
|
||||
setUser(null)
|
||||
return
|
||||
}
|
||||
|
||||
if (api === 'gql') {
|
||||
await gql(`mutation {
|
||||
logoutUser
|
||||
}`)
|
||||
|
||||
setUser(null)
|
||||
}
|
||||
}, [api])
|
||||
|
||||
// On mount, get user and set
|
||||
useEffect(() => {
|
||||
const fetchMe = async () => {
|
||||
if (api === 'rest') {
|
||||
const user = await rest(
|
||||
`${process.env.NEXT_PUBLIC_PAYLOAD_URL}/api/users/me`,
|
||||
{},
|
||||
{
|
||||
method: 'GET',
|
||||
},
|
||||
)
|
||||
setUser(user)
|
||||
}
|
||||
|
||||
if (api === 'gql') {
|
||||
const { meUser } = await gql(`query {
|
||||
meUser {
|
||||
user {
|
||||
${USER}
|
||||
}
|
||||
exp
|
||||
}
|
||||
}`)
|
||||
|
||||
setUser(meUser.user)
|
||||
}
|
||||
}
|
||||
|
||||
fetchMe()
|
||||
}, [api])
|
||||
|
||||
const forgotPassword = useCallback<ForgotPassword>(
|
||||
async (args) => {
|
||||
if (api === 'rest') {
|
||||
const user = await rest(
|
||||
`${process.env.NEXT_PUBLIC_PAYLOAD_URL}/api/users/forgot-password`,
|
||||
args,
|
||||
)
|
||||
setUser(user)
|
||||
return user
|
||||
}
|
||||
|
||||
if (api === 'gql') {
|
||||
const { forgotPasswordUser } = await gql(`mutation {
|
||||
forgotPasswordUser(email: "${args.email}")
|
||||
}`)
|
||||
|
||||
return forgotPasswordUser
|
||||
}
|
||||
},
|
||||
[api],
|
||||
)
|
||||
|
||||
const resetPassword = useCallback<ResetPassword>(
|
||||
async (args) => {
|
||||
if (api === 'rest') {
|
||||
const user = await rest(
|
||||
`${process.env.NEXT_PUBLIC_PAYLOAD_URL}/api/users/reset-password`,
|
||||
args,
|
||||
)
|
||||
setUser(user)
|
||||
return user
|
||||
}
|
||||
|
||||
if (api === 'gql') {
|
||||
const { resetPasswordUser } = await gql(`mutation {
|
||||
resetPasswordUser(password: "${args.password}", token: "${args.token}") {
|
||||
user {
|
||||
${USER}
|
||||
}
|
||||
}
|
||||
}`)
|
||||
|
||||
setUser(resetPasswordUser.user)
|
||||
return resetPasswordUser.user
|
||||
}
|
||||
},
|
||||
[api],
|
||||
)
|
||||
|
||||
return (
|
||||
<Context.Provider
|
||||
value={{
|
||||
user,
|
||||
setUser,
|
||||
login,
|
||||
logout,
|
||||
create,
|
||||
resetPassword,
|
||||
forgotPassword,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</Context.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
type UseAuth<T = User> = () => AuthContext // eslint-disable-line no-unused-vars
|
||||
|
||||
export const useAuth: UseAuth = () => useContext(Context)
|
||||
@@ -1,34 +0,0 @@
|
||||
import type { User } from '../../payload-types'
|
||||
|
||||
export const rest = async (
|
||||
url: string,
|
||||
args?: any, // eslint-disable-line @typescript-eslint/no-explicit-any
|
||||
options?: RequestInit,
|
||||
): Promise<User | null | undefined> => {
|
||||
const method = options?.method || 'POST'
|
||||
|
||||
try {
|
||||
const res = await fetch(url, {
|
||||
method,
|
||||
...(method === 'POST' ? { body: JSON.stringify(args) } : {}),
|
||||
credentials: 'include',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...options?.headers,
|
||||
},
|
||||
...options,
|
||||
})
|
||||
|
||||
const { errors, user } = await res.json()
|
||||
|
||||
if (errors) {
|
||||
throw new Error(errors[0].message)
|
||||
}
|
||||
|
||||
if (res.ok) {
|
||||
return user
|
||||
}
|
||||
} catch (e: unknown) {
|
||||
throw new Error(e as string)
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user