chore(templates): add generated templates [no-lint] (#6604)

Generate static template variations
This commit is contained in:
Elliot DeNolf
2024-06-05 15:39:28 -04:00
committed by GitHub
parent 2077da8665
commit 1dbb29e847
137 changed files with 17523 additions and 1454 deletions

View File

@@ -56,7 +56,8 @@ jobs:
echo "templates: ${{ steps.filter.outputs.templates }}" echo "templates: ${{ steps.filter.outputs.templates }}"
lint: lint:
if: github.event_name == 'pull_request' 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-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
@@ -500,6 +501,36 @@ jobs:
yarn build yarn build
yarn generate:types yarn generate:types
generated-templates:
needs: build
runs-on: ubuntu-latest
steps:
# 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
with:
node-version: ${{ env.NODE_VERSION }}
- name: Install pnpm
uses: pnpm/action-setup@v3
with:
version: ${{ env.PNPM_VERSION }}
run_install: false
- name: Restore build
uses: actions/cache@v4
timeout-minutes: 10
with:
path: ./*
key: ${{ github.sha }}-${{ github.run_number }}
- name: Build all generated templates
run: pnpm tsx ./scripts/build-generated-templates.ts
all-green: all-green:
name: All Green name: All Green
if: always() if: always()

View File

@@ -61,6 +61,7 @@
"reinstall": "pnpm clean:all && pnpm install", "reinstall": "pnpm clean:all && pnpm install",
"release:alpha": "tsx ./scripts/release.ts --bump prerelease --tag alpha", "release:alpha": "tsx ./scripts/release.ts --bump prerelease --tag alpha",
"release:beta": "tsx ./scripts/release.ts --bump prerelease --tag beta", "release:beta": "tsx ./scripts/release.ts --bump prerelease --tag beta",
"script:gen-templates": "tsx ./scripts/generate-template-variations.ts",
"script:list-published": "tsx scripts/lib/getPackageRegistryVersions.ts", "script:list-published": "tsx scripts/lib/getPackageRegistryVersions.ts",
"script:pack": "tsx scripts/pack-all-to-dest.ts", "script:pack": "tsx scripts/pack-all-to-dest.ts",
"pretest": "pnpm build", "pretest": "pnpm build",
@@ -107,6 +108,7 @@
"changelogen": "^0.5.5", "changelogen": "^0.5.5",
"comment-json": "^4.2.3", "comment-json": "^4.2.3",
"copyfiles": "2.4.1", "copyfiles": "2.4.1",
"create-payload-app": "workspace:*",
"cross-env": "7.0.3", "cross-env": "7.0.3",
"dotenv": "16.4.5", "dotenv": "16.4.5",
"drizzle-kit": "0.20.14-1f2c838", "drizzle-kit": "0.20.14-1f2c838",

View File

@@ -10,10 +10,24 @@
"license": "MIT", "license": "MIT",
"type": "module", "type": "module",
"exports": { "exports": {
"./types": {
"import": "./src/types.ts",
"require": "./src/types.ts"
},
"./commands": { "./commands": {
"import": "./src/lib/init-next.ts", "import": "./src/lib/init-next.ts",
"require": "./src/lib/init-next.ts", "require": "./src/lib/init-next.ts",
"types": "./src/lib/init-next.ts" "types": "./src/lib/init-next.ts"
},
"./lib/*": {
"import": "./src/lib/*",
"require": "./src/lib/*",
"types": "./src/lib/*"
},
"./utils/*": {
"import": "./src/utils/*",
"require": "./src/utils/*",
"types": "./src/utils/*"
} }
}, },
"bin": { "bin": {

View File

@@ -1,21 +1,24 @@
import fse from 'fs-extra' import fse from 'fs-extra'
import globby from 'globby' import globby from 'globby'
import { fileURLToPath } from 'node:url'
import path from 'path' import path from 'path'
const filename = fileURLToPath(import.meta.url)
const dirname = path.dirname(filename)
import type { DbDetails } from '../types.js' import type { DbType, StorageAdapterType } from '../types.js'
import { warning } from '../utils/log.js' import { warning } from '../utils/log.js'
import { dbReplacements } from './packages.js' import { configReplacements, dbReplacements, storageReplacements } from './replacements.js'
/** Update payload config with necessary imports and adapters */ /** Update payload config with necessary imports and adapters */
export async function configurePayloadConfig(args: { export async function configurePayloadConfig(args: {
dbDetails: DbDetails | undefined dbType?: DbType
envNames?: {
dbUri: string
}
packageJsonName?: string
projectDirOrConfigPath: { payloadConfigPath: string } | { projectDir: string } projectDirOrConfigPath: { payloadConfigPath: string } | { projectDir: string }
storageAdapter?: StorageAdapterType
sharp?: boolean
}): Promise<void> { }): Promise<void> {
if (!args.dbDetails) { if (!args.dbType) {
return return
} }
@@ -28,7 +31,7 @@ export async function configurePayloadConfig(args: {
try { try {
const packageObj = await fse.readJson(packageJsonPath) const packageObj = await fse.readJson(packageJsonPath)
const dbPackage = dbReplacements[args.dbDetails.type] const dbPackage = dbReplacements[args.dbType]
// Delete all other db adapters // Delete all other db adapters
Object.values(dbReplacements).forEach((p) => { Object.values(dbReplacements).forEach((p) => {
@@ -40,6 +43,24 @@ export async function configurePayloadConfig(args: {
// Set version of db adapter to match payload version // Set version of db adapter to match payload version
packageObj.dependencies[dbPackage.packageName] = packageObj.dependencies['payload'] packageObj.dependencies[dbPackage.packageName] = packageObj.dependencies['payload']
if (args.storageAdapter) {
const storagePackage = storageReplacements[args.storageAdapter]
if (storagePackage?.packageName) {
// Set version of storage adapter to match payload version
packageObj.dependencies[storagePackage.packageName] = packageObj.dependencies['payload']
}
}
// Sharp provided by default, only remove if explicitly set to false
if (args.sharp === false) {
delete packageObj.dependencies['sharp']
}
if (args.packageJsonName) {
packageObj.name = args.packageJsonName
}
await fse.writeJson(packageJsonPath, packageObj, { spaces: 2 }) await fse.writeJson(packageJsonPath, packageObj, { spaces: 2 })
} catch (err: unknown) { } catch (err: unknown) {
warning(`Unable to configure Payload in package.json`) warning(`Unable to configure Payload in package.json`)
@@ -66,35 +87,54 @@ export async function configurePayloadConfig(args: {
} }
const configContent = fse.readFileSync(payloadConfigPath, 'utf-8') const configContent = fse.readFileSync(payloadConfigPath, 'utf-8')
const configLines = configContent.split('\n') let configLines = configContent.split('\n')
const dbReplacement = dbReplacements[args.dbDetails.type] // DB Replacement
const dbReplacement = dbReplacements[args.dbType]
let dbConfigStartLineIndex: number | undefined configLines = replaceInConfigLines({
let dbConfigEndLineIndex: number | undefined replacement: dbReplacement.configReplacement(args.envNames?.dbUri),
startMatch: `// database-adapter-config-start`,
configLines.forEach((l, i) => { endMatch: `// database-adapter-config-end`,
if (l.includes('// database-adapter-import')) { lines: configLines,
configLines[i] = dbReplacement.importReplacement
}
if (l.includes('// database-adapter-config-start')) {
dbConfigStartLineIndex = i
}
if (l.includes('// database-adapter-config-end')) {
dbConfigEndLineIndex = i
}
}) })
if (!dbConfigStartLineIndex || !dbConfigEndLineIndex) { configLines = replaceInConfigLines({
warning('Unable to update payload.config.ts with database adapter import') replacement: [dbReplacement.importReplacement],
} else { startMatch: '// database-adapter-import',
// Replaces lines between `// database-adapter-config-start` and `// database-adapter-config-end` lines: configLines,
configLines.splice( })
dbConfigStartLineIndex,
dbConfigEndLineIndex - dbConfigStartLineIndex + 1, // Storage Adapter Replacement
...dbReplacement.configReplacement, if (args.storageAdapter) {
) const replacement = storageReplacements[args.storageAdapter]
configLines = replaceInConfigLines({
replacement: replacement.configReplacement,
startMatch: '// storage-adapter-placeholder',
lines: configLines,
})
if (replacement?.importReplacement) {
configLines = replaceInConfigLines({
replacement: [replacement.importReplacement],
startMatch: '// storage-adapter-import-placeholder',
lines: configLines,
})
}
}
// Sharp Replacement (provided by default, only remove if explicitly set to false)
if (args.sharp === false) {
configLines = replaceInConfigLines({
replacement: [],
startMatch: 'sharp,',
lines: configLines,
})
configLines = replaceInConfigLines({
replacement: [],
startMatch: "import sharp from 'sharp'",
lines: configLines,
})
} }
fse.writeFileSync(payloadConfigPath, configLines.join('\n')) fse.writeFileSync(payloadConfigPath, configLines.join('\n'))
@@ -104,3 +144,30 @@ export async function configurePayloadConfig(args: {
) )
} }
} }
function replaceInConfigLines({
replacement,
startMatch,
endMatch,
lines,
}: {
replacement: string[]
startMatch: string
/** Optional endMatch to replace multiple lines */
endMatch?: string
lines: string[]
}) {
if (!replacement) {
return lines
}
const startIndex = lines.findIndex((l) => l.includes(startMatch))
const endIndex = endMatch ? lines.findIndex((l) => l.includes(endMatch)) : startIndex
if (startIndex === -1 || endIndex === -1) {
return lines
}
lines.splice(startIndex, endIndex - startIndex + 1, ...replacement)
return lines
}

View File

@@ -2,7 +2,7 @@ import fse from 'fs-extra'
import path from 'path' import path from 'path'
import type { CliArgs, DbType, ProjectTemplate } from '../types.js' import type { CliArgs, DbType, ProjectTemplate } from '../types.js'
import { createProject } from './create-project.js' import { createProject } from './create-project.js'
import { dbReplacements } from './packages.js' import { dbReplacements } from './replacements.js'
import { getValidTemplates } from './templates.js' import { getValidTemplates } from './templates.js'
import globby from 'globby' import globby from 'globby'
import { jest } from '@jest/globals' import { jest } from '@jest/globals'
@@ -125,7 +125,7 @@ describe('createProject', () => {
expect(content).not.toContain('// database-adapter-config-start') expect(content).not.toContain('// database-adapter-config-start')
expect(content).not.toContain('// database-adapter-config-end') expect(content).not.toContain('// database-adapter-config-end')
expect(content).toContain(dbReplacement.configReplacement.join('\n')) expect(content).toContain(dbReplacement.configReplacement().join('\n'))
}) })
}) })
}) })

