fix(examples): ensure working multi-tenant example with pg (#11501)
### What? There were a couple issues with the implementation within the example when using postgres. - `ensureUniqueUsername` tenant was being extracted incorrectly, should not constrain query unless it was present - `ensureUniqueSlug` was querying by NaN when tenant was not present on data or originalDoc - `users` read access was not correctly extracting the tenant id in the correct type depending on DB Fixes https://github.com/payloadcms/payload/issues/11484
This commit is contained in:
@@ -1,3 +1,5 @@
|
|||||||
DATABASE_URI=mongodb://127.0.0.1/payload-example-multi-tenant
|
DATABASE_URI=mongodb://127.0.0.1/payload-example-multi-tenant
|
||||||
|
POSTGRES_URL=postgres://127.0.0.1:5432/payload-example-multi-tenant
|
||||||
PAYLOAD_SECRET=PAYLOAD_MULTI_TENANT_EXAMPLE_SECRET_KEY
|
PAYLOAD_SECRET=PAYLOAD_MULTI_TENANT_EXAMPLE_SECRET_KEY
|
||||||
PAYLOAD_PUBLIC_SERVER_URL=http://localhost:3000
|
PAYLOAD_PUBLIC_SERVER_URL=http://localhost:3000
|
||||||
|
SEED_DB=true
|
||||||
@@ -46,12 +46,12 @@ See the [Collections](https://payloadcms.com/docs/configuration/collections) doc
|
|||||||
|
|
||||||
**Domain-based Tenant Setting**:
|
**Domain-based Tenant Setting**:
|
||||||
|
|
||||||
This example also supports domain-based tenant selection, where tenants can be associated with a specific domain. If a tenant is associated with a domain (e.g., `gold.test:3000`), when a user logs in from that domain, they will be automatically scoped to the matching tenant. This is accomplished through an optional `afterLogin` hook that sets a `payload-tenant` cookie based on the domain.
|
This example also supports domain-based tenant selection, where tenants can be associated with a specific domain. If a tenant is associated with a domain (e.g., `gold.localhost:3000`), when a user logs in from that domain, they will be automatically scoped to the matching tenant. This is accomplished through an optional `afterLogin` hook that sets a `payload-tenant` cookie based on the domain.
|
||||||
|
|
||||||
For the domain portion of the example to function properly, you will need to add the following entries to your system's `/etc/hosts` file:
|
For the domain portion of the example to function properly, you will need to add the following entries to your system's `/etc/hosts` file:
|
||||||
|
|
||||||
```
|
```
|
||||||
127.0.0.1 gold.test silver.test bronze.test
|
127.0.0.1 gold.localhost silver.localhost bronze.localhost
|
||||||
```
|
```
|
||||||
|
|
||||||
- #### Pages
|
- #### Pages
|
||||||
|
|||||||
2
examples/multi-tenant/next-env.d.ts
vendored
2
examples/multi-tenant/next-env.d.ts
vendored
@@ -2,4 +2,4 @@
|
|||||||
/// <reference types="next/image-types/global" />
|
/// <reference types="next/image-types/global" />
|
||||||
|
|
||||||
// NOTE: This file should not be edited
|
// NOTE: This file should not be edited
|
||||||
// see https://nextjs.org/docs/app/building-your-application/configuring/typescript for more information.
|
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@payloadcms/db-mongodb": "latest",
|
"@payloadcms/db-mongodb": "latest",
|
||||||
|
"@payloadcms/db-postgres": "^3.25.0",
|
||||||
"@payloadcms/next": "latest",
|
"@payloadcms/next": "latest",
|
||||||
"@payloadcms/plugin-multi-tenant": "latest",
|
"@payloadcms/plugin-multi-tenant": "latest",
|
||||||
"@payloadcms/richtext-lexical": "latest",
|
"@payloadcms/richtext-lexical": "latest",
|
||||||
|
|||||||
939
examples/multi-tenant/pnpm-lock.yaml
generated
939
examples/multi-tenant/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -10,10 +10,10 @@ export default async ({ params: paramsPromise }: { params: Promise<{ slug: strin
|
|||||||
<p>When you visit a tenant by domain, the domain is used to determine the tenant.</p>
|
<p>When you visit a tenant by domain, the domain is used to determine the tenant.</p>
|
||||||
<p>
|
<p>
|
||||||
For example, visiting{' '}
|
For example, visiting{' '}
|
||||||
<a href="http://gold.test:3000/tenant-domains/login">
|
<a href="http://gold.localhost:3000/tenant-domains/login">
|
||||||
http://gold.test:3000/tenant-domains/login
|
http://gold.localhost:3000/tenant-domains/login
|
||||||
</a>{' '}
|
</a>{' '}
|
||||||
will show the tenant with the domain "gold.test".
|
will show the tenant with the domain "gold.localhost".
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2>Slugs</h2>
|
<h2>Slugs</h2>
|
||||||
|
|||||||
@@ -3,10 +3,7 @@ import { TenantSelector as TenantSelector_1d0591e3cf4f332c83a86da13a0de59a } fro
|
|||||||
import { TenantSelectionProvider as TenantSelectionProvider_d6d5f193a167989e2ee7d14202901e62 } from '@payloadcms/plugin-multi-tenant/rsc'
|
import { TenantSelectionProvider as TenantSelectionProvider_d6d5f193a167989e2ee7d14202901e62 } from '@payloadcms/plugin-multi-tenant/rsc'
|
||||||
|
|
||||||
export const importMap = {
|
export const importMap = {
|
||||||
'@payloadcms/plugin-multi-tenant/client#TenantField':
|
"@payloadcms/plugin-multi-tenant/client#TenantField": TenantField_1d0591e3cf4f332c83a86da13a0de59a,
|
||||||
TenantField_1d0591e3cf4f332c83a86da13a0de59a,
|
"@payloadcms/plugin-multi-tenant/client#TenantSelector": TenantSelector_1d0591e3cf4f332c83a86da13a0de59a,
|
||||||
'@payloadcms/plugin-multi-tenant/client#TenantSelector':
|
"@payloadcms/plugin-multi-tenant/rsc#TenantSelectionProvider": TenantSelectionProvider_d6d5f193a167989e2ee7d14202901e62
|
||||||
TenantSelector_1d0591e3cf4f332c83a86da13a0de59a,
|
|
||||||
'@payloadcms/plugin-multi-tenant/rsc#TenantSelectionProvider':
|
|
||||||
TenantSelectionProvider_d6d5f193a167989e2ee7d14202901e62,
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import type { FieldHook } from 'payload'
|
import type { FieldHook, Where } from 'payload'
|
||||||
|
|
||||||
import { ValidationError } from 'payload'
|
import { ValidationError } from 'payload'
|
||||||
|
|
||||||
import { getUserTenantIDs } from '../../../utilities/getUserTenantIDs'
|
import { getUserTenantIDs } from '../../../utilities/getUserTenantIDs'
|
||||||
|
import { extractID } from '@/utilities/extractID'
|
||||||
|
|
||||||
export const ensureUniqueSlug: FieldHook = async ({ data, originalDoc, req, value }) => {
|
export const ensureUniqueSlug: FieldHook = async ({ data, originalDoc, req, value }) => {
|
||||||
// if value is unchanged, skip validation
|
// if value is unchanged, skip validation
|
||||||
@@ -10,26 +11,30 @@ export const ensureUniqueSlug: FieldHook = async ({ data, originalDoc, req, valu
|
|||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
|
||||||
const incomingTenantID = typeof data?.tenant === 'object' ? data.tenant.id : data?.tenant
|
const constraints: Where[] = [
|
||||||
const currentTenantID =
|
|
||||||
typeof originalDoc?.tenant === 'object' ? originalDoc.tenant.id : originalDoc?.tenant
|
|
||||||
const tenantIDToMatch = incomingTenantID || currentTenantID
|
|
||||||
|
|
||||||
const findDuplicatePages = await req.payload.find({
|
|
||||||
collection: 'pages',
|
|
||||||
where: {
|
|
||||||
and: [
|
|
||||||
{
|
|
||||||
tenant: {
|
|
||||||
equals: tenantIDToMatch,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
slug: {
|
slug: {
|
||||||
equals: value,
|
equals: value,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
]
|
||||||
|
|
||||||
|
const incomingTenantID = extractID(data?.tenant)
|
||||||
|
const currentTenantID = extractID(originalDoc?.tenant)
|
||||||
|
const tenantIDToMatch = incomingTenantID || currentTenantID
|
||||||
|
|
||||||
|
if (tenantIDToMatch) {
|
||||||
|
constraints.push({
|
||||||
|
tenant: {
|
||||||
|
equals: tenantIDToMatch,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const findDuplicatePages = await req.payload.find({
|
||||||
|
collection: 'pages',
|
||||||
|
where: {
|
||||||
|
and: constraints,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import type { User } from '@/payload-types'
|
import type { User } from '@/payload-types'
|
||||||
import type { Access, Where } from 'payload'
|
import type { Access, Where } from 'payload'
|
||||||
|
import { getTenantFromCookie } from '@payloadcms/plugin-multi-tenant/utilities'
|
||||||
import { parseCookies } from 'payload'
|
|
||||||
|
|
||||||
import { isSuperAdmin } from '../../../access/isSuperAdmin'
|
import { isSuperAdmin } from '../../../access/isSuperAdmin'
|
||||||
import { getUserTenantIDs } from '../../../utilities/getUserTenantIDs'
|
import { getUserTenantIDs } from '../../../utilities/getUserTenantIDs'
|
||||||
import { isAccessingSelf } from './isAccessingSelf'
|
import { isAccessingSelf } from './isAccessingSelf'
|
||||||
|
import { getCollectionIDType } from '@/utilities/getCollectionIDType'
|
||||||
|
|
||||||
export const readAccess: Access<User> = ({ req, id }) => {
|
export const readAccess: Access<User> = ({ req, id }) => {
|
||||||
if (!req?.user) {
|
if (!req?.user) {
|
||||||
@@ -16,9 +16,11 @@ export const readAccess: Access<User> = ({ req, id }) => {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
const cookies = parseCookies(req.headers)
|
|
||||||
const superAdmin = isSuperAdmin(req.user)
|
const superAdmin = isSuperAdmin(req.user)
|
||||||
const selectedTenant = cookies.get('payload-tenant')
|
const selectedTenant = getTenantFromCookie(
|
||||||
|
req.headers,
|
||||||
|
getCollectionIDType({ payload: req.payload, collectionSlug: 'tenants' }),
|
||||||
|
)
|
||||||
const adminTenantAccessIDs = getUserTenantIDs(req.user, 'tenant-admin')
|
const adminTenantAccessIDs = getUserTenantIDs(req.user, 'tenant-admin')
|
||||||
|
|
||||||
if (selectedTenant) {
|
if (selectedTenant) {
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
import type { FieldHook } from 'payload'
|
import type { FieldHook, Where } from 'payload'
|
||||||
|
|
||||||
import { ValidationError } from 'payload'
|
import { ValidationError } from 'payload'
|
||||||
|
|
||||||
import { getUserTenantIDs } from '../../../utilities/getUserTenantIDs'
|
import { getUserTenantIDs } from '../../../utilities/getUserTenantIDs'
|
||||||
|
import { extractID } from '@/utilities/extractID'
|
||||||
|
import { getTenantFromCookie } from '@payloadcms/plugin-multi-tenant/utilities'
|
||||||
|
import { getCollectionIDType } from '@/utilities/getCollectionIDType'
|
||||||
|
|
||||||
export const ensureUniqueUsername: FieldHook = async ({ data, originalDoc, req, value }) => {
|
export const ensureUniqueUsername: FieldHook = async ({ data, originalDoc, req, value }) => {
|
||||||
// if value is unchanged, skip validation
|
// if value is unchanged, skip validation
|
||||||
@@ -10,26 +13,31 @@ export const ensureUniqueUsername: FieldHook = async ({ data, originalDoc, req,
|
|||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
|
||||||
const incomingTenantID = typeof data?.tenant === 'object' ? data.tenant.id : data?.tenant
|
const constraints: Where[] = [
|
||||||
const currentTenantID =
|
|
||||||
typeof originalDoc?.tenant === 'object' ? originalDoc.tenant.id : originalDoc?.tenant
|
|
||||||
const tenantIDToMatch = incomingTenantID || currentTenantID
|
|
||||||
|
|
||||||
const findDuplicateUsers = await req.payload.find({
|
|
||||||
collection: 'users',
|
|
||||||
where: {
|
|
||||||
and: [
|
|
||||||
{
|
|
||||||
'tenants.tenant': {
|
|
||||||
equals: tenantIDToMatch,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
username: {
|
username: {
|
||||||
equals: value,
|
equals: value,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
]
|
||||||
|
|
||||||
|
const selectedTenant = getTenantFromCookie(
|
||||||
|
req.headers,
|
||||||
|
getCollectionIDType({ payload: req.payload, collectionSlug: 'tenants' }),
|
||||||
|
)
|
||||||
|
|
||||||
|
if (selectedTenant) {
|
||||||
|
constraints.push({
|
||||||
|
'tenants.tenant': {
|
||||||
|
equals: selectedTenant,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const findDuplicateUsers = await req.payload.find({
|
||||||
|
collection: 'users',
|
||||||
|
where: {
|
||||||
|
and: constraints,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -39,7 +47,8 @@ export const ensureUniqueUsername: FieldHook = async ({ data, originalDoc, req,
|
|||||||
// provide a more specific error message
|
// provide a more specific error message
|
||||||
if (req.user.roles?.includes('super-admin') || tenantIDs.length > 1) {
|
if (req.user.roles?.includes('super-admin') || tenantIDs.length > 1) {
|
||||||
const attemptedTenantChange = await req.payload.findByID({
|
const attemptedTenantChange = await req.payload.findByID({
|
||||||
id: tenantIDToMatch,
|
// @ts-ignore - selectedTenant will match DB ID type
|
||||||
|
id: selectedTenant,
|
||||||
collection: 'tenants',
|
collection: 'tenants',
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ export const setCookieBasedOnDomain: CollectionAfterLoginHook = async ({ req, us
|
|||||||
expires: getCookieExpiration({ seconds: 7200 }),
|
expires: getCookieExpiration({ seconds: 7200 }),
|
||||||
path: '/',
|
path: '/',
|
||||||
returnCookieAsObject: false,
|
returnCookieAsObject: false,
|
||||||
value: relatedOrg.docs[0].id,
|
value: String(relatedOrg.docs[0].id),
|
||||||
})
|
})
|
||||||
|
|
||||||
// Merge existing responseHeaders with the new Set-Cookie header
|
// Merge existing responseHeaders with the new Set-Cookie header
|
||||||
|
|||||||
@@ -6,10 +6,65 @@
|
|||||||
* and re-run `payload generate:types` to regenerate this file.
|
* and re-run `payload generate:types` to regenerate this file.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Supported timezones in IANA format.
|
||||||
|
*
|
||||||
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
|
* via the `definition` "supportedTimezones".
|
||||||
|
*/
|
||||||
|
export type SupportedTimezones =
|
||||||
|
| 'Pacific/Midway'
|
||||||
|
| 'Pacific/Niue'
|
||||||
|
| 'Pacific/Honolulu'
|
||||||
|
| 'Pacific/Rarotonga'
|
||||||
|
| 'America/Anchorage'
|
||||||
|
| 'Pacific/Gambier'
|
||||||
|
| 'America/Los_Angeles'
|
||||||
|
| 'America/Tijuana'
|
||||||
|
| 'America/Denver'
|
||||||
|
| 'America/Phoenix'
|
||||||
|
| 'America/Chicago'
|
||||||
|
| 'America/Guatemala'
|
||||||
|
| 'America/New_York'
|
||||||
|
| 'America/Bogota'
|
||||||
|
| 'America/Caracas'
|
||||||
|
| 'America/Santiago'
|
||||||
|
| 'America/Buenos_Aires'
|
||||||
|
| 'America/Sao_Paulo'
|
||||||
|
| 'Atlantic/South_Georgia'
|
||||||
|
| 'Atlantic/Azores'
|
||||||
|
| 'Atlantic/Cape_Verde'
|
||||||
|
| 'Europe/London'
|
||||||
|
| 'Europe/Berlin'
|
||||||
|
| 'Africa/Lagos'
|
||||||
|
| 'Europe/Athens'
|
||||||
|
| 'Africa/Cairo'
|
||||||
|
| 'Europe/Moscow'
|
||||||
|
| 'Asia/Riyadh'
|
||||||
|
| 'Asia/Dubai'
|
||||||
|
| 'Asia/Baku'
|
||||||
|
| 'Asia/Karachi'
|
||||||
|
| 'Asia/Tashkent'
|
||||||
|
| 'Asia/Calcutta'
|
||||||
|
| 'Asia/Dhaka'
|
||||||
|
| 'Asia/Almaty'
|
||||||
|
| 'Asia/Jakarta'
|
||||||
|
| 'Asia/Bangkok'
|
||||||
|
| 'Asia/Shanghai'
|
||||||
|
| 'Asia/Singapore'
|
||||||
|
| 'Asia/Tokyo'
|
||||||
|
| 'Asia/Seoul'
|
||||||
|
| 'Australia/Sydney'
|
||||||
|
| 'Pacific/Guam'
|
||||||
|
| 'Pacific/Noumea'
|
||||||
|
| 'Pacific/Auckland'
|
||||||
|
| 'Pacific/Fiji';
|
||||||
|
|
||||||
export interface Config {
|
export interface Config {
|
||||||
auth: {
|
auth: {
|
||||||
users: UserAuthOperations;
|
users: UserAuthOperations;
|
||||||
};
|
};
|
||||||
|
blocks: {};
|
||||||
collections: {
|
collections: {
|
||||||
pages: Page;
|
pages: Page;
|
||||||
users: User;
|
users: User;
|
||||||
@@ -28,7 +83,7 @@ export interface Config {
|
|||||||
'payload-migrations': PayloadMigrationsSelect<false> | PayloadMigrationsSelect<true>;
|
'payload-migrations': PayloadMigrationsSelect<false> | PayloadMigrationsSelect<true>;
|
||||||
};
|
};
|
||||||
db: {
|
db: {
|
||||||
defaultIDType: string;
|
defaultIDType: number;
|
||||||
};
|
};
|
||||||
globals: {};
|
globals: {};
|
||||||
globalsSelect: {};
|
globalsSelect: {};
|
||||||
@@ -64,8 +119,8 @@ export interface UserAuthOperations {
|
|||||||
* via the `definition` "pages".
|
* via the `definition` "pages".
|
||||||
*/
|
*/
|
||||||
export interface Page {
|
export interface Page {
|
||||||
id: string;
|
id: number;
|
||||||
tenant?: (string | null) | Tenant;
|
tenant?: (number | null) | Tenant;
|
||||||
title?: string | null;
|
title?: string | null;
|
||||||
slug?: string | null;
|
slug?: string | null;
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
@@ -76,7 +131,7 @@ export interface Page {
|
|||||||
* via the `definition` "tenants".
|
* via the `definition` "tenants".
|
||||||
*/
|
*/
|
||||||
export interface Tenant {
|
export interface Tenant {
|
||||||
id: string;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
/**
|
/**
|
||||||
* Used for domain-based tenant handling
|
* Used for domain-based tenant handling
|
||||||
@@ -98,12 +153,12 @@ export interface Tenant {
|
|||||||
* via the `definition` "users".
|
* via the `definition` "users".
|
||||||
*/
|
*/
|
||||||
export interface User {
|
export interface User {
|
||||||
id: string;
|
id: number;
|
||||||
roles?: ('super-admin' | 'user')[] | null;
|
roles?: ('super-admin' | 'user')[] | null;
|
||||||
username?: string | null;
|
username?: string | null;
|
||||||
tenants?:
|
tenants?:
|
||||||
| {
|
| {
|
||||||
tenant: string | Tenant;
|
tenant: number | Tenant;
|
||||||
roles: ('tenant-admin' | 'tenant-viewer')[];
|
roles: ('tenant-admin' | 'tenant-viewer')[];
|
||||||
id?: string | null;
|
id?: string | null;
|
||||||
}[]
|
}[]
|
||||||
@@ -124,24 +179,24 @@ export interface User {
|
|||||||
* via the `definition` "payload-locked-documents".
|
* via the `definition` "payload-locked-documents".
|
||||||
*/
|
*/
|
||||||
export interface PayloadLockedDocument {
|
export interface PayloadLockedDocument {
|
||||||
id: string;
|
id: number;
|
||||||
document?:
|
document?:
|
||||||
| ({
|
| ({
|
||||||
relationTo: 'pages';
|
relationTo: 'pages';
|
||||||
value: string | Page;
|
value: number | Page;
|
||||||
} | null)
|
} | null)
|
||||||
| ({
|
| ({
|
||||||
relationTo: 'users';
|
relationTo: 'users';
|
||||||
value: string | User;
|
value: number | User;
|
||||||
} | null)
|
} | null)
|
||||||
| ({
|
| ({
|
||||||
relationTo: 'tenants';
|
relationTo: 'tenants';
|
||||||
value: string | Tenant;
|
value: number | Tenant;
|
||||||
} | null);
|
} | null);
|
||||||
globalSlug?: string | null;
|
globalSlug?: string | null;
|
||||||
user: {
|
user: {
|
||||||
relationTo: 'users';
|
relationTo: 'users';
|
||||||
value: string | User;
|
value: number | User;
|
||||||
};
|
};
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
@@ -151,10 +206,10 @@ export interface PayloadLockedDocument {
|
|||||||
* via the `definition` "payload-preferences".
|
* via the `definition` "payload-preferences".
|
||||||
*/
|
*/
|
||||||
export interface PayloadPreference {
|
export interface PayloadPreference {
|
||||||
id: string;
|
id: number;
|
||||||
user: {
|
user: {
|
||||||
relationTo: 'users';
|
relationTo: 'users';
|
||||||
value: string | User;
|
value: number | User;
|
||||||
};
|
};
|
||||||
key?: string | null;
|
key?: string | null;
|
||||||
value?:
|
value?:
|
||||||
@@ -174,7 +229,7 @@ export interface PayloadPreference {
|
|||||||
* via the `definition` "payload-migrations".
|
* via the `definition` "payload-migrations".
|
||||||
*/
|
*/
|
||||||
export interface PayloadMigration {
|
export interface PayloadMigration {
|
||||||
id: string;
|
id: number;
|
||||||
name?: string | null;
|
name?: string | null;
|
||||||
batch?: number | null;
|
batch?: number | null;
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { mongooseAdapter } from '@payloadcms/db-mongodb'
|
import { mongooseAdapter } from '@payloadcms/db-mongodb'
|
||||||
|
import { postgresAdapter } from '@payloadcms/db-postgres'
|
||||||
import { lexicalEditor } from '@payloadcms/richtext-lexical'
|
import { lexicalEditor } from '@payloadcms/richtext-lexical'
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
import { buildConfig } from 'payload'
|
import { buildConfig } from 'payload'
|
||||||
@@ -11,6 +12,7 @@ import { multiTenantPlugin } from '@payloadcms/plugin-multi-tenant'
|
|||||||
import { isSuperAdmin } from './access/isSuperAdmin'
|
import { isSuperAdmin } from './access/isSuperAdmin'
|
||||||
import type { Config } from './payload-types'
|
import type { Config } from './payload-types'
|
||||||
import { getUserTenantIDs } from './utilities/getUserTenantIDs'
|
import { getUserTenantIDs } from './utilities/getUserTenantIDs'
|
||||||
|
import { seed } from './seed'
|
||||||
|
|
||||||
const filename = fileURLToPath(import.meta.url)
|
const filename = fileURLToPath(import.meta.url)
|
||||||
const dirname = path.dirname(filename)
|
const dirname = path.dirname(filename)
|
||||||
@@ -21,9 +23,19 @@ export default buildConfig({
|
|||||||
user: 'users',
|
user: 'users',
|
||||||
},
|
},
|
||||||
collections: [Pages, Users, Tenants],
|
collections: [Pages, Users, Tenants],
|
||||||
db: mongooseAdapter({
|
// db: mongooseAdapter({
|
||||||
url: process.env.DATABASE_URI as string,
|
// url: process.env.DATABASE_URI as string,
|
||||||
|
// }),
|
||||||
|
db: postgresAdapter({
|
||||||
|
pool: {
|
||||||
|
connectionString: process.env.POSTGRES_URL,
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
|
onInit: async (args) => {
|
||||||
|
if (process.env.SEED_DB) {
|
||||||
|
await seed(args)
|
||||||
|
}
|
||||||
|
},
|
||||||
editor: lexicalEditor({}),
|
editor: lexicalEditor({}),
|
||||||
graphQL: {
|
graphQL: {
|
||||||
schemaOutputFile: path.resolve(dirname, 'generated-schema.graphql'),
|
schemaOutputFile: path.resolve(dirname, 'generated-schema.graphql'),
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import type { MigrateUpArgs } from '@payloadcms/db-mongodb'
|
import { Config } from 'payload'
|
||||||
|
|
||||||
export async function up({ payload }: MigrateUpArgs): Promise<void> {
|
export const seed: NonNullable<Config['onInit']> = async (payload): Promise<void> => {
|
||||||
const tenant1 = await payload.create({
|
const tenant1 = await payload.create({
|
||||||
collection: 'tenants',
|
collection: 'tenants',
|
||||||
data: {
|
data: {
|
||||||
name: 'Tenant 1',
|
name: 'Tenant 1',
|
||||||
slug: 'gold',
|
slug: 'gold',
|
||||||
domain: 'gold.test',
|
domain: 'gold.localhost',
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -15,7 +15,7 @@ export async function up({ payload }: MigrateUpArgs): Promise<void> {
|
|||||||
data: {
|
data: {
|
||||||
name: 'Tenant 2',
|
name: 'Tenant 2',
|
||||||
slug: 'silver',
|
slug: 'silver',
|
||||||
domain: 'silver.test',
|
domain: 'silver.localhost',
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -24,7 +24,7 @@ export async function up({ payload }: MigrateUpArgs): Promise<void> {
|
|||||||
data: {
|
data: {
|
||||||
name: 'Tenant 3',
|
name: 'Tenant 3',
|
||||||
slug: 'bronze',
|
slug: 'bronze',
|
||||||
domain: 'bronze.test',
|
domain: 'bronze.localhost',
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import type { CollectionSlug, Payload } from 'payload'
|
||||||
|
|
||||||
|
type Args = {
|
||||||
|
collectionSlug: CollectionSlug
|
||||||
|
payload: Payload
|
||||||
|
}
|
||||||
|
export const getCollectionIDType = ({ collectionSlug, payload }: Args): 'number' | 'text' => {
|
||||||
|
return payload.collections[collectionSlug]?.customIDType ?? payload.db.defaultIDType
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user