@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.
Files changed (88) hide show
  1. package/LICENSE.md +22 -0
  2. package/README.md +204 -0
  3. package/dist/components/GlobalViewRedirect/index.d.ts +10 -0
  4. package/dist/components/GlobalViewRedirect/index.d.ts.map +1 -0
  5. package/dist/components/GlobalViewRedirect/index.js +18 -0
  6. package/dist/components/GlobalViewRedirect/index.js.map +1 -0
  7. package/dist/components/TenantField/index.client.d.ts +10 -0
  8. package/dist/components/TenantField/index.client.d.ts.map +1 -0
  9. package/dist/components/TenantField/index.client.js +34 -0
  10. package/dist/components/TenantField/index.client.js.map +1 -0
  11. package/dist/components/TenantField/index.d.ts +3 -0
  12. package/dist/components/TenantField/index.d.ts.map +1 -0
  13. package/dist/components/TenantField/index.js +33 -0
  14. package/dist/components/TenantField/index.js.map +1 -0
  15. package/dist/components/TenantSelector/index.client.d.ts +11 -0
  16. package/dist/components/TenantSelector/index.client.d.ts.map +1 -0
  17. package/dist/components/TenantSelector/index.client.js +61 -0
  18. package/dist/components/TenantSelector/index.client.js.map +1 -0
  19. package/dist/components/TenantSelector/index.d.ts +10 -0
  20. package/dist/components/TenantSelector/index.d.ts.map +1 -0
  21. package/dist/components/TenantSelector/index.js +31 -0
  22. package/dist/components/TenantSelector/index.js.map +1 -0
  23. package/dist/components/TenantSelector/index.scss +4 -0
  24. package/dist/exports/rsc.d.ts +4 -0
  25. package/dist/exports/rsc.d.ts.map +1 -0
  26. package/dist/exports/rsc.js +5 -0
  27. package/dist/exports/rsc.js.map +1 -0
  28. package/dist/exports/types.d.ts +2 -0
  29. package/dist/exports/types.d.ts.map +1 -0
  30. package/dist/exports/types.js +3 -0
  31. package/dist/exports/types.js.map +1 -0
  32. package/dist/exports/utilities.d.ts +6 -0
  33. package/dist/exports/utilities.d.ts.map +1 -0
  34. package/dist/exports/utilities.js +7 -0
  35. package/dist/exports/utilities.js.map +1 -0
  36. package/dist/fields/tenantField/index.d.ts +12 -0
  37. package/dist/fields/tenantField/index.d.ts.map +1 -0
  38. package/dist/fields/tenantField/index.js +38 -0
  39. package/dist/fields/tenantField/index.js.map +1 -0
  40. package/dist/fields/userTenantsArrayField/index.d.ts +4 -0
  41. package/dist/fields/userTenantsArrayField/index.d.ts.map +1 -0
  42. package/dist/fields/userTenantsArrayField/index.js +20 -0
  43. package/dist/fields/userTenantsArrayField/index.js.map +1 -0
  44. package/dist/index.d.ts +4 -0
  45. package/dist/index.d.ts.map +1 -0
  46. package/dist/index.js +112 -0
  47. package/dist/index.js.map +1 -0
  48. package/dist/types.d.ts +80 -0
  49. package/dist/types.d.ts.map +1 -0
  50. package/dist/types.js +3 -0
  51. package/dist/types.js.map +1 -0
  52. package/dist/utilities/combineWhereConstraints.d.ts +3 -0
  53. package/dist/utilities/combineWhereConstraints.d.ts.map +1 -0
  54. package/dist/utilities/combineWhereConstraints.js +19 -0
  55. package/dist/utilities/combineWhereConstraints.js.map +1 -0
  56. package/dist/utilities/extractID.d.ts +3 -0
  57. package/dist/utilities/extractID.d.ts.map +1 -0
  58. package/dist/utilities/extractID.js +8 -0
  59. package/dist/utilities/extractID.js.map +1 -0
  60. package/dist/utilities/getGlobalViewRedirect.d.ts +10 -0
  61. package/dist/utilities/getGlobalViewRedirect.d.ts.map +1 -0
  62. package/dist/utilities/getGlobalViewRedirect.js +44 -0
  63. package/dist/utilities/getGlobalViewRedirect.js.map +1 -0
  64. package/dist/utilities/getTenantAccess.d.ts +5 -0
  65. package/dist/utilities/getTenantAccess.d.ts.map +1 -0
  66. package/dist/utilities/getTenantAccess.js +14 -0
  67. package/dist/utilities/getTenantAccess.js.map +1 -0
  68. package/dist/utilities/getTenantFromCookie.d.ts +2 -0
  69. package/dist/utilities/getTenantFromCookie.d.ts.map +1 -0
  70. package/dist/utilities/getTenantFromCookie.js +8 -0
  71. package/dist/utilities/getTenantFromCookie.js.map +1 -0
  72. package/dist/utilities/getTenantListFilter.d.ts +8 -0
  73. package/dist/utilities/getTenantListFilter.d.ts.map +1 -0
  74. package/dist/utilities/getTenantListFilter.js +15 -0
  75. package/dist/utilities/getTenantListFilter.js.map +1 -0
  76. package/dist/utilities/getUserTenantIDs.d.ts +9 -0
  77. package/dist/utilities/getUserTenantIDs.d.ts.map +1 -0
  78. package/dist/utilities/getUserTenantIDs.js +22 -0
  79. package/dist/utilities/getUserTenantIDs.js.map +1 -0
  80. package/dist/utilities/withTenantAccess.d.ts +9 -0
  81. package/dist/utilities/withTenantAccess.d.ts.map +1 -0
  82. package/dist/utilities/withTenantAccess.js +26 -0
  83. package/dist/utilities/withTenantAccess.js.map +1 -0
  84. package/dist/utilities/withTenantListFilter.d.ts +13 -0
  85. package/dist/utilities/withTenantListFilter.d.ts.map +1 -0
  86. package/dist/utilities/withTenantListFilter.js +36 -0
  87. package/dist/utilities/withTenantListFilter.js.map +1 -0
  88. 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,4 @@