View File

@@ -90,7 +90,10 @@ export async function createProject(args: {
await updatePackageJSON({ projectDir, projectName }) await updatePackageJSON({ projectDir, projectName })
spinner.message('Configuring Payload...') spinner.message('Configuring Payload...')
await configurePayloadConfig({ dbDetails, projectDirOrConfigPath: { projectDir } }) await configurePayloadConfig({
dbType: dbDetails?.type,
projectDirOrConfigPath: { projectDir },
})
// Remove yarn.lock file. This is only desired in Payload Cloud. // Remove yarn.lock file. This is only desired in Payload Cloud.
const lockPath = path.resolve(projectDir, 'yarn.lock') const lockPath = path.resolve(projectDir, 'yarn.lock')

View File

@@ -1,35 +0,0 @@
import type { DbType } from '../types.js'
type DbAdapterReplacement = {
configReplacement: string[]
importReplacement: string
packageName: string
}
const mongodbReplacement: DbAdapterReplacement = {
importReplacement: "import { mongooseAdapter } from '@payloadcms/db-mongodb'",
packageName: '@payloadcms/db-mongodb',
// Replacement between `// database-adapter-config-start` and `// database-adapter-config-end`
configReplacement: [
' db: mongooseAdapter({',
" url: process.env.DATABASE_URI || '',",
' }),',
],
}
const postgresReplacement: DbAdapterReplacement = {
configReplacement: [
' db: postgresAdapter({',
' pool: {',
" connectionString: process.env.DATABASE_URI || '',",
' },',
' }),',
],
importReplacement: "import { postgresAdapter } from '@payloadcms/db-postgres'",
packageName: '@payloadcms/db-postgres',
}
export const dbReplacements: Record<DbType, DbAdapterReplacement> = {
mongodb: mongodbReplacement,
postgres: postgresReplacement,
}

View File

@@ -0,0 +1,96 @@
import type { DbType, StorageAdapterType } from '../types.js'
type DbAdapterReplacement = {
configReplacement: (envName?: string) => string[]
importReplacement: string
packageName: string
}
const mongodbReplacement: DbAdapterReplacement = {
// Replacement between `// database-adapter-config-start` and `// database-adapter-config-end`
configReplacement: (envName = 'DATABASE_URI') => [
' db: mongooseAdapter({',
` url: process.env.${envName} || '',`,
' }),',
],
importReplacement: "import { mongooseAdapter } from '@payloadcms/db-mongodb'",
packageName: '@payloadcms/db-mongodb',
}
const postgresReplacement: DbAdapterReplacement = {
configReplacement: (envName = 'DATABASE_URI') => [
' db: postgresAdapter({',
' pool: {',
` connectionString: process.env.${envName} || '',`,
' },',
' }),',
],
importReplacement: "import { postgresAdapter } from '@payloadcms/db-postgres'",
packageName: '@payloadcms/db-postgres',
}
export const dbReplacements: Record<DbType, DbAdapterReplacement> = {
mongodb: mongodbReplacement,
postgres: postgresReplacement,
}
type StorageAdapterReplacement = {
configReplacement: string[]
importReplacement?: string
packageName?: string
}
const vercelBlobStorageReplacement: StorageAdapterReplacement = {
// Replacement of `// storage-adapter-placeholder`
configReplacement: [
' vercelBlobStorage({',
' collections: {',
' [Media.slug]: true,',
' },',
" token: process.env.BLOB_READ_WRITE_TOKEN || '',",
' }),',
],
importReplacement: "import { vercelBlobStorage } from '@payloadcms/storage-vercel-blob'",
packageName: '@payloadcms/storage-vercel-blob',
}
const payloadCloudReplacement: StorageAdapterReplacement = {
configReplacement: [' payloadCloudPlugin(),'],
importReplacement: "import { payloadCloudPlugin } from '@payloadcms/plugin-cloud'",
packageName: '@payloadcms/plugin-cloud',
}
// Removes placeholders
const diskReplacement: StorageAdapterReplacement = {
configReplacement: [],
}
export const storageReplacements: Record<StorageAdapterType, StorageAdapterReplacement> = {
payloadCloud: payloadCloudReplacement,
vercelBlobStorage: vercelBlobStorageReplacement,
localDisk: diskReplacement,
}
/**
* Generic config replacement
*/
type ConfigReplacement = {
configReplacement: {
match: string
replacement: string
}
importReplacement: string
packageName: string
}
export const configReplacements: Record<string, ConfigReplacement> = {
sharp: {
// Replacement of `sharp, // Now optional`
configReplacement: {
match: 'sharp,',
replacement: ' // sharp,',
},
importReplacement: "import sharp from 'sharp'",
packageName: 'sharp',
},
}

View File

@@ -95,7 +95,7 @@ export class Main {
message: chalk.bold(`Upgrade Payload in this project?`), message: chalk.bold(`Upgrade Payload in this project?`),
}) })
if (!p.isCancel(shouldUpdate) || shouldUpdate) { if (!p.isCancel(shouldUpdate) && shouldUpdate) {
const { message, success: updateSuccess } = await updatePayloadInProject(nextAppDetails) const { message, success: updateSuccess } = await updatePayloadInProject(nextAppDetails)
if (updateSuccess) { if (updateSuccess) {
info(message) info(message)
@@ -156,7 +156,7 @@ export class Main {
} }
await configurePayloadConfig({ await configurePayloadConfig({
dbDetails, dbType: dbDetails?.type,
projectDirOrConfigPath: { projectDirOrConfigPath: {
payloadConfigPath: result.payloadConfigPath, payloadConfigPath: result.payloadConfigPath,
}, },

View File

@@ -76,3 +76,5 @@ export type NextAppDetails = {
} }
export type NextConfigType = 'cjs' | 'esm' export type NextConfigType = 'cjs' | 'esm'
export type StorageAdapterType = 'payloadCloud' | 'vercelBlobStorage' | 'localDisk'

View File

@@ -3,6 +3,8 @@ import path from 'path'
/** /**
* Recursively copy files from src to dest * Recursively copy files from src to dest
*
* @internal
*/ */
export function copyRecursiveSync(src: string, dest: string, debug?: boolean) { export function copyRecursiveSync(src: string, dest: string, debug?: boolean) {
const exists = fs.existsSync(src) const exists = fs.existsSync(src)

3
pnpm-lock.yaml generated
View File

@@ -95,6 +95,9 @@ importers:
copyfiles: copyfiles:
specifier: 2.4.1 specifier: 2.4.1
version: 2.4.1 version: 2.4.1
create-payload-app:
specifier: workspace:*
version: link:packages/create-payload-app
cross-env: cross-env:
specifier: 7.0.3 specifier: 7.0.3
version: 7.0.3 version: 7.0.3

View File

@@ -0,0 +1,36 @@
import * as fs from 'node:fs/promises'
import { fileURLToPath } from 'node:url'
import path from 'path'
import { execSync } from 'child_process'
const filename = fileURLToPath(import.meta.url)
const dirname = path.dirname(filename)
main().catch((error) => {
console.error(error)
process.exit(1)
})
async function main() {
// Get all directories in `templates` directory
const repoRoot = path.resolve(dirname, '..')
const templatesDir = path.resolve(repoRoot, 'templates')
const rawTemplateDirs = await fs.readdir(templatesDir, { withFileTypes: true })
const templateDirnames = rawTemplateDirs
.filter(
(dirent) =>
dirent.isDirectory() && (dirent.name.startsWith('with') || dirent.name == 'blank'),
)
.map((dirent) => dirent.name)
console.log(`Found generated templates: ${templateDirnames}`)
// Build each template
for (const template of templateDirnames) {
const cmd = `cd ${templatesDir}/${template} && pnpm install --ignore-workspace --no-frozen-lockfile && pnpm build`
console.log(`🔧 Building ${template}...`)
console.log(` cmd: ${cmd}\n\n`)
execSync(cmd, { stdio: 'inherit' })
}
}

View File

@@ -0,0 +1,196 @@
/**
* This script generates variations of the templates into the `templates` directory.
*
* How to use:
*
* pnpm run script:gen-templates
*
* NOTE: You will likely have to commit by using the `--no-verify` flag to avoid the repo linting
* There is no way currently to have lint-staged ignore the templates directory.
*/
import type { DbType, StorageAdapterType } from 'packages/create-payload-app/src/types.js'
import { configurePayloadConfig } from 'create-payload-app/lib/configure-payload-config.js'
import { copyRecursiveSync } from 'create-payload-app/utils/copy-recursive-sync.js'
import * as fs from 'node:fs/promises'
import { fileURLToPath } from 'node:url'
import path from 'path'
import { execSync } from 'child_process'
const filename = fileURLToPath(import.meta.url)
const dirname = path.dirname(filename)
type TemplateVariations = {
/** package.json name */
name: string
/** Directory in templates dir */
dirname: string
db: DbType
storage: StorageAdapterType
sharp: boolean
vercelDeployButtonLink?: string
envNames?: {
dbUri: string
}
}
main().catch((error) => {
console.error(error)
process.exit(1)
})
async function main() {
const templatesDir = path.resolve(dirname, '../templates')
// WARNING: This will need to be updated when this merges into main
const templateRepoUrlBase = `https://github.com/payloadcms/payload/tree/beta/templates`
const variations: TemplateVariations[] = [
{
name: 'payload-vercel-postgres-template',
dirname: 'with-vercel-postgres',
db: 'postgres',
storage: 'vercelBlobStorage',
sharp: false,
vercelDeployButtonLink:
`https://vercel.com/new/clone?repository-url=` +
encodeURI(
`${templateRepoUrlBase}/${dirname}` +
'&project-name=payload-project' +
'&env=PAYLOAD_SECRET' +
'&build-command=pnpm run ci' +
'&stores=[{"type":"postgres"},{"type":"blob"}]', // Postgres and Vercel Blob Storage
),
envNames: {
// This will replace the process.env.DATABASE_URI to process.env.POSTGRES_URL
dbUri: 'POSTGRES_URL',
},
},
{
name: 'payload-vercel-mongodb-template',
dirname: 'with-vercel-mongodb',
db: 'mongodb',
storage: 'vercelBlobStorage',
sharp: false,
vercelDeployButtonLink:
`https://vercel.com/new/clone?repository-url=` +
encodeURI(
`${templateRepoUrlBase}/${dirname}` +
'&project-name=payload-project' +
'&env=PAYLOAD_SECRET' +
'&build-command=pnpm run ci' +
'&stores=[{"type":"blob"}]' + // Vercel Blob Storage
'&integration-ids=oac_jnzmjqM10gllKmSrG0SGrHOH', // MongoDB Atlas
),
envNames: {
dbUri: 'MONGODB_URI',
},
},
{
name: 'blank',
dirname: 'blank',
db: 'mongodb',
storage: 'localDisk',
sharp: true,
},
{
name: 'payload-cloud-mongodb-template',
dirname: 'with-payload-cloud',
db: 'mongodb',
storage: 'payloadCloud',
sharp: true,
},
]
for (const {
name,
dirname,
db,
storage,
vercelDeployButtonLink,
envNames,
sharp,
} of variations) {
console.log(`Generating ${name}...`)
const destDir = path.join(templatesDir, dirname)
copyRecursiveSync(path.join(templatesDir, '_template'), destDir)
console.log(`Generated ${name} in ${destDir}`)
// Configure payload config
await configurePayloadConfig({
dbType: db,
packageJsonName: name,
projectDirOrConfigPath: { projectDir: destDir },
storageAdapter: storage,
sharp,
envNames,
})
await generateReadme({
destDir,
data: {
name,
description: name, // TODO: Add descriptions
attributes: { db, storage },
...(vercelDeployButtonLink && { vercelDeployButtonLink }),
},
})
// Copy in initial migration if db is postgres. This contains user and media.
if (db === 'postgres') {
const migrationSrcDir = path.join(templatesDir, '_data/migrations')
const migrationDestDir = path.join(destDir, 'src/migrations')
// Make directory if it doesn't exist
if ((await fs.stat(migrationDestDir).catch(() => null)) === null) {
await fs.mkdir(migrationDestDir, { recursive: true })
}
console.log(`Copying migrations from ${migrationSrcDir} to ${migrationDestDir}`)
copyRecursiveSync(migrationSrcDir, migrationDestDir)
}
// TODO: Email?
// TODO: Sharp?
console.log(`Done configuring payload config for ${destDir}/src/payload.config.ts`)
}
// TODO: Run prettier manually on the generated files, husky blows up
console.log('Running prettier on generated files...')
execSync(`pnpm prettier --write templates "*.{js,jsx,ts,tsx}"`)
console.log('Template generation complete!')
}
async function generateReadme({
destDir,
data: { name, description, attributes, vercelDeployButtonLink },
}: {
destDir: string
data: {
name: string
description: string
attributes: Pick<TemplateVariations, 'db' | 'storage'>
vercelDeployButtonLink?: string
}
}) {
let header = `# ${name}\n`
if (vercelDeployButtonLink) {
header += `\n[![Deploy with Vercel](https://vercel.com/button)](${vercelDeployButtonLink})`
}
const readmeContent = `${header}
${description}
## Attributes
- **Database**: ${attributes.db}
- **Storage Adapter**: ${attributes.storage}
`
const readmePath = path.join(destDir, 'README.md')
await fs.writeFile(readmePath, readmeContent)
console.log(`Generated README.md in ${readmePath}`)
}

View File

@@ -0,0 +1,375 @@
{
"id": "8146d795-d1a9-49be-857d-4320898b38fb",
"prevId": "00000000-0000-0000-0000-000000000000",
"version": "5",
"dialect": "pg",
"tables": {
"users": {
"name": "users",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "serial",
"primaryKey": true,
"notNull": true
},
"updated_at": {
"name": "updated_at",
"type": "timestamp(3) with time zone",
"primaryKey": false,
"notNull": true,
"default": "now()"
},
"created_at": {
"name": "created_at",
"type": "timestamp(3) with time zone",
"primaryKey": false,
"notNull": true,
"default": "now()"
},
"email": {
"name": "email",
"type": "varchar",
"primaryKey": false,
"notNull": true
},
"reset_password_token": {
"name": "reset_password_token",
"type": "varchar",
"primaryKey": false,
"notNull": false
},
"reset_password_expiration": {
"name": "reset_password_expiration",
"type": "timestamp(3) with time zone",
"primaryKey": false,
"notNull": false
},
"salt": {
"name": "salt",
"type": "varchar",
"primaryKey": false,
"notNull": false
},
"hash": {
"name": "hash",
"type": "varchar",
"primaryKey": false,
"notNull": false
},
"login_attempts": {
"name": "login_attempts",
"type": "numeric",
"primaryKey": false,
"notNull": false
},
"lock_until": {
"name": "lock_until",
"type": "timestamp(3) with time zone",
"primaryKey": false,
"notNull": false
}
},
"indexes": {
"users_created_at_idx": {
"name": "users_created_at_idx",
"columns": ["created_at"],
"isUnique": false
},
"users_email_idx": {
"name": "users_email_idx",
"columns": ["email"],
"isUnique": true
}
},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"media": {
"name": "media",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "serial",
"primaryKey": true,
"notNull": true
},
"alt": {
"name": "alt",
"type": "varchar",
"primaryKey": false,
"notNull": true
},
"updated_at": {
"name": "updated_at",
"type": "timestamp(3) with time zone",
"primaryKey": false,
"notNull": true,
"default": "now()"
},
"created_at": {
"name": "created_at",
"type": "timestamp(3) with time zone",
"primaryKey": false,
"notNull": true,
"default": "now()"
},
"url": {
"name": "url",
"type": "varchar",
"primaryKey": false,
"notNull": false
},
"thumbnail_u_r_l": {
"name": "thumbnail_u_r_l",
"type": "varchar",
"primaryKey": false,
"notNull": false
},
"filename": {
"name": "filename",
"type": "varchar",
"primaryKey": false,
"notNull": false
},
"mime_type": {
"name": "mime_type",
"type": "varchar",
"primaryKey": false,
"notNull": false
},
"filesize": {
"name": "filesize",
"type": "numeric",
"primaryKey": false,
"notNull": false
},
"width": {
"name": "width",
"type": "numeric",
"primaryKey": false,
"notNull": false
},
"height": {
"name": "height",
"type": "numeric",
"primaryKey": false,
"notNull": false
},
"focal_x": {
"name": "focal_x",
"type": "numeric",
"primaryKey": false,
"notNull": false
},
"focal_y": {
"name": "focal_y",
"type": "numeric",
"primaryKey": false,
"notNull": false
}
},
"indexes": {
"media_created_at_idx": {
"name": "media_created_at_idx",
"columns": ["created_at"],
"isUnique": false
},
"media_filename_idx": {
"name": "media_filename_idx",
"columns": ["filename"],
"isUnique": true
}
},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"payload_preferences": {
"name": "payload_preferences",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "serial",
"primaryKey": true,
"notNull": true
},
"key": {
"name": "key",
"type": "varchar",
"primaryKey": false,
"notNull": false
},
"value": {
"name": "value",
"type": "jsonb",
"primaryKey": false,
"notNull": false
},
"updated_at": {
"name": "updated_at",
"type": "timestamp(3) with time zone",
"primaryKey": false,
"notNull": true,
"default": "now()"
},
"created_at": {
"name": "created_at",
"type": "timestamp(3) with time zone",
"primaryKey": false,
"notNull": true,
"default": "now()"
}
},
"indexes": {
"payload_preferences_key_idx": {
"name": "payload_preferences_key_idx",
"columns": ["key"],
"isUnique": false
},
"payload_preferences_created_at_idx": {
"name": "payload_preferences_created_at_idx",
"columns": ["created_at"],
"isUnique": false
}
},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"payload_preferences_rels": {
"name": "payload_preferences_rels",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "serial",
"primaryKey": true,
"notNull": true
},
"order": {
"name": "order",
"type": "integer",
"primaryKey": false,
"notNull": false
},
"parent_id": {
"name": "parent_id",
"type": "integer",
"primaryKey": false,
"notNull": true
},
"path": {
"name": "path",
"type": "varchar",
"primaryKey": false,
"notNull": true
},
"users_id": {
"name": "users_id",
"type": "integer",
"primaryKey": false,
"notNull": false
}
},
"indexes": {
"payload_preferences_rels_order_idx": {
"name": "payload_preferences_rels_order_idx",
"columns": ["order"],
"isUnique": false
},
"payload_preferences_rels_parent_idx": {
"name": "payload_preferences_rels_parent_idx",
"columns": ["parent_id"],
"isUnique": false
},
"payload_preferences_rels_path_idx": {
"name": "payload_preferences_rels_path_idx",
"columns": ["path"],
"isUnique": false
}
},
"foreignKeys": {
"payload_preferences_rels_parent_fk": {
"name": "payload_preferences_rels_parent_fk",
"tableFrom": "payload_preferences_rels",
"tableTo": "payload_preferences",
"columnsFrom": ["parent_id"],
"columnsTo": ["id"],
"onDelete": "cascade",
"onUpdate": "no action"
},
"payload_preferences_rels_users_fk": {
"name": "payload_preferences_rels_users_fk",
"tableFrom": "payload_preferences_rels",
"tableTo": "users",
"columnsFrom": ["users_id"],
"columnsTo": ["id"],
"onDelete": "cascade",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"payload_migrations": {
"name": "payload_migrations",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "serial",
"primaryKey": true,
"notNull": true
},
"name": {
"name": "name",
"type": "varchar",
"primaryKey": false,
"notNull": false
},
"batch": {
"name": "batch",
"type": "numeric",
"primaryKey": false,
"notNull": false
},
"updated_at": {
"name": "updated_at",
"type": "timestamp(3) with time zone",
"primaryKey": false,
"notNull": true,
"default": "now()"
},
"created_at": {
"name": "created_at",
"type": "timestamp(3) with time zone",
"primaryKey": false,
"notNull": true,
"default": "now()"
}
},
"indexes": {
"payload_migrations_created_at_idx": {
"name": "payload_migrations_created_at_idx",
"columns": ["created_at"],
"isUnique": false
}
},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
}
},
"enums": {},
"schemas": {},
"_meta": {
"schemas": {},
"tables": {},
"columns": {}
}
}

View File

@@ -0,0 +1,93 @@
import type { MigrateDownArgs, MigrateUpArgs } from '@payloadcms/db-postgres'
import { sql } from '@payloadcms/db-postgres'
export async function up({ payload }: MigrateUpArgs): Promise<void> {
await payload.db.drizzle.execute(sql`
CREATE TABLE IF NOT EXISTS "users" (
"id" serial PRIMARY KEY NOT NULL,
"updated_at" timestamp(3) with time zone DEFAULT now() NOT NULL,
"created_at" timestamp(3) with time zone DEFAULT now() NOT NULL,
"email" varchar NOT NULL,
"reset_password_token" varchar,
"reset_password_expiration" timestamp(3) with time zone,
"salt" varchar,
"hash" varchar,
"login_attempts" numeric,
"lock_until" timestamp(3) with time zone
);
CREATE TABLE IF NOT EXISTS "media" (
"id" serial PRIMARY KEY NOT NULL,
"alt" varchar NOT NULL,
"updated_at" timestamp(3) with time zone DEFAULT now() NOT NULL,
"created_at" timestamp(3) with time zone DEFAULT now() NOT NULL,
"url" varchar,
"thumbnail_u_r_l" varchar,
"filename" varchar,
"mime_type" varchar,
"filesize" numeric,
"width" numeric,
"height" numeric,
"focal_x" numeric,
"focal_y" numeric
);
CREATE TABLE IF NOT EXISTS "payload_preferences" (
"id" serial PRIMARY KEY NOT NULL,
"key" varchar,
"value" jsonb,
"updated_at" timestamp(3) with time zone DEFAULT now() NOT NULL,
"created_at" timestamp(3) with time zone DEFAULT now() NOT NULL
);
CREATE TABLE IF NOT EXISTS "payload_preferences_rels" (
"id" serial PRIMARY KEY NOT NULL,
"order" integer,
"parent_id" integer NOT NULL,
"path" varchar NOT NULL,
"users_id" integer
);
CREATE TABLE IF NOT EXISTS "payload_migrations" (
"id" serial PRIMARY KEY NOT NULL,
"name" varchar,
"batch" numeric,
"updated_at" timestamp(3) with time zone DEFAULT now() NOT NULL,
"created_at" timestamp(3) with time zone DEFAULT now() NOT NULL
);
CREATE INDEX IF NOT EXISTS "users_created_at_idx" ON "users" ("created_at");
CREATE UNIQUE INDEX IF NOT EXISTS "users_email_idx" ON "users" ("email");
CREATE INDEX IF NOT EXISTS "media_created_at_idx" ON "media" ("created_at");
CREATE UNIQUE INDEX IF NOT EXISTS "media_filename_idx" ON "media" ("filename");
CREATE INDEX IF NOT EXISTS "payload_preferences_key_idx" ON "payload_preferences" ("key");
CREATE INDEX IF NOT EXISTS "payload_preferences_created_at_idx" ON "payload_preferences" ("created_at");
CREATE INDEX IF NOT EXISTS "payload_preferences_rels_order_idx" ON "payload_preferences_rels" ("order");
CREATE INDEX IF NOT EXISTS "payload_preferences_rels_parent_idx" ON "payload_preferences_rels" ("parent_id");
CREATE INDEX IF NOT EXISTS "payload_preferences_rels_path_idx" ON "payload_preferences_rels" ("path");
CREATE INDEX IF NOT EXISTS "payload_migrations_created_at_idx" ON "payload_migrations" ("created_at");
DO $$ BEGIN
ALTER TABLE "payload_preferences_rels" ADD CONSTRAINT "payload_preferences_rels_parent_fk" FOREIGN KEY ("parent_id") REFERENCES "payload_preferences"("id") ON DELETE cascade ON UPDATE no action;
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
DO $$ BEGIN
ALTER TABLE "payload_preferences_rels" ADD CONSTRAINT "payload_preferences_rels_users_fk" FOREIGN KEY ("users_id") REFERENCES "users"("id") ON DELETE cascade ON UPDATE no action;
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
`)
}
export async function down({ payload }: MigrateDownArgs): Promise<void> {
await payload.db.drizzle.execute(sql`
DROP TABLE "users";
DROP TABLE "media";
DROP TABLE "payload_preferences";
DROP TABLE "payload_preferences_rels";
DROP TABLE "payload_migrations";`)
}

View File

@@ -0,0 +1,2 @@
DATABASE_URI=mongodb://127.0.0.1/payload-template-blank-3-0
PAYLOAD_SECRET=YOUR_SECRET_HERE

View File

@@ -0,0 +1,8 @@
/** @type {import('eslint').Linter.Config} */
module.exports = {
extends: ['next/core-web-vitals'],
parserOptions: {
project: ['./tsconfig.json'],
tsconfigRootDir: __dirname,
},
}

43
templates/_template/.gitignore vendored Normal file
View File

@@ -0,0 +1,43 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
.yarn/install-state.gz
/.idea/*
!/.idea/runConfigurations
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# local env files
.env*.local
# vercel
.vercel
# typescript
*.tsbuildinfo
next-env.d.ts
.env
/media

View File

@@ -0,0 +1,6 @@
{
"singleQuote": true,
"trailingComma": "all",
"printWidth": 100,
"semi": false
}

View File

@@ -0,0 +1 @@
--install.ignore-engines true

View File

@@ -0,0 +1,69 @@
# From https://github.com/vercel/next.js/blob/canary/examples/with-docker/Dockerfile
FROM node:18-alpine AS base
# Install dependencies only when needed
FROM base AS deps
# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.
RUN apk add --no-cache libc6-compat
WORKDIR /app
# Install dependencies based on the preferred package manager
COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* ./
RUN \
if [ -f yarn.lock ]; then yarn --frozen-lockfile; \
elif [ -f package-lock.json ]; then npm ci; \
elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm i --frozen-lockfile; \
else echo "Lockfile not found." && exit 1; \
fi
# Rebuild the source code only when needed
FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
# Next.js collects completely anonymous telemetry data about general usage.
# Learn more here: https://nextjs.org/telemetry
# Uncomment the following line in case you want to disable telemetry during the build.
# ENV NEXT_TELEMETRY_DISABLED 1
RUN \
if [ -f yarn.lock ]; then yarn run build; \
elif [ -f package-lock.json ]; then npm run build; \
elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm run build; \
else echo "Lockfile not found." && exit 1; \
fi
# Production image, copy all the files and run next
FROM base AS runner
WORKDIR /app
ENV NODE_ENV production
# Uncomment the following line in case you want to disable telemetry during runtime.
# ENV NEXT_TELEMETRY_DISABLED 1
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
COPY --from=builder /app/public ./public
# Set the correct permission for prerender cache
RUN mkdir .next
RUN chown nextjs:nodejs .next
# Automatically leverage output traces to reduce image size
# https://nextjs.org/docs/advanced-features/output-file-tracing
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
USER nextjs
EXPOSE 3000
ENV PORT 3000
# server.js is created by next build from the standalone output
# https://nextjs.org/docs/pages/api-reference/next-config-js/output
CMD HOSTNAME="0.0.0.0" node server.js

View File

@@ -0,0 +1,42 @@
# Payload Blank Template
A blank template for [Payload](https://github.com/payloadcms/payload) to help you get up and running quickly. This repo may have been created by running `npx create-payload-app@latest` and selecting the "blank" template or by cloning this template on [Payload Cloud](https://payloadcms.com/new/clone/blank).
See the official [Examples Directory](https://github.com/payloadcms/payload/tree/main/examples) for details on how to use Payload in a variety of different ways.
## Development
To spin up the project locally, follow these steps:
1. First clone the repo
1. Then `cd YOUR_PROJECT_REPO && cp .env.example .env`
1. Next `yarn && yarn dev` (or `docker-compose up`, see [Docker](#docker))
1. Now `open http://localhost:3000/admin` to access the admin panel
1. Create your first admin user using the form on the page
That's it! Changes made in `./src` will be reflected in your app.
### Docker
Alternatively, you can use [Docker](https://www.docker.com) to spin up this project locally. To do so, follow these steps:
1. Follow [steps 1 and 2 from above](#development), the docker-compose file will automatically use the `.env` file in your project root
1. Next run `docker-compose up`
1. Follow [steps 4 and 5 from above](#development) to login and create your first admin user
That's it! The Docker instance will help you get up and running quickly while also standardizing the development environment across your teams.
## Production
To run Payload in production, you need to build and serve the Admin panel. To do so, follow these steps:
1. First invoke the `payload build` script by running `yarn build` or `npm run build` in your project root. This creates a `./build` directory with a production-ready admin bundle.
1. Then run `yarn serve` or `npm run serve` to run Node in production and serve Payload from the `./build` directory.
### Deployment
The easiest way to deploy your project is to use [Payload Cloud](https://payloadcms.com/new/import), a one-click hosting solution to deploy production-ready instances of your Payload apps directly from your GitHub repo. You can also deploy your app manually, check out the [deployment documentation](https://payloadcms.com/docs/production/deployment) for full 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).

View File

@@ -0,0 +1,43 @@
version: '3'
services:
payload:
image: node:18-alpine
ports:
- '3000:3000'
volumes:
- .:/home/node/app
- node_modules:/home/node/app/node_modules
working_dir: /home/node/app/
command: sh -c "corepack enable && corepack prepare pnpm@latest --activate && pnpm install && pnpm dev"
depends_on:
- mongo
# - postgres
env_file:
- .env
# Ensure your DATABASE_URI uses 'mongo' as the hostname ie. mongodb://mongo/my-db-name
mongo:
image: mongo:latest
ports:
- '27017:27017'
command:
- --storageEngine=wiredTiger
volumes:
- data:/data/db
logging:
driver: none
# Uncomment the following to use postgres
# postgres:
# restart: always
# image: postgres:latest
# volumes:
# - pgdata:/var/lib/postgresql/data
# ports:
# - "5432:5432"
volumes:
data:
# pgdata:
node_modules:

View File

@@ -0,0 +1,8 @@
import { withPayload } from '@payloadcms/next/withPayload'
/** @type {import('next').NextConfig} */
const nextConfig = {
// Your Next.js config here
}
export default withPayload(nextConfig)

View File

@@ -0,0 +1,51 @@
{
"name": "template-blank-3.0",
"version": "1.0.0",
"description": "A blank template to get started with Payload 3.0",
"license": "MIT",
"type": "module",
"scripts": {
"build": "cross-env NODE_OPTIONS=--no-deprecation next build",
"dev": "cross-env NODE_OPTIONS=--no-deprecation next dev",
"devsafe": "rm -rf .next && cross-env NODE_OPTIONS=--no-deprecation next dev",
"generate:types": "payload generate:types",
"lint": "cross-env NODE_OPTIONS=--no-deprecation next lint",
"payload": "cross-env NODE_OPTIONS=--no-deprecation payload",
"start": "cross-env NODE_OPTIONS=--no-deprecation next start"
},
"dependencies": {
"@payloadcms/db-mongodb": "beta",
"@payloadcms/next": "beta",
"@payloadcms/plugin-cloud": "beta",
"@payloadcms/richtext-lexical": "beta",
"cross-env": "^7.0.3",
"graphql": "^16.8.1",
"next": "15.0.0-rc.0",
"payload": "beta",
"react": "^19.0.0-rc-f994737d14-20240522",
"react-dom": "^19.0.0-rc-f994737d14-20240522",
"sharp": "0.32.6"
},
"devDependencies": {
"@types/node": "^20.12.12",
"@types/react": "npm:types-react@19.0.0-beta.2",
"@types/react-dom": "npm:types-react-dom@19.0.0-beta.2",
"dotenv": "^16.4.5",
"eslint": "^8",
"eslint-config-next": "^14.2.3",
"typescript": "^5.4.5"
},
"engines": {
"node": "^18.20.2 || >=20.6.0"
},
"pnpm": {
"overrides": {
"@types/react": "npm:types-react@19.0.0-beta.2",
"@types/react-dom": "npm:types-react-dom@19.0.0-beta.2"
}
},
"overrides": {
"@types/react": "npm:types-react@19.0.0-beta.2",
"@types/react-dom": "npm:types-react-dom@19.0.0-beta.2"
}
}

View File

@@ -0,0 +1,22 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
import type { Metadata } from 'next'
import config from '@payload-config'
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import { NotFoundPage, generatePageMetadata } from '@payloadcms/next/views'
type Args = {
params: {
segments: string[]
}
searchParams: {
[key: string]: string | string[]
}
}
export const generateMetadata = ({ params, searchParams }: Args): Promise<Metadata> =>
generatePageMetadata({ config, params, searchParams })
const NotFound = ({ params, searchParams }: Args) => NotFoundPage({ config, params, searchParams })
export default NotFound

View File

@@ -0,0 +1,22 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
import type { Metadata } from 'next'
import config from '@payload-config'
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import { RootPage, generatePageMetadata } from '@payloadcms/next/views'
type Args = {
params: {
segments: string[]
}
searchParams: {
[key: string]: string | string[]
}
}
export const generateMetadata = ({ params, searchParams }: Args): Promise<Metadata> =>
generatePageMetadata({ config, params, searchParams })
const Page = ({ params, searchParams }: Args) => RootPage({ config, params, searchParams })
export default Page

View File

@@ -0,0 +1,10 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
/* DO NOT MODIFY it because it could be re-written at any time. */
import config from '@payload-config'
import { REST_DELETE, REST_GET, REST_OPTIONS, REST_PATCH, REST_POST } from '@payloadcms/next/routes'
export const GET = REST_GET(config)
export const POST = REST_POST(config)
export const DELETE = REST_DELETE(config)
export const PATCH = REST_PATCH(config)
export const OPTIONS = REST_OPTIONS(config)

View File

@@ -0,0 +1,6 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
/* DO NOT MODIFY it because it could be re-written at any time. */
import config from '@payload-config'
import { GRAPHQL_PLAYGROUND_GET } from '@payloadcms/next/routes'
export const GET = GRAPHQL_PLAYGROUND_GET(config)

View File

@@ -0,0 +1,6 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
/* DO NOT MODIFY it because it could be re-written at any time. */
import config from '@payload-config'
import { GRAPHQL_POST } from '@payloadcms/next/routes'
export const POST = GRAPHQL_POST(config)

View File

@@ -0,0 +1,16 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
import configPromise from '@payload-config'
import '@payloadcms/next/css'
import { RootLayout } from '@payloadcms/next/layouts'
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import React from 'react'
import './custom.scss'
type Args = {
children: React.ReactNode
}
const Layout = ({ children }: Args) => <RootLayout config={configPromise}>{children}</RootLayout>
export default Layout

View File

@@ -0,0 +1,14 @@
import configPromise from '@payload-config'
import { getPayload } from 'payload'
export const GET = async () => {
const payload = await getPayload({
config: configPromise,
})
const data = await payload.find({
collection: 'users',
})
return Response.json(data)
}

View File

@@ -0,0 +1,16 @@
import type { CollectionConfig } from 'payload/types'
export const Media: CollectionConfig = {
slug: 'media',
access: {
read: () => true,
},
fields: [
{
name: 'alt',
type: 'text',
required: true,
},
],
upload: true,
}

View File

@@ -0,0 +1,13 @@
import type { CollectionConfig } from 'payload/types'
export const Users: CollectionConfig = {
slug: 'users',
admin: {
useAsTitle: 'email',
},
auth: true,
fields: [
// Email added by default
// Add more fields as needed
],
}

View File

@@ -0,0 +1,34 @@
// storage-adapter-import-placeholder
import { mongooseAdapter } from '@payloadcms/db-mongodb' // database-adapter-import
import { lexicalEditor } from '@payloadcms/richtext-lexical'
import path from 'path'
import { buildConfig } from 'payload/config'
import { fileURLToPath } from 'url'
import sharp from 'sharp'
import { Users } from './collections/Users'
import { Media } from './collections/Media'
const filename = fileURLToPath(import.meta.url)
const dirname = path.dirname(filename)
export default buildConfig({
admin: {
user: Users.slug,
},
collections: [Users, Media],
editor: lexicalEditor(),
secret: process.env.PAYLOAD_SECRET || '',
typescript: {
outputFile: path.resolve(dirname, 'payload-types.ts'),
},
// database-adapter-config-start
db: mongooseAdapter({
url: process.env.DATABASE_URI || '',
}),
// database-adapter-config-end
sharp,
plugins: [
// storage-adapter-placeholder
],
})

View File

@@ -0,0 +1,44 @@
{
"compilerOptions": {
"baseUrl": ".",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"plugins": [
{
"name": "next"
}
],
"paths": {
"@/*": [
"./src/*"
],
"@payload-config": [
"./src/payload.config.ts"
]
},
"target": "ES2017"
},
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx",
".next/types/**/*.ts"
],
"exclude": [
"node_modules"
]
}

View File

@@ -0,0 +1,16 @@
import type { CollectionConfig } from 'payload/types'
export const Media: CollectionConfig = {
slug: 'media',
access: {
read: () => true,
},
fields: [
{
name: 'alt',
type: 'text',
required: true,
},
],
upload: true,
}

View File

@@ -1,12 +1,13 @@
// storage-adapter-import-placeholder
import { mongooseAdapter } from '@payloadcms/db-mongodb' // database-adapter-import import { mongooseAdapter } from '@payloadcms/db-mongodb' // database-adapter-import
// import { payloadCloud } from '@payloadcms/plugin-cloud'
import { lexicalEditor } from '@payloadcms/richtext-lexical' import { lexicalEditor } from '@payloadcms/richtext-lexical'
import path from 'path' import path from 'path'
import { buildConfig } from 'payload/config' import { buildConfig } from 'payload/config'
// import sharp from 'sharp'
import { fileURLToPath } from 'url' import { fileURLToPath } from 'url'
import sharp from 'sharp'
import { Users } from './collections/Users' import { Users } from './collections/Users'
import { Media } from './collections/Media'
const filename = fileURLToPath(import.meta.url) const filename = fileURLToPath(import.meta.url)
const dirname = path.dirname(filename) const dirname = path.dirname(filename)
@@ -15,9 +16,8 @@ export default buildConfig({
admin: { admin: {
user: Users.slug, user: Users.slug,
}, },
collections: [Users], collections: [Users, Media],
editor: lexicalEditor({}), editor: lexicalEditor(),
// plugins: [payloadCloud()], // TODO: Re-enable when cloud supports 3.0
secret: process.env.PAYLOAD_SECRET || '', secret: process.env.PAYLOAD_SECRET || '',
typescript: { typescript: {
outputFile: path.resolve(dirname, 'payload-types.ts'), outputFile: path.resolve(dirname, 'payload-types.ts'),
@@ -27,13 +27,8 @@ export default buildConfig({
url: process.env.DATABASE_URI || '', url: process.env.DATABASE_URI || '',
}), }),
// database-adapter-config-end // database-adapter-config-end
sharp,
// Sharp is now an optional dependency - plugins: [
// if you want to resize images, crop, set focal point, etc. // storage-adapter-placeholder
// make sure to install it and pass it to the config. ],
// This is temporary - we may make an adapter pattern
// for this before reaching 3.0 stable
// sharp,
}) })

View File

@@ -0,0 +1,2 @@
DATABASE_URI=mongodb://127.0.0.1/payload-template-blank-3-0
PAYLOAD_SECRET=YOUR_SECRET_HERE

View File

@@ -0,0 +1,8 @@
/** @type {import('eslint').Linter.Config} */
module.exports = {
extends: ['next/core-web-vitals'],
parserOptions: {
project: ['./tsconfig.json'],
tsconfigRootDir: __dirname,
},
}

43
templates/blank/.gitignore vendored Normal file
View File

@@ -0,0 +1,43 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
.yarn/install-state.gz
/.idea/*
!/.idea/runConfigurations
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# local env files
.env*.local
# vercel
.vercel
# typescript
*.tsbuildinfo
next-env.d.ts
.env
/media

View File

@@ -0,0 +1,6 @@
{
"singleQuote": true,
"trailingComma": "all",
"printWidth": 100,
"semi": false
}

1
templates/blank/.yarnrc Normal file
View File

@@ -0,0 +1 @@
--install.ignore-engines true

View File

@@ -0,0 +1,69 @@
# From https://github.com/vercel/next.js/blob/canary/examples/with-docker/Dockerfile
FROM node:18-alpine AS base
# Install dependencies only when needed
FROM base AS deps
# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.
RUN apk add --no-cache libc6-compat
WORKDIR /app
# Install dependencies based on the preferred package manager
COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* ./
RUN \
if [ -f yarn.lock ]; then yarn --frozen-lockfile; \
elif [ -f package-lock.json ]; then npm ci; \
elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm i --frozen-lockfile; \
else echo "Lockfile not found." && exit 1; \
fi
# Rebuild the source code only when needed
FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
# Next.js collects completely anonymous telemetry data about general usage.
# Learn more here: https://nextjs.org/telemetry
# Uncomment the following line in case you want to disable telemetry during the build.
# ENV NEXT_TELEMETRY_DISABLED 1
RUN \
if [ -f yarn.lock ]; then yarn run build; \
elif [ -f package-lock.json ]; then npm run build; \
elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm run build; \
else echo "Lockfile not found." && exit 1; \
fi
# Production image, copy all the files and run next
FROM base AS runner
WORKDIR /app
ENV NODE_ENV production
# Uncomment the following line in case you want to disable telemetry during runtime.
# ENV NEXT_TELEMETRY_DISABLED 1
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
COPY --from=builder /app/public ./public
# Set the correct permission for prerender cache
RUN mkdir .next
RUN chown nextjs:nodejs .next
# Automatically leverage output traces to reduce image size
# https://nextjs.org/docs/advanced-features/output-file-tracing
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
USER nextjs
EXPOSE 3000
ENV PORT 3000
# server.js is created by next build from the standalone output
# https://nextjs.org/docs/pages/api-reference/next-config-js/output
CMD HOSTNAME="0.0.0.0" node server.js

View File

@@ -0,0 +1,8 @@
# blank
blank
## Attributes
- **Database**: mongodb
- **Storage Adapter**: localDisk

View File

@@ -0,0 +1,43 @@
version: '3'
services:
payload:
image: node:18-alpine
ports:
- '3000:3000'
volumes:
- .:/home/node/app
- node_modules:/home/node/app/node_modules
working_dir: /home/node/app/
command: sh -c "corepack enable && corepack prepare pnpm@latest --activate && pnpm install && pnpm dev"
depends_on:
- mongo
# - postgres
env_file:
- .env
# Ensure your DATABASE_URI uses 'mongo' as the hostname ie. mongodb://mongo/my-db-name
mongo:
image: mongo:latest
ports:
- '27017:27017'
command:
- --storageEngine=wiredTiger
volumes:
- data:/data/db
logging:
driver: none
# Uncomment the following to use postgres
# postgres:
# restart: always
# image: postgres:latest
# volumes:
# - pgdata:/var/lib/postgresql/data
# ports:
# - "5432:5432"
volumes:
data:
# pgdata:
node_modules:

View File

@@ -0,0 +1,8 @@
import { withPayload } from '@payloadcms/next/withPayload'
/** @type {import('next').NextConfig} */
const nextConfig = {
// Your Next.js config here
}
export default withPayload(nextConfig)

View File

@@ -0,0 +1,51 @@
{
"name": "blank",
"version": "1.0.0",
"description": "A blank template to get started with Payload 3.0",
"license": "MIT",
"type": "module",
"scripts": {
"build": "cross-env NODE_OPTIONS=--no-deprecation next build",
"dev": "cross-env NODE_OPTIONS=--no-deprecation next dev",
"devsafe": "rm -rf .next && cross-env NODE_OPTIONS=--no-deprecation next dev",
"generate:types": "payload generate:types",
"lint": "cross-env NODE_OPTIONS=--no-deprecation next lint",
"payload": "cross-env NODE_OPTIONS=--no-deprecation payload",
"start": "cross-env NODE_OPTIONS=--no-deprecation next start"
},
"dependencies": {
"@payloadcms/db-mongodb": "beta",
"@payloadcms/next": "beta",
"@payloadcms/plugin-cloud": "beta",
"@payloadcms/richtext-lexical": "beta",
"cross-env": "^7.0.3",
"graphql": "^16.8.1",
"next": "15.0.0-rc.0",
"payload": "beta",
"react": "^19.0.0-rc-f994737d14-20240522",
"react-dom": "^19.0.0-rc-f994737d14-20240522",
"sharp": "0.32.6"
},
"devDependencies": {
"@types/node": "^20.12.12",
"@types/react": "npm:types-react@19.0.0-beta.2",
"@types/react-dom": "npm:types-react-dom@19.0.0-beta.2",
"dotenv": "^16.4.5",
"eslint": "^8",
"eslint-config-next": "^14.2.3",
"typescript": "^5.4.5"
},
"engines": {
"node": "^18.20.2 || >=20.6.0"
},
"pnpm": {
"overrides": {
"@types/react": "npm:types-react@19.0.0-beta.2",
"@types/react-dom": "npm:types-react-dom@19.0.0-beta.2"
}
},
"overrides": {
"@types/react": "npm:types-react@19.0.0-beta.2",
"@types/react-dom": "npm:types-react-dom@19.0.0-beta.2"
}
}

View File

@@ -0,0 +1,22 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
import type { Metadata } from 'next'
import config from '@payload-config'
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import { NotFoundPage, generatePageMetadata } from '@payloadcms/next/views'
type Args = {
params: {
segments: string[]
}
searchParams: {
[key: string]: string | string[]
}
}
export const generateMetadata = ({ params, searchParams }: Args): Promise<Metadata> =>
generatePageMetadata({ config, params, searchParams })
const NotFound = ({ params, searchParams }: Args) => NotFoundPage({ config, params, searchParams })
export default NotFound

View File

@@ -0,0 +1,22 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
import type { Metadata } from 'next'
import config from '@payload-config'
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import { RootPage, generatePageMetadata } from '@payloadcms/next/views'
type Args = {
params: {
segments: string[]
}
searchParams: {
[key: string]: string | string[]
}
}
export const generateMetadata = ({ params, searchParams }: Args): Promise<Metadata> =>
generatePageMetadata({ config, params, searchParams })
const Page = ({ params, searchParams }: Args) => RootPage({ config, params, searchParams })
export default Page

View File

@@ -0,0 +1,10 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
/* DO NOT MODIFY it because it could be re-written at any time. */
import config from '@payload-config'
import { REST_DELETE, REST_GET, REST_OPTIONS, REST_PATCH, REST_POST } from '@payloadcms/next/routes'
export const GET = REST_GET(config)
export const POST = REST_POST(config)
export const DELETE = REST_DELETE(config)
export const PATCH = REST_PATCH(config)
export const OPTIONS = REST_OPTIONS(config)

View File

@@ -0,0 +1,6 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
/* DO NOT MODIFY it because it could be re-written at any time. */
import config from '@payload-config'
import { GRAPHQL_PLAYGROUND_GET } from '@payloadcms/next/routes'
export const GET = GRAPHQL_PLAYGROUND_GET(config)

View File

@@ -0,0 +1,6 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
/* DO NOT MODIFY it because it could be re-written at any time. */
import config from '@payload-config'
import { GRAPHQL_POST } from '@payloadcms/next/routes'
export const POST = GRAPHQL_POST(config)

View File

@@ -0,0 +1,16 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
import configPromise from '@payload-config'
import '@payloadcms/next/css'
import { RootLayout } from '@payloadcms/next/layouts'
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import React from 'react'
import './custom.scss'
type Args = {
children: React.ReactNode
}
const Layout = ({ children }: Args) => <RootLayout config={configPromise}>{children}</RootLayout>
export default Layout

View File

@@ -0,0 +1,14 @@
import configPromise from '@payload-config'
import { getPayload } from 'payload'
export const GET = async () => {
const payload = await getPayload({
config: configPromise,
})
const data = await payload.find({
collection: 'users',
})
return Response.json(data)
}

View File

@@ -0,0 +1,16 @@
import type { CollectionConfig } from 'payload/types'
export const Media: CollectionConfig = {
slug: 'media',
access: {
read: () => true,
},
fields: [
{
name: 'alt',
type: 'text',
required: true,
},
],
upload: true,
}

View File

@@ -0,0 +1,13 @@
import type { CollectionConfig } from 'payload/types'
export const Users: CollectionConfig = {
slug: 'users',
admin: {
useAsTitle: 'email',
},
auth: true,
fields: [
// Email added by default
// Add more fields as needed
],
}

View File

@@ -0,0 +1,30 @@
// storage-adapter-import-placeholder
import { mongooseAdapter } from '@payloadcms/db-mongodb'
import { lexicalEditor } from '@payloadcms/richtext-lexical'
import path from 'path'
import { buildConfig } from 'payload/config'
import { fileURLToPath } from 'url'
import sharp from 'sharp'
import { Users } from './collections/Users'
import { Media } from './collections/Media'
const filename = fileURLToPath(import.meta.url)
const dirname = path.dirname(filename)
export default buildConfig({
admin: {
user: Users.slug,
},
collections: [Users, Media],
editor: lexicalEditor(),
secret: process.env.PAYLOAD_SECRET || '',
typescript: {
outputFile: path.resolve(dirname, 'payload-types.ts'),
},
db: mongooseAdapter({
url: process.env.DATABASE_URI || '',
}),
sharp,
plugins: [],
})

View File

@@ -0,0 +1,44 @@
{
"compilerOptions": {
"baseUrl": ".",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"plugins": [
{
"name": "next"
}
],
"paths": {
"@/*": [
"./src/*"
],
"@payload-config": [
"./src/payload.config.ts"
]
},
"target": "ES2017"
},
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx",
".next/types/**/*.ts"
],
"exclude": [
"node_modules"
]
}

