Skip to content

Commit 7321f9f

Browse files
authored
templates: add support for scheduled publish to the website template [no lint] (#10455)
Adds configuration for vercel cron jobs and scheduled publish/unpublish in website templates
1 parent 17e7ee2 commit 7321f9f

File tree

14 files changed

+432
-7
lines changed

14 files changed

+432
-7
lines changed

scripts/generate-template-variations.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ async function main() {
9595
encodeURI(
9696
`${templateRepoUrlBase}/with-vercel-website` +
9797
'&project-name=payload-project' +
98-
'&env=PAYLOAD_SECRET' +
98+
'&env=PAYLOAD_SECRET%2CCRON_SECRET' +
9999
'&build-command=pnpm run ci' +
100100
'&stores=[{"type":"postgres"},{"type":"blob"}]', // Postgres and Vercel Blob Storage
101101
),

templates/website/.env.example

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,6 @@ PAYLOAD_SECRET=YOUR_SECRET_HERE
99

1010
# Used to configure CORS, format links and more. No trailing slash
1111
NEXT_PUBLIC_SERVER_URL=http://localhost:3000
12+
13+
# Secret used to authenticate cron jobs
14+
CRON_SECRET=YOUR_CRON_SECRET_HERE

templates/website/README.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,38 @@ Although Next.js includes a robust set of caching strategies out of the box, Pay
179179

180180
To spin up this example locally, follow the [Quick Start](#quick-start). Then [Seed](#seed) the database with a few pages, posts, and projects.
181181

182+
### Working with Postgres
183+
184+
Postgres and other SQL-based databases follow a strict schema for managing your data. In comparison to our MongoDB adapter, this means that there's a few extra steps to working with Postgres.
185+
186+
Note that often times when making big schema changes you can run the risk of losing data if you're not manually migrating it.
187+
188+
#### Local development
189+
190+
Ideally we recommend running a local copy of your database so that schema updates are as fast as possible. By default the Postgres adapter has `push: true` for development environments. This will let you add, modify and remove fields and collections without needing to run any data migrations.
191+
192+
If your database is pointed to production you will want to set `push: false` otherwise you will risk losing data or having your migrations out of sync.
193+
194+
#### Migrations
195+
196+
[Migrations](https://payloadcms.com/docs/database/migrations) are essentially SQL code versions that keeps track of your schema. When deploy with Postgres you will need to make sure you create and then run your migrations.
197+
198+
Locally create a migration
199+
200+
```bash
201+
pnpm payload migrate:create
202+
```
203+
204+
This creates the migration files you will need to push alongside with your new configuration.
205+
206+
On the server after building and before running `pnpm start` you will want to run your migrations
207+
208+
```bash
209+
pnpm payload migrate
210+
```
211+
212+
This command will check for any migrations that have not yet been run and try to run them and it will keep a record of migrations that have been run in the database.
213+
182214
### Docker
183215

184216
Alternatively, you can use [Docker](https://www.docker.com) to spin up this template locally. To do so, follow these steps:

templates/website/src/collections/Pages/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ export const Pages: CollectionConfig<'pages'> = {
132132
autosave: {
133133
interval: 100, // We set this interval for optimal live preview
134134
},
135+
schedulePublish: true,
135136
},
136137
maxPerDoc: 50,
137138
},

templates/website/src/collections/Posts/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,7 @@ export const Posts: CollectionConfig<'posts'> = {
229229
autosave: {
230230
interval: 100, // We set this interval for optimal live preview
231231
},
232+
schedulePublish: true,
232233
},
233234
maxPerDoc: 50,
234235
},

templates/website/src/payload-types.ts

Lines changed: 158 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ export interface Config {
2020
forms: Form;
2121
'form-submissions': FormSubmission;
2222
search: Search;
23+
'payload-jobs': PayloadJob;
2324
'payload-locked-documents': PayloadLockedDocument;
2425
'payload-preferences': PayloadPreference;
2526
'payload-migrations': PayloadMigration;
@@ -35,6 +36,7 @@ export interface Config {
3536
forms: FormsSelect<false> | FormsSelect<true>;
3637
'form-submissions': FormSubmissionsSelect<false> | FormSubmissionsSelect<true>;
3738
search: SearchSelect<false> | SearchSelect<true>;
39+
'payload-jobs': PayloadJobsSelect<false> | PayloadJobsSelect<true>;
3840
'payload-locked-documents': PayloadLockedDocumentsSelect<false> | PayloadLockedDocumentsSelect<true>;
3941
'payload-preferences': PayloadPreferencesSelect<false> | PayloadPreferencesSelect<true>;
4042
'payload-migrations': PayloadMigrationsSelect<false> | PayloadMigrationsSelect<true>;
@@ -55,7 +57,13 @@ export interface Config {
5557
collection: 'users';
5658
};
5759
jobs: {
58-
tasks: unknown;
60+
tasks: {
61+
schedulePublish: TaskSchedulePublish;
62+
inline: {
63+
input: unknown;
64+
output: unknown;
65+
};
66+
};
5967
workflows: unknown;
6068
};
6169
}
@@ -735,6 +743,98 @@ export interface Search {
735743
updatedAt: string;
736744
createdAt: string;
737745
}
746+
/**
747+
* This interface was referenced by `Config`'s JSON-Schema
748+
* via the `definition` "payload-jobs".
749+
*/
750+
export interface PayloadJob {
751+
id: string;
752+
/**
753+
* Input data provided to the job
754+
*/
755+
input?:
756+
| {
757+
[k: string]: unknown;
758+
}
759+
| unknown[]
760+
| string
761+
| number
762+
| boolean
763+
| null;
764+
taskStatus?:
765+
| {
766+
[k: string]: unknown;
767+
}
768+
| unknown[]
769+
| string
770+
| number
771+
| boolean
772+
| null;
773+
completedAt?: string | null;
774+
totalTried?: number | null;
775+
/**
776+
* If hasError is true this job will not be retried
777+
*/
778+
hasError?: boolean | null;
779+
/**
780+
* If hasError is true, this is the error that caused it
781+
*/
782+
error?:
783+
| {
784+
[k: string]: unknown;
785+
}
786+
| unknown[]
787+
| string
788+
| number
789+
| boolean
790+
| null;
791+
/**
792+
* Task execution log
793+
*/
794+
log?:
795+
| {
796+
executedAt: string;
797+
completedAt: string;
798+
taskSlug: 'inline' | 'schedulePublish';
799+
taskID: string;
800+
input?:
801+
| {
802+
[k: string]: unknown;
803+
}
804+
| unknown[]
805+
| string
806+
| number
807+
| boolean
808+
| null;
809+
output?:
810+
| {
811+
[k: string]: unknown;
812+
}
813+
| unknown[]
814+
| string
815+
| number
816+
| boolean
817+
| null;
818+
state: 'failed' | 'succeeded';
819+
error?:
820+
| {
821+
[k: string]: unknown;
822+
}
823+
| unknown[]
824+
| string
825+
| number
826+
| boolean
827+
| null;
828+
id?: string | null;
829+
}[]
830+
| null;
831+
taskSlug?: ('inline' | 'schedulePublish') | null;
832+
queue?: string | null;
833+
waitUntil?: string | null;
834+
processing?: boolean | null;
835+
updatedAt: string;
836+
createdAt: string;
837+
}
738838
/**
739839
* This interface was referenced by `Config`'s JSON-Schema
740840
* via the `definition` "payload-locked-documents".
@@ -777,6 +877,10 @@ export interface PayloadLockedDocument {
777877
| ({
778878
relationTo: 'search';
779879
value: string | Search;
880+
} | null)
881+
| ({
882+
relationTo: 'payload-jobs';
883+
value: string | PayloadJob;
780884
} | null);
781885
globalSlug?: string | null;
782886
user: {
@@ -1305,6 +1409,37 @@ export interface SearchSelect<T extends boolean = true> {
13051409
updatedAt?: T;
13061410
createdAt?: T;
13071411
}
1412+
/**
1413+
* This interface was referenced by `Config`'s JSON-Schema
1414+
* via the `definition` "payload-jobs_select".
1415+
*/
1416+
export interface PayloadJobsSelect<T extends boolean = true> {
1417+
input?: T;
1418+
taskStatus?: T;
1419+
completedAt?: T;
1420+
totalTried?: T;
1421+
hasError?: T;
1422+
error?: T;
1423+
log?:
1424+
| T
1425+
| {
1426+
executedAt?: T;
1427+
completedAt?: T;
1428+
taskSlug?: T;
1429+
taskID?: T;
1430+
input?: T;
1431+
output?: T;
1432+
state?: T;
1433+
error?: T;
1434+
id?: T;
1435+
};
1436+
taskSlug?: T;
1437+
queue?: T;
1438+
waitUntil?: T;
1439+
processing?: T;
1440+
updatedAt?: T;
1441+
createdAt?: T;
1442+
}
13081443
/**
13091444
* This interface was referenced by `Config`'s JSON-Schema
13101445
* via the `definition` "payload-locked-documents_select".
@@ -1441,6 +1576,28 @@ export interface FooterSelect<T extends boolean = true> {
14411576
createdAt?: T;
14421577
globalType?: T;
14431578
}
1579+
/**
1580+
* This interface was referenced by `Config`'s JSON-Schema
1581+
* via the `definition` "TaskSchedulePublish".
1582+
*/
1583+
export interface TaskSchedulePublish {
1584+
input: {
1585+
type?: ('publish' | 'unpublish') | null;
1586+
locale?: string | null;
1587+
doc?:
1588+
| ({
1589+
relationTo: 'pages';
1590+
value: string | Page;
1591+
} | null)
1592+
| ({
1593+
relationTo: 'posts';
1594+
value: string | Post;
1595+
} | null);
1596+
global?: string | null;
1597+
user?: (string | null) | User;
1598+
};
1599+
output?: unknown;
1600+
}
14441601
/**
14451602
* This interface was referenced by `Config`'s JSON-Schema
14461603
* via the `definition` "BannerBlock".

templates/website/src/payload.config.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { mongooseAdapter } from '@payloadcms/db-mongodb' // database-adapter-imp
33

44
import sharp from 'sharp' // sharp-import
55
import path from 'path'
6-
import { buildConfig } from 'payload'
6+
import { buildConfig, PayloadRequest } from 'payload'
77
import { fileURLToPath } from 'url'
88

99
import { Categories } from './collections/Categories'
@@ -76,4 +76,19 @@ export default buildConfig({
7676
typescript: {
7777
outputFile: path.resolve(dirname, 'payload-types.ts'),
7878
},
79+
jobs: {
80+
access: {
81+
run: ({ req }: { req: PayloadRequest }): boolean => {
82+
// Allow logged in users to execute this endpoint (default)
83+
if (req.user) return true
84+
85+
// If there is no logged in user, then check
86+
// for the Vercel Cron secret to be present as an
87+
// Authorization header:
88+
const authHeader = req.headers.get('authorization')
89+
return authHeader === `Bearer ${process.env.CRON_SECRET}`
90+
},
91+
},
92+
tasks: []
93+
},
7994
})

templates/with-vercel-website/.env.example

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,6 @@ POSTGRES_URL=postgresql://127.0.0.1:5432/your-database-name
33
# Used to encrypt JWT tokens
44
PAYLOAD_SECRET=YOUR_SECRET_HERE
55
# Used to configure CORS, format links and more. No trailing slash
6-
NEXT_PUBLIC_SERVER_URL=http://localhost:3000
6+
NEXT_PUBLIC_SERVER_URL=http://localhost:3000
7+
# Secret used to authenticate cron jobs
8+
CRON_SECRET=YOUR_CRON_SECRET_HERE

templates/with-vercel-website/README.md

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ This is the official [Payload Website Template](https://github.com/payloadcms/pa
44

55
You can deploy to Vercel, using Neon and Vercel Blob Storage with one click:
66

7-
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https://github.com/payloadcms/payload/tree/main/templates/with-vercel-website&project-name=payload-project&env=PAYLOAD_SECRET&build-command=pnpm%20run%20ci&stores=%5B%7B%22type%22:%22postgres%22%7D,%7B%22type%22:%22blob%22%7D%5D)
7+
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https://github.com/payloadcms/payload/tree/main/templates/with-vercel-website&project-name=payload-project&env=PAYLOAD_SECRET%2CCRON_SECRET&build-command=pnpm%20run%20ci&stores=%5B%7B%22type%22:%22postgres%22%7D,%7B%22type%22:%22blob%22%7D%5D)
88

99
This template is right for you if you are working on:
1010

@@ -172,6 +172,38 @@ Core features:
172172

173173
To spin up this example locally, follow the [Quick Start](#quick-start). Then [Seed](#seed) the database with a few pages, posts, and projects.
174174

175+
### Working with Postgres
176+
177+
Postgres and other SQL-based databases follow a strict schema for managing your data. In comparison to our MongoDB adapter, this means that there's a few extra steps to working with Postgres.
178+
179+
Note that often times when making big schema changes you can run the risk of losing data if you're not manually migrating it.
180+
181+
#### Local development
182+
183+
Ideally we recommend running a local copy of your database so that schema updates are as fast as possible. By default the Postgres adapter has `push: true` for development environments. This will let you add, modify and remove fields and collections without needing to run any data migrations.
184+
185+
If your database is pointed to production you will want to set `push: false` otherwise you will risk losing data or having your migrations out of sync.
186+
187+
#### Migrations
188+
189+
[Migrations](https://payloadcms.com/docs/database/migrations) are essentially SQL code versions that keeps track of your schema. When deploy with Postgres you will need to make sure you create and then run your migrations.
190+
191+
Locally create a migration
192+
193+
```bash
194+
pnpm payload migrate:create
195+
```
196+
197+
This creates the migration files you will need to push alongside with your new configuration.
198+
199+
On the server after building and before running `pnpm start` you will want to run your migrations
200+
201+
```bash
202+
pnpm payload migrate
203+
```
204+
205+
This command will check for any migrations that have not yet been run and try to run them and it will keep a record of migrations that have been run in the database.
206+
175207
### Docker
176208

177209
Alternatively, you can use [Docker](https://www.docker.com) to spin up this template locally. To do so, follow these steps:

templates/with-vercel-website/src/collections/Pages/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ export const Pages: CollectionConfig<'pages'> = {
132132
autosave: {
133133
interval: 100, // We set this interval for optimal live preview
134134
},
135+
schedulePublish: true,
135136
},
136137
maxPerDoc: 50,
137138
},

0 commit comments

Comments
 (0)