fix(templates): seeding in website template moved to a separate route so timeout can be customised (#9327)
This commit is contained in:
@@ -37,7 +37,7 @@ Go to Payload Cloud and [clone this template](https://payloadcms.com/new/clone/w
|
||||
Use the `create-payload-app` CLI to clone this template directly to your machine:
|
||||
|
||||
```bash
|
||||
pnpx create-payload-app@beta my-project -t website
|
||||
pnpx create-payload-app my-project -t website
|
||||
```
|
||||
|
||||
#### Method 3
|
||||
@@ -63,13 +63,13 @@ The Payload config is tailored specifically to the needs of most websites. It is
|
||||
|
||||
### Collections
|
||||
|
||||
See the [Collections](https://payloadcms.com/docs/beta/configuration/collections) docs for details on how to extend this functionality.
|
||||
See the [Collections](https://payloadcms.com/docs/configuration/collections) docs for details on how to extend this functionality.
|
||||
|
||||
- #### Users (Authentication)
|
||||
|
||||
Users are auth-enabled collections that have access to the admin panel and unpublished content. See [Access Control](#access-control) for more details.
|
||||
|
||||
For additional help, see the official [Auth Example](https://github.com/payloadcms/payload/tree/beta/examples/auth) or the [Authentication](https://payloadcms.com/docs/beta/authentication/overview#authentication-overview) docs.
|
||||
For additional help, see the official [Auth Example](https://github.com/payloadcms/payload/tree/examples/auth) or the [Authentication](https://payloadcms.com/docs/authentication/overview#authentication-overview) docs.
|
||||
|
||||
- #### Posts
|
||||
|
||||
@@ -85,7 +85,7 @@ See the [Collections](https://payloadcms.com/docs/beta/configuration/collections
|
||||
|
||||
- #### Categories
|
||||
|
||||
A taxonomy used to group posts together. Categories can be nested inside of one another, for example "News > Technology". See the official [Payload Nested Docs Plugin](https://payloadcms.com/docs/beta/plugins/nested-docs) for more details.
|
||||
A taxonomy used to group posts together. Categories can be nested inside of one another, for example "News > Technology". See the official [Payload Nested Docs Plugin](https://payloadcms.com/docs/plugins/nested-docs) for more details.
|
||||
|
||||
### Globals
|
||||
|
||||
@@ -107,7 +107,7 @@ Basic access control is setup to limit access to various content based based on
|
||||
- `posts`: Everyone can access published posts, but only users can create, update, or delete them.
|
||||
- `pages`: Everyone can access published pages, but only users can create, update, or delete them.
|
||||
|
||||
For more details on how to extend this functionality, see the [Payload Access Control](https://payloadcms.com/docs/beta/access-control/overview#access-control) docs.
|
||||
For more details on how to extend this functionality, see the [Payload Access Control](https://payloadcms.com/docs/access-control/overview#access-control) docs.
|
||||
|
||||
## Layout Builder
|
||||
|
||||
@@ -123,31 +123,31 @@ Each block is fully designed and built into the front-end website that comes wit
|
||||
|
||||
## Lexical editor
|
||||
|
||||
A deep editorial experience that allows complete freedom to focus just on writing content without breaking out of the flow with support for Payload blocks, media, links and other features provided out of the box. See [Lexical](https://payloadcms.com/docs/beta/lexical/overview) docs.
|
||||
A deep editorial experience that allows complete freedom to focus just on writing content without breaking out of the flow with support for Payload blocks, media, links and other features provided out of the box. See [Lexical](https://payloadcms.com/docs/lexical/overview) docs.
|
||||
|
||||
## Draft Preview
|
||||
|
||||
All posts and pages are draft-enabled so you can preview them before publishing them to your website. To do this, these collections use [Versions](https://payloadcms.com/docs/beta/configuration/collections#versions) with `drafts` set to `true`. This means that when you create a new post, project, or page, it will be saved as a draft and will not be visible on your website until you publish it. This also means that you can preview your draft before publishing it to your website. To do this, we automatically format a custom URL which redirects to your front-end to securely fetch the draft version of your content.
|
||||
All posts and pages are draft-enabled so you can preview them before publishing them to your website. To do this, these collections use [Versions](https://payloadcms.com/docs/configuration/collections#versions) with `drafts` set to `true`. This means that when you create a new post, project, or page, it will be saved as a draft and will not be visible on your website until you publish it. This also means that you can preview your draft before publishing it to your website. To do this, we automatically format a custom URL which redirects to your front-end to securely fetch the draft version of your content.
|
||||
|
||||
Since the front-end of this template is statically generated, this also means that pages, posts, and projects will need to be regenerated as changes are made to published documents. To do this, we use an `afterChange` hook to regenerate the front-end when a document has changed and its `_status` is `published`.
|
||||
|
||||
For more details on how to extend this functionality, see the official [Draft Preview Example](https://github.com/payloadcms/payload/tree/beta/examples/draft-preview).
|
||||
For more details on how to extend this functionality, see the official [Draft Preview Example](https://github.com/payloadcms/payload/tree/examples/draft-preview).
|
||||
|
||||
## Live preview
|
||||
|
||||
In addition to draft previews you can also enable live preview to view your end resulting page as you're editing content with full support for SSR rendering. See [Live preview docs](https://payloadcms.com/docs/beta/live-preview/overview) for more details.
|
||||
In addition to draft previews you can also enable live preview to view your end resulting page as you're editing content with full support for SSR rendering. See [Live preview docs](https://payloadcms.com/docs/live-preview/overview) for more details.
|
||||
|
||||
## SEO
|
||||
|
||||
This template comes pre-configured with the official [Payload SEO Plugin](https://payloadcms.com/docs/beta/plugins/seo) for complete SEO control from the admin panel. All SEO data is fully integrated into the front-end website that comes with this template. See [Website](#website) for more details.
|
||||
This template comes pre-configured with the official [Payload SEO Plugin](https://payloadcms.com/docs/plugins/seo) for complete SEO control from the admin panel. All SEO data is fully integrated into the front-end website that comes with this template. See [Website](#website) for more details.
|
||||
|
||||
## Search
|
||||
|
||||
This template also pre-configured with the official [Payload Saerch Plugin](https://payloadcms.com/docs/beta/plugins/search) to showcase how SSR search features can easily be implemented into Next.js with Payload. See [Website](#website) for more details.
|
||||
This template also pre-configured with the official [Payload Saerch Plugin](https://payloadcms.com/docs/plugins/search) to showcase how SSR search features can easily be implemented into Next.js with Payload. See [Website](#website) for more details.
|
||||
|
||||
## Redirects
|
||||
|
||||
If you are migrating an existing site or moving content to a new URL, you can use the `redirects` collection to create a proper redirect from old URLs to new ones. This will ensure that proper request status codes are returned to search engines and that your users are not left with a broken link. This template comes pre-configured with the official [Payload Redirects Plugin](https://payloadcms.com/docs/beta/plugins/redirects) for complete redirect control from the admin panel. All redirects are fully integrated into the front-end website that comes with this template. See [Website](#website) for more details.
|
||||
If you are migrating an existing site or moving content to a new URL, you can use the `redirects` collection to create a proper redirect from old URLs to new ones. This will ensure that proper request status codes are returned to search engines and that your users are not left with a broken link. This template comes pre-configured with the official [Payload Redirects Plugin](https://payloadcms.com/docs/plugins/redirects) for complete redirect control from the admin panel. All redirects are fully integrated into the front-end website that comes with this template. See [Website](#website) for more details.
|
||||
|
||||
## Website
|
||||
|
||||
@@ -207,7 +207,7 @@ To run Payload in production, you need to build and start the Admin panel. To do
|
||||
|
||||
1. Invoke the `next build` script by running `pnpm build` or `npm run build` in your project root. This creates a `.next` directory with a production-ready admin bundle.
|
||||
1. Finally run `pnpm start` or `npm run start` to run Node in production and serve Payload from the `.build` directory.
|
||||
1. When you're ready to go live, see [Deployment](#deployment) for more details.
|
||||
1. When you're ready to go live, see Deployment below for more details.
|
||||
|
||||
### Deploying to Payload Cloud
|
||||
|
||||
@@ -258,7 +258,7 @@ export default buildConfig({
|
||||
// ...
|
||||
```
|
||||
|
||||
There is also a simplified [one click deploy](https://github.com/payloadcms/payload/tree/beta/templates/with-vercel-postgres) to Vercel should you need it.
|
||||
There is also a simplified [one click deploy](https://github.com/payloadcms/payload/tree/templates/with-vercel-postgres) to Vercel should you need it.
|
||||
|
||||
### Self-hosting
|
||||
|
||||
@@ -267,7 +267,7 @@ Before deploying your app, you need to:
|
||||
1. Ensure your app builds and serves in production. See [Production](#production) for more details.
|
||||
2. You can then deploy Payload as you would any other Node.js or Next.js application either directly on a VPS, DigitalOcean's Apps Platform, via Coolify or more. More guides coming soon.
|
||||
|
||||
You can also deploy your app manually, check out the [deployment documentation](https://payloadcms.com/docs/beta/production/deployment) for full details.
|
||||
You can also deploy your app manually, check out the [deployment documentation](https://payloadcms.com/docs/production/deployment) for full details.
|
||||
|
||||
## Questions
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
"graphql": "^16.8.2",
|
||||
"jsonwebtoken": "9.0.2",
|
||||
"lucide-react": "^0.378.0",
|
||||
"next": "15.0.0",
|
||||
"next": "^15",
|
||||
"payload": "latest",
|
||||
"payload-admin-bar": "^1.0.6",
|
||||
"prism-react-renderer": "^2.3.1",
|
||||
|
||||
2
templates/website/pnpm-lock.yaml
generated
2
templates/website/pnpm-lock.yaml
generated
@@ -79,7 +79,7 @@ importers:
|
||||
specifier: ^0.378.0
|
||||
version: 0.378.0(react@19.0.0-rc-65a56d0e-20241020)
|
||||
next:
|
||||
specifier: 15.0.0
|
||||
specifier: ^15
|
||||
version: 15.0.0(react-dom@19.0.0-rc-65a56d0e-20241020(react@19.0.0-rc-65a56d0e-20241020))(react@19.0.0-rc-65a56d0e-20241020)(sass@1.77.4)
|
||||
payload:
|
||||
specifier: latest
|
||||
|
||||
44
templates/website/src/app/(frontend)/next/seed/route.ts
Normal file
44
templates/website/src/app/(frontend)/next/seed/route.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import jwt from 'jsonwebtoken'
|
||||
import { createLocalReq, getPayload } from 'payload'
|
||||
import { seed } from '@/endpoints/seed'
|
||||
import config from '@payload-config'
|
||||
|
||||
const payloadToken = 'payload-token'
|
||||
export const maxDuration = 60 // This function can run for a maximum of 60 seconds
|
||||
|
||||
export async function POST(
|
||||
req: Request & {
|
||||
cookies: {
|
||||
get: (name: string) => {
|
||||
value: string
|
||||
}
|
||||
}
|
||||
},
|
||||
): Promise<Response> {
|
||||
const payload = await getPayload({ config })
|
||||
const token = req.cookies.get(payloadToken)?.value
|
||||
|
||||
let user
|
||||
|
||||
try {
|
||||
user = jwt.verify(token, payload.secret)
|
||||
} catch (error) {
|
||||
payload.logger.error('Error verifying token for live preview:', error)
|
||||
}
|
||||
|
||||
if (!user) {
|
||||
return new Response('Action forbidden.', { status: 403 })
|
||||
}
|
||||
|
||||
try {
|
||||
// Create a Payload request object to pass to the Local API for transactions
|
||||
// At this point you should pass in a user, locale, and any other context you need for the Local API
|
||||
const payloadReq = await createLocalReq({ user }, payload)
|
||||
|
||||
await seed({ payload, req: payloadReq })
|
||||
|
||||
return Response.json({ success: true })
|
||||
} catch {
|
||||
return new Response('Error seeding data.')
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,7 @@ export const Media: CollectionConfig = {
|
||||
{
|
||||
name: 'alt',
|
||||
type: 'text',
|
||||
required: true,
|
||||
//required: true,
|
||||
},
|
||||
{
|
||||
name: 'caption',
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
.seedButton {
|
||||
appearance: none;
|
||||
background: none;
|
||||
border: none;
|
||||
padding: 0;
|
||||
text-decoration: underline;
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
opacity: 0.85;
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,8 @@
|
||||
import React, { Fragment, useCallback, useState } from 'react'
|
||||
import { toast } from '@payloadcms/ui'
|
||||
|
||||
import './index.scss'
|
||||
|
||||
const SuccessMessage: React.FC = () => (
|
||||
<div>
|
||||
Database seeded! You can now{' '}
|
||||
@@ -20,19 +22,53 @@ export const SeedButton: React.FC = () => {
|
||||
const handleClick = useCallback(
|
||||
async (e) => {
|
||||
e.preventDefault()
|
||||
if (loading || seeded) return
|
||||
|
||||
if (seeded) {
|
||||
toast.info('Database already seeded.')
|
||||
return
|
||||
}
|
||||
if (loading) {
|
||||
toast.info('Seeding already in progress.')
|
||||
return
|
||||
}
|
||||
if (error) {
|
||||
toast.error(`An error occurred, please refresh and try again.`)
|
||||
return
|
||||
}
|
||||
|
||||
setLoading(true)
|
||||
|
||||
try {
|
||||
await fetch('/api/seed')
|
||||
setSeeded(true)
|
||||
toast.success(<SuccessMessage />, { duration: 5000 })
|
||||
toast.promise(
|
||||
new Promise((resolve, reject) => {
|
||||
try {
|
||||
fetch('/next/seed', { method: 'POST', credentials: 'include' })
|
||||
.then((res) => {
|
||||
if (res.ok) {
|
||||
resolve(true)
|
||||
setSeeded(true)
|
||||
} else {
|
||||
reject('An error occurred while seeding.')
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
reject(error)
|
||||
})
|
||||
} catch (error) {
|
||||
reject(error)
|
||||
}
|
||||
}),
|
||||
{
|
||||
loading: 'Seeding with data....',
|
||||
success: <SuccessMessage />,
|
||||
error: 'An error occurred while seeding.',
|
||||
},
|
||||
)
|
||||
} catch (err) {
|
||||
setError(err)
|
||||
}
|
||||
},
|
||||
[loading, seeded],
|
||||
[loading, seeded, error],
|
||||
)
|
||||
|
||||
let message = ''
|
||||
@@ -42,9 +78,9 @@ export const SeedButton: React.FC = () => {
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<a href="/api/seed" onClick={handleClick} rel="noopener noreferrer" target="_blank">
|
||||
<button className="seedButton" onClick={handleClick}>
|
||||
Seed your database
|
||||
</a>
|
||||
</button>
|
||||
{message}
|
||||
</Fragment>
|
||||
)
|
||||
|
||||
@@ -43,7 +43,7 @@ export const ImageMedia: React.FC<MediaProps> = (props) => {
|
||||
|
||||
width = fullWidth!
|
||||
height = fullHeight!
|
||||
alt = altFromResource
|
||||
alt = altFromResource || ''
|
||||
|
||||
src = `${process.env.NEXT_PUBLIC_SERVER_URL}${url}`
|
||||
}
|
||||
|
||||
@@ -54,9 +54,9 @@ export interface Config {
|
||||
user: User & {
|
||||
collection: 'users';
|
||||
};
|
||||
jobs?: {
|
||||
jobs: {
|
||||
tasks: unknown;
|
||||
workflows?: unknown;
|
||||
workflows: unknown;
|
||||
};
|
||||
}
|
||||
export interface UserAuthOperations {
|
||||
@@ -138,7 +138,7 @@ export interface Page {
|
||||
*/
|
||||
export interface Media {
|
||||
id: string;
|
||||
alt: string;
|
||||
alt?: string | null;
|
||||
caption?: {
|
||||
root: {
|
||||
type: string;
|
||||
|
||||
@@ -11,7 +11,6 @@ import { Media } from './collections/Media'
|
||||
import { Pages } from './collections/Pages'
|
||||
import { Posts } from './collections/Posts'
|
||||
import { Users } from './collections/Users'
|
||||
import { seedHandler } from './endpoints/seedHandler'
|
||||
import { Footer } from './Footer/config'
|
||||
import { Header } from './Header/config'
|
||||
import { plugins } from './plugins'
|
||||
@@ -66,15 +65,6 @@ export default buildConfig({
|
||||
// database-adapter-config-end
|
||||
collections: [Pages, Posts, Media, Categories, Users],
|
||||
cors: [process.env.NEXT_PUBLIC_SERVER_URL || ''].filter(Boolean),
|
||||
endpoints: [
|
||||
// The seed endpoint is used to populate the database with some example data
|
||||
// You should delete this endpoint before deploying your site to production
|
||||
{
|
||||
handler: seedHandler,
|
||||
method: 'get',
|
||||
path: '/seed',
|
||||
},
|
||||
],
|
||||
globals: [Header, Footer],
|
||||
plugins: [
|
||||
...plugins,
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
"graphql": "^16.8.2",
|
||||
"jsonwebtoken": "9.0.2",
|
||||
"lucide-react": "^0.378.0",
|
||||
"next": "15.0.0",
|
||||
"next": "^15",
|
||||
"payload": "latest",
|
||||
"payload-admin-bar": "^1.0.6",
|
||||
"prism-react-renderer": "^2.3.1",
|
||||
|
||||
11519
templates/with-vercel-website/pnpm-lock.yaml
generated
Normal file
11519
templates/with-vercel-website/pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,44 @@
|
||||
import jwt from 'jsonwebtoken'
|
||||
import { createLocalReq, getPayload } from 'payload'
|
||||
import { seed } from '@/endpoints/seed'
|
||||
import config from '@payload-config'
|
||||
|
||||
const payloadToken = 'payload-token'
|
||||
export const maxDuration = 60 // This function can run for a maximum of 60 seconds
|
||||
|
||||
export async function POST(
|
||||
req: Request & {
|
||||
cookies: {
|
||||
get: (name: string) => {
|
||||
value: string
|
||||
}
|
||||
}
|
||||
},
|
||||
): Promise<Response> {
|
||||
const payload = await getPayload({ config })
|
||||
const token = req.cookies.get(payloadToken)?.value
|
||||
|
||||
let user
|
||||
|
||||
try {
|
||||
user = jwt.verify(token, payload.secret)
|
||||
} catch (error) {
|
||||
payload.logger.error('Error verifying token for live preview:', error)
|
||||
}
|
||||
|
||||
if (!user) {
|
||||
return new Response('Action forbidden.', { status: 403 })
|
||||
}
|
||||
|
||||
try {
|
||||
// Create a Payload request object to pass to the Local API for transactions
|
||||
// At this point you should pass in a user, locale, and any other context you need for the Local API
|
||||
const payloadReq = await createLocalReq({ user }, payload)
|
||||
|
||||
await seed({ payload, req: payloadReq })
|
||||
|
||||
return Response.json({ success: true })
|
||||
} catch {
|
||||
return new Response('Error seeding data.')
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
.seedButton {
|
||||
appearance: none;
|
||||
background: none;
|
||||
border: none;
|
||||
padding: 0;
|
||||
text-decoration: underline;
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
opacity: 0.85;
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,8 @@
|
||||
import React, { Fragment, useCallback, useState } from 'react'
|
||||
import { toast } from '@payloadcms/ui'
|
||||
|
||||
import './index.scss'
|
||||
|
||||
const SuccessMessage: React.FC = () => (
|
||||
<div>
|
||||
Database seeded! You can now{' '}
|
||||
@@ -20,19 +22,53 @@ export const SeedButton: React.FC = () => {
|
||||
const handleClick = useCallback(
|
||||
async (e) => {
|
||||
e.preventDefault()
|
||||
if (loading || seeded) return
|
||||
|
||||
if (seeded) {
|
||||
toast.info('Database already seeded.')
|
||||
return
|
||||
}
|
||||
if (loading) {
|
||||
toast.info('Seeding already in progress.')
|
||||
return
|
||||
}
|
||||
if (error) {
|
||||
toast.error(`An error occurred, please refresh and try again.`)
|
||||
return
|
||||
}
|
||||
|
||||
setLoading(true)
|
||||
|
||||
try {
|
||||
await fetch('/api/seed')
|
||||
setSeeded(true)
|
||||
toast.success(<SuccessMessage />, { duration: 5000 })
|
||||
toast.promise(
|
||||
new Promise((resolve, reject) => {
|
||||
try {
|
||||
const seeded = fetch('/next/seed', { method: 'POST', credentials: 'include' })
|
||||
.then((res) => {
|
||||
if (res.ok) {
|
||||
resolve(true)
|
||||
setSeeded(true)
|
||||
} else {
|
||||
reject('An error occurred while seeding.')
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
reject(error)
|
||||
})
|
||||
} catch (error) {
|
||||
reject(error)
|
||||
}
|
||||
}),
|
||||
{
|
||||
loading: 'Seeding with data....',
|
||||
success: <SuccessMessage />,
|
||||
error: 'An error occurred while seeding.',
|
||||
},
|
||||
)
|
||||
} catch (err) {
|
||||
setError(err)
|
||||
}
|
||||
},
|
||||
[loading, seeded],
|
||||
[loading, seeded, error],
|
||||
)
|
||||
|
||||
let message = ''
|
||||
@@ -42,9 +78,9 @@ export const SeedButton: React.FC = () => {
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<a href="/api/seed" onClick={handleClick} rel="noopener noreferrer" target="_blank">
|
||||
<button className="seedButton" onClick={handleClick}>
|
||||
Seed your database
|
||||
</a>
|
||||
</button>
|
||||
{message}
|
||||
</Fragment>
|
||||
)
|
||||
|
||||
@@ -43,7 +43,7 @@ export const ImageMedia: React.FC<MediaProps> = (props) => {
|
||||
|
||||
width = fullWidth!
|
||||
height = fullHeight!
|
||||
alt = altFromResource
|
||||
alt = altFromResource || ''
|
||||
|
||||
src = `${process.env.NEXT_PUBLIC_SERVER_URL}${url}`
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@ import { Media } from './collections/Media'
|
||||
import { Pages } from './collections/Pages'
|
||||
import { Posts } from './collections/Posts'
|
||||
import { Users } from './collections/Users'
|
||||
import { seedHandler } from './endpoints/seedHandler'
|
||||
import { Footer } from './Footer/config'
|
||||
import { Header } from './Header/config'
|
||||
import { plugins } from './plugins'
|
||||
@@ -66,15 +65,6 @@ export default buildConfig({
|
||||
}),
|
||||
collections: [Pages, Posts, Media, Categories, Users],
|
||||
cors: [process.env.NEXT_PUBLIC_SERVER_URL || ''].filter(Boolean),
|
||||
endpoints: [
|
||||
// The seed endpoint is used to populate the database with some example data
|
||||
// You should delete this endpoint before deploying your site to production
|
||||
{
|
||||
handler: seedHandler,
|
||||
method: 'get',
|
||||
path: '/seed',
|
||||
},
|
||||
],
|
||||
globals: [Header, Footer],
|
||||
plugins: [
|
||||
...plugins,
|
||||
|
||||
Reference in New Issue
Block a user