View File

@@ -1,5 +1,5 @@
import { withPayload } from '@payloadcms/next/withPayload' import { withPayload } from '@payloadcms/next/withPayload'
/** @type {import('next').NextConfig} */ /** @type {import('next').NextConfig} */
const nextConfig = {}; const nextConfig = {}
export default withPayload(nextConfig); export default withPayload(nextConfig)

File diff suppressed because it is too large Load Diff

View File

@@ -3,4 +3,4 @@ module.exports = {
tailwindcss: {}, tailwindcss: {},
autoprefixer: {}, autoprefixer: {},
}, },
}; }

View File

@@ -1,4 +1,4 @@
import type { MigrateDownArgs, MigrateUpArgs} from '@payloadcms/db-postgres'; import type { MigrateDownArgs, MigrateUpArgs } from '@payloadcms/db-postgres'
import { sql } from '@payloadcms/db-postgres' import { sql } from '@payloadcms/db-postgres'

View File

@@ -1,20 +1,19 @@
import type { Config } from "tailwindcss"; import type { Config } from 'tailwindcss'
const config: Config = { const config: Config = {
content: [ content: [
"./src/pages/**/*.{js,ts,jsx,tsx,mdx}", './src/pages/**/*.{js,ts,jsx,tsx,mdx}',
"./src/components/**/*.{js,ts,jsx,tsx,mdx}", './src/components/**/*.{js,ts,jsx,tsx,mdx}',
"./src/app/**/*.{js,ts,jsx,tsx,mdx}", './src/app/**/*.{js,ts,jsx,tsx,mdx}',
], ],
theme: { theme: {
extend: { extend: {
backgroundImage: { backgroundImage: {
"gradient-radial": "radial-gradient(var(--tw-gradient-stops))", 'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))',
"gradient-conic": 'gradient-conic': 'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))',
"conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))",
}, },
}, },
}, },
plugins: [], plugins: [],
}; }
export default config; export default config

