@7365admin1/core 3.134.0 → 3.135.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/CHANGELOG.md +6 -0
- package/dist/index.d.ts +11 -2
- package/dist/index.js +49 -16
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +49 -16
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/test/e2e/member-site-access.e2e.test.mjs +91 -2
package/dist/index.mjs
CHANGED
|
@@ -4286,7 +4286,7 @@ function useMemberRepo() {
|
|
|
4286
4286
|
throw error;
|
|
4287
4287
|
}
|
|
4288
4288
|
}
|
|
4289
|
-
async function updateRoleById(_id, role) {
|
|
4289
|
+
async function updateRoleById(_id, role, createdBy) {
|
|
4290
4290
|
try {
|
|
4291
4291
|
_id = new ObjectId14(_id);
|
|
4292
4292
|
} catch (error) {
|
|
@@ -4302,6 +4302,9 @@ function useMemberRepo() {
|
|
|
4302
4302
|
role,
|
|
4303
4303
|
updatedAt: /* @__PURE__ */ new Date()
|
|
4304
4304
|
};
|
|
4305
|
+
if (createdBy && ObjectId14.isValid(String(createdBy))) {
|
|
4306
|
+
updateValue.createdBy = new ObjectId14(String(createdBy));
|
|
4307
|
+
}
|
|
4305
4308
|
const res = await collection.updateOne({ _id }, { $set: updateValue });
|
|
4306
4309
|
if (res.modifiedCount === 0) {
|
|
4307
4310
|
throw new InternalServerError6("Unable to update feedback status.");
|
|
@@ -8833,11 +8836,27 @@ async function siteInvitedPins(db2, userId, memberships) {
|
|
|
8833
8836
|
const keys = /* @__PURE__ */ new Set();
|
|
8834
8837
|
if (!pinned.length || !ObjectId26.isValid(userId))
|
|
8835
8838
|
return keys;
|
|
8839
|
+
const both3 = (ids2) => [...new Set(ids2.map(String))].flatMap((v) => [v, new ObjectId26(v)]);
|
|
8840
|
+
const wanted = new Set(pinned.map((m) => pinKey(m.org, m.siteId)));
|
|
8841
|
+
const placed = await db2.collection("members").find(
|
|
8842
|
+
{
|
|
8843
|
+
user: new ObjectId26(userId),
|
|
8844
|
+
status: { $ne: "deleted" },
|
|
8845
|
+
org: { $in: both3(pinned.map((m) => m.org)) },
|
|
8846
|
+
siteId: { $in: both3(pinned.map((m) => m.siteId)) },
|
|
8847
|
+
createdBy: { $type: "objectId", $ne: new ObjectId26(userId) }
|
|
8848
|
+
},
|
|
8849
|
+
{ projection: { org: 1, siteId: 1 } }
|
|
8850
|
+
).toArray();
|
|
8851
|
+
for (const row of placed) {
|
|
8852
|
+
const key = pinKey(row.org, row.siteId);
|
|
8853
|
+
if (wanted.has(key))
|
|
8854
|
+
keys.add(key);
|
|
8855
|
+
}
|
|
8836
8856
|
const user = await db2.collection("users").findOne({ _id: new ObjectId26(userId) }, { projection: { email: 1 } });
|
|
8837
8857
|
const email = String(user?.email ?? "").trim();
|
|
8838
8858
|
if (!email)
|
|
8839
8859
|
return keys;
|
|
8840
|
-
const both3 = (ids2) => [...new Set(ids2.map(String))].flatMap((v) => [v, new ObjectId26(v)]);
|
|
8841
8860
|
const invites = await db2.collection("verifications").find(
|
|
8842
8861
|
{
|
|
8843
8862
|
email,
|
|
@@ -8853,7 +8872,6 @@ async function siteInvitedPins(db2, userId, memberships) {
|
|
|
8853
8872
|
collation: { locale: "en", strength: 2 }
|
|
8854
8873
|
}
|
|
8855
8874
|
).toArray();
|
|
8856
|
-
const wanted = new Set(pinned.map((m) => pinKey(m.org, m.siteId)));
|
|
8857
8875
|
for (const invite of invites) {
|
|
8858
8876
|
const key = pinKey(invite.metadata?.org, invite.metadata?.siteId);
|
|
8859
8877
|
if (wanted.has(key))
|
|
@@ -24813,7 +24831,8 @@ function useRoleRepo() {
|
|
|
24813
24831
|
sort = {},
|
|
24814
24832
|
type = "",
|
|
24815
24833
|
org = "",
|
|
24816
|
-
site = ""
|
|
24834
|
+
site = "",
|
|
24835
|
+
scope = ""
|
|
24817
24836
|
}) {
|
|
24818
24837
|
page2 = page2 > 0 ? page2 - 1 : 0;
|
|
24819
24838
|
if (org && typeof org === "string" && org.length === 24) {
|
|
@@ -24835,6 +24854,9 @@ function useRoleRepo() {
|
|
|
24835
24854
|
if (site) {
|
|
24836
24855
|
query2.$or = [{ site }, { site: "" }, { site: null }];
|
|
24837
24856
|
cacheOptions.site = site.toString();
|
|
24857
|
+
} else if (scope === "organisation") {
|
|
24858
|
+
query2.site = { $in: ["", null] };
|
|
24859
|
+
cacheOptions.scope = scope;
|
|
24838
24860
|
}
|
|
24839
24861
|
sort = Object.keys(sort).length > 0 ? sort : { _id: -1 };
|
|
24840
24862
|
cacheOptions.sort = JSON.stringify(sort);
|
|
@@ -27290,7 +27312,9 @@ function useRoleController() {
|
|
|
27290
27312
|
limit: Joi28.number().integer().min(1).max(100).allow("", null).default(10),
|
|
27291
27313
|
type: Joi28.string().optional().allow("", null),
|
|
27292
27314
|
org: Joi28.string().hex().optional().allow("", null),
|
|
27293
|
-
site: Joi28.string().hex().optional().allow("", null)
|
|
27315
|
+
site: Joi28.string().hex().optional().allow("", null),
|
|
27316
|
+
// "organisation": only the roles with no site (Organization app, service roles made once)
|
|
27317
|
+
scope: Joi28.string().valid("organisation").optional().allow("", null)
|
|
27294
27318
|
});
|
|
27295
27319
|
const query2 = { ...req.query };
|
|
27296
27320
|
const { error } = validation.validate(query2);
|
|
@@ -27305,18 +27329,19 @@ function useRoleController() {
|
|
|
27305
27329
|
const type = req.query.type ?? "";
|
|
27306
27330
|
const org = req.query.org ?? "";
|
|
27307
27331
|
const site = req.query.site ?? "";
|
|
27332
|
+
const scope = req.query.scope ?? "";
|
|
27308
27333
|
try {
|
|
27309
27334
|
if (org) {
|
|
27310
27335
|
await requireOrgReach(req, org);
|
|
27311
27336
|
}
|
|
27312
|
-
let data = await _getRoles({ search, page: page2, limit, type, org, site });
|
|
27313
|
-
const listIsWholeOrg = org && !search && !site && page2 === 1;
|
|
27337
|
+
let data = await _getRoles({ search, page: page2, limit, type, org, site, scope });
|
|
27338
|
+
const listIsWholeOrg = org && !search && !site && !scope && page2 === 1;
|
|
27314
27339
|
const listed = data?.items;
|
|
27315
27340
|
if (listIsWholeOrg && listed?.length === 0) {
|
|
27316
27341
|
const seeded = await healDefaultRoles(org).catch(() => 0);
|
|
27317
27342
|
if (seeded > 0) {
|
|
27318
27343
|
try {
|
|
27319
|
-
data = await _getRoles({ search, page: page2, limit, type, org, site });
|
|
27344
|
+
data = await _getRoles({ search, page: page2, limit, type, org, site, scope });
|
|
27320
27345
|
} catch (error2) {
|
|
27321
27346
|
logger42.log({ level: "error", message: error2.message });
|
|
27322
27347
|
}
|
|
@@ -27768,7 +27793,8 @@ function useMemberService() {
|
|
|
27768
27793
|
}
|
|
27769
27794
|
}
|
|
27770
27795
|
}
|
|
27771
|
-
|
|
27796
|
+
const placedBy = member.siteId && !newRole?.site && callerId5 && ObjectId60.isValid(callerId5) && String(member.user ?? "") !== callerId5 ? callerId5 : void 0;
|
|
27797
|
+
await _updateRoleById(id, role, placedBy);
|
|
27772
27798
|
return { message: "Member role updated successfully." };
|
|
27773
27799
|
}
|
|
27774
27800
|
async function updateSiteById(id, siteId, siteName) {
|
|
@@ -27816,6 +27842,7 @@ function useMemberService() {
|
|
|
27816
27842
|
import { BadRequestError as BadRequestError56, UnauthorizedError as UnauthorizedError7, useAtlas as useAtlas44 } from "@7365admin1/node-server-utils";
|
|
27817
27843
|
import { ObjectId as ObjectId61 } from "mongodb";
|
|
27818
27844
|
var EXCLUDED_TYPES = ["resident", PLATFORM_STAFF_MEMBER_TYPE];
|
|
27845
|
+
var ORG_ROLE_EXCLUDED = [...EXCLUDED_TYPES, "organization"];
|
|
27819
27846
|
var both = (id) => [id, new ObjectId61(id)];
|
|
27820
27847
|
function useMemberSiteAccessService() {
|
|
27821
27848
|
const { add, updateMemberStatus, updateRoleById } = useMemberRepo();
|
|
@@ -27855,10 +27882,13 @@ function useMemberSiteAccessService() {
|
|
|
27855
27882
|
const roles = await db2().collection("roles").find({
|
|
27856
27883
|
$or: [
|
|
27857
27884
|
{ _id: { $in: roleIds.map((r) => new ObjectId61(r)) } },
|
|
27858
|
-
{ org: { $in: both(org) }, site: { $in: [...sites.keys()].flatMap(both) }, status: "active" }
|
|
27885
|
+
{ org: { $in: both(org) }, site: { $in: [...sites.keys()].flatMap(both) }, status: "active" },
|
|
27886
|
+
// organisation-level service roles (no site; never the org-console type)
|
|
27887
|
+
{ org: { $in: both(org) }, site: { $in: ["", null] }, status: "active", type: { $nin: ORG_ROLE_EXCLUDED } }
|
|
27859
27888
|
]
|
|
27860
27889
|
}).project({ name: 1, type: 1, site: 1, org: 1, status: 1 }).toArray();
|
|
27861
27890
|
const byId = new Map(roles.map((r) => [String(r._id), r]));
|
|
27891
|
+
const orgRoles = roles.filter((r) => !String(r.site ?? "") && String(r.org ?? "") === org && r.status === "active" && !ORG_ROLE_EXCLUDED.includes(r.type)).map((r) => ({ _id: String(r._id), name: r.name ?? "", type: r.type ?? "", scope: "organisation" })).sort((a, b) => a.name.localeCompare(b.name));
|
|
27862
27892
|
const view = (m) => ({
|
|
27863
27893
|
_id: String(m._id),
|
|
27864
27894
|
role: String(m.role ?? ""),
|
|
@@ -27872,7 +27902,7 @@ function useMemberSiteAccessService() {
|
|
|
27872
27902
|
...s,
|
|
27873
27903
|
membership: here[0] ?? null,
|
|
27874
27904
|
memberships: here,
|
|
27875
|
-
roles: roles.filter((r) => String(r.site ?? "") === s.site && String(r.org ?? "") === org && r.status === "active" && !EXCLUDED_TYPES.includes(r.type)).map((r) => ({ _id: String(r._id), name: r.name ?? "", type: r.type ?? "" }))
|
|
27905
|
+
roles: roles.filter((r) => String(r.site ?? "") === s.site && String(r.org ?? "") === org && r.status === "active" && !EXCLUDED_TYPES.includes(r.type)).map((r) => ({ _id: String(r._id), name: r.name ?? "", type: r.type ?? "", scope: "site" })).concat(orgRoles)
|
|
27876
27906
|
};
|
|
27877
27907
|
}).sort((a, b) => a.name.localeCompare(b.name));
|
|
27878
27908
|
const orgWide = members.filter((m) => !String(m.siteId ?? "")).map(view);
|
|
@@ -27898,7 +27928,7 @@ function useMemberSiteAccessService() {
|
|
|
27898
27928
|
throw new BadRequestError56("A site is listed twice.");
|
|
27899
27929
|
seen2.add(choice.site);
|
|
27900
27930
|
if (!row.roles.some((r) => r._id === choice.role)) {
|
|
27901
|
-
throw new BadRequestError56(`Choose a role created for ${row.name || "that site"}.`);
|
|
27931
|
+
throw new BadRequestError56(`Choose a role created for ${row.name || "that site"}, or one of the organisation's service roles.`);
|
|
27902
27932
|
}
|
|
27903
27933
|
}
|
|
27904
27934
|
const userDoc = await db2().collection("users").findOne({ _id: new ObjectId61(user) }, { projection: { email: 1, name: 1 } });
|
|
@@ -27912,10 +27942,11 @@ function useMemberSiteAccessService() {
|
|
|
27912
27942
|
const choice = sites.find((c) => c.site === row.site);
|
|
27913
27943
|
const role = choice ? row.roles.find((r) => r._id === choice.role) : null;
|
|
27914
27944
|
const keep = role ? row.memberships.find((m) => m.type === role.type) : void 0;
|
|
27915
|
-
if (role && keep && keep.role !== role._id) {
|
|
27945
|
+
if (role && keep && (keep.role !== role._id || role.scope === "organisation")) {
|
|
27916
27946
|
reroles.push(async () => {
|
|
27917
|
-
await updateRoleById(keep._id, role._id);
|
|
27918
|
-
|
|
27947
|
+
await updateRoleById(keep._id, role._id, actor);
|
|
27948
|
+
if (keep.role !== role._id)
|
|
27949
|
+
applied.changed.push(row.site);
|
|
27919
27950
|
});
|
|
27920
27951
|
} else if (role && !keep) {
|
|
27921
27952
|
adds.push(async () => {
|
|
@@ -27924,7 +27955,7 @@ function useMemberSiteAccessService() {
|
|
|
27924
27955
|
{ projection: { _id: 1 } }
|
|
27925
27956
|
);
|
|
27926
27957
|
if (old) {
|
|
27927
|
-
await updateRoleById(old._id, role._id);
|
|
27958
|
+
await updateRoleById(old._id, role._id, actor);
|
|
27928
27959
|
await updateMemberStatus(old._id, { status: suspended ? "suspended" : "active" });
|
|
27929
27960
|
} else {
|
|
27930
27961
|
await add({
|
|
@@ -27938,6 +27969,8 @@ function useMemberSiteAccessService() {
|
|
|
27938
27969
|
siteId: row.site,
|
|
27939
27970
|
siteName: row.name,
|
|
27940
27971
|
dateInvited: (/* @__PURE__ */ new Date()).toISOString(),
|
|
27972
|
+
// "placed at this site by the editor": confines an organisation role to it (siteInvitedPins)
|
|
27973
|
+
createdBy: actor,
|
|
27941
27974
|
...suspended ? { status: "suspended" } : {}
|
|
27942
27975
|
});
|
|
27943
27976
|
}
|