@payloadcms/plugin-multi-tenant 0.0.1
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/LICENSE.md +22 -0
- package/README.md +204 -0
- package/dist/components/GlobalViewRedirect/index.d.ts +10 -0
- package/dist/components/GlobalViewRedirect/index.d.ts.map +1 -0
- package/dist/components/GlobalViewRedirect/index.js +18 -0
- package/dist/components/GlobalViewRedirect/index.js.map +1 -0
- package/dist/components/TenantField/index.client.d.ts +10 -0
- package/dist/components/TenantField/index.client.d.ts.map +1 -0
- package/dist/components/TenantField/index.client.js +34 -0
- package/dist/components/TenantField/index.client.js.map +1 -0
- package/dist/components/TenantField/index.d.ts +3 -0
- package/dist/components/TenantField/index.d.ts.map +1 -0
- package/dist/components/TenantField/index.js +33 -0
- package/dist/components/TenantField/index.js.map +1 -0
- package/dist/components/TenantSelector/index.client.d.ts +11 -0
- package/dist/components/TenantSelector/index.client.d.ts.map +1 -0
- package/dist/components/TenantSelector/index.client.js +61 -0
- package/dist/components/TenantSelector/index.client.js.map +1 -0
- package/dist/components/TenantSelector/index.d.ts +10 -0
- package/dist/components/TenantSelector/index.d.ts.map +1 -0
- package/dist/components/TenantSelector/index.js +31 -0
- package/dist/components/TenantSelector/index.js.map +1 -0
- package/dist/components/TenantSelector/index.scss +4 -0
- package/dist/exports/rsc.d.ts +4 -0
- package/dist/exports/rsc.d.ts.map +1 -0
- package/dist/exports/rsc.js +5 -0
- package/dist/exports/rsc.js.map +1 -0
- package/dist/exports/types.d.ts +2 -0
- package/dist/exports/types.d.ts.map +1 -0
- package/dist/exports/types.js +3 -0
- package/dist/exports/types.js.map +1 -0
- package/dist/exports/utilities.d.ts +6 -0
- package/dist/exports/utilities.d.ts.map +1 -0
- package/dist/exports/utilities.js +7 -0
- package/dist/exports/utilities.js.map +1 -0
- package/dist/fields/tenantField/index.d.ts +12 -0
- package/dist/fields/tenantField/index.d.ts.map +1 -0
- package/dist/fields/tenantField/index.js +38 -0
- package/dist/fields/tenantField/index.js.map +1 -0
- package/dist/fields/userTenantsArrayField/index.d.ts +4 -0
- package/dist/fields/userTenantsArrayField/index.d.ts.map +1 -0
- package/dist/fields/userTenantsArrayField/index.js +20 -0
- package/dist/fields/userTenantsArrayField/index.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +112 -0
- package/dist/index.js.map +1 -0
- package/dist/types.d.ts +80 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/dist/utilities/combineWhereConstraints.d.ts +3 -0
- package/dist/utilities/combineWhereConstraints.d.ts.map +1 -0
- package/dist/utilities/combineWhereConstraints.js +19 -0
- package/dist/utilities/combineWhereConstraints.js.map +1 -0
- package/dist/utilities/extractID.d.ts +3 -0
- package/dist/utilities/extractID.d.ts.map +1 -0
- package/dist/utilities/extractID.js +8 -0
- package/dist/utilities/extractID.js.map +1 -0
- package/dist/utilities/getGlobalViewRedirect.d.ts +10 -0
- package/dist/utilities/getGlobalViewRedirect.d.ts.map +1 -0
- package/dist/utilities/getGlobalViewRedirect.js +44 -0
- package/dist/utilities/getGlobalViewRedirect.js.map +1 -0
- package/dist/utilities/getTenantAccess.d.ts +5 -0
- package/dist/utilities/getTenantAccess.d.ts.map +1 -0
- package/dist/utilities/getTenantAccess.js +14 -0
- package/dist/utilities/getTenantAccess.js.map +1 -0
- package/dist/utilities/getTenantFromCookie.d.ts +2 -0
- package/dist/utilities/getTenantFromCookie.d.ts.map +1 -0
- package/dist/utilities/getTenantFromCookie.js +8 -0
- package/dist/utilities/getTenantFromCookie.js.map +1 -0
- package/dist/utilities/getTenantListFilter.d.ts +8 -0
- package/dist/utilities/getTenantListFilter.d.ts.map +1 -0
- package/dist/utilities/getTenantListFilter.js +15 -0
- package/dist/utilities/getTenantListFilter.js.map +1 -0
- package/dist/utilities/getUserTenantIDs.d.ts +9 -0
- package/dist/utilities/getUserTenantIDs.d.ts.map +1 -0
- package/dist/utilities/getUserTenantIDs.js +22 -0
- package/dist/utilities/getUserTenantIDs.js.map +1 -0
- package/dist/utilities/withTenantAccess.d.ts +9 -0
- package/dist/utilities/withTenantAccess.d.ts.map +1 -0
- package/dist/utilities/withTenantAccess.js +26 -0
- package/dist/utilities/withTenantAccess.js.map +1 -0
- package/dist/utilities/withTenantListFilter.d.ts +13 -0
- package/dist/utilities/withTenantListFilter.d.ts.map +1 -0
- package/dist/utilities/withTenantListFilter.js +36 -0
- package/dist/utilities/withTenantListFilter.js.map +1 -0
- package/package.json +79 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/fields/tenantField/index.ts"],"sourcesContent":["import { type RelationshipField } from 'payload'\nimport { APIError } from 'payload'\n\nimport type { MultiTenantPluginConfig } from '../../types.js'\n\nimport { getTenantFromCookie } from '../../utilities/getTenantFromCookie.js'\n\ntype Args = {\n access: MultiTenantPluginConfig['documentTenantField']['access']\n debug?: boolean\n name: string\n tenantsCollectionSlug: MultiTenantPluginConfig['tenantsSlug']\n unique: boolean\n}\nexport const tenantField = ({\n name,\n access,\n debug,\n tenantsCollectionSlug,\n unique,\n}: Args): RelationshipField => ({\n name,\n type: 'relationship',\n access,\n admin: {\n components: {\n Field: {\n clientProps: {\n debug,\n tenantsCollectionSlug,\n },\n path: '@payloadcms/plugin-multi-tenant/rsc#TenantField',\n },\n },\n position: debug ? 'sidebar' : undefined,\n },\n hasMany: false,\n hooks: {\n beforeChange: [\n ({ req, value }) => {\n if (!value) {\n const tenantFromCookie = getTenantFromCookie(req.headers, req.payload.db.defaultIDType)\n if (tenantFromCookie) {\n return tenantFromCookie\n }\n throw new APIError('You must select a tenant', 400, null, true)\n }\n },\n ],\n },\n index: true,\n relationTo: tenantsCollectionSlug,\n unique,\n})\n"],"names":["APIError","getTenantFromCookie","tenantField","name","access","debug","tenantsCollectionSlug","unique","type","admin","components","Field","clientProps","path","position","undefined","hasMany","hooks","beforeChange","req","value","tenantFromCookie","headers","payload","db","defaultIDType","index","relationTo"],"mappings":"AACA,SAASA,QAAQ,QAAQ,UAAS;AAIlC,SAASC,mBAAmB,QAAQ,yCAAwC;AAS5E,OAAO,MAAMC,cAAc,CAAC,EAC1BC,IAAI,EACJC,MAAM,EACNC,KAAK,EACLC,qBAAqB,EACrBC,MAAM,EACD,GAAyB,CAAA;QAC9BJ;QACAK,MAAM;QACNJ;QACAK,OAAO;YACLC,YAAY;gBACVC,OAAO;oBACLC,aAAa;wBACXP;wBACAC;oBACF;oBACAO,MAAM;gBACR;YACF;YACAC,UAAUT,QAAQ,YAAYU;QAChC;QACAC,SAAS;QACTC,OAAO;YACLC,cAAc;gBACZ,CAAC,EAAEC,GAAG,EAAEC,KAAK,EAAE;oBACb,IAAI,CAACA,OAAO;wBACV,MAAMC,mBAAmBpB,oBAAoBkB,IAAIG,OAAO,EAAEH,IAAII,OAAO,CAACC,EAAE,CAACC,aAAa;wBACtF,IAAIJ,kBAAkB;4BACpB,OAAOA;wBACT;wBACA,MAAM,IAAIrB,SAAS,4BAA4B,KAAK,MAAM;oBAC5D;gBACF;aACD;QACH;QACA0B,OAAO;QACPC,YAAYrB;QACZC;IACF,CAAA,EAAE"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/fields/userTenantsArrayField/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAEzC,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAA;AAE7D,eAAO,MAAM,gBAAgB,SACrB,uBAAuB,CAAC,kBAAkB,CAAC,KAChD,UAiBD,CAAA"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export const userTenantsField = (args)=>({
|
|
2
|
+
name: 'tenants',
|
|
3
|
+
type: 'array',
|
|
4
|
+
access: args?.access,
|
|
5
|
+
fields: [
|
|
6
|
+
{
|
|
7
|
+
name: 'tenant',
|
|
8
|
+
type: 'relationship',
|
|
9
|
+
access: args.access,
|
|
10
|
+
index: true,
|
|
11
|
+
relationTo: 'tenants',
|
|
12
|
+
required: true,
|
|
13
|
+
saveToJWT: true
|
|
14
|
+
},
|
|
15
|
+
...args?.rowFields || []
|
|
16
|
+
],
|
|
17
|
+
saveToJWT: true
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/fields/userTenantsArrayField/index.ts"],"sourcesContent":["import type { ArrayField } from 'payload'\n\nimport type { MultiTenantPluginConfig } from '../../types.js'\n\nexport const userTenantsField = (\n args: MultiTenantPluginConfig['userTenantsField'],\n): ArrayField => ({\n name: 'tenants',\n type: 'array',\n access: args?.access,\n fields: [\n {\n name: 'tenant',\n type: 'relationship',\n access: args.access,\n index: true,\n relationTo: 'tenants',\n required: true,\n saveToJWT: true,\n },\n ...(args?.rowFields || []),\n ],\n saveToJWT: true,\n})\n"],"names":["userTenantsField","args","name","type","access","fields","index","relationTo","required","saveToJWT","rowFields"],"mappings":"AAIA,OAAO,MAAMA,mBAAmB,CAC9BC,OACgB,CAAA;QAChBC,MAAM;QACNC,MAAM;QACNC,QAAQH,MAAMG;QACdC,QAAQ;YACN;gBACEH,MAAM;gBACNC,MAAM;gBACNC,QAAQH,KAAKG,MAAM;gBACnBE,OAAO;gBACPC,YAAY;gBACZC,UAAU;gBACVC,WAAW;YACb;eACIR,MAAMS,aAAa,EAAE;SAC1B;QACDD,WAAW;IACb,CAAA,EAAE"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,SAAS,CAAA;AAErC,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAA;AAazD,eAAO,MAAM,iBAAiB,iBACb,uBAAuB,sBACrB,MAAM,KAAG,MA+HzB,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { tenantField } from './fields/tenantField/index.js';
|
|
2
|
+
import { userTenantsField } from './fields/userTenantsArrayField/index.js';
|
|
3
|
+
import { withTenantAccess } from './utilities/withTenantAccess.js';
|
|
4
|
+
import { withTenantListFilter } from './utilities/withTenantListFilter.js';
|
|
5
|
+
const defaults = {
|
|
6
|
+
tenantCollectionSlug: 'tenants',
|
|
7
|
+
tenantFieldName: 'tenant',
|
|
8
|
+
userTenantsArrayFieldName: 'tenants'
|
|
9
|
+
};
|
|
10
|
+
export const multiTenantPlugin = (pluginConfig)=>(incomingConfig)=>{
|
|
11
|
+
if (pluginConfig.enabled === false) {
|
|
12
|
+
return incomingConfig;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Set defaults
|
|
16
|
+
*/ pluginConfig.userHasAccessToAllTenants = typeof pluginConfig.userHasAccessToAllTenants === 'function' ? pluginConfig.userHasAccessToAllTenants : ()=>false;
|
|
17
|
+
const tenantsCollectionSlug = pluginConfig.tenantsSlug = pluginConfig.tenantsSlug || defaults.tenantCollectionSlug;
|
|
18
|
+
const tenantFieldName = pluginConfig.documentTenantField.name || defaults.tenantFieldName;
|
|
19
|
+
/**
|
|
20
|
+
* Add tenants array field to users collection
|
|
21
|
+
*/ const adminUsersCollection = incomingConfig.collections.find(({ slug, auth })=>{
|
|
22
|
+
if (incomingConfig.admin?.user) {
|
|
23
|
+
return slug === incomingConfig.admin.user;
|
|
24
|
+
} else if (auth) {
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
adminUsersCollection.fields.push(userTenantsField(pluginConfig?.userTenantsField || {}));
|
|
29
|
+
const globalCollectionSlugs = [];
|
|
30
|
+
/**
|
|
31
|
+
* Modify collections
|
|
32
|
+
*/ incomingConfig.collections.forEach((collection)=>{
|
|
33
|
+
if (collection.slug === tenantsCollectionSlug) {
|
|
34
|
+
/**
|
|
35
|
+
* Modify tenants collection
|
|
36
|
+
*/ collection.access = Object.keys(collection.access || {}).reduce((acc, key)=>{
|
|
37
|
+
const accessFunction = collection.access[key];
|
|
38
|
+
acc[key] = withTenantAccess({
|
|
39
|
+
accessFunction,
|
|
40
|
+
userHasAccessToAllTenants: pluginConfig.userHasAccessToAllTenants
|
|
41
|
+
});
|
|
42
|
+
return acc;
|
|
43
|
+
}, {});
|
|
44
|
+
} else if (pluginConfig.collections?.[collection.slug]) {
|
|
45
|
+
if (!collection.admin) {
|
|
46
|
+
collection.admin = {};
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Add tenant field to enabled collections
|
|
50
|
+
*/ collection.fields.push(tenantField({
|
|
51
|
+
...pluginConfig.documentTenantField,
|
|
52
|
+
name: tenantFieldName,
|
|
53
|
+
debug: pluginConfig.debug,
|
|
54
|
+
tenantsCollectionSlug,
|
|
55
|
+
unique: Boolean(pluginConfig.collections[collection.slug].isGlobal)
|
|
56
|
+
}));
|
|
57
|
+
if (pluginConfig.collections[collection.slug].useBaseListFilter !== false) {
|
|
58
|
+
/**
|
|
59
|
+
* Collection baseListFilter with selected tenant constraint (if selected)
|
|
60
|
+
*/ collection.admin.baseListFilter = withTenantListFilter({
|
|
61
|
+
baseListFilter: collection.admin?.baseListFilter,
|
|
62
|
+
tenantFieldName
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
if (pluginConfig.collections[collection.slug].useTenantAccess !== false) {
|
|
66
|
+
/**
|
|
67
|
+
* Collection access functions with user assigned tenant constraints
|
|
68
|
+
*/ collection.access = Object.keys(collection.access || {}).reduce((acc, key)=>{
|
|
69
|
+
const accessFunction = collection.access[key];
|
|
70
|
+
acc[key] = withTenantAccess({
|
|
71
|
+
accessFunction,
|
|
72
|
+
userHasAccessToAllTenants: pluginConfig.userHasAccessToAllTenants
|
|
73
|
+
});
|
|
74
|
+
return acc;
|
|
75
|
+
}, {});
|
|
76
|
+
}
|
|
77
|
+
if (pluginConfig.collections[collection.slug].isGlobal) {
|
|
78
|
+
globalCollectionSlugs.push(collection.slug);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
if (!incomingConfig.admin?.components) {
|
|
83
|
+
incomingConfig.admin.components = {
|
|
84
|
+
actions: [],
|
|
85
|
+
beforeNavLinks: []
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Add global redirect action
|
|
90
|
+
*/ if (globalCollectionSlugs.length) {
|
|
91
|
+
incomingConfig.admin.components.actions.push({
|
|
92
|
+
path: '@payloadcms/plugin-multi-tenant/rsc#GlobalViewRedirect',
|
|
93
|
+
serverProps: {
|
|
94
|
+
globalSlugs: globalCollectionSlugs
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
if (!incomingConfig.admin.components.beforeNavLinks) {
|
|
99
|
+
incomingConfig.admin.components.beforeNavLinks = [];
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Add tenant selector to admin UI
|
|
103
|
+
*/ incomingConfig.admin.components.beforeNavLinks.push({
|
|
104
|
+
clientProps: {
|
|
105
|
+
tenantsCollectionSlug
|
|
106
|
+
},
|
|
107
|
+
path: '@payloadcms/plugin-multi-tenant/rsc#TenantSelector'
|
|
108
|
+
});
|
|
109
|
+
return incomingConfig;
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type { Config } from 'payload'\n\nimport type { MultiTenantPluginConfig } from './types.js'\n\nimport { tenantField } from './fields/tenantField/index.js'\nimport { userTenantsField } from './fields/userTenantsArrayField/index.js'\nimport { withTenantAccess } from './utilities/withTenantAccess.js'\nimport { withTenantListFilter } from './utilities/withTenantListFilter.js'\n\nconst defaults = {\n tenantCollectionSlug: 'tenants',\n tenantFieldName: 'tenant',\n userTenantsArrayFieldName: 'tenants',\n}\n\nexport const multiTenantPlugin =\n (pluginConfig: MultiTenantPluginConfig) =>\n (incomingConfig: Config): Config => {\n if (pluginConfig.enabled === false) {\n return incomingConfig\n }\n\n /**\n * Set defaults\n */\n pluginConfig.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.documentTenantField.name || defaults.tenantFieldName\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 adminUsersCollection.fields.push(userTenantsField(pluginConfig?.userTenantsField || {}))\n\n const globalCollectionSlugs = []\n\n /**\n * Modify collections\n */\n incomingConfig.collections.forEach((collection) => {\n if (collection.slug === tenantsCollectionSlug) {\n /**\n * Modify tenants collection\n */\n collection.access = Object.keys(collection.access || {}).reduce((acc, key) => {\n const accessFunction = collection.access[key]\n acc[key] = withTenantAccess({\n accessFunction,\n userHasAccessToAllTenants: pluginConfig.userHasAccessToAllTenants,\n })\n\n return acc\n }, {})\n } else if (pluginConfig.collections?.[collection.slug]) {\n if (!collection.admin) {\n collection.admin = {}\n }\n /**\n * Add tenant field to enabled collections\n */\n collection.fields.push(\n tenantField({\n ...pluginConfig.documentTenantField,\n name: tenantFieldName,\n debug: pluginConfig.debug,\n tenantsCollectionSlug,\n unique: Boolean(pluginConfig.collections[collection.slug].isGlobal),\n }),\n )\n\n if (pluginConfig.collections[collection.slug].useBaseListFilter !== false) {\n /**\n * Collection baseListFilter with selected tenant constraint (if selected)\n */\n collection.admin.baseListFilter = withTenantListFilter({\n baseListFilter: collection.admin?.baseListFilter,\n tenantFieldName,\n })\n }\n\n if (pluginConfig.collections[collection.slug].useTenantAccess !== false) {\n /**\n * Collection access functions with user assigned tenant constraints\n */\n collection.access = Object.keys(collection.access || {}).reduce((acc, key) => {\n const accessFunction = collection.access[key]\n acc[key] = withTenantAccess({\n accessFunction,\n userHasAccessToAllTenants: pluginConfig.userHasAccessToAllTenants,\n })\n\n return acc\n }, {})\n }\n\n if (pluginConfig.collections[collection.slug].isGlobal) {\n globalCollectionSlugs.push(collection.slug)\n }\n }\n })\n\n if (!incomingConfig.admin?.components) {\n incomingConfig.admin.components = {\n actions: [],\n beforeNavLinks: [],\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 },\n })\n }\n\n if (!incomingConfig.admin.components.beforeNavLinks) {\n incomingConfig.admin.components.beforeNavLinks = []\n }\n /**\n * Add tenant selector to admin UI\n */\n incomingConfig.admin.components.beforeNavLinks.push({\n clientProps: {\n tenantsCollectionSlug,\n },\n path: '@payloadcms/plugin-multi-tenant/rsc#TenantSelector',\n })\n\n return incomingConfig\n }\n"],"names":["tenantField","userTenantsField","withTenantAccess","withTenantListFilter","defaults","tenantCollectionSlug","tenantFieldName","userTenantsArrayFieldName","multiTenantPlugin","pluginConfig","incomingConfig","enabled","userHasAccessToAllTenants","tenantsCollectionSlug","tenantsSlug","documentTenantField","name","adminUsersCollection","collections","find","slug","auth","admin","user","fields","push","globalCollectionSlugs","forEach","collection","access","Object","keys","reduce","acc","key","accessFunction","debug","unique","Boolean","isGlobal","useBaseListFilter","baseListFilter","useTenantAccess","components","actions","beforeNavLinks","length","path","serverProps","globalSlugs","clientProps"],"mappings":"AAIA,SAASA,WAAW,QAAQ,gCAA+B;AAC3D,SAASC,gBAAgB,QAAQ,0CAAyC;AAC1E,SAASC,gBAAgB,QAAQ,kCAAiC;AAClE,SAASC,oBAAoB,QAAQ,sCAAqC;AAE1E,MAAMC,WAAW;IACfC,sBAAsB;IACtBC,iBAAiB;IACjBC,2BAA2B;AAC7B;AAEA,OAAO,MAAMC,oBACX,CAACC,eACD,CAACC;QACC,IAAID,aAAaE,OAAO,KAAK,OAAO;YAClC,OAAOD;QACT;QAEA;;KAEC,GACDD,aAAaG,yBAAyB,GACpC,OAAOH,aAAaG,yBAAyB,KAAK,aAC9CH,aAAaG,yBAAyB,GACtC,IAAM;QACZ,MAAMC,wBAAyBJ,aAAaK,WAAW,GACrDL,aAAaK,WAAW,IAAIV,SAASC,oBAAoB;QAC3D,MAAMC,kBAAkBG,aAAaM,mBAAmB,CAACC,IAAI,IAAIZ,SAASE,eAAe;QAEzF;;KAEC,GACD,MAAMW,uBAAuBP,eAAeQ,WAAW,CAACC,IAAI,CAAC,CAAC,EAAEC,IAAI,EAAEC,IAAI,EAAE;YAC1E,IAAIX,eAAeY,KAAK,EAAEC,MAAM;gBAC9B,OAAOH,SAASV,eAAeY,KAAK,CAACC,IAAI;YAC3C,OAAO,IAAIF,MAAM;gBACf,OAAO;YACT;QACF;QACAJ,qBAAqBO,MAAM,CAACC,IAAI,CAACxB,iBAAiBQ,cAAcR,oBAAoB,CAAC;QAErF,MAAMyB,wBAAwB,EAAE;QAEhC;;KAEC,GACDhB,eAAeQ,WAAW,CAACS,OAAO,CAAC,CAACC;YAClC,IAAIA,WAAWR,IAAI,KAAKP,uBAAuB;gBAC7C;;SAEC,GACDe,WAAWC,MAAM,GAAGC,OAAOC,IAAI,CAACH,WAAWC,MAAM,IAAI,CAAC,GAAGG,MAAM,CAAC,CAACC,KAAKC;oBACpE,MAAMC,iBAAiBP,WAAWC,MAAM,CAACK,IAAI;oBAC7CD,GAAG,CAACC,IAAI,GAAGhC,iBAAiB;wBAC1BiC;wBACAvB,2BAA2BH,aAAaG,yBAAyB;oBACnE;oBAEA,OAAOqB;gBACT,GAAG,CAAC;YACN,OAAO,IAAIxB,aAAaS,WAAW,EAAE,CAACU,WAAWR,IAAI,CAAC,EAAE;gBACtD,IAAI,CAACQ,WAAWN,KAAK,EAAE;oBACrBM,WAAWN,KAAK,GAAG,CAAC;gBACtB;gBACA;;SAEC,GACDM,WAAWJ,MAAM,CAACC,IAAI,CACpBzB,YAAY;oBACV,GAAGS,aAAaM,mBAAmB;oBACnCC,MAAMV;oBACN8B,OAAO3B,aAAa2B,KAAK;oBACzBvB;oBACAwB,QAAQC,QAAQ7B,aAAaS,WAAW,CAACU,WAAWR,IAAI,CAAC,CAACmB,QAAQ;gBACpE;gBAGF,IAAI9B,aAAaS,WAAW,CAACU,WAAWR,IAAI,CAAC,CAACoB,iBAAiB,KAAK,OAAO;oBACzE;;WAEC,GACDZ,WAAWN,KAAK,CAACmB,cAAc,GAAGtC,qBAAqB;wBACrDsC,gBAAgBb,WAAWN,KAAK,EAAEmB;wBAClCnC;oBACF;gBACF;gBAEA,IAAIG,aAAaS,WAAW,CAACU,WAAWR,IAAI,CAAC,CAACsB,eAAe,KAAK,OAAO;oBACvE;;WAEC,GACDd,WAAWC,MAAM,GAAGC,OAAOC,IAAI,CAACH,WAAWC,MAAM,IAAI,CAAC,GAAGG,MAAM,CAAC,CAACC,KAAKC;wBACpE,MAAMC,iBAAiBP,WAAWC,MAAM,CAACK,IAAI;wBAC7CD,GAAG,CAACC,IAAI,GAAGhC,iBAAiB;4BAC1BiC;4BACAvB,2BAA2BH,aAAaG,yBAAyB;wBACnE;wBAEA,OAAOqB;oBACT,GAAG,CAAC;gBACN;gBAEA,IAAIxB,aAAaS,WAAW,CAACU,WAAWR,IAAI,CAAC,CAACmB,QAAQ,EAAE;oBACtDb,sBAAsBD,IAAI,CAACG,WAAWR,IAAI;gBAC5C;YACF;QACF;QAEA,IAAI,CAACV,eAAeY,KAAK,EAAEqB,YAAY;YACrCjC,eAAeY,KAAK,CAACqB,UAAU,GAAG;gBAChCC,SAAS,EAAE;gBACXC,gBAAgB,EAAE;YACpB;QACF;QACA;;KAEC,GACD,IAAInB,sBAAsBoB,MAAM,EAAE;YAChCpC,eAAeY,KAAK,CAACqB,UAAU,CAACC,OAAO,CAACnB,IAAI,CAAC;gBAC3CsB,MAAM;gBACNC,aAAa;oBACXC,aAAavB;gBACf;YACF;QACF;QAEA,IAAI,CAAChB,eAAeY,KAAK,CAACqB,UAAU,CAACE,cAAc,EAAE;YACnDnC,eAAeY,KAAK,CAACqB,UAAU,CAACE,cAAc,GAAG,EAAE;QACrD;QACA;;KAEC,GACDnC,eAAeY,KAAK,CAACqB,UAAU,CAACE,cAAc,CAACpB,IAAI,CAAC;YAClDyB,aAAa;gBACXrC;YACF;YACAkC,MAAM;QACR;QAEA,OAAOrC;IACT,EAAC"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import type { ArrayField, Field, RelationshipField, User } from 'payload';
|
|
2
|
+
export type MultiTenantPluginConfig = {
|
|
3
|
+
collections: {
|
|
4
|
+
[collectionSlug: string]: {
|
|
5
|
+
/**
|
|
6
|
+
* Set to `true` if you want the collection to behave as a global
|
|
7
|
+
*
|
|
8
|
+
* @default false
|
|
9
|
+
*/
|
|
10
|
+
isGlobal?: boolean;
|
|
11
|
+
/**
|
|
12
|
+
* Set to `false` if you want to manually apply the baseListFilter
|
|
13
|
+
*
|
|
14
|
+
* @default true
|
|
15
|
+
*/
|
|
16
|
+
useBaseListFilter?: boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Set to `false` if you want to handle collection access manually without the multi-tenant constraints applied
|
|
19
|
+
*
|
|
20
|
+
* @default true
|
|
21
|
+
*/
|
|
22
|
+
useTenantAccess?: boolean;
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Enables debug mode
|
|
27
|
+
* - Makes the tenant field visible in the admin UI within applicable collections
|
|
28
|
+
*
|
|
29
|
+
* @default false
|
|
30
|
+
*/
|
|
31
|
+
debug?: boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Field configuration for the field added to all tenant enabled collections
|
|
34
|
+
*/
|
|
35
|
+
documentTenantField: {
|
|
36
|
+
access: RelationshipField['access'];
|
|
37
|
+
/**
|
|
38
|
+
* The name of the field added to all tenant enabled collections
|
|
39
|
+
*
|
|
40
|
+
* @default 'tenant'
|
|
41
|
+
*/
|
|
42
|
+
name?: string;
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* Enables the multi-tenant plugin
|
|
46
|
+
*
|
|
47
|
+
* @default true
|
|
48
|
+
*/
|
|
49
|
+
enabled?: boolean;
|
|
50
|
+
/**
|
|
51
|
+
* The slug for the tenant collection
|
|
52
|
+
*
|
|
53
|
+
* @default 'tenants'
|
|
54
|
+
*/
|
|
55
|
+
tenantsSlug?: string;
|
|
56
|
+
/**
|
|
57
|
+
* Function that determines if a user has access to _all_ tenants
|
|
58
|
+
*
|
|
59
|
+
* Useful for super-admin type users
|
|
60
|
+
*/
|
|
61
|
+
userHasAccessToAllTenants?: (user: User) => boolean;
|
|
62
|
+
/**
|
|
63
|
+
* Field configuration for the field added to the users collection
|
|
64
|
+
*/
|
|
65
|
+
userTenantsField?: {
|
|
66
|
+
access?: ArrayField['access'];
|
|
67
|
+
rowFields?: Field[];
|
|
68
|
+
};
|
|
69
|
+
};
|
|
70
|
+
export type Tenant<IDType = number | string> = {
|
|
71
|
+
id: IDType;
|
|
72
|
+
name: string;
|
|
73
|
+
};
|
|
74
|
+
export type UserWithTenantsField = {
|
|
75
|
+
tenants: {
|
|
76
|
+
roles: string[];
|
|
77
|
+
tenant: number | string | Tenant;
|
|
78
|
+
}[];
|
|
79
|
+
} & User;
|
|
80
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,iBAAiB,EAAE,IAAI,EAAE,MAAM,SAAS,CAAA;AAEzE,MAAM,MAAM,uBAAuB,GAAG;IACpC,WAAW,EAAE;QACX,CAAC,cAAc,EAAE,MAAM,GAAG;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,CAAA;KACF,CAAA;IACD;;;;;OAKG;IACH,KAAK,CAAC,EAAE,OAAO,CAAA;IACf;;OAEG;IACH,mBAAmB,EAAE;QACnB,MAAM,EAAE,iBAAiB,CAAC,QAAQ,CAAC,CAAA;QACnC;;;;WAIG;QACH,IAAI,CAAC,EAAE,MAAM,CAAA;KACd,CAAA;IACD;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB;;;;OAIG;IACH,yBAAyB,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,CAAA;IACnD;;OAEG;IACH,gBAAgB,CAAC,EAAE;QACjB,MAAM,CAAC,EAAE,UAAU,CAAC,QAAQ,CAAC,CAAA;QAC7B,SAAS,CAAC,EAAE,KAAK,EAAE,CAAA;KACpB,CAAA;CACF,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,KAAK,EAAE,MAAM,EAAE,CAAA;QACf,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAA;KACjC,EAAE,CAAA;CACJ,GAAG,IAAI,CAAA"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/types.ts"],"sourcesContent":["import type { ArrayField, Field, RelationshipField, User } from 'payload'\n\nexport type MultiTenantPluginConfig = {\n collections: {\n [collectionSlug: string]: {\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 * Field configuration for the field added to all tenant enabled collections\n */\n documentTenantField: {\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 * Enables the multi-tenant plugin\n *\n * @default true\n */\n enabled?: boolean\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?: (user: User) => boolean\n /**\n * Field configuration for the field added to the users collection\n */\n userTenantsField?: {\n access?: ArrayField['access']\n rowFields?: Field[]\n }\n}\n\nexport type Tenant<IDType = number | string> = {\n id: IDType\n name: string\n}\n\nexport type UserWithTenantsField = {\n tenants: {\n roles: string[]\n tenant: number | string | Tenant\n }[]\n} & User\n"],"names":[],"mappings":"AA4EA,WAKQ"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"combineWhereConstraints.d.ts","sourceRoot":"","sources":["../../src/utilities/combineWhereConstraints.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAA;AAEpC,wBAAgB,uBAAuB,CAAC,WAAW,EAAE,KAAK,CAAC,KAAK,CAAC,GAAG,KAAK,CAgBxE"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export function combineWhereConstraints(constraints) {
|
|
2
|
+
if (constraints.length === 0) {
|
|
3
|
+
return {};
|
|
4
|
+
}
|
|
5
|
+
if (constraints.length === 1) {
|
|
6
|
+
return constraints[0];
|
|
7
|
+
}
|
|
8
|
+
const andConstraint = {
|
|
9
|
+
and: []
|
|
10
|
+
};
|
|
11
|
+
constraints.forEach((constraint)=>{
|
|
12
|
+
if (typeof constraint === 'object') {
|
|
13
|
+
andConstraint.and.push(constraint);
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
return andConstraint;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
//# sourceMappingURL=combineWhereConstraints.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/utilities/combineWhereConstraints.ts"],"sourcesContent":["import type { Where } from 'payload'\n\nexport function combineWhereConstraints(constraints: Array<Where>): Where {\n if (constraints.length === 0) {\n return {}\n }\n if (constraints.length === 1) {\n return constraints[0]\n }\n const andConstraint: Where = {\n and: [],\n }\n constraints.forEach((constraint) => {\n if (typeof constraint === 'object') {\n andConstraint.and.push(constraint)\n }\n })\n return andConstraint\n}\n"],"names":["combineWhereConstraints","constraints","length","andConstraint","and","forEach","constraint","push"],"mappings":"AAEA,OAAO,SAASA,wBAAwBC,WAAyB;IAC/D,IAAIA,YAAYC,MAAM,KAAK,GAAG;QAC5B,OAAO,CAAC;IACV;IACA,IAAID,YAAYC,MAAM,KAAK,GAAG;QAC5B,OAAOD,WAAW,CAAC,EAAE;IACvB;IACA,MAAME,gBAAuB;QAC3BC,KAAK,EAAE;IACT;IACAH,YAAYI,OAAO,CAAC,CAACC;QACnB,IAAI,OAAOA,eAAe,UAAU;YAClCH,cAAcC,GAAG,CAACG,IAAI,CAACD;QACzB;IACF;IACA,OAAOH;AACT"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extractID.d.ts","sourceRoot":"","sources":["../../src/utilities/extractID.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAEzC,eAAO,MAAM,SAAS,GAAI,MAAM,SAAS,MAAM,GAAG,MAAM,cAC1C,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,KAClC,MAMF,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/utilities/extractID.ts"],"sourcesContent":["import type { Tenant } from '../types.js'\n\nexport const extractID = <IDType extends number | string>(\n objectOrID: IDType | Tenant<IDType>,\n): IDType => {\n if (typeof objectOrID === 'string' || typeof objectOrID === 'number') {\n return objectOrID\n }\n\n return objectOrID.id\n}\n"],"names":["extractID","objectOrID","id"],"mappings":"AAEA,OAAO,MAAMA,YAAY,CACvBC;IAEA,IAAI,OAAOA,eAAe,YAAY,OAAOA,eAAe,UAAU;QACpE,OAAOA;IACT;IAEA,OAAOA,WAAWC,EAAE;AACtB,EAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Payload } from 'payload';
|
|
2
|
+
type Args = {
|
|
3
|
+
docID?: number | string;
|
|
4
|
+
payload: Payload;
|
|
5
|
+
slug: string;
|
|
6
|
+
view: 'edit' | 'list';
|
|
7
|
+
};
|
|
8
|
+
export declare function getGlobalViewRedirect({ slug, docID, payload, view, }: Args): Promise<string | void>;
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=getGlobalViewRedirect.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getGlobalViewRedirect.d.ts","sourceRoot":"","sources":["../../src/utilities/getGlobalViewRedirect.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AAMtC,KAAK,IAAI,GAAG;IACV,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;IACvB,OAAO,EAAE,OAAO,CAAA;IAChB,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,GAAG,MAAM,CAAA;CACtB,CAAA;AACD,wBAAsB,qBAAqB,CAAC,EAC1C,IAAI,EACJ,KAAK,EACL,OAAO,EACP,IAAI,GACL,EAAE,IAAI,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CA0C/B"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { headers as getHeaders } from 'next/headers.js';
|
|
2
|
+
import { getTenantFromCookie } from './getTenantFromCookie.js';
|
|
3
|
+
export async function getGlobalViewRedirect({ slug, docID, payload, view }) {
|
|
4
|
+
const headers = await getHeaders();
|
|
5
|
+
const tenant = getTenantFromCookie(headers, payload.db.defaultIDType);
|
|
6
|
+
let redirectRoute;
|
|
7
|
+
if (tenant) {
|
|
8
|
+
try {
|
|
9
|
+
const { docs } = await payload.find({
|
|
10
|
+
collection: slug,
|
|
11
|
+
depth: 0,
|
|
12
|
+
limit: 1,
|
|
13
|
+
where: {
|
|
14
|
+
tenant: {
|
|
15
|
+
equals: tenant
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
const tenantDocID = docs?.[0]?.id;
|
|
20
|
+
if (view === 'edit') {
|
|
21
|
+
if (docID && !tenantDocID) {
|
|
22
|
+
// viewing a document with an id but does not match the selected tenant, redirect to create route
|
|
23
|
+
redirectRoute = `${payload.config.routes.admin}/collections/${slug}/create`;
|
|
24
|
+
} else if (tenantDocID && docID !== tenantDocID) {
|
|
25
|
+
// tenant document already exists but does not match current route doc ID, redirect to matching tenant doc
|
|
26
|
+
redirectRoute = `${payload.config.routes.admin}/collections/${slug}/${tenantDocID}`;
|
|
27
|
+
}
|
|
28
|
+
} else if (view === 'list') {
|
|
29
|
+
if (tenantDocID) {
|
|
30
|
+
// tenant document exists, redirect to edit view
|
|
31
|
+
redirectRoute = `${payload.config.routes.admin}/collections/${slug}/${tenantDocID}`;
|
|
32
|
+
} else {
|
|
33
|
+
// tenant document does not exist, redirect to create route
|
|
34
|
+
redirectRoute = `${payload.config.routes.admin}/collections/${slug}/create`;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
} catch (e) {
|
|
38
|
+
payload.logger.error(e, `${e?.message} - Multi Tenant Redirect`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return redirectRoute;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
//# sourceMappingURL=getGlobalViewRedirect.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/utilities/getGlobalViewRedirect.ts"],"sourcesContent":["import type { Payload } from 'payload'\n\nimport { headers as getHeaders } from 'next/headers.js'\n\nimport { getTenantFromCookie } from './getTenantFromCookie.js'\n\ntype Args = {\n docID?: number | string\n payload: Payload\n slug: string\n view: 'edit' | 'list'\n}\nexport async function getGlobalViewRedirect({\n slug,\n docID,\n payload,\n view,\n}: Args): Promise<string | void> {\n const headers = await getHeaders()\n const tenant = getTenantFromCookie(headers, payload.db.defaultIDType)\n let redirectRoute\n\n if (tenant) {\n try {\n const { docs } = await payload.find({\n collection: slug,\n depth: 0,\n limit: 1,\n where: {\n tenant: {\n equals: tenant,\n },\n },\n })\n\n const tenantDocID = docs?.[0]?.id\n\n if (view === 'edit') {\n if (docID && !tenantDocID) {\n // viewing a document with an id but does not match the selected tenant, redirect to create route\n redirectRoute = `${payload.config.routes.admin}/collections/${slug}/create`\n } else if (tenantDocID && docID !== tenantDocID) {\n // tenant document already exists but does not match current route doc ID, redirect to matching tenant doc\n redirectRoute = `${payload.config.routes.admin}/collections/${slug}/${tenantDocID}`\n }\n } else if (view === 'list') {\n if (tenantDocID) {\n // tenant document exists, redirect to edit view\n redirectRoute = `${payload.config.routes.admin}/collections/${slug}/${tenantDocID}`\n } else {\n // tenant document does not exist, redirect to create route\n redirectRoute = `${payload.config.routes.admin}/collections/${slug}/create`\n }\n }\n } catch (e) {\n payload.logger.error(e, `${e?.message} - Multi Tenant Redirect`)\n }\n }\n return redirectRoute\n}\n"],"names":["headers","getHeaders","getTenantFromCookie","getGlobalViewRedirect","slug","docID","payload","view","tenant","db","defaultIDType","redirectRoute","docs","find","collection","depth","limit","where","equals","tenantDocID","id","config","routes","admin","e","logger","error","message"],"mappings":"AAEA,SAASA,WAAWC,UAAU,QAAQ,kBAAiB;AAEvD,SAASC,mBAAmB,QAAQ,2BAA0B;AAQ9D,OAAO,eAAeC,sBAAsB,EAC1CC,IAAI,EACJC,KAAK,EACLC,OAAO,EACPC,IAAI,EACC;IACL,MAAMP,UAAU,MAAMC;IACtB,MAAMO,SAASN,oBAAoBF,SAASM,QAAQG,EAAE,CAACC,aAAa;IACpE,IAAIC;IAEJ,IAAIH,QAAQ;QACV,IAAI;YACF,MAAM,EAAEI,IAAI,EAAE,GAAG,MAAMN,QAAQO,IAAI,CAAC;gBAClCC,YAAYV;gBACZW,OAAO;gBACPC,OAAO;gBACPC,OAAO;oBACLT,QAAQ;wBACNU,QAAQV;oBACV;gBACF;YACF;YAEA,MAAMW,cAAcP,MAAM,CAAC,EAAE,EAAEQ;YAE/B,IAAIb,SAAS,QAAQ;gBACnB,IAAIF,SAAS,CAACc,aAAa;oBACzB,iGAAiG;oBACjGR,gBAAgB,GAAGL,QAAQe,MAAM,CAACC,MAAM,CAACC,KAAK,CAAC,aAAa,EAAEnB,KAAK,OAAO,CAAC;gBAC7E,OAAO,IAAIe,eAAed,UAAUc,aAAa;oBAC/C,0GAA0G;oBAC1GR,gBAAgB,GAAGL,QAAQe,MAAM,CAACC,MAAM,CAACC,KAAK,CAAC,aAAa,EAAEnB,KAAK,CAAC,EAAEe,aAAa;gBACrF;YACF,OAAO,IAAIZ,SAAS,QAAQ;gBAC1B,IAAIY,aAAa;oBACf,gDAAgD;oBAChDR,gBAAgB,GAAGL,QAAQe,MAAM,CAACC,MAAM,CAACC,KAAK,CAAC,aAAa,EAAEnB,KAAK,CAAC,EAAEe,aAAa;gBACrF,OAAO;oBACL,2DAA2D;oBAC3DR,gBAAgB,GAAGL,QAAQe,MAAM,CAACC,MAAM,CAACC,KAAK,CAAC,aAAa,EAAEnB,KAAK,OAAO,CAAC;gBAC7E;YACF;QACF,EAAE,OAAOoB,GAAG;YACVlB,QAAQmB,MAAM,CAACC,KAAK,CAACF,GAAG,GAAGA,GAAGG,QAAQ,wBAAwB,CAAC;QACjE;IACF;IACA,OAAOhB;AACT"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getTenantAccess.d.ts","sourceRoot":"","sources":["../../src/utilities/getTenantAccess.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAA;AAMpC,wBAAgB,eAAe,CAAC,EAAE,IAAI,EAAE;;CAAA,GAAG,IAAI,GAAG,KAAK,CAYtD"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { getUserTenantIDs } from './getUserTenantIDs.js';
|
|
2
|
+
export function getTenantAccess({ user }) {
|
|
3
|
+
const userAssignedTenantIDs = getUserTenantIDs(user);
|
|
4
|
+
if (userAssignedTenantIDs.length) {
|
|
5
|
+
return {
|
|
6
|
+
tenant: {
|
|
7
|
+
in: userAssignedTenantIDs
|
|
8
|
+
}
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
//# sourceMappingURL=getTenantAccess.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/utilities/getTenantAccess.ts"],"sourcesContent":["import type { Where } from 'payload'\n\nimport type { UserWithTenantsField } from '../types.js'\n\nimport { getUserTenantIDs } from './getUserTenantIDs.js'\n\nexport function getTenantAccess({ user }): null | Where {\n const userAssignedTenantIDs = getUserTenantIDs(user as UserWithTenantsField)\n\n if (userAssignedTenantIDs.length) {\n return {\n tenant: {\n in: userAssignedTenantIDs,\n },\n }\n }\n\n return null\n}\n"],"names":["getUserTenantIDs","getTenantAccess","user","userAssignedTenantIDs","length","tenant","in"],"mappings":"AAIA,SAASA,gBAAgB,QAAQ,wBAAuB;AAExD,OAAO,SAASC,gBAAgB,EAAEC,IAAI,EAAE;IACtC,MAAMC,wBAAwBH,iBAAiBE;IAE/C,IAAIC,sBAAsBC,MAAM,EAAE;QAChC,OAAO;YACLC,QAAQ;gBACNC,IAAIH;YACN;QACF;IACF;IAEA,OAAO;AACT"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getTenantFromCookie.d.ts","sourceRoot":"","sources":["../../src/utilities/getTenantFromCookie.ts"],"names":[],"mappings":"AAEA,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,GAAG,MAAM,mBAI9E"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { parseCookies } from 'payload';
|
|
2
|
+
export function getTenantFromCookie(headers, idType) {
|
|
3
|
+
const cookies = parseCookies(headers);
|
|
4
|
+
const selectedTenant = cookies.get('payload-tenant') || null;
|
|
5
|
+
return selectedTenant ? idType === 'number' ? parseInt(selectedTenant) : selectedTenant : null;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
//# sourceMappingURL=getTenantFromCookie.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/utilities/getTenantFromCookie.ts"],"sourcesContent":["import { parseCookies } from 'payload'\n\nexport function getTenantFromCookie(headers: Headers, idType: 'number' | 'text') {\n const cookies = parseCookies(headers)\n const selectedTenant = cookies.get('payload-tenant') || null\n return selectedTenant ? (idType === 'number' ? parseInt(selectedTenant) : selectedTenant) : null\n}\n"],"names":["parseCookies","getTenantFromCookie","headers","idType","cookies","selectedTenant","get","parseInt"],"mappings":"AAAA,SAASA,YAAY,QAAQ,UAAS;AAEtC,OAAO,SAASC,oBAAoBC,OAAgB,EAAEC,MAAyB;IAC7E,MAAMC,UAAUJ,aAAaE;IAC7B,MAAMG,iBAAiBD,QAAQE,GAAG,CAAC,qBAAqB;IACxD,OAAOD,iBAAkBF,WAAW,WAAWI,SAASF,kBAAkBA,iBAAkB;AAC9F"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { PayloadRequest, Where } from 'payload';
|
|
2
|
+
type Args = {
|
|
3
|
+
req: PayloadRequest;
|
|
4
|
+
tenantFieldName: string;
|
|
5
|
+
};
|
|
6
|
+
export declare const getTenantListFilter: ({ req, tenantFieldName }: Args) => null | Where;
|
|
7
|
+
export {};
|
|
8
|
+
//# sourceMappingURL=getTenantListFilter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getTenantListFilter.d.ts","sourceRoot":"","sources":["../../src/utilities/getTenantListFilter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,SAAS,CAAA;AAIpD,KAAK,IAAI,GAAG;IACV,GAAG,EAAE,cAAc,CAAA;IACnB,eAAe,EAAE,MAAM,CAAA;CACxB,CAAA;AACD,eAAO,MAAM,mBAAmB,6BAA8B,IAAI,KAAG,IAAI,GAAG,KAa3E,CAAA"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { getTenantFromCookie } from './getTenantFromCookie.js';
|
|
2
|
+
export const getTenantListFilter = ({ req, tenantFieldName })=>{
|
|
3
|
+
const selectedTenant = getTenantFromCookie(req.headers, req.payload.db.defaultIDType);
|
|
4
|
+
if (selectedTenant) {
|
|
5
|
+
return {
|
|
6
|
+
[tenantFieldName]: {
|
|
7
|
+
equals: selectedTenant
|
|
8
|
+
}
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
// Access control will take it from here
|
|
12
|
+
return null;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
//# sourceMappingURL=getTenantListFilter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/utilities/getTenantListFilter.ts"],"sourcesContent":["import type { PayloadRequest, Where } from 'payload'\n\nimport { getTenantFromCookie } from './getTenantFromCookie.js'\n\ntype Args = {\n req: PayloadRequest\n tenantFieldName: string\n}\nexport const getTenantListFilter = ({ req, tenantFieldName }: Args): null | Where => {\n const selectedTenant = getTenantFromCookie(req.headers, req.payload.db.defaultIDType)\n\n if (selectedTenant) {\n return {\n [tenantFieldName]: {\n equals: selectedTenant,\n },\n }\n }\n\n // Access control will take it from here\n return null\n}\n"],"names":["getTenantFromCookie","getTenantListFilter","req","tenantFieldName","selectedTenant","headers","payload","db","defaultIDType","equals"],"mappings":"AAEA,SAASA,mBAAmB,QAAQ,2BAA0B;AAM9D,OAAO,MAAMC,sBAAsB,CAAC,EAAEC,GAAG,EAAEC,eAAe,EAAQ;IAChE,MAAMC,iBAAiBJ,oBAAoBE,IAAIG,OAAO,EAAEH,IAAII,OAAO,CAACC,EAAE,CAACC,aAAa;IAEpF,IAAIJ,gBAAgB;QAClB,OAAO;YACL,CAACD,gBAAgB,EAAE;gBACjBM,QAAQL;YACV;QACF;IACF;IAEA,wCAAwC;IACxC,OAAO;AACT,EAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { UserWithTenantsField } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Returns array of all tenant IDs assigned to a user
|
|
4
|
+
*
|
|
5
|
+
* @param user - User object with tenants field
|
|
6
|
+
* @param role - Optional role to filter by
|
|
7
|
+
*/
|
|
8
|
+
export declare const getUserTenantIDs: <IDType extends number | string>(user: null | UserWithTenantsField, role?: string) => IDType[];
|
|
9
|
+
//# sourceMappingURL=getUserTenantIDs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getUserTenantIDs.d.ts","sourceRoot":"","sources":["../../src/utilities/getUserTenantIDs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAU,oBAAoB,EAAE,MAAM,aAAa,CAAA;AAI/D;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,GAAI,MAAM,SAAS,MAAM,GAAG,MAAM,QACvD,IAAI,GAAG,oBAAoB,SAC1B,MAAM,KACZ,MAAM,EAkBR,CAAA"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { extractID } from './extractID.js';
|
|
2
|
+
/**
|
|
3
|
+
* Returns array of all tenant IDs assigned to a user
|
|
4
|
+
*
|
|
5
|
+
* @param user - User object with tenants field
|
|
6
|
+
* @param role - Optional role to filter by
|
|
7
|
+
*/ export const getUserTenantIDs = (user, role)=>{
|
|
8
|
+
if (!user) {
|
|
9
|
+
return [];
|
|
10
|
+
}
|
|
11
|
+
return user?.tenants?.reduce((acc, { roles, tenant })=>{
|
|
12
|
+
if (role && !roles.includes(role)) {
|
|
13
|
+
return acc;
|
|
14
|
+
}
|
|
15
|
+
if (tenant) {
|
|
16
|
+
acc.push(extractID(tenant));
|
|
17
|
+
}
|
|
18
|
+
return acc;
|
|
19
|
+
}, []) || [];
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
//# sourceMappingURL=getUserTenantIDs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/utilities/getUserTenantIDs.ts"],"sourcesContent":["import type { Tenant, UserWithTenantsField } from '../types.js'\n\nimport { extractID } from './extractID.js'\n\n/**\n * Returns array of all tenant IDs assigned to a user\n *\n * @param user - User object with tenants field\n * @param role - Optional role to filter by\n */\nexport const getUserTenantIDs = <IDType extends number | string>(\n user: null | UserWithTenantsField,\n role?: string,\n): IDType[] => {\n if (!user) {\n return []\n }\n\n return (\n user?.tenants?.reduce<IDType[]>((acc, { roles, tenant }) => {\n if (role && !roles.includes(role)) {\n return acc\n }\n\n if (tenant) {\n acc.push(extractID<IDType>(tenant as Tenant<IDType>))\n }\n\n return acc\n }, []) || []\n )\n}\n"],"names":["extractID","getUserTenantIDs","user","role","tenants","reduce","acc","roles","tenant","includes","push"],"mappings":"AAEA,SAASA,SAAS,QAAQ,iBAAgB;AAE1C;;;;;CAKC,GACD,OAAO,MAAMC,mBAAmB,CAC9BC,MACAC;IAEA,IAAI,CAACD,MAAM;QACT,OAAO,EAAE;IACX;IAEA,OACEA,MAAME,SAASC,OAAiB,CAACC,KAAK,EAAEC,KAAK,EAAEC,MAAM,EAAE;QACrD,IAAIL,QAAQ,CAACI,MAAME,QAAQ,CAACN,OAAO;YACjC,OAAOG;QACT;QAEA,IAAIE,QAAQ;YACVF,IAAII,IAAI,CAACV,UAAkBQ;QAC7B;QAEA,OAAOF;IACT,GAAG,EAAE,KAAK,EAAE;AAEhB,EAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Access, AccessArgs, AccessResult } from 'payload';
|
|
2
|
+
import type { MultiTenantPluginConfig } from '../types.js';
|
|
3
|
+
type Args = {
|
|
4
|
+
accessFunction?: Access;
|
|
5
|
+
userHasAccessToAllTenants: MultiTenantPluginConfig['userHasAccessToAllTenants'];
|
|
6
|
+
};
|
|
7
|
+
export declare const withTenantAccess: ({ accessFunction, userHasAccessToAllTenants }: Args) => (args: AccessArgs) => Promise<AccessResult>;
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=withTenantAccess.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"withTenantAccess.d.ts","sourceRoot":"","sources":["../../src/utilities/withTenantAccess.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAE/D,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAA;AAK1D,KAAK,IAAI,GAAG;IACV,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,yBAAyB,EAAE,uBAAuB,CAAC,2BAA2B,CAAC,CAAA;CAChF,CAAA;AACD,eAAO,MAAM,gBAAgB,kDACqB,IAAI,YACvC,UAAU,KAAG,OAAO,CAAC,YAAY,CAyB7C,CAAA"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { combineWhereConstraints } from './combineWhereConstraints.js';
|
|
2
|
+
import { getTenantAccess } from './getTenantAccess.js';
|
|
3
|
+
export const withTenantAccess = ({ accessFunction, userHasAccessToAllTenants })=>async (args)=>{
|
|
4
|
+
let accessResult;
|
|
5
|
+
const constraints = [];
|
|
6
|
+
if (typeof accessFunction === 'function') {
|
|
7
|
+
accessResult = await accessFunction(args);
|
|
8
|
+
if (accessResult === false) {
|
|
9
|
+
return false;
|
|
10
|
+
} else if (accessResult && typeof accessResult === 'object') {
|
|
11
|
+
constraints.push(accessResult);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
if (!userHasAccessToAllTenants(args.req.user)) {
|
|
15
|
+
const tenantConstraint = getTenantAccess({
|
|
16
|
+
user: args.req.user
|
|
17
|
+
});
|
|
18
|
+
if (tenantConstraint) {
|
|
19
|
+
constraints.push(tenantConstraint);
|
|
20
|
+
return combineWhereConstraints(constraints);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return accessResult;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
//# sourceMappingURL=withTenantAccess.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/utilities/withTenantAccess.ts"],"sourcesContent":["import type { Access, AccessArgs, AccessResult } from 'payload'\n\nimport type { MultiTenantPluginConfig } from '../types.js'\n\nimport { combineWhereConstraints } from './combineWhereConstraints.js'\nimport { getTenantAccess } from './getTenantAccess.js'\n\ntype Args = {\n accessFunction?: Access\n userHasAccessToAllTenants: MultiTenantPluginConfig['userHasAccessToAllTenants']\n}\nexport const withTenantAccess =\n ({ accessFunction, userHasAccessToAllTenants }: Args) =>\n async (args: AccessArgs): Promise<AccessResult> => {\n let accessResult: AccessResult\n const constraints = []\n\n if (typeof accessFunction === 'function') {\n accessResult = await accessFunction(args)\n if (accessResult === false) {\n return false\n } else if (accessResult && typeof accessResult === 'object') {\n constraints.push(accessResult)\n }\n }\n\n if (!userHasAccessToAllTenants(args.req.user)) {\n const tenantConstraint = getTenantAccess({\n user: args.req.user,\n })\n\n if (tenantConstraint) {\n constraints.push(tenantConstraint)\n return combineWhereConstraints(constraints)\n }\n }\n\n return accessResult\n }\n"],"names":["combineWhereConstraints","getTenantAccess","withTenantAccess","accessFunction","userHasAccessToAllTenants","args","accessResult","constraints","push","req","user","tenantConstraint"],"mappings":"AAIA,SAASA,uBAAuB,QAAQ,+BAA8B;AACtE,SAASC,eAAe,QAAQ,uBAAsB;AAMtD,OAAO,MAAMC,mBACX,CAAC,EAAEC,cAAc,EAAEC,yBAAyB,EAAQ,GACpD,OAAOC;QACL,IAAIC;QACJ,MAAMC,cAAc,EAAE;QAEtB,IAAI,OAAOJ,mBAAmB,YAAY;YACxCG,eAAe,MAAMH,eAAeE;YACpC,IAAIC,iBAAiB,OAAO;gBAC1B,OAAO;YACT,OAAO,IAAIA,gBAAgB,OAAOA,iBAAiB,UAAU;gBAC3DC,YAAYC,IAAI,CAACF;YACnB;QACF;QAEA,IAAI,CAACF,0BAA0BC,KAAKI,GAAG,CAACC,IAAI,GAAG;YAC7C,MAAMC,mBAAmBV,gBAAgB;gBACvCS,MAAML,KAAKI,GAAG,CAACC,IAAI;YACrB;YAEA,IAAIC,kBAAkB;gBACpBJ,YAAYC,IAAI,CAACG;gBACjB,OAAOX,wBAAwBO;YACjC;QACF;QAEA,OAAOD;IACT,EAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { BaseListFilter } from 'payload';
|
|
2
|
+
type Args = {
|
|
3
|
+
baseListFilter?: BaseListFilter;
|
|
4
|
+
tenantFieldName: string;
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* Combines a base list filter with a tenant list filter
|
|
8
|
+
*
|
|
9
|
+
* Combines where constraints inside of an AND operator
|
|
10
|
+
*/
|
|
11
|
+
export declare const withTenantListFilter: ({ baseListFilter, tenantFieldName }: Args) => BaseListFilter;
|
|
12
|
+
export {};
|
|
13
|
+
//# sourceMappingURL=withTenantListFilter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"withTenantListFilter.d.ts","sourceRoot":"","sources":["../../src/utilities/withTenantListFilter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAS,MAAM,SAAS,CAAA;AAIpD,KAAK,IAAI,GAAG;IACV,cAAc,CAAC,EAAE,cAAc,CAAA;IAC/B,eAAe,EAAE,MAAM,CAAA;CACxB,CAAA;AACD;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,wCACO,IAAI,KAAG,cAiC5C,CAAA"}
|