View File

@@ -0,0 +1,2 @@
DATABASE_URI=mongodb://127.0.0.1/payload-template-blank-3-0
PAYLOAD_SECRET=YOUR_SECRET_HERE

View File

@@ -0,0 +1,8 @@
/** @type {import('eslint').Linter.Config} */
module.exports = {
extends: ['next/core-web-vitals'],
parserOptions: {
project: ['./tsconfig.json'],
tsconfigRootDir: __dirname,
},
}

43
templates/with-payload-cloud/.gitignore vendored Normal file
View File

@@ -0,0 +1,43 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
.yarn/install-state.gz
/.idea/*
!/.idea/runConfigurations
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# local env files
.env*.local
# vercel
.vercel
# typescript
*.tsbuildinfo
next-env.d.ts
.env
/media

View File

@@ -0,0 +1,6 @@
{
"singleQuote": true,
"trailingComma": "all",
"printWidth": 100,
"semi": false
}

View File

@@ -0,0 +1 @@
--install.ignore-engines true

View File

@@ -0,0 +1,69 @@
# From https://github.com/vercel/next.js/blob/canary/examples/with-docker/Dockerfile
FROM node:18-alpine AS base
# Install dependencies only when needed
FROM base AS deps
# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.
RUN apk add --no-cache libc6-compat
WORKDIR /app
# Install dependencies based on the preferred package manager
COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* ./
RUN \
if [ -f yarn.lock ]; then yarn --frozen-lockfile; \
elif [ -f package-lock.json ]; then npm ci; \
elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm i --frozen-lockfile; \
else echo "Lockfile not found." && exit 1; \
fi
# Rebuild the source code only when needed
FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
# Next.js collects completely anonymous telemetry data about general usage.
# Learn more here: https://nextjs.org/telemetry
# Uncomment the following line in case you want to disable telemetry during the build.
# ENV NEXT_TELEMETRY_DISABLED 1
RUN \
if [ -f yarn.lock ]; then yarn run build; \
elif [ -f package-lock.json ]; then npm run build; \
elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm run build; \
else echo "Lockfile not found." && exit 1; \
fi
# Production image, copy all the files and run next
FROM base AS runner
WORKDIR /app
ENV NODE_ENV production
# Uncomment the following line in case you want to disable telemetry during runtime.
# ENV NEXT_TELEMETRY_DISABLED 1
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
COPY --from=builder /app/public ./public
# Set the correct permission for prerender cache
RUN mkdir .next
RUN chown nextjs:nodejs .next
# Automatically leverage output traces to reduce image size
# https://nextjs.org/docs/advanced-features/output-file-tracing
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
USER nextjs
EXPOSE 3000
ENV PORT 3000
# server.js is created by next build from the standalone output
# https://nextjs.org/docs/pages/api-reference/next-config-js/output
CMD HOSTNAME="0.0.0.0" node server.js

View File

@@ -0,0 +1,8 @@
# payload-cloud-mongodb-template
payload-cloud-mongodb-template
## Attributes
- **Database**: mongodb
- **Storage Adapter**: payloadCloud

View File

@@ -0,0 +1,43 @@
version: '3'
services:
payload:
image: node:18-alpine
ports:
- '3000:3000'
volumes:
- .:/home/node/app
- node_modules:/home/node/app/node_modules
working_dir: /home/node/app/
command: sh -c "corepack enable && corepack prepare pnpm@latest --activate && pnpm install && pnpm dev"
depends_on:
- mongo
# - postgres
env_file:
- .env
# Ensure your DATABASE_URI uses 'mongo' as the hostname ie. mongodb://mongo/my-db-name
mongo:
image: mongo:latest
ports:
- '27017:27017'
command:
- --storageEngine=wiredTiger
volumes:
- data:/data/db
logging:
driver: none
# Uncomment the following to use postgres
# postgres:
# restart: always
# image: postgres:latest
# volumes:
# - pgdata:/var/lib/postgresql/data
# ports:
# - "5432:5432"
volumes:
data:
# pgdata:
node_modules:

View File

@@ -0,0 +1,8 @@
import { withPayload } from '@payloadcms/next/withPayload'
/** @type {import('next').NextConfig} */
const nextConfig = {
// Your Next.js config here
}
export default withPayload(nextConfig)

