@easy-cms/plugin-multi-tenant 0.47.3 → 0.48.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +6 -0
- package/dist/index.js +55 -5
- package/package.json +7 -7
package/dist/index.d.ts
CHANGED
|
@@ -42,6 +42,12 @@ interface MultiTenantOptions<S extends string = string, T extends string = strin
|
|
|
42
42
|
readonly collections: readonly S[];
|
|
43
43
|
/** Globals with a value per tenant, e.g. `['site-settings']`. Others are shared. */
|
|
44
44
|
readonly globals?: readonly string[];
|
|
45
|
+
/**
|
|
46
|
+
* Shared collections and globals (not listed above) that the people of a tenant, its admins
|
|
47
|
+
* included, may change. A change applies to every tenant. Default: none; only users with
|
|
48
|
+
* access to all tenants change shared data.
|
|
49
|
+
*/
|
|
50
|
+
readonly editShared?: readonly string[];
|
|
45
51
|
/** Slug of the tenants collection. Default `tenants`. */
|
|
46
52
|
readonly tenantsSlug?: T;
|
|
47
53
|
/**
|
package/dist/index.js
CHANGED
|
@@ -61,7 +61,7 @@ import {
|
|
|
61
61
|
// src/info.ts
|
|
62
62
|
var INFO = {
|
|
63
63
|
name: "@easy-cms/plugin-multi-tenant",
|
|
64
|
-
...true ? { version: "0.
|
|
64
|
+
...true ? { version: "0.48.0" } : {}
|
|
65
65
|
};
|
|
66
66
|
|
|
67
67
|
// src/members.ts
|
|
@@ -70,6 +70,24 @@ import {
|
|
|
70
70
|
NotFoundError,
|
|
71
71
|
ValidationError
|
|
72
72
|
} from "@easy-cms/core";
|
|
73
|
+
async function addedEmail(cms, tenantsSlug, tenant, to, origin) {
|
|
74
|
+
const found = await cms.findById(tenantsSlug, tenant, { depth: 0 });
|
|
75
|
+
const name = typeof found?.name === "string" ? found.name : String(tenant);
|
|
76
|
+
const admin = `${cms.config.serverURL ?? origin}${cms.config.admin.path}`;
|
|
77
|
+
return {
|
|
78
|
+
to,
|
|
79
|
+
subject: `You were added to ${name}`,
|
|
80
|
+
text: [
|
|
81
|
+
`An admin of ${name} added you to it. Sign in as before to work there:`,
|
|
82
|
+
admin,
|
|
83
|
+
"",
|
|
84
|
+
`\u0E1C\u0E39\u0E49\u0E14\u0E39\u0E41\u0E25\u0E02\u0E2D\u0E07 ${name} \u0E40\u0E1E\u0E34\u0E48\u0E21\u0E04\u0E38\u0E13\u0E40\u0E02\u0E49\u0E32\u0E44\u0E1B\u0E41\u0E25\u0E49\u0E27 \u0E40\u0E02\u0E49\u0E32\u0E2A\u0E39\u0E48\u0E23\u0E30\u0E1A\u0E1A\u0E14\u0E49\u0E27\u0E22\u0E1A\u0E31\u0E0D\u0E0A\u0E35\u0E40\u0E14\u0E34\u0E21\u0E40\u0E1E\u0E37\u0E48\u0E2D\u0E17\u0E33\u0E07\u0E32\u0E19\u0E17\u0E35\u0E48\u0E19\u0E31\u0E48\u0E19:`,
|
|
85
|
+
admin,
|
|
86
|
+
"",
|
|
87
|
+
"If you don't know why, ask your admin. / \u0E2B\u0E32\u0E01\u0E44\u0E21\u0E48\u0E17\u0E23\u0E32\u0E1A\u0E17\u0E35\u0E48\u0E21\u0E32 \u0E42\u0E1B\u0E23\u0E14\u0E2A\u0E2D\u0E1A\u0E16\u0E32\u0E21\u0E1C\u0E39\u0E49\u0E14\u0E39\u0E41\u0E25"
|
|
88
|
+
].join("\n")
|
|
89
|
+
};
|
|
90
|
+
}
|
|
73
91
|
function membersEndpoints(options) {
|
|
74
92
|
const tenantFor = (user, context) => {
|
|
75
93
|
const { tenant } = tenantOf(context);
|
|
@@ -139,6 +157,12 @@ function membersEndpoints(options) {
|
|
|
139
157
|
});
|
|
140
158
|
const existing = docs[0];
|
|
141
159
|
if (existing) {
|
|
160
|
+
const account = { id: existing.id, email, role: existing.role };
|
|
161
|
+
const memberRoles = cms.config.auth.members?.roles ?? [];
|
|
162
|
+
if (memberRoles.includes(String(existing.role)) || options.isSuper(account))
|
|
163
|
+
throw new ValidationError("users", [
|
|
164
|
+
{ field: "email", message: "can't be added to a tenant" }
|
|
165
|
+
]);
|
|
142
166
|
const list = rows(existing).filter((row) => !sameId(row.tenant, tenant));
|
|
143
167
|
const doc2 = await cms.update(
|
|
144
168
|
"users",
|
|
@@ -146,7 +170,17 @@ function membersEndpoints(options) {
|
|
|
146
170
|
{ [MEMBERSHIPS_FIELD]: [...list, { tenant, role }] },
|
|
147
171
|
{ depth: 0 }
|
|
148
172
|
);
|
|
149
|
-
|
|
173
|
+
let invited2 = false;
|
|
174
|
+
if (cms.auth.canSendPasswordLinks(url.origin)) {
|
|
175
|
+
await cms.sendEmail(
|
|
176
|
+
await addedEmail(cms, options.tenantsSlug, tenant, email, url.origin)
|
|
177
|
+
);
|
|
178
|
+
invited2 = true;
|
|
179
|
+
}
|
|
180
|
+
return Response.json(
|
|
181
|
+
{ member: { ...member(doc2, tenant), name: null }, invited: invited2 },
|
|
182
|
+
{ status: 201 }
|
|
183
|
+
);
|
|
150
184
|
}
|
|
151
185
|
const doc = await cms.create(
|
|
152
186
|
"users",
|
|
@@ -240,6 +274,8 @@ function multiTenantPlugin(options) {
|
|
|
240
274
|
);
|
|
241
275
|
const superUser = (user, _context) => !!user && user.member !== true && user.scoped !== true && isSuper(user);
|
|
242
276
|
const everywhere = (user, context) => tenantOf(context).allTenants || superUser(user, context);
|
|
277
|
+
const editShared = new Set(options.editShared ?? []);
|
|
278
|
+
const sharedWrite = (slug, original) => async (args) => args.user?.scoped === true && !editShared.has(slug) ? false : (original ?? isLoggedIn)(args);
|
|
243
279
|
const tenantFilter = (args, op) => {
|
|
244
280
|
const { tenant } = tenantOf(args.context);
|
|
245
281
|
if (op === "create") return tenant !== null || superUser(args.user, args.context);
|
|
@@ -482,7 +518,17 @@ function multiTenantPlugin(options) {
|
|
|
482
518
|
access: { ...c.access, ...usersAccess(c.access) }
|
|
483
519
|
});
|
|
484
520
|
} else if (scoped.has(c.slug)) collections.push(scopeCollection(c, BUILTIN_ACCESS[c.slug]));
|
|
485
|
-
else
|
|
521
|
+
else
|
|
522
|
+
collections.push({
|
|
523
|
+
...c,
|
|
524
|
+
fields: withTenantFilters(c.fields),
|
|
525
|
+
access: {
|
|
526
|
+
...c.access,
|
|
527
|
+
create: sharedWrite(c.slug, c.access?.create),
|
|
528
|
+
update: sharedWrite(c.slug, c.access?.update),
|
|
529
|
+
delete: sharedWrite(c.slug, c.access?.delete)
|
|
530
|
+
}
|
|
531
|
+
});
|
|
486
532
|
}
|
|
487
533
|
if (!hasUsers) collections.push({ slug: "users", fields: [memberships], access: usersAccess() });
|
|
488
534
|
for (const builtin of [MEDIA, MEDIA_FOLDERS]) {
|
|
@@ -500,7 +546,11 @@ function multiTenantPlugin(options) {
|
|
|
500
546
|
}
|
|
501
547
|
const globals = (config.globals ?? []).map((g) => {
|
|
502
548
|
const filtered = { ...g, fields: withTenantFilters(g.fields) };
|
|
503
|
-
if (!perTenant.has(g.slug))
|
|
549
|
+
if (!perTenant.has(g.slug))
|
|
550
|
+
return {
|
|
551
|
+
...filtered,
|
|
552
|
+
access: { ...g.access, update: sharedWrite(g.slug, g.access?.update) }
|
|
553
|
+
};
|
|
504
554
|
return {
|
|
505
555
|
...filtered,
|
|
506
556
|
// One value per tenant; none when the request has no tenant.
|
|
@@ -662,7 +712,7 @@ of its own. Run it once after adding the plugin to a site that has content.
|
|
|
662
712
|
...config.endpoints ?? [],
|
|
663
713
|
optionsEndpoint,
|
|
664
714
|
impactEndpoint,
|
|
665
|
-
...membersEndpoints({ tenantsSlug, superUser })
|
|
715
|
+
...membersEndpoints({ tenantsSlug, superUser, isSuper })
|
|
666
716
|
],
|
|
667
717
|
commands: [...config.commands ?? [], assign],
|
|
668
718
|
// Audit log entries belong to their tenant: its admins see them.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@easy-cms/plugin-multi-tenant",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.48.0",
|
|
4
4
|
"description": "Multi-tenant Easy CMS: several sites or clients in one CMS, each with its own content, members and roles",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -17,14 +17,14 @@
|
|
|
17
17
|
"dist"
|
|
18
18
|
],
|
|
19
19
|
"peerDependencies": {
|
|
20
|
-
"@easy-cms/core": "^0.
|
|
20
|
+
"@easy-cms/core": "^0.48.0"
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
|
-
"@easy-cms/core": "^0.
|
|
24
|
-
"@easy-cms/db-sqlite": "^0.
|
|
25
|
-
"@easy-cms/plugin-form-builder": "^0.
|
|
26
|
-
"@easy-cms/plugin-nested-docs": "^0.
|
|
27
|
-
"@easy-cms/plugin-redirects": "^0.
|
|
23
|
+
"@easy-cms/core": "^0.48.0",
|
|
24
|
+
"@easy-cms/db-sqlite": "^0.48.0",
|
|
25
|
+
"@easy-cms/plugin-form-builder": "^0.48.0",
|
|
26
|
+
"@easy-cms/plugin-nested-docs": "^0.48.0",
|
|
27
|
+
"@easy-cms/plugin-redirects": "^0.48.0"
|
|
28
28
|
},
|
|
29
29
|
"engines": {
|
|
30
30
|
"node": ">=22.12"
|