@payloadcms/plugin-multi-tenant 3.21.0-canary.c6481e1 → 3.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/TenantSelector/index.js +1 -1
- package/dist/components/TenantSelector/index.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -8
- package/dist/index.js.map +1 -1
- package/dist/providers/TenantSelectionProvider/index.d.ts.map +1 -1
- package/dist/providers/TenantSelectionProvider/index.js +10 -6
- package/dist/providers/TenantSelectionProvider/index.js.map +1 -1
- package/dist/types.d.ts +4 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/utilities/getTenantFromCookie.d.ts.map +1 -1
- package/dist/utilities/getTenantFromCookie.js +2 -1
- package/dist/utilities/getTenantFromCookie.js.map +1 -1
- package/package.json +5 -5
|
@@ -34,7 +34,7 @@ export const TenantSelector = ({ viewType })=>{
|
|
|
34
34
|
onChange: handleChange,
|
|
35
35
|
options: options,
|
|
36
36
|
path: "setTenant",
|
|
37
|
-
value: selectedTenantID ? selectedTenantID === SELECT_ALL ? undefined :
|
|
37
|
+
value: selectedTenantID ? selectedTenantID === SELECT_ALL ? undefined : selectedTenantID : undefined
|
|
38
38
|
})
|
|
39
39
|
});
|
|
40
40
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/TenantSelector/index.tsx"],"sourcesContent":["'use client'\nimport type { ReactSelectOption } from '@payloadcms/ui'\nimport type { ViewTypes } from 'payload'\n\nimport { SelectInput } from '@payloadcms/ui'\n\nimport './index.scss'\n\nimport React from 'react'\n\nimport { SELECT_ALL } from '../../constants.js'\nimport { useTenantSelection } from '../../providers/TenantSelectionProvider/index.client.js'\n\nexport const TenantSelector = ({ viewType }: { viewType?: ViewTypes }) => {\n const { options, selectedTenantID, setTenant } = useTenantSelection()\n\n const handleChange = React.useCallback(\n (option: ReactSelectOption | ReactSelectOption[]) => {\n if (option && 'value' in option) {\n setTenant({ id: option.value as string, refresh: true })\n } else {\n setTenant({ id: undefined, refresh: true })\n }\n },\n [setTenant],\n )\n\n if (options.length <= 1) {\n return null\n }\n\n return (\n <div className=\"tenant-selector\">\n <SelectInput\n isClearable={viewType === 'list'}\n label=\"Tenant\"\n name=\"setTenant\"\n onChange={handleChange}\n options={options}\n path=\"setTenant\"\n value={\n selectedTenantID\n ? selectedTenantID === SELECT_ALL\n ? undefined\n :
|
|
1
|
+
{"version":3,"sources":["../../../src/components/TenantSelector/index.tsx"],"sourcesContent":["'use client'\nimport type { ReactSelectOption } from '@payloadcms/ui'\nimport type { ViewTypes } from 'payload'\n\nimport { SelectInput } from '@payloadcms/ui'\n\nimport './index.scss'\n\nimport React from 'react'\n\nimport { SELECT_ALL } from '../../constants.js'\nimport { useTenantSelection } from '../../providers/TenantSelectionProvider/index.client.js'\n\nexport const TenantSelector = ({ viewType }: { viewType?: ViewTypes }) => {\n const { options, selectedTenantID, setTenant } = useTenantSelection()\n\n const handleChange = React.useCallback(\n (option: ReactSelectOption | ReactSelectOption[]) => {\n if (option && 'value' in option) {\n setTenant({ id: option.value as string, refresh: true })\n } else {\n setTenant({ id: undefined, refresh: true })\n }\n },\n [setTenant],\n )\n\n if (options.length <= 1) {\n return null\n }\n\n return (\n <div className=\"tenant-selector\">\n <SelectInput\n isClearable={viewType === 'list'}\n label=\"Tenant\"\n name=\"setTenant\"\n onChange={handleChange}\n options={options}\n path=\"setTenant\"\n value={\n selectedTenantID\n ? selectedTenantID === SELECT_ALL\n ? undefined\n : (selectedTenantID as string)\n : undefined\n }\n />\n </div>\n )\n}\n"],"names":["SelectInput","React","SELECT_ALL","useTenantSelection","TenantSelector","viewType","options","selectedTenantID","setTenant","handleChange","useCallback","option","id","value","refresh","undefined","length","div","className","isClearable","label","name","onChange","path"],"mappings":"AAAA;;AAIA,SAASA,WAAW,QAAQ,iBAAgB;AAE5C,OAAO,eAAc;AAErB,OAAOC,WAAW,QAAO;AAEzB,SAASC,UAAU,QAAQ,qBAAoB;AAC/C,SAASC,kBAAkB,QAAQ,0DAAyD;AAE5F,OAAO,MAAMC,iBAAiB,CAAC,EAAEC,QAAQ,EAA4B;IACnE,MAAM,EAAEC,OAAO,EAAEC,gBAAgB,EAAEC,SAAS,EAAE,GAAGL;IAEjD,MAAMM,eAAeR,MAAMS,WAAW,CACpC,CAACC;QACC,IAAIA,UAAU,WAAWA,QAAQ;YAC/BH,UAAU;gBAAEI,IAAID,OAAOE,KAAK;gBAAYC,SAAS;YAAK;QACxD,OAAO;YACLN,UAAU;gBAAEI,IAAIG;gBAAWD,SAAS;YAAK;QAC3C;IACF,GACA;QAACN;KAAU;IAGb,IAAIF,QAAQU,MAAM,IAAI,GAAG;QACvB,OAAO;IACT;IAEA,qBACE,KAACC;QAAIC,WAAU;kBACb,cAAA,KAAClB;YACCmB,aAAad,aAAa;YAC1Be,OAAM;YACNC,MAAK;YACLC,UAAUb;YACVH,SAASA;YACTiB,MAAK;YACLV,OACEN,mBACIA,qBAAqBL,aACnBa,YACCR,mBACHQ;;;AAKd,EAAC"}
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAoB,MAAM,EAAE,MAAM,SAAS,CAAA;AAEvD,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAA;AAgBzD,eAAO,MAAM,iBAAiB,GAC3B,UAAU,gBAAgB,uBAAuB,CAAC,UAAU,CAAC,sBAC7C,MAAM,KAAG,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAoB,MAAM,EAAE,MAAM,SAAS,CAAA;AAEvD,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAA;AAgBzD,eAAO,MAAM,iBAAiB,GAC3B,UAAU,gBAAgB,uBAAuB,CAAC,UAAU,CAAC,sBAC7C,MAAM,KAAG,MA4OzB,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -91,14 +91,16 @@ export const multiTenantPlugin = (pluginConfig)=>(incomingConfig)=>{
|
|
|
91
91
|
* Modify tenants collection
|
|
92
92
|
*/ if (collection.slug === tenantsCollectionSlug) {
|
|
93
93
|
tenantCollection = collection;
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
94
|
+
if (pluginConfig.useTenantsCollectionAccess !== false) {
|
|
95
|
+
/**
|
|
96
|
+
* Add access control constraint to tenants collection
|
|
97
|
+
* - constrains access a users assigned tenants
|
|
98
|
+
*/ addCollectionAccess({
|
|
99
|
+
collection,
|
|
100
|
+
fieldName: 'id',
|
|
101
|
+
userHasAccessToAllTenants
|
|
102
|
+
});
|
|
103
|
+
}
|
|
102
104
|
if (pluginConfig.cleanupAfterTenantDelete !== false) {
|
|
103
105
|
/**
|
|
104
106
|
* Add cleanup logic when tenant is deleted
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type { CollectionConfig, Config } from 'payload'\n\nimport type { MultiTenantPluginConfig } from './types.js'\n\nimport { tenantField } from './fields/tenantField/index.js'\nimport { tenantsArrayField } from './fields/tenantsArrayField/index.js'\nimport { addTenantCleanup } from './hooks/afterTenantDelete.js'\nimport { addCollectionAccess } from './utilities/addCollectionAccess.js'\nimport { addFilterOptionsToFields } from './utilities/addFilterOptionsToFields.js'\nimport { withTenantListFilter } from './utilities/withTenantListFilter.js'\n\nconst defaults = {\n tenantCollectionSlug: 'tenants',\n tenantFieldName: 'tenant',\n tenantsArrayFieldName: 'tenants',\n tenantsArrayTenantFieldName: 'tenant',\n}\n\nexport const multiTenantPlugin =\n <ConfigType>(pluginConfig: MultiTenantPluginConfig<ConfigType>) =>\n (incomingConfig: Config): Config => {\n if (pluginConfig.enabled === false) {\n return incomingConfig\n }\n\n /**\n * Set defaults\n */\n const userHasAccessToAllTenants: Required<\n MultiTenantPluginConfig<ConfigType>\n >['userHasAccessToAllTenants'] =\n typeof pluginConfig.userHasAccessToAllTenants === 'function'\n ? pluginConfig.userHasAccessToAllTenants\n : () => false\n const tenantsCollectionSlug = (pluginConfig.tenantsSlug =\n pluginConfig.tenantsSlug || defaults.tenantCollectionSlug)\n const tenantFieldName = pluginConfig?.tenantField?.name || defaults.tenantFieldName\n const tenantsArrayFieldName =\n pluginConfig?.tenantsArrayField?.arrayFieldName || defaults.tenantsArrayFieldName\n const tenantsArrayTenantFieldName =\n pluginConfig?.tenantsArrayField?.arrayTenantFieldName || defaults.tenantsArrayTenantFieldName\n\n /**\n * Add defaults for admin properties\n */\n if (!incomingConfig.admin) {\n incomingConfig.admin = {}\n }\n if (!incomingConfig.admin?.components) {\n incomingConfig.admin.components = {\n actions: [],\n beforeNavLinks: [],\n providers: [],\n }\n }\n if (!incomingConfig.admin.components?.providers) {\n incomingConfig.admin.components.providers = []\n }\n if (!incomingConfig.admin.components?.actions) {\n incomingConfig.admin.components.actions = []\n }\n if (!incomingConfig.admin.components?.beforeNavLinks) {\n incomingConfig.admin.components.beforeNavLinks = []\n }\n if (!incomingConfig.collections) {\n incomingConfig.collections = []\n }\n\n /**\n * Add tenants array field to users collection\n */\n const adminUsersCollection = incomingConfig.collections.find(({ slug, auth }) => {\n if (incomingConfig.admin?.user) {\n return slug === incomingConfig.admin.user\n } else if (auth) {\n return true\n }\n })\n\n if (!adminUsersCollection) {\n throw Error('An auth enabled collection was not found')\n }\n\n /**\n * Add tenants array field to users collection\n */\n if (pluginConfig?.tenantsArrayField?.includeDefaultField !== false) {\n adminUsersCollection.fields.push(\n tenantsArrayField({\n ...(pluginConfig?.tenantsArrayField || {}),\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n tenantsCollectionSlug,\n }),\n )\n }\n\n addCollectionAccess({\n collection: adminUsersCollection,\n fieldName: `${tenantsArrayFieldName}.${tenantsArrayTenantFieldName}`,\n userHasAccessToAllTenants,\n })\n\n let tenantCollection: CollectionConfig | undefined\n\n const [collectionSlugs, globalCollectionSlugs] = Object.keys(pluginConfig.collections).reduce<\n [string[], string[]]\n >(\n (acc, slug) => {\n if (pluginConfig?.collections?.[slug]?.isGlobal) {\n acc[1].push(slug)\n } else {\n acc[0].push(slug)\n }\n\n return acc\n },\n [[], []],\n )\n\n /**\n * Modify collections\n */\n incomingConfig.collections.forEach((collection) => {\n /**\n * Modify tenants collection\n */\n if (collection.slug === tenantsCollectionSlug) {\n tenantCollection = collection\n\n /**\n * Add access control constraint to tenants collection\n * - constrains access a users assigned tenants\n */\n addCollectionAccess({\n collection,\n fieldName: 'id',\n userHasAccessToAllTenants,\n })\n\n if (pluginConfig.cleanupAfterTenantDelete !== false) {\n /**\n * Add cleanup logic when tenant is deleted\n * - delete documents related to tenant\n * - remove tenant from users\n */\n addTenantCleanup({\n collection,\n enabledSlugs: [...collectionSlugs, ...globalCollectionSlugs],\n tenantFieldName,\n tenantsCollectionSlug,\n usersSlug: adminUsersCollection.slug,\n usersTenantsArrayFieldName: tenantsArrayFieldName,\n usersTenantsArrayTenantFieldName: tenantsArrayTenantFieldName,\n })\n }\n } else if (pluginConfig.collections?.[collection.slug]) {\n const isGlobal = Boolean(pluginConfig.collections[collection.slug]?.isGlobal)\n\n if (isGlobal) {\n collection.disableDuplicate = true\n }\n\n /**\n * Modify enabled collections\n */\n addFilterOptionsToFields({\n fields: collection.fields,\n tenantEnabledCollectionSlugs: collectionSlugs,\n tenantEnabledGlobalSlugs: globalCollectionSlugs,\n tenantFieldName,\n tenantsCollectionSlug,\n })\n\n /**\n * Add tenant field to enabled collections\n */\n collection.fields.splice(\n 0,\n 0,\n tenantField({\n ...(pluginConfig?.tenantField || {}),\n name: tenantFieldName,\n debug: pluginConfig.debug,\n tenantsCollectionSlug,\n unique: isGlobal,\n }),\n )\n\n if (pluginConfig.collections[collection.slug]?.useBaseListFilter !== false) {\n /**\n * Collection baseListFilter with selected tenant constraint (if selected)\n */\n if (!collection.admin) {\n collection.admin = {}\n }\n collection.admin.baseListFilter = withTenantListFilter({\n baseListFilter: collection.admin?.baseListFilter,\n tenantFieldName,\n tenantsCollectionSlug,\n })\n }\n\n if (pluginConfig.collections[collection.slug]?.useTenantAccess !== false) {\n /**\n * Add access control constraint to tenant enabled collection\n */\n addCollectionAccess({\n collection,\n fieldName: tenantFieldName,\n userHasAccessToAllTenants,\n })\n }\n }\n })\n\n if (!tenantCollection) {\n throw new Error(`Tenants collection not found with slug: ${tenantsCollectionSlug}`)\n }\n\n /**\n * Add TenantSelectionProvider to admin providers\n */\n incomingConfig.admin.components.providers.push({\n clientProps: {\n tenantsCollectionSlug: tenantCollection.slug,\n useAsTitle: tenantCollection.admin?.useAsTitle || 'id',\n },\n path: '@payloadcms/plugin-multi-tenant/rsc#TenantSelectionProvider',\n })\n\n /**\n * Add global redirect action\n */\n if (globalCollectionSlugs.length) {\n incomingConfig.admin.components.actions.push({\n path: '@payloadcms/plugin-multi-tenant/rsc#GlobalViewRedirect',\n serverProps: {\n globalSlugs: globalCollectionSlugs,\n tenantFieldName,\n tenantsCollectionSlug,\n useAsTitle: tenantCollection.admin?.useAsTitle || 'id',\n },\n })\n }\n\n /**\n * Add tenant selector to admin UI\n */\n incomingConfig.admin.components.beforeNavLinks.push({\n path: '@payloadcms/plugin-multi-tenant/client#TenantSelector',\n })\n\n return incomingConfig\n }\n"],"names":["tenantField","tenantsArrayField","addTenantCleanup","addCollectionAccess","addFilterOptionsToFields","withTenantListFilter","defaults","tenantCollectionSlug","tenantFieldName","tenantsArrayFieldName","tenantsArrayTenantFieldName","multiTenantPlugin","pluginConfig","incomingConfig","enabled","userHasAccessToAllTenants","tenantsCollectionSlug","tenantsSlug","name","arrayFieldName","arrayTenantFieldName","admin","components","actions","beforeNavLinks","providers","collections","adminUsersCollection","find","slug","auth","user","Error","includeDefaultField","fields","push","collection","fieldName","tenantCollection","collectionSlugs","globalCollectionSlugs","Object","keys","reduce","acc","isGlobal","forEach","cleanupAfterTenantDelete","enabledSlugs","usersSlug","usersTenantsArrayFieldName","usersTenantsArrayTenantFieldName","Boolean","disableDuplicate","tenantEnabledCollectionSlugs","tenantEnabledGlobalSlugs","splice","debug","unique","useBaseListFilter","baseListFilter","useTenantAccess","clientProps","useAsTitle","path","length","serverProps","globalSlugs"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type { CollectionConfig, Config } from 'payload'\n\nimport type { MultiTenantPluginConfig } from './types.js'\n\nimport { tenantField } from './fields/tenantField/index.js'\nimport { tenantsArrayField } from './fields/tenantsArrayField/index.js'\nimport { addTenantCleanup } from './hooks/afterTenantDelete.js'\nimport { addCollectionAccess } from './utilities/addCollectionAccess.js'\nimport { addFilterOptionsToFields } from './utilities/addFilterOptionsToFields.js'\nimport { withTenantListFilter } from './utilities/withTenantListFilter.js'\n\nconst defaults = {\n tenantCollectionSlug: 'tenants',\n tenantFieldName: 'tenant',\n tenantsArrayFieldName: 'tenants',\n tenantsArrayTenantFieldName: 'tenant',\n}\n\nexport const multiTenantPlugin =\n <ConfigType>(pluginConfig: MultiTenantPluginConfig<ConfigType>) =>\n (incomingConfig: Config): Config => {\n if (pluginConfig.enabled === false) {\n return incomingConfig\n }\n\n /**\n * Set defaults\n */\n const userHasAccessToAllTenants: Required<\n MultiTenantPluginConfig<ConfigType>\n >['userHasAccessToAllTenants'] =\n typeof pluginConfig.userHasAccessToAllTenants === 'function'\n ? pluginConfig.userHasAccessToAllTenants\n : () => false\n const tenantsCollectionSlug = (pluginConfig.tenantsSlug =\n pluginConfig.tenantsSlug || defaults.tenantCollectionSlug)\n const tenantFieldName = pluginConfig?.tenantField?.name || defaults.tenantFieldName\n const tenantsArrayFieldName =\n pluginConfig?.tenantsArrayField?.arrayFieldName || defaults.tenantsArrayFieldName\n const tenantsArrayTenantFieldName =\n pluginConfig?.tenantsArrayField?.arrayTenantFieldName || defaults.tenantsArrayTenantFieldName\n\n /**\n * Add defaults for admin properties\n */\n if (!incomingConfig.admin) {\n incomingConfig.admin = {}\n }\n if (!incomingConfig.admin?.components) {\n incomingConfig.admin.components = {\n actions: [],\n beforeNavLinks: [],\n providers: [],\n }\n }\n if (!incomingConfig.admin.components?.providers) {\n incomingConfig.admin.components.providers = []\n }\n if (!incomingConfig.admin.components?.actions) {\n incomingConfig.admin.components.actions = []\n }\n if (!incomingConfig.admin.components?.beforeNavLinks) {\n incomingConfig.admin.components.beforeNavLinks = []\n }\n if (!incomingConfig.collections) {\n incomingConfig.collections = []\n }\n\n /**\n * Add tenants array field to users collection\n */\n const adminUsersCollection = incomingConfig.collections.find(({ slug, auth }) => {\n if (incomingConfig.admin?.user) {\n return slug === incomingConfig.admin.user\n } else if (auth) {\n return true\n }\n })\n\n if (!adminUsersCollection) {\n throw Error('An auth enabled collection was not found')\n }\n\n /**\n * Add tenants array field to users collection\n */\n if (pluginConfig?.tenantsArrayField?.includeDefaultField !== false) {\n adminUsersCollection.fields.push(\n tenantsArrayField({\n ...(pluginConfig?.tenantsArrayField || {}),\n tenantsArrayFieldName,\n tenantsArrayTenantFieldName,\n tenantsCollectionSlug,\n }),\n )\n }\n\n addCollectionAccess({\n collection: adminUsersCollection,\n fieldName: `${tenantsArrayFieldName}.${tenantsArrayTenantFieldName}`,\n userHasAccessToAllTenants,\n })\n\n let tenantCollection: CollectionConfig | undefined\n\n const [collectionSlugs, globalCollectionSlugs] = Object.keys(pluginConfig.collections).reduce<\n [string[], string[]]\n >(\n (acc, slug) => {\n if (pluginConfig?.collections?.[slug]?.isGlobal) {\n acc[1].push(slug)\n } else {\n acc[0].push(slug)\n }\n\n return acc\n },\n [[], []],\n )\n\n /**\n * Modify collections\n */\n incomingConfig.collections.forEach((collection) => {\n /**\n * Modify tenants collection\n */\n if (collection.slug === tenantsCollectionSlug) {\n tenantCollection = collection\n\n if (pluginConfig.useTenantsCollectionAccess !== false) {\n /**\n * Add access control constraint to tenants collection\n * - constrains access a users assigned tenants\n */\n addCollectionAccess({\n collection,\n fieldName: 'id',\n userHasAccessToAllTenants,\n })\n }\n\n if (pluginConfig.cleanupAfterTenantDelete !== false) {\n /**\n * Add cleanup logic when tenant is deleted\n * - delete documents related to tenant\n * - remove tenant from users\n */\n addTenantCleanup({\n collection,\n enabledSlugs: [...collectionSlugs, ...globalCollectionSlugs],\n tenantFieldName,\n tenantsCollectionSlug,\n usersSlug: adminUsersCollection.slug,\n usersTenantsArrayFieldName: tenantsArrayFieldName,\n usersTenantsArrayTenantFieldName: tenantsArrayTenantFieldName,\n })\n }\n } else if (pluginConfig.collections?.[collection.slug]) {\n const isGlobal = Boolean(pluginConfig.collections[collection.slug]?.isGlobal)\n\n if (isGlobal) {\n collection.disableDuplicate = true\n }\n\n /**\n * Modify enabled collections\n */\n addFilterOptionsToFields({\n fields: collection.fields,\n tenantEnabledCollectionSlugs: collectionSlugs,\n tenantEnabledGlobalSlugs: globalCollectionSlugs,\n tenantFieldName,\n tenantsCollectionSlug,\n })\n\n /**\n * Add tenant field to enabled collections\n */\n collection.fields.splice(\n 0,\n 0,\n tenantField({\n ...(pluginConfig?.tenantField || {}),\n name: tenantFieldName,\n debug: pluginConfig.debug,\n tenantsCollectionSlug,\n unique: isGlobal,\n }),\n )\n\n if (pluginConfig.collections[collection.slug]?.useBaseListFilter !== false) {\n /**\n * Collection baseListFilter with selected tenant constraint (if selected)\n */\n if (!collection.admin) {\n collection.admin = {}\n }\n collection.admin.baseListFilter = withTenantListFilter({\n baseListFilter: collection.admin?.baseListFilter,\n tenantFieldName,\n tenantsCollectionSlug,\n })\n }\n\n if (pluginConfig.collections[collection.slug]?.useTenantAccess !== false) {\n /**\n * Add access control constraint to tenant enabled collection\n */\n addCollectionAccess({\n collection,\n fieldName: tenantFieldName,\n userHasAccessToAllTenants,\n })\n }\n }\n })\n\n if (!tenantCollection) {\n throw new Error(`Tenants collection not found with slug: ${tenantsCollectionSlug}`)\n }\n\n /**\n * Add TenantSelectionProvider to admin providers\n */\n incomingConfig.admin.components.providers.push({\n clientProps: {\n tenantsCollectionSlug: tenantCollection.slug,\n useAsTitle: tenantCollection.admin?.useAsTitle || 'id',\n },\n path: '@payloadcms/plugin-multi-tenant/rsc#TenantSelectionProvider',\n })\n\n /**\n * Add global redirect action\n */\n if (globalCollectionSlugs.length) {\n incomingConfig.admin.components.actions.push({\n path: '@payloadcms/plugin-multi-tenant/rsc#GlobalViewRedirect',\n serverProps: {\n globalSlugs: globalCollectionSlugs,\n tenantFieldName,\n tenantsCollectionSlug,\n useAsTitle: tenantCollection.admin?.useAsTitle || 'id',\n },\n })\n }\n\n /**\n * Add tenant selector to admin UI\n */\n incomingConfig.admin.components.beforeNavLinks.push({\n path: '@payloadcms/plugin-multi-tenant/client#TenantSelector',\n })\n\n return incomingConfig\n }\n"],"names":["tenantField","tenantsArrayField","addTenantCleanup","addCollectionAccess","addFilterOptionsToFields","withTenantListFilter","defaults","tenantCollectionSlug","tenantFieldName","tenantsArrayFieldName","tenantsArrayTenantFieldName","multiTenantPlugin","pluginConfig","incomingConfig","enabled","userHasAccessToAllTenants","tenantsCollectionSlug","tenantsSlug","name","arrayFieldName","arrayTenantFieldName","admin","components","actions","beforeNavLinks","providers","collections","adminUsersCollection","find","slug","auth","user","Error","includeDefaultField","fields","push","collection","fieldName","tenantCollection","collectionSlugs","globalCollectionSlugs","Object","keys","reduce","acc","isGlobal","forEach","useTenantsCollectionAccess","cleanupAfterTenantDelete","enabledSlugs","usersSlug","usersTenantsArrayFieldName","usersTenantsArrayTenantFieldName","Boolean","disableDuplicate","tenantEnabledCollectionSlugs","tenantEnabledGlobalSlugs","splice","debug","unique","useBaseListFilter","baseListFilter","useTenantAccess","clientProps","useAsTitle","path","length","serverProps","globalSlugs"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/providers/TenantSelectionProvider/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAgB,OAAO,EAAE,IAAI,EAAE,MAAM,SAAS,CAAA;AAQ1D,KAAK,IAAI,GAAG;IACV,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;IACzB,OAAO,EAAE,OAAO,CAAA;IAChB,qBAAqB,EAAE,MAAM,CAAA;IAC7B,UAAU,EAAE,MAAM,CAAA;IAClB,IAAI,EAAE,IAAI,CAAA;CACX,CAAA;AAED,eAAO,MAAM,uBAAuB,oEAMjC,IAAI,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/providers/TenantSelectionProvider/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAgB,OAAO,EAAE,IAAI,EAAE,MAAM,SAAS,CAAA;AAQ1D,KAAK,IAAI,GAAG;IACV,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;IACzB,OAAO,EAAE,OAAO,CAAA;IAChB,qBAAqB,EAAE,MAAM,CAAA;IAC7B,UAAU,EAAE,MAAM,CAAA;IAClB,IAAI,EAAE,IAAI,CAAA;CACX,CAAA;AAED,eAAO,MAAM,uBAAuB,oEAMjC,IAAI,yCA4CN,CAAA"}
|
|
@@ -15,7 +15,7 @@ export const TenantSelectionProvider = async ({ children, payload, tenantsCollec
|
|
|
15
15
|
});
|
|
16
16
|
tenantOptions = docs.map((doc)=>({
|
|
17
17
|
label: String(doc[useAsTitle]),
|
|
18
|
-
value:
|
|
18
|
+
value: doc.id
|
|
19
19
|
}));
|
|
20
20
|
} catch (_) {
|
|
21
21
|
// user likely does not have access
|
|
@@ -23,12 +23,16 @@ export const TenantSelectionProvider = async ({ children, payload, tenantsCollec
|
|
|
23
23
|
const cookies = await getCookies();
|
|
24
24
|
let tenantCookie = cookies.get('payload-tenant')?.value;
|
|
25
25
|
let initialValue = undefined;
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
initialValue = tenantCookie;
|
|
26
|
+
if (tenantOptions.length > 1 && tenantCookie === SELECT_ALL) {
|
|
27
|
+
initialValue = SELECT_ALL;
|
|
29
28
|
} else {
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
const matchingOption = tenantOptions.find((option)=>String(option.value) === tenantCookie);
|
|
30
|
+
if (matchingOption) {
|
|
31
|
+
initialValue = matchingOption.value;
|
|
32
|
+
} else {
|
|
33
|
+
tenantCookie = undefined;
|
|
34
|
+
initialValue = tenantOptions.length > 1 ? SELECT_ALL : tenantOptions[0]?.value;
|
|
35
|
+
}
|
|
32
36
|
}
|
|
33
37
|
return /*#__PURE__*/ _jsx(TenantSelectionProviderClient, {
|
|
34
38
|
initialValue: initialValue,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/providers/TenantSelectionProvider/index.tsx"],"sourcesContent":["import type { OptionObject, Payload, User } from 'payload'\n\nimport { cookies as getCookies } from 'next/headers.js'\n\nimport { SELECT_ALL } from '../../constants.js'\nimport { findTenantOptions } from '../../queries/findTenantOptions.js'\nimport { TenantSelectionProviderClient } from './index.client.js'\n\ntype Args = {\n children: React.ReactNode\n payload: Payload\n tenantsCollectionSlug: string\n useAsTitle: string\n user: User\n}\n\nexport const TenantSelectionProvider = async ({\n children,\n payload,\n tenantsCollectionSlug,\n useAsTitle,\n user,\n}: Args) => {\n let tenantOptions: OptionObject[] = []\n\n try {\n const { docs } = await findTenantOptions({\n limit: 0,\n payload,\n tenantsCollectionSlug,\n useAsTitle,\n user,\n })\n tenantOptions = docs.map((doc) => ({\n label: String(doc[useAsTitle]),\n value:
|
|
1
|
+
{"version":3,"sources":["../../../src/providers/TenantSelectionProvider/index.tsx"],"sourcesContent":["import type { OptionObject, Payload, User } from 'payload'\n\nimport { cookies as getCookies } from 'next/headers.js'\n\nimport { SELECT_ALL } from '../../constants.js'\nimport { findTenantOptions } from '../../queries/findTenantOptions.js'\nimport { TenantSelectionProviderClient } from './index.client.js'\n\ntype Args = {\n children: React.ReactNode\n payload: Payload\n tenantsCollectionSlug: string\n useAsTitle: string\n user: User\n}\n\nexport const TenantSelectionProvider = async ({\n children,\n payload,\n tenantsCollectionSlug,\n useAsTitle,\n user,\n}: Args) => {\n let tenantOptions: OptionObject[] = []\n\n try {\n const { docs } = await findTenantOptions({\n limit: 0,\n payload,\n tenantsCollectionSlug,\n useAsTitle,\n user,\n })\n tenantOptions = docs.map((doc) => ({\n label: String(doc[useAsTitle]),\n value: doc.id,\n }))\n } catch (_) {\n // user likely does not have access\n }\n\n const cookies = await getCookies()\n let tenantCookie = cookies.get('payload-tenant')?.value\n let initialValue = undefined\n\n if (tenantOptions.length > 1 && tenantCookie === SELECT_ALL) {\n initialValue = SELECT_ALL\n } else {\n const matchingOption = tenantOptions.find((option) => String(option.value) === tenantCookie)\n if (matchingOption) {\n initialValue = matchingOption.value\n } else {\n tenantCookie = undefined\n initialValue = tenantOptions.length > 1 ? SELECT_ALL : tenantOptions[0]?.value\n }\n }\n\n return (\n <TenantSelectionProviderClient\n initialValue={initialValue}\n tenantCookie={tenantCookie}\n tenantOptions={tenantOptions}\n >\n {children}\n </TenantSelectionProviderClient>\n )\n}\n"],"names":["cookies","getCookies","SELECT_ALL","findTenantOptions","TenantSelectionProviderClient","TenantSelectionProvider","children","payload","tenantsCollectionSlug","useAsTitle","user","tenantOptions","docs","limit","map","doc","label","String","value","id","_","tenantCookie","get","initialValue","undefined","length","matchingOption","find","option"],"mappings":";AAEA,SAASA,WAAWC,UAAU,QAAQ,kBAAiB;AAEvD,SAASC,UAAU,QAAQ,qBAAoB;AAC/C,SAASC,iBAAiB,QAAQ,qCAAoC;AACtE,SAASC,6BAA6B,QAAQ,oBAAmB;AAUjE,OAAO,MAAMC,0BAA0B,OAAO,EAC5CC,QAAQ,EACRC,OAAO,EACPC,qBAAqB,EACrBC,UAAU,EACVC,IAAI,EACC;IACL,IAAIC,gBAAgC,EAAE;IAEtC,IAAI;QACF,MAAM,EAAEC,IAAI,EAAE,GAAG,MAAMT,kBAAkB;YACvCU,OAAO;YACPN;YACAC;YACAC;YACAC;QACF;QACAC,gBAAgBC,KAAKE,GAAG,CAAC,CAACC,MAAS,CAAA;gBACjCC,OAAOC,OAAOF,GAAG,CAACN,WAAW;gBAC7BS,OAAOH,IAAII,EAAE;YACf,CAAA;IACF,EAAE,OAAOC,GAAG;IACV,mCAAmC;IACrC;IAEA,MAAMpB,UAAU,MAAMC;IACtB,IAAIoB,eAAerB,QAAQsB,GAAG,CAAC,mBAAmBJ;IAClD,IAAIK,eAAeC;IAEnB,IAAIb,cAAcc,MAAM,GAAG,KAAKJ,iBAAiBnB,YAAY;QAC3DqB,eAAerB;IACjB,OAAO;QACL,MAAMwB,iBAAiBf,cAAcgB,IAAI,CAAC,CAACC,SAAWX,OAAOW,OAAOV,KAAK,MAAMG;QAC/E,IAAIK,gBAAgB;YAClBH,eAAeG,eAAeR,KAAK;QACrC,OAAO;YACLG,eAAeG;YACfD,eAAeZ,cAAcc,MAAM,GAAG,IAAIvB,aAAaS,aAAa,CAAC,EAAE,EAAEO;QAC3E;IACF;IAEA,qBACE,KAACd;QACCmB,cAAcA;QACdF,cAAcA;QACdV,eAAeA;kBAEdL;;AAGP,EAAC"}
|
package/dist/types.d.ts
CHANGED
|
@@ -118,6 +118,10 @@ export type MultiTenantPluginConfig<ConfigTypes = unknown> = {
|
|
|
118
118
|
userHasAccessToAllTenants?: (user: ConfigTypes extends {
|
|
119
119
|
user: unknown;
|
|
120
120
|
} ? ConfigTypes['user'] : User) => boolean;
|
|
121
|
+
/**
|
|
122
|
+
* Opt out of adding access constraints to the tenants collection
|
|
123
|
+
*/
|
|
124
|
+
useTenantsCollectionAccess?: boolean;
|
|
121
125
|
};
|
|
122
126
|
export type Tenant<IDType = number | string> = {
|
|
123
127
|
id: IDType;
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAE,KAAK,EAAE,iBAAiB,EAAE,IAAI,EAAE,MAAM,SAAS,CAAA;AAEzF,MAAM,MAAM,uBAAuB,CAAC,WAAW,GAAG,OAAO,IAAI;IAC3D;;;;;;OAMG;IACH,wBAAwB,CAAC,EAAE,OAAO,CAAA;IAClC;;OAEG;IACH,WAAW,EAAE;SACV,GAAG,IAAI,cAAc,CAAC,CAAC,EAAE;YACxB;;;;eAIG;YACH,QAAQ,CAAC,EAAE,OAAO,CAAA;YAClB;;;;eAIG;YACH,iBAAiB,CAAC,EAAE,OAAO,CAAA;YAC3B;;;;eAIG;YACH,eAAe,CAAC,EAAE,OAAO,CAAA;SAC1B;KACF,CAAA;IACD;;;;;OAKG;IACH,KAAK,CAAC,EAAE,OAAO,CAAA;IACf;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB;;OAEG;IACH,WAAW,CAAC,EAAE;QACZ,MAAM,CAAC,EAAE,iBAAiB,CAAC,QAAQ,CAAC,CAAA;QACpC;;;;WAIG;QACH,IAAI,CAAC,EAAE,MAAM,CAAA;KACd,CAAA;IACD;;;;;OAKG;IACH,iBAAiB,CAAC,EACd;QACE;;WAEG;QACH,gBAAgB,CAAC,EAAE,UAAU,CAAC,QAAQ,CAAC,CAAA;QACvC;;;;WAIG;QACH,cAAc,CAAC,EAAE,MAAM,CAAA;QACvB;;;;WAIG;QACH,oBAAoB,CAAC,EAAE,MAAM,CAAA;QAC7B;;WAEG;QACH,mBAAmB,CAAC,EAAE,IAAI,CAAA;QAC1B;;WAEG;QACH,SAAS,CAAC,EAAE,KAAK,EAAE,CAAA;QACnB;;WAEG;QACH,iBAAiB,CAAC,EAAE,iBAAiB,CAAC,QAAQ,CAAC,CAAA;KAChD,GACD;QACE,gBAAgB,CAAC,EAAE,KAAK,CAAA;QACxB,cAAc,CAAC,EAAE,MAAM,CAAA;QACvB,oBAAoB,CAAC,EAAE,MAAM,CAAA;QAC7B;;WAEG;QACH,mBAAmB,CAAC,EAAE,KAAK,CAAA;QAC3B,SAAS,CAAC,EAAE,KAAK,CAAA;QACjB,iBAAiB,CAAC,EAAE,KAAK,CAAA;KAC1B,CAAA;IACL;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB;;;;OAIG;IACH,yBAAyB,CAAC,EAAE,CAC1B,IAAI,EAAE,WAAW,SAAS;QAAE,IAAI,EAAE,OAAO,CAAA;KAAE,GAAG,WAAW,CAAC,MAAM,CAAC,GAAG,IAAI,KACrE,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAE,KAAK,EAAE,iBAAiB,EAAE,IAAI,EAAE,MAAM,SAAS,CAAA;AAEzF,MAAM,MAAM,uBAAuB,CAAC,WAAW,GAAG,OAAO,IAAI;IAC3D;;;;;;OAMG;IACH,wBAAwB,CAAC,EAAE,OAAO,CAAA;IAClC;;OAEG;IACH,WAAW,EAAE;SACV,GAAG,IAAI,cAAc,CAAC,CAAC,EAAE;YACxB;;;;eAIG;YACH,QAAQ,CAAC,EAAE,OAAO,CAAA;YAClB;;;;eAIG;YACH,iBAAiB,CAAC,EAAE,OAAO,CAAA;YAC3B;;;;eAIG;YACH,eAAe,CAAC,EAAE,OAAO,CAAA;SAC1B;KACF,CAAA;IACD;;;;;OAKG;IACH,KAAK,CAAC,EAAE,OAAO,CAAA;IACf;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB;;OAEG;IACH,WAAW,CAAC,EAAE;QACZ,MAAM,CAAC,EAAE,iBAAiB,CAAC,QAAQ,CAAC,CAAA;QACpC;;;;WAIG;QACH,IAAI,CAAC,EAAE,MAAM,CAAA;KACd,CAAA;IACD;;;;;OAKG;IACH,iBAAiB,CAAC,EACd;QACE;;WAEG;QACH,gBAAgB,CAAC,EAAE,UAAU,CAAC,QAAQ,CAAC,CAAA;QACvC;;;;WAIG;QACH,cAAc,CAAC,EAAE,MAAM,CAAA;QACvB;;;;WAIG;QACH,oBAAoB,CAAC,EAAE,MAAM,CAAA;QAC7B;;WAEG;QACH,mBAAmB,CAAC,EAAE,IAAI,CAAA;QAC1B;;WAEG;QACH,SAAS,CAAC,EAAE,KAAK,EAAE,CAAA;QACnB;;WAEG;QACH,iBAAiB,CAAC,EAAE,iBAAiB,CAAC,QAAQ,CAAC,CAAA;KAChD,GACD;QACE,gBAAgB,CAAC,EAAE,KAAK,CAAA;QACxB,cAAc,CAAC,EAAE,MAAM,CAAA;QACvB,oBAAoB,CAAC,EAAE,MAAM,CAAA;QAC7B;;WAEG;QACH,mBAAmB,CAAC,EAAE,KAAK,CAAA;QAC3B,SAAS,CAAC,EAAE,KAAK,CAAA;QACjB,iBAAiB,CAAC,EAAE,KAAK,CAAA;KAC1B,CAAA;IACL;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB;;;;OAIG;IACH,yBAAyB,CAAC,EAAE,CAC1B,IAAI,EAAE,WAAW,SAAS;QAAE,IAAI,EAAE,OAAO,CAAA;KAAE,GAAG,WAAW,CAAC,MAAM,CAAC,GAAG,IAAI,KACrE,OAAO,CAAA;IACZ;;OAEG;IACH,0BAA0B,CAAC,EAAE,OAAO,CAAA;CACrC,CAAA;AAED,MAAM,MAAM,MAAM,CAAC,MAAM,GAAG,MAAM,GAAG,MAAM,IAAI;IAC7C,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;CACb,CAAA;AAED,MAAM,MAAM,oBAAoB,GAAG;IACjC,OAAO,EAAE;QACP,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAA;KACjC,EAAE,CAAA;CACJ,GAAG,IAAI,CAAA"}
|
package/dist/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/types.ts"],"sourcesContent":["import type { ArrayField, CollectionSlug, Field, RelationshipField, User } from 'payload'\n\nexport type MultiTenantPluginConfig<ConfigTypes = unknown> = {\n /**\n * After a tenant is deleted, the plugin will attempt to clean up related documents\n * - removing documents with the tenant ID\n * - removing the tenant from users\n *\n * @default true\n */\n cleanupAfterTenantDelete?: boolean\n /**\n * Automatically\n */\n collections: {\n [key in CollectionSlug]?: {\n /**\n * Set to `true` if you want the collection to behave as a global\n *\n * @default false\n */\n isGlobal?: boolean\n /**\n * Set to `false` if you want to manually apply the baseListFilter\n *\n * @default true\n */\n useBaseListFilter?: boolean\n /**\n * Set to `false` if you want to handle collection access manually without the multi-tenant constraints applied\n *\n * @default true\n */\n useTenantAccess?: boolean\n }\n }\n /**\n * Enables debug mode\n * - Makes the tenant field visible in the admin UI within applicable collections\n *\n * @default false\n */\n debug?: boolean\n /**\n * Enables the multi-tenant plugin\n *\n * @default true\n */\n enabled?: boolean\n /**\n * Field configuration for the field added to all tenant enabled collections\n */\n tenantField?: {\n access?: RelationshipField['access']\n /**\n * The name of the field added to all tenant enabled collections\n *\n * @default 'tenant'\n */\n name?: string\n }\n /**\n * Field configuration for the field added to the users collection\n *\n * If `includeDefaultField` is `false`, you must include the field on your users collection manually\n * This is useful if you want to customize the field or place the field in a specific location\n */\n tenantsArrayField?:\n | {\n /**\n * Access configuration for the array field\n */\n arrayFieldAccess?: ArrayField['access']\n /**\n * Name of the array field\n *\n * @default 'tenants'\n */\n arrayFieldName?: string\n /**\n * Name of the tenant field\n *\n * @default 'tenant'\n */\n arrayTenantFieldName?: string\n /**\n * When `includeDefaultField` is `true`, the field will be added to the users collection automatically\n */\n includeDefaultField?: true\n /**\n * Additional fields to include on the tenants array field\n */\n rowFields?: Field[]\n /**\n * Access configuration for the tenant field\n */\n tenantFieldAccess?: RelationshipField['access']\n }\n | {\n arrayFieldAccess?: never\n arrayFieldName?: string\n arrayTenantFieldName?: string\n /**\n * When `includeDefaultField` is `false`, you must include the field on your users collection manually\n */\n includeDefaultField?: false\n rowFields?: never\n tenantFieldAccess?: never\n }\n /**\n * The slug for the tenant collection\n *\n * @default 'tenants'\n */\n tenantsSlug?: string\n /**\n * Function that determines if a user has access to _all_ tenants\n *\n * Useful for super-admin type users\n */\n userHasAccessToAllTenants?: (\n user: ConfigTypes extends { user: unknown } ? ConfigTypes['user'] : User,\n ) => boolean\n}\n\nexport type Tenant<IDType = number | string> = {\n id: IDType\n name: string\n}\n\nexport type UserWithTenantsField = {\n tenants: {\n tenant: number | string | Tenant\n }[]\n} & User\n"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../src/types.ts"],"sourcesContent":["import type { ArrayField, CollectionSlug, Field, RelationshipField, User } from 'payload'\n\nexport type MultiTenantPluginConfig<ConfigTypes = unknown> = {\n /**\n * After a tenant is deleted, the plugin will attempt to clean up related documents\n * - removing documents with the tenant ID\n * - removing the tenant from users\n *\n * @default true\n */\n cleanupAfterTenantDelete?: boolean\n /**\n * Automatically\n */\n collections: {\n [key in CollectionSlug]?: {\n /**\n * Set to `true` if you want the collection to behave as a global\n *\n * @default false\n */\n isGlobal?: boolean\n /**\n * Set to `false` if you want to manually apply the baseListFilter\n *\n * @default true\n */\n useBaseListFilter?: boolean\n /**\n * Set to `false` if you want to handle collection access manually without the multi-tenant constraints applied\n *\n * @default true\n */\n useTenantAccess?: boolean\n }\n }\n /**\n * Enables debug mode\n * - Makes the tenant field visible in the admin UI within applicable collections\n *\n * @default false\n */\n debug?: boolean\n /**\n * Enables the multi-tenant plugin\n *\n * @default true\n */\n enabled?: boolean\n /**\n * Field configuration for the field added to all tenant enabled collections\n */\n tenantField?: {\n access?: RelationshipField['access']\n /**\n * The name of the field added to all tenant enabled collections\n *\n * @default 'tenant'\n */\n name?: string\n }\n /**\n * Field configuration for the field added to the users collection\n *\n * If `includeDefaultField` is `false`, you must include the field on your users collection manually\n * This is useful if you want to customize the field or place the field in a specific location\n */\n tenantsArrayField?:\n | {\n /**\n * Access configuration for the array field\n */\n arrayFieldAccess?: ArrayField['access']\n /**\n * Name of the array field\n *\n * @default 'tenants'\n */\n arrayFieldName?: string\n /**\n * Name of the tenant field\n *\n * @default 'tenant'\n */\n arrayTenantFieldName?: string\n /**\n * When `includeDefaultField` is `true`, the field will be added to the users collection automatically\n */\n includeDefaultField?: true\n /**\n * Additional fields to include on the tenants array field\n */\n rowFields?: Field[]\n /**\n * Access configuration for the tenant field\n */\n tenantFieldAccess?: RelationshipField['access']\n }\n | {\n arrayFieldAccess?: never\n arrayFieldName?: string\n arrayTenantFieldName?: string\n /**\n * When `includeDefaultField` is `false`, you must include the field on your users collection manually\n */\n includeDefaultField?: false\n rowFields?: never\n tenantFieldAccess?: never\n }\n /**\n * The slug for the tenant collection\n *\n * @default 'tenants'\n */\n tenantsSlug?: string\n /**\n * Function that determines if a user has access to _all_ tenants\n *\n * Useful for super-admin type users\n */\n userHasAccessToAllTenants?: (\n user: ConfigTypes extends { user: unknown } ? ConfigTypes['user'] : User,\n ) => boolean\n /**\n * Opt out of adding access constraints to the tenants collection\n */\n useTenantsCollectionAccess?: boolean\n}\n\nexport type Tenant<IDType = number | string> = {\n id: IDType\n name: string\n}\n\nexport type UserWithTenantsField = {\n tenants: {\n tenant: number | string | Tenant\n }[]\n} & User\n"],"names":[],"mappings":"AAsIA,WAIQ"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getTenantFromCookie.d.ts","sourceRoot":"","sources":["../../src/utilities/getTenantFromCookie.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"getTenantFromCookie.d.ts","sourceRoot":"","sources":["../../src/utilities/getTenantFromCookie.ts"],"names":[],"mappings":"AAGA;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,QAAQ,GAAG,MAAM,GACxB,IAAI,GAAG,MAAM,GAAG,MAAM,CAQxB"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { parseCookies } from 'payload';
|
|
2
|
+
import { isNumber } from 'payload/shared';
|
|
2
3
|
/**
|
|
3
4
|
* A function that takes request headers and an idType and returns the current tenant ID from the cookie
|
|
4
5
|
*
|
|
@@ -8,7 +9,7 @@ import { parseCookies } from 'payload';
|
|
|
8
9
|
*/ export function getTenantFromCookie(headers, idType) {
|
|
9
10
|
const cookies = parseCookies(headers);
|
|
10
11
|
const selectedTenant = cookies.get('payload-tenant') || null;
|
|
11
|
-
return selectedTenant ? idType === 'number' ? parseFloat(selectedTenant) : selectedTenant : null;
|
|
12
|
+
return selectedTenant ? idType === 'number' && isNumber(selectedTenant) ? parseFloat(selectedTenant) : selectedTenant : null;
|
|
12
13
|
}
|
|
13
14
|
|
|
14
15
|
//# sourceMappingURL=getTenantFromCookie.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/utilities/getTenantFromCookie.ts"],"sourcesContent":["import { parseCookies } from 'payload'\n\n/**\n * A function that takes request headers and an idType and returns the current tenant ID from the cookie\n *\n * @param headers Headers, usually derived from req.headers or next/headers\n * @param idType can be 'number' | 'text', usually derived from payload.db.defaultIDType\n * @returns string | number | null\n */\nexport function getTenantFromCookie(\n headers: Headers,\n idType: 'number' | 'text',\n): null | number | string {\n const cookies = parseCookies(headers)\n const selectedTenant = cookies.get('payload-tenant') || null\n return selectedTenant
|
|
1
|
+
{"version":3,"sources":["../../src/utilities/getTenantFromCookie.ts"],"sourcesContent":["import { parseCookies } from 'payload'\nimport { isNumber } from 'payload/shared'\n\n/**\n * A function that takes request headers and an idType and returns the current tenant ID from the cookie\n *\n * @param headers Headers, usually derived from req.headers or next/headers\n * @param idType can be 'number' | 'text', usually derived from payload.db.defaultIDType\n * @returns string | number | null\n */\nexport function getTenantFromCookie(\n headers: Headers,\n idType: 'number' | 'text',\n): null | number | string {\n const cookies = parseCookies(headers)\n const selectedTenant = cookies.get('payload-tenant') || null\n return selectedTenant\n ? idType === 'number' && isNumber(selectedTenant)\n ? parseFloat(selectedTenant)\n : selectedTenant\n : null\n}\n"],"names":["parseCookies","isNumber","getTenantFromCookie","headers","idType","cookies","selectedTenant","get","parseFloat"],"mappings":"AAAA,SAASA,YAAY,QAAQ,UAAS;AACtC,SAASC,QAAQ,QAAQ,iBAAgB;AAEzC;;;;;;CAMC,GACD,OAAO,SAASC,oBACdC,OAAgB,EAChBC,MAAyB;IAEzB,MAAMC,UAAUL,aAAaG;IAC7B,MAAMG,iBAAiBD,QAAQE,GAAG,CAAC,qBAAqB;IACxD,OAAOD,iBACHF,WAAW,YAAYH,SAASK,kBAC9BE,WAAWF,kBACXA,iBACF;AACN"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@payloadcms/plugin-multi-tenant",
|
|
3
|
-
"version": "3.21.0
|
|
3
|
+
"version": "3.21.0",
|
|
4
4
|
"description": "Multi Tenant plugin for Payload",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"payload",
|
|
@@ -67,13 +67,13 @@
|
|
|
67
67
|
],
|
|
68
68
|
"devDependencies": {
|
|
69
69
|
"@payloadcms/eslint-config": "3.9.0",
|
|
70
|
-
"
|
|
71
|
-
"
|
|
70
|
+
"@payloadcms/ui": "3.21.0",
|
|
71
|
+
"payload": "3.21.0"
|
|
72
72
|
},
|
|
73
73
|
"peerDependencies": {
|
|
74
74
|
"next": "^15.0.3",
|
|
75
|
-
"@payloadcms/ui": "3.21.0
|
|
76
|
-
"payload": "3.21.0
|
|
75
|
+
"@payloadcms/ui": "3.21.0",
|
|
76
|
+
"payload": "3.21.0"
|
|
77
77
|
},
|
|
78
78
|
"homepage:": "https://payloadcms.com",
|
|
79
79
|
"scripts": {
|