View File

@@ -0,0 +1,51 @@
{
"name": "payload-cloud-mongodb-template",
"version": "1.0.0",
"description": "A blank template to get started with Payload 3.0",
"license": "MIT",
"type": "module",
"scripts": {
"build": "cross-env NODE_OPTIONS=--no-deprecation next build",
"dev": "cross-env NODE_OPTIONS=--no-deprecation next dev",
"devsafe": "rm -rf .next && cross-env NODE_OPTIONS=--no-deprecation next dev",
"generate:types": "payload generate:types",
"lint": "cross-env NODE_OPTIONS=--no-deprecation next lint",
"payload": "cross-env NODE_OPTIONS=--no-deprecation payload",
"start": "cross-env NODE_OPTIONS=--no-deprecation next start"
},
"dependencies": {
"@payloadcms/db-mongodb": "beta",
"@payloadcms/next": "beta",
"@payloadcms/plugin-cloud": "beta",
"@payloadcms/richtext-lexical": "beta",
"cross-env": "^7.0.3",
"graphql": "^16.8.1",
"next": "15.0.0-rc.0",
"payload": "beta",
"react": "^19.0.0-rc-f994737d14-20240522",
"react-dom": "^19.0.0-rc-f994737d14-20240522",
"sharp": "0.32.6"
},
"devDependencies": {
"@types/node": "^20.12.12",
"@types/react": "npm:types-react@19.0.0-beta.2",
"@types/react-dom": "npm:types-react-dom@19.0.0-beta.2",
"dotenv": "^16.4.5",
"eslint": "^8",
"eslint-config-next": "^14.2.3",
"typescript": "^5.4.5"
},
"engines": {
"node": "^18.20.2 || >=20.6.0"
},
"pnpm": {
"overrides": {
"@types/react": "npm:types-react@19.0.0-beta.2",
"@types/react-dom": "npm:types-react-dom@19.0.0-beta.2"
}
},
"overrides": {
"@types/react": "npm:types-react@19.0.0-beta.2",
"@types/react-dom": "npm:types-react-dom@19.0.0-beta.2"
}
}

