chore(examples/multi-tenant): migrates to 2.0 (#3512)
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
MONGODB_URI=mongodb://127.0.0.1/payload-example-auth
|
||||
PAYLOAD_SECRET=PAYLOAD_AUTH_EXAMPLE_SECRET_KEY
|
||||
DATABASE_URI=mongodb://127.0.0.1/payload-example-multi-tenant
|
||||
PAYLOAD_SECRET=PAYLOAD_MULTI_TENANT_EXAMPLE_SECRET_KEY
|
||||
PAYLOAD_PUBLIC_SERVER_URL=http://localhost:3000
|
||||
PAYLOAD_SEED=true
|
||||
PAYLOAD_DROP_DATABASE=true
|
||||
|
||||
@@ -10,7 +10,7 @@ To spin up this example locally, follow these steps:
|
||||
1. Then `cd YOUR_PROJECT_REPO && cp .env.example .env`
|
||||
1. Next `yarn && yarn dev`
|
||||
1. Now `open http://localhost:3000/admin` to access the admin panel
|
||||
1. Login with email `dev@payloadcms.com` and password `test`
|
||||
1. Login with email `demo@payloadcms.com` and password `demo`
|
||||
|
||||
That's it! Changes made in `./src` will be reflected in your app. See the [Development](#development) section for more details on how to log in as a tenant.
|
||||
|
||||
@@ -79,7 +79,7 @@ To spin up this example locally, follow the [Quick Start](#quick-start).
|
||||
|
||||
### Seed
|
||||
|
||||
On boot, a seed script is included to scaffold a basic database for you to use as an example. This is done by setting the `PAYLOAD_DROP_DATABASE` and `PAYLOAD_SEED` environment variables which are included in the `.env.example` by default. You can remove these from your `.env` to prevent this behavior. You can also freshly seed your project at any time by running `yarn seed`. This seed creates a super-admin user with email `dev@payloadcms.com` and password `test` along with the following tenants:
|
||||
On boot, a seed script is included to scaffold a basic database for you to use as an example. This is done by setting the `PAYLOAD_DROP_DATABASE` and `PAYLOAD_SEED` environment variables which are included in the `.env.example` by default. You can remove these from your `.env` to prevent this behavior. You can also freshly seed your project at any time by running `yarn seed`. This seed creates a super-admin user with email `demo@payloadcms.com` and password `demo` along with the following tenants:
|
||||
|
||||
- `ABC`
|
||||
- Domains:
|
||||
|
||||
@@ -18,6 +18,9 @@
|
||||
"lint:fix": "eslint --fix --ext .ts,.tsx src"
|
||||
},
|
||||
"dependencies": {
|
||||
"@payloadcms/bundler-webpack": "^1.0.0-beta.5",
|
||||
"@payloadcms/db-mongodb": "^1.0.0-beta.8",
|
||||
"@payloadcms/richtext-slate": "^1.0.0-beta.4",
|
||||
"dotenv": "^8.2.0",
|
||||
"express": "^4.17.1",
|
||||
"payload": "latest"
|
||||
@@ -43,4 +46,4 @@
|
||||
"ts-node": "^9.1.1",
|
||||
"typescript": "^4.8.4"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { Access } from 'payload/config'
|
||||
|
||||
import { checkUserRoles } from '../../utilities/checkUserRoles'
|
||||
import { checkUserRoles } from '../../../utilities/checkUserRoles'
|
||||
|
||||
// the user must be an admin of the document's tenant
|
||||
export const tenantAdmins: Access = ({ req: { user } }) => {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { Access } from 'payload/types'
|
||||
|
||||
import { isSuperAdmin } from '../../utilities/isSuperAdmin'
|
||||
import { isSuperAdmin } from '../../../utilities/isSuperAdmin'
|
||||
|
||||
export const tenants: Access = ({ req: { user }, data }) =>
|
||||
// individual documents
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { CollectionConfig } from 'payload/types'
|
||||
|
||||
import richText from '../fields/richText'
|
||||
import { tenant } from '../fields/tenant'
|
||||
import richText from '../../fields/richText'
|
||||
import { tenant } from '../../fields/tenant'
|
||||
import { loggedIn } from './access/loggedIn'
|
||||
import { tenantAdmins } from './access/tenantAdmins'
|
||||
import { tenants } from './access/tenants'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { Access } from 'payload/config'
|
||||
|
||||
import { isSuperAdmin } from '../../utilities/isSuperAdmin'
|
||||
import { isSuperAdmin } from '../../../utilities/isSuperAdmin'
|
||||
|
||||
// the user must be an admin of the tenant being accessed
|
||||
export const tenantAdmins: Access = ({ req: { user } }) => {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { CollectionConfig } from 'payload/types'
|
||||
|
||||
import { superAdmins } from '../access/superAdmins'
|
||||
import { superAdmins } from '../../access/superAdmins'
|
||||
import { tenantAdmins } from './access/tenantAdmins'
|
||||
|
||||
export const Tenants: CollectionConfig = {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { Access } from 'payload/config'
|
||||
import type { User } from 'payload/generated-types'
|
||||
|
||||
import { isSuperAdmin } from '../../utilities/isSuperAdmin'
|
||||
import { isSuperAdmin } from '../../../utilities/isSuperAdmin'
|
||||
import { User } from '../../../payload-types'
|
||||
|
||||
export const adminsAndSelf: Access<any, User> = async ({ req: { user } }) => {
|
||||
if (user) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { FieldAccess } from 'payload/types'
|
||||
|
||||
import { checkUserRoles } from '../../utilities/checkUserRoles'
|
||||
import { checkUserRoles } from '../../../utilities/checkUserRoles'
|
||||
import { checkTenantRoles } from '../utilities/checkTenantRoles'
|
||||
|
||||
export const tenantAdmins: FieldAccess = args => {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { CollectionConfig } from 'payload/types'
|
||||
|
||||
import { anyone } from '../access/anyone'
|
||||
import { superAdminFieldAccess } from '../access/superAdmins'
|
||||
import { anyone } from '../../access/anyone'
|
||||
import { superAdminFieldAccess } from '../../access/superAdmins'
|
||||
import { adminsAndSelf } from './access/adminsAndSelf'
|
||||
import { tenantAdmins } from './access/tenantAdmins'
|
||||
import { loginAfterCreate } from './hooks/loginAfterCreate'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { PayloadRequest } from 'payload/dist/types'
|
||||
|
||||
import { isSuperAdmin } from '../../utilities/isSuperAdmin'
|
||||
import { isSuperAdmin } from '../../../utilities/isSuperAdmin'
|
||||
|
||||
const logs = false
|
||||
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
import type { RichTextElement } from 'payload/dist/fields/config/types'
|
||||
|
||||
const elements: RichTextElement[] = ['blockquote', 'h2', 'h3', 'h4', 'h5', 'h6', 'link']
|
||||
|
||||
export default elements
|
||||
@@ -1,86 +0,0 @@
|
||||
import type { RichTextElement, RichTextField, RichTextLeaf } from 'payload/dist/fields/config/types'
|
||||
|
||||
import deepMerge from '../../utilities/deepMerge'
|
||||
import link from '../link'
|
||||
import elements from './elements'
|
||||
import leaves from './leaves'
|
||||
|
||||
type RichText = (
|
||||
overrides?: Partial<RichTextField>,
|
||||
additions?: {
|
||||
elements?: RichTextElement[]
|
||||
leaves?: RichTextLeaf[]
|
||||
},
|
||||
) => RichTextField
|
||||
|
||||
const richText: RichText = (
|
||||
overrides,
|
||||
additions = {
|
||||
elements: [],
|
||||
leaves: [],
|
||||
},
|
||||
) =>
|
||||
deepMerge<RichTextField, Partial<RichTextField>>(
|
||||
{
|
||||
name: 'richText',
|
||||
type: 'richText',
|
||||
required: true,
|
||||
admin: {
|
||||
upload: {
|
||||
collections: {
|
||||
media: {
|
||||
fields: [
|
||||
{
|
||||
type: 'richText',
|
||||
name: 'caption',
|
||||
label: 'Caption',
|
||||
admin: {
|
||||
elements: [...elements],
|
||||
leaves: [...leaves],
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'radio',
|
||||
name: 'alignment',
|
||||
label: 'Alignment',
|
||||
options: [
|
||||
{
|
||||
label: 'Left',
|
||||
value: 'left',
|
||||
},
|
||||
{
|
||||
label: 'Center',
|
||||
value: 'center',
|
||||
},
|
||||
{
|
||||
label: 'Right',
|
||||
value: 'right',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'enableLink',
|
||||
type: 'checkbox',
|
||||
label: 'Enable Link',
|
||||
},
|
||||
link({
|
||||
appearances: false,
|
||||
disableLabel: true,
|
||||
overrides: {
|
||||
admin: {
|
||||
condition: (_, data) => Boolean(data?.enableLink),
|
||||
},
|
||||
},
|
||||
}),
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
elements: [...elements, ...(additions.elements || [])],
|
||||
leaves: [...leaves, ...(additions.leaves || [])],
|
||||
},
|
||||
},
|
||||
overrides,
|
||||
)
|
||||
|
||||
export default richText
|
||||
3
examples/multi-tenant/src/dotenv.js
Normal file
3
examples/multi-tenant/src/dotenv.js
Normal file
@@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
config: () => null,
|
||||
}
|
||||
5
examples/multi-tenant/src/fields/richText/elements.ts
Normal file
5
examples/multi-tenant/src/fields/richText/elements.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import type { RichTextElement } from '@payloadcms/richtext-slate'
|
||||
|
||||
const elements: RichTextElement[] = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'blockquote', 'link']
|
||||
|
||||
export default elements
|
||||
92
examples/multi-tenant/src/fields/richText/index.ts
Normal file
92
examples/multi-tenant/src/fields/richText/index.ts
Normal file
@@ -0,0 +1,92 @@
|
||||
import { slateEditor } from '@payloadcms/richtext-slate'
|
||||
import type { RichTextElement, RichTextLeaf } from '@payloadcms/richtext-slate/dist/types'
|
||||
import type { RichTextField } from 'payload/types'
|
||||
|
||||
import deepMerge from '../../utilities/deepMerge'
|
||||
import link from '../link'
|
||||
import elements from './elements'
|
||||
import leaves from './leaves'
|
||||
|
||||
type RichText = (
|
||||
overrides?: Partial<RichTextField>,
|
||||
additions?: {
|
||||
elements?: RichTextElement[]
|
||||
leaves?: RichTextLeaf[]
|
||||
},
|
||||
) => RichTextField
|
||||
|
||||
const richText: RichText = (
|
||||
overrides,
|
||||
additions = {
|
||||
elements: [],
|
||||
leaves: [],
|
||||
},
|
||||
) =>
|
||||
deepMerge<RichTextField, Partial<RichTextField>>(
|
||||
{
|
||||
name: 'richText',
|
||||
type: 'richText',
|
||||
required: true,
|
||||
editor: slateEditor({
|
||||
admin: {
|
||||
upload: {
|
||||
collections: {
|
||||
media: {
|
||||
fields: [
|
||||
{
|
||||
type: 'richText',
|
||||
name: 'caption',
|
||||
label: 'Caption',
|
||||
editor: slateEditor({
|
||||
admin: {
|
||||
elements: [...elements],
|
||||
leaves: [...leaves],
|
||||
},
|
||||
}),
|
||||
},
|
||||
{
|
||||
type: 'radio',
|
||||
name: 'alignment',
|
||||
label: 'Alignment',
|
||||
options: [
|
||||
{
|
||||
label: 'Left',
|
||||
value: 'left',
|
||||
},
|
||||
{
|
||||
label: 'Center',
|
||||
value: 'center',
|
||||
},
|
||||
{
|
||||
label: 'Right',
|
||||
value: 'right',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'enableLink',
|
||||
type: 'checkbox',
|
||||
label: 'Enable Link',
|
||||
},
|
||||
link({
|
||||
appearances: false,
|
||||
disableLabel: true,
|
||||
overrides: {
|
||||
admin: {
|
||||
condition: (_, data) => Boolean(data?.enableLink),
|
||||
},
|
||||
},
|
||||
}),
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
elements: [...elements, ...(additions.elements || [])],
|
||||
leaves: [...leaves, ...(additions.leaves || [])],
|
||||
},
|
||||
}),
|
||||
},
|
||||
overrides,
|
||||
)
|
||||
|
||||
export default richText
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { RichTextLeaf } from 'payload/dist/fields/config/types'
|
||||
import { RichTextLeaf } from '@payloadcms/richtext-slate'
|
||||
|
||||
const defaultLeaves: RichTextLeaf[] = ['bold', 'italic', 'underline']
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* This file was automatically generated by Payload.
|
||||
* DO NOT MODIFY IT BY HAND. Instead, modify your source Payload config,
|
||||
@@ -10,6 +11,8 @@ export interface Config {
|
||||
users: User
|
||||
tenants: Tenant
|
||||
pages: Page
|
||||
'payload-preferences': PayloadPreference
|
||||
'payload-migrations': PayloadMigration
|
||||
}
|
||||
globals: {}
|
||||
}
|
||||
@@ -17,18 +20,20 @@ export interface User {
|
||||
id: string
|
||||
firstName?: string
|
||||
lastName?: string
|
||||
roles: Array<'super-admin' | 'user'>
|
||||
tenants?: Array<{
|
||||
roles: ('super-admin' | 'user')[]
|
||||
tenants?: {
|
||||
tenant: string | Tenant
|
||||
roles: Array<'admin' | 'user'>
|
||||
roles: ('admin' | 'user')[]
|
||||
id?: string
|
||||
}>
|
||||
}[]
|
||||
lastLoggedInTenant?: string | Tenant
|
||||
updatedAt: string
|
||||
createdAt: string
|
||||
email?: string
|
||||
email: string
|
||||
resetPasswordToken?: string
|
||||
resetPasswordExpiration?: string
|
||||
salt?: string
|
||||
hash?: string
|
||||
loginAttempts?: number
|
||||
lockUntil?: string
|
||||
password?: string
|
||||
@@ -36,10 +41,10 @@ export interface User {
|
||||
export interface Tenant {
|
||||
id: string
|
||||
name: string
|
||||
domains: Array<{
|
||||
domains?: {
|
||||
domain: string
|
||||
id?: string
|
||||
}>
|
||||
}[]
|
||||
updatedAt: string
|
||||
createdAt: string
|
||||
}
|
||||
@@ -48,9 +53,47 @@ export interface Page {
|
||||
title: string
|
||||
slug?: string
|
||||
tenant?: string | Tenant
|
||||
richText: Array<{
|
||||
richText: {
|
||||
[k: string]: unknown
|
||||
}>
|
||||
}[]
|
||||
updatedAt: string
|
||||
createdAt: string
|
||||
}
|
||||
export interface PayloadPreference {
|
||||
id: string
|
||||
user: {
|
||||
relationTo: 'users'
|
||||
value: string | User
|
||||
}
|
||||
key?: string
|
||||
value?:
|
||||
| {
|
||||
[k: string]: unknown
|
||||
}
|
||||
| unknown[]
|
||||
| string
|
||||
| number
|
||||
| boolean
|
||||
| null
|
||||
updatedAt: string
|
||||
createdAt: string
|
||||
}
|
||||
export interface PayloadMigration {
|
||||
id: string
|
||||
name?: string
|
||||
batch?: number
|
||||
updatedAt: string
|
||||
createdAt: string
|
||||
}
|
||||
|
||||
declare module 'payload' {
|
||||
export interface GeneratedTypes {
|
||||
collections: {
|
||||
users: User
|
||||
tenants: Tenant
|
||||
pages: Page
|
||||
'payload-preferences': PayloadPreference
|
||||
'payload-migrations': PayloadMigration
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
import { webpackBundler } from '@payloadcms/bundler-webpack'
|
||||
import { mongooseAdapter } from '@payloadcms/db-mongodb'
|
||||
import { slateEditor } from '@payloadcms/richtext-slate'
|
||||
import dotenv from 'dotenv'
|
||||
import path from 'path'
|
||||
|
||||
@@ -13,6 +16,23 @@ import { Users } from './collections/Users'
|
||||
|
||||
export default buildConfig({
|
||||
collections: [Users, Tenants, Pages],
|
||||
admin: {
|
||||
bundler: webpackBundler(),
|
||||
webpack: config => ({
|
||||
...config,
|
||||
resolve: {
|
||||
...config.resolve,
|
||||
alias: {
|
||||
...config.resolve.alias,
|
||||
dotenv: path.resolve(__dirname, './dotenv.js'),
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
editor: slateEditor({}),
|
||||
db: mongooseAdapter({
|
||||
url: process.env.DATABASE_URI,
|
||||
}),
|
||||
typescript: {
|
||||
outputFile: path.resolve(__dirname, 'payload-types.ts'),
|
||||
},
|
||||
|
||||
@@ -5,8 +5,8 @@ export const seed = async (payload: Payload): Promise<void> => {
|
||||
await payload.create({
|
||||
collection: 'users',
|
||||
data: {
|
||||
email: 'dev@payloadcms.com',
|
||||
password: 'test',
|
||||
email: 'demo@payloadcms.com',
|
||||
password: 'demo',
|
||||
roles: ['super-admin'],
|
||||
},
|
||||
})
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { User } from '../../payload-types'
|
||||
import type { User } from '../payload-types'
|
||||
|
||||
export const checkUserRoles = (allRoles: User['roles'] = [], user: User = undefined): boolean => {
|
||||
if (user) {
|
||||
@@ -1,5 +1,4 @@
|
||||
import type { User } from 'payload/generated-types'
|
||||
|
||||
import { User } from '../payload-types'
|
||||
import { checkUserRoles } from './checkUserRoles'
|
||||
|
||||
export const isSuperAdmin = (user: User): boolean => checkUserRoles(['super-admin'], user)
|
||||
@@ -16,7 +16,6 @@
|
||||
"sourceMap": true,
|
||||
"resolveJsonModule": true,
|
||||
"paths": {
|
||||
"payload/generated-types": ["./src/payload-types.ts"],
|
||||
"node_modules/*": ["./node_modules/*"]
|
||||
},
|
||||
},
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user