1
+ import type { ArrayField } from 'payload';
2
+ import type { MultiTenantPluginConfig } from '../../types.js';
3
+ export declare const userTenantsField: (args: MultiTenantPluginConfig["userTenantsField"]) => ArrayField;
4
+ //# sourceMappingURL=index.d.ts.map
@@ -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"}
@@ -0,0 +1,4 @@
1
+ import type { Config } from 'payload';
2
+ import type { MultiTenantPluginConfig } from './types.js';
3
+ export declare const multiTenantPlugin: (pluginConfig: MultiTenantPluginConfig) => (incomingConfig: Config) => Config;
4
+ //# sourceMappingURL=index.d.ts.map
@@ -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":"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"}
@@ -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,3 @@
1
+ export { };
2
+
3
+ //# sourceMappingURL=types.js.map
@@ -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,3 @@
1
+ import type { Where } from 'payload';
2
+ export declare function combineWhereConstraints(constraints: Array<Where>): Where;
3
+ //# sourceMappingURL=combineWhereConstraints.d.ts.map
@@ -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,3 @@
1
+ import type { Tenant } from '../types.js';
2
+ export declare const extractID: <IDType extends number | string>(objectOrID: IDType | Tenant<IDType>) => IDType;
3
+ //# sourceMappingURL=extractID.d.ts.map
@@ -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,8 @@
1
+ export const extractID = (objectOrID)=>{
2
+ if (typeof objectOrID === 'string' || typeof objectOrID === 'number') {
3
+ return objectOrID;
4
+ }
5
+ return objectOrID.id;
6
+ };
7
+
8
+ //# sourceMappingURL=extractID.js.map
@@ -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,5 @@
1
+ import type { Where } from 'payload';
2
+ export declare function getTenantAccess({ user }: {
3
+ user: any;
4
+ }): null | Where;
5
+ //# sourceMappingURL=getTenantAccess.d.ts.map
@@ -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,2 @@
1
+ export declare function getTenantFromCookie(headers: Headers, idType: 'number' | 'text'): string | number;
2
+ //# sourceMappingURL=getTenantFromCookie.d.ts.map
@@ -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"}