9812
templates/with-payload-cloud/pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,22 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
import type { Metadata } from 'next'
import config from '@payload-config'
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import { NotFoundPage, generatePageMetadata } from '@payloadcms/next/views'
type Args = {
params: {
segments: string[]
}
searchParams: {
[key: string]: string | string[]
}
}
export const generateMetadata = ({ params, searchParams }: Args): Promise<Metadata> =>
generatePageMetadata({ config, params, searchParams })
const NotFound = ({ params, searchParams }: Args) => NotFoundPage({ config, params, searchParams })
export default NotFound

View File

@@ -0,0 +1,22 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
import type { Metadata } from 'next'
import config from '@payload-config'
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import { RootPage, generatePageMetadata } from '@payloadcms/next/views'
type Args = {
params: {
segments: string[]
}
searchParams: {
[key: string]: string | string[]
}
}
export const generateMetadata = ({ params, searchParams }: Args): Promise<Metadata> =>
generatePageMetadata({ config, params, searchParams })
const Page = ({ params, searchParams }: Args) => RootPage({ config, params, searchParams })
export default Page

View File

@@ -0,0 +1,10 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
/* DO NOT MODIFY it because it could be re-written at any time. */
import config from '@payload-config'
import { REST_DELETE, REST_GET, REST_OPTIONS, REST_PATCH, REST_POST } from '@payloadcms/next/routes'
export const GET = REST_GET(config)
export const POST = REST_POST(config)
export const DELETE = REST_DELETE(config)
export const PATCH = REST_PATCH(config)
export const OPTIONS = REST_OPTIONS(config)

View File

@@ -0,0 +1,6 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
/* DO NOT MODIFY it because it could be re-written at any time. */
import config from '@payload-config'
import { GRAPHQL_PLAYGROUND_GET } from '@payloadcms/next/routes'
export const GET = GRAPHQL_PLAYGROUND_GET(config)

View File

@@ -0,0 +1,6 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
/* DO NOT MODIFY it because it could be re-written at any time. */
import config from '@payload-config'
import { GRAPHQL_POST } from '@payloadcms/next/routes'
export const POST = GRAPHQL_POST(config)

View File

@@ -0,0 +1,16 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
import configPromise from '@payload-config'
import '@payloadcms/next/css'
import { RootLayout } from '@payloadcms/next/layouts'
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import React from 'react'
import './custom.scss'
type Args = {
children: React.ReactNode
}
const Layout = ({ children }: Args) => <RootLayout config={configPromise}>{children}</RootLayout>
export default Layout

View File

@@ -0,0 +1,14 @@
import configPromise from '@payload-config'
import { getPayload } from 'payload'
export const GET = async () => {
const payload = await getPayload({
config: configPromise,
})
const data = await payload.find({
collection: 'users',
})
return Response.json(data)
}

View File

@@ -0,0 +1,16 @@
import type { CollectionConfig } from 'payload/types'
export const Media: CollectionConfig = {
slug: 'media',
access: {
read: () => true,
},
fields: [
{
name: 'alt',
type: 'text',
required: true,
},
],
upload: true,
}

View File

@@ -0,0 +1,13 @@
import type { CollectionConfig } from 'payload/types'
export const Users: CollectionConfig = {
slug: 'users',
admin: {
useAsTitle: 'email',
},
auth: true,
fields: [
// Email added by default
// Add more fields as needed
],
}

View File

@@ -0,0 +1,30 @@
import { payloadCloudPlugin } from '@payloadcms/plugin-cloud'
import { mongooseAdapter } from '@payloadcms/db-mongodb'
import { lexicalEditor } from '@payloadcms/richtext-lexical'
import path from 'path'
import { buildConfig } from 'payload/config'
import { fileURLToPath } from 'url'
import sharp from 'sharp'
import { Users } from './collections/Users'
import { Media } from './collections/Media'
const filename = fileURLToPath(import.meta.url)
const dirname = path.dirname(filename)
export default buildConfig({
admin: {
user: Users.slug,
},
collections: [Users, Media],
editor: lexicalEditor(),
secret: process.env.PAYLOAD_SECRET || '',
typescript: {
outputFile: path.resolve(dirname, 'payload-types.ts'),
},
db: mongooseAdapter({
url: process.env.DATABASE_URI || '',
}),
sharp,
plugins: [payloadCloudPlugin()],
})

View File

@@ -0,0 +1,44 @@
{
"compilerOptions": {
"baseUrl": ".",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"plugins": [
{
"name": "next"
}
],
"paths": {
"@/*": [
"./src/*"
],
"@payload-config": [
"./src/payload.config.ts"
]
},
"target": "ES2017"
},
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx",
".next/types/**/*.ts"
],
"exclude": [
"node_modules"
]
}

View File

@@ -0,0 +1,2 @@
DATABASE_URI=mongodb://127.0.0.1/payload-template-blank-3-0
PAYLOAD_SECRET=YOUR_SECRET_HERE

View File

@@ -0,0 +1,8 @@
/** @type {import('eslint').Linter.Config} */
module.exports = {
extends: ['next/core-web-vitals'],
parserOptions: {
project: ['./tsconfig.json'],
tsconfigRootDir: __dirname,
},
}

View File

@@ -0,0 +1,43 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
.yarn/install-state.gz
/.idea/*
!/.idea/runConfigurations
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# local env files
.env*.local
# vercel
.vercel
# typescript
*.tsbuildinfo
next-env.d.ts
.env
/media

View File

@@ -0,0 +1,6 @@
{
"singleQuote": true,
"trailingComma": "all",
"printWidth": 100,
"semi": false
}

View File

@@ -0,0 +1 @@
--install.ignore-engines true

View File

@@ -0,0 +1,69 @@
# From https://github.com/vercel/next.js/blob/canary/examples/with-docker/Dockerfile
FROM node:18-alpine AS base
# Install dependencies only when needed
FROM base AS deps
# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.
RUN apk add --no-cache libc6-compat
WORKDIR /app
# Install dependencies based on the preferred package manager
COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* ./
RUN \
if [ -f yarn.lock ]; then yarn --frozen-lockfile; \
elif [ -f package-lock.json ]; then npm ci; \
elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm i --frozen-lockfile; \
else echo "Lockfile not found." && exit 1; \
fi
# Rebuild the source code only when needed
FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
# Next.js collects completely anonymous telemetry data about general usage.
# Learn more here: https://nextjs.org/telemetry
# Uncomment the following line in case you want to disable telemetry during the build.
# ENV NEXT_TELEMETRY_DISABLED 1
RUN \
if [ -f yarn.lock ]; then yarn run build; \
elif [ -f package-lock.json ]; then npm run build; \
elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm run build; \
else echo "Lockfile not found." && exit 1; \
fi
# Production image, copy all the files and run next
FROM base AS runner
WORKDIR /app
ENV NODE_ENV production
# Uncomment the following line in case you want to disable telemetry during runtime.
# ENV NEXT_TELEMETRY_DISABLED 1
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
COPY --from=builder /app/public ./public
# Set the correct permission for prerender cache
RUN mkdir .next
RUN chown nextjs:nodejs .next
# Automatically leverage output traces to reduce image size
# https://nextjs.org/docs/advanced-features/output-file-tracing
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
USER nextjs
EXPOSE 3000
ENV PORT 3000
# server.js is created by next build from the standalone output
# https://nextjs.org/docs/pages/api-reference/next-config-js/output
CMD HOSTNAME="0.0.0.0" node server.js

View File

@@ -0,0 +1,10 @@
# payload-vercel-mongodb-template
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https://github.com/payloadcms/payload/tree/beta/templates//Users/elliot/dev/core/scripts&project-name=payload-project&env=PAYLOAD_SECRET&build-command=pnpm%20run%20ci&stores=%5B%7B%22type%22:%22blob%22%7D%5D&integration-ids=oac_jnzmjqM10gllKmSrG0SGrHOH)
payload-vercel-mongodb-template
## Attributes
- **Database**: mongodb
- **Storage Adapter**: vercelBlobStorage

View File

@@ -0,0 +1,43 @@
version: '3'
services:
payload:
image: node:18-alpine
ports:
- '3000:3000'
volumes:
- .:/home/node/app
- node_modules:/home/node/app/node_modules
working_dir: /home/node/app/
command: sh -c "corepack enable && corepack prepare pnpm@latest --activate && pnpm install && pnpm dev"
depends_on:
- mongo
# - postgres
env_file:
- .env
# Ensure your DATABASE_URI uses 'mongo' as the hostname ie. mongodb://mongo/my-db-name
mongo:
image: mongo:latest
ports:
- '27017:27017'
command:
- --storageEngine=wiredTiger
volumes:
- data:/data/db
logging:
driver: none
# Uncomment the following to use postgres
# postgres:
# restart: always
# image: postgres:latest
# volumes:
# - pgdata:/var/lib/postgresql/data
# ports:
# - "5432:5432"
volumes:
data:
# pgdata:
node_modules:

View File

@@ -0,0 +1,8 @@
import { withPayload } from '@payloadcms/next/withPayload'
/** @type {import('next').NextConfig} */
const nextConfig = {
// Your Next.js config here
}
export default withPayload(nextConfig)

View File

@@ -0,0 +1,51 @@
{
"name": "payload-vercel-mongodb-template",
"version": "1.0.0",
"description": "A blank template to get started with Payload 3.0",
"license": "MIT",
"type": "module",
"scripts": {
"build": "cross-env NODE_OPTIONS=--no-deprecation next build",
"dev": "cross-env NODE_OPTIONS=--no-deprecation next dev",
"devsafe": "rm -rf .next && cross-env NODE_OPTIONS=--no-deprecation next dev",
"generate:types": "payload generate:types",
"lint": "cross-env NODE_OPTIONS=--no-deprecation next lint",
"payload": "cross-env NODE_OPTIONS=--no-deprecation payload",
"start": "cross-env NODE_OPTIONS=--no-deprecation next start"
},
"dependencies": {
"@payloadcms/db-mongodb": "beta",
"@payloadcms/next": "beta",
"@payloadcms/plugin-cloud": "beta",
"@payloadcms/richtext-lexical": "beta",
"cross-env": "^7.0.3",
"graphql": "^16.8.1",
"next": "15.0.0-rc.0",
"payload": "beta",
"react": "^19.0.0-rc-f994737d14-20240522",
"react-dom": "^19.0.0-rc-f994737d14-20240522",
"@payloadcms/storage-vercel-blob": "beta"
},
"devDependencies": {
"@types/node": "^20.12.12",
"@types/react": "npm:types-react@19.0.0-beta.2",
"@types/react-dom": "npm:types-react-dom@19.0.0-beta.2",
"dotenv": "^16.4.5",
"eslint": "^8",
"eslint-config-next": "^14.2.3",
"typescript": "^5.4.5"
},
"engines": {
"node": "^18.20.2 || >=20.6.0"
},
"pnpm": {
"overrides": {
"@types/react": "npm:types-react@19.0.0-beta.2",
"@types/react-dom": "npm:types-react-dom@19.0.0-beta.2"
}
},
"overrides": {
"@types/react": "npm:types-react@19.0.0-beta.2",
"@types/react-dom": "npm:types-react-dom@19.0.0-beta.2"
}
}

Some files were not shown because too many files have changed in this diff Show More