@7365admin1/core 3.64.3 → 3.64.4

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 CHANGED
@@ -1,5 +1,26 @@
1
1
  # @iservice365/core
2
2
 
3
+ ## 3.64.4
4
+
5
+ ### Patch Changes
6
+
7
+ - 3255256: Lay the groundwork for module lists becoming a gate, without narrowing anyone.
8
+
9
+ - New `utils/module-gate.util.ts`: the list of modules a client's list can
10
+ govern (generated from layer-common 5a96f95), the spelling groups, the
11
+ always-included set, and pure functions that work out which modules a
12
+ caller's apps must hide (`deniedModules`) and who would lose what if a list
13
+ changed (`moduleImpact`). Nothing calls them yet.
14
+ - The organisation and site module checks now treat every spelling of a module
15
+ as the same module (ticking `workOrder` also allows `work_orders`,
16
+ `work_order` and `work-orders`), and never refuse members, invitations, roles,
17
+ sites, customers, subscriptions, payment methods or site settings. This only
18
+ ever refuses less than before.
19
+ - A Site Settings save (`PATCH /api/sites/id/:id`) now always keeps the site's
20
+ stored module list. Before this, the save replaced `metadata` wholesale and
21
+ could wipe or overwrite that list. `PATCH /api/sites/:id/modules` is the only
22
+ way to change it.
23
+
3
24
  ## 3.64.3
4
25
 
5
26
  ### Patch Changes
package/dist/index.d.ts CHANGED
@@ -7547,7 +7547,6 @@ declare function UseAccessManagementRepo(): {
7547
7547
  userType: string;
7548
7548
  type: string;
7549
7549
  search: string;
7550
- userId?: string | null;
7551
7550
  }) => Promise<bson.Document>;
7552
7551
  acknowlegdeCardRepo: (params: {
7553
7552
  userId: string[];
package/dist/index.js CHANGED
@@ -11679,13 +11679,42 @@ function normaliseModules(modules) {
11679
11679
  }
11680
11680
  return out;
11681
11681
  }
11682
+ var ALWAYS_ALLOWED = Object.freeze([
11683
+ "sites",
11684
+ "subscriptions",
11685
+ "customers",
11686
+ "payment-methods",
11687
+ "members",
11688
+ "invitations",
11689
+ "roles",
11690
+ "roles-and-permissions",
11691
+ "site-settings"
11692
+ ]);
11693
+ var SPELLING_GROUPS = Object.freeze(
11694
+ [
11695
+ ["work_orders", "workOrder", "work_order", "work-orders"],
11696
+ ["visitor-mgmt", "visitorManagement"],
11697
+ ["bulletin-board", "bulletin-board-mgmt"],
11698
+ ["facility-booking-mgmt", "facility-booking"],
11699
+ ["feedbacks", "feedback"],
11700
+ ["building-mgmt", "buildingManagement"],
11701
+ ["roles", "roles-and-permissions"],
11702
+ ["service-provider-mgmt", "service-provider"],
11703
+ ["online-form-configuration", "online-forms"]
11704
+ ].map((group) => Object.freeze(group))
11705
+ );
11706
+ function moduleSpellings(key) {
11707
+ return SPELLING_GROUPS.find((group) => group.includes(key)) ?? [key];
11708
+ }
11682
11709
  function orgAllowsModule(modules, resource) {
11683
11710
  if (!Array.isArray(modules) || modules.length === 0)
11684
11711
  return true;
11685
11712
  const key = typeof resource === "string" ? resource.trim() : "";
11686
11713
  if (!key)
11687
11714
  return true;
11688
- return modules.includes(key);
11715
+ if (ALWAYS_ALLOWED.includes(key))
11716
+ return true;
11717
+ return moduleSpellings(key).some((spelling) => modules.includes(spelling));
11689
11718
  }
11690
11719
 
11691
11720
  // src/models/site.model.ts
@@ -11712,7 +11741,15 @@ function rejectedSiteModules(orgModules, requested) {
11712
11741
  if (!narrows(orgModules))
11713
11742
  return [];
11714
11743
  const org = normaliseModules(orgModules);
11715
- return normaliseModules(requested).filter((key) => !org.includes(key));
11744
+ return normaliseModules(requested).filter((key) => !orgAllowsModule(org, key));
11745
+ }
11746
+ function keepStoredSiteModules(metadata, stored) {
11747
+ if (!metadata || typeof metadata !== "object" || Array.isArray(metadata)) {
11748
+ return metadata;
11749
+ }
11750
+ const { modules: _sent, ...rest } = metadata;
11751
+ const kept = stored && typeof stored === "object" && !Array.isArray(stored) ? stored.modules : void 0;
11752
+ return kept === void 0 ? rest : { ...rest, modules: kept };
11716
11753
  }
11717
11754
  function rejectedSiteModulesMessage(rejected) {
11718
11755
  return `This organisation has not been given ${rejected.join(", ")}, so a site cannot offer it. Ask Seven365 to add it to the organisation first.`;
@@ -38536,8 +38573,8 @@ function useSiteController() {
38536
38573
  }
38537
38574
  const { _id, ...rest } = value;
38538
38575
  await requireSiteWrite(req, _id);
38576
+ const existing = rest?.metadata ? await _getSiteById(_id) : null;
38539
38577
  if (rest?.metadata?.residentAppModules) {
38540
- const existing = await _getSiteById(_id);
38541
38578
  const org = existing?.orgId ? await _getOrgById(existing.orgId.toString()).catch(
38542
38579
  () => null
38543
38580
  ) : null;
@@ -38552,6 +38589,12 @@ function useSiteController() {
38552
38589
  return;
38553
38590
  }
38554
38591
  }
38592
+ if (rest?.metadata) {
38593
+ rest.metadata = keepStoredSiteModules(
38594
+ rest.metadata,
38595
+ existing?.metadata
38596
+ );
38597
+ }
38555
38598
  await _updateById(_id, rest);
38556
38599
  res.json({ message: "Successfully updated site." });
38557
38600
  return;
@@ -55505,7 +55548,6 @@ function UseAccessManagementRepo() {
55505
55548
  const type = params.type;
55506
55549
  const search = params.search;
55507
55550
  const typeFilter = type.toLowerCase() === "all" ? [] : [{ $eq: ["$type", type] }];
55508
- const userIdFilter = params.userId ? [{ $eq: ["$userId", new import_mongodb90.ObjectId(params.userId)] }] : [];
55509
55551
  const searchFilter = search ? [
55510
55552
  {
55511
55553
  $or: [
@@ -55580,7 +55622,9 @@ function UseAccessManagementRepo() {
55580
55622
  { $eq: ["$isActivated", true] },
55581
55623
  { $eq: ["$userType", userType] },
55582
55624
  ...typeFilter,
55583
- ...userIdFilter
55625
+ // Cards actually held by a user only - a card sitting
55626
+ // unassigned on the unit (`userId: null`) is left out.
55627
+ { $ne: ["$userId", null] }
55584
55628
  ]
55585
55629
  },
55586
55630
  ...searchFilter[0]
@@ -70586,24 +70630,21 @@ function useAccessManagementController() {
70586
70630
  unitId,
70587
70631
  userType,
70588
70632
  type,
70589
- search = "",
70590
- userId = ""
70633
+ search = ""
70591
70634
  } = req.query;
70592
70635
  const schema2 = import_joi101.default.object({
70593
70636
  site: import_joi101.default.string().hex().required(),
70594
70637
  unitId: import_joi101.default.string().hex().required(),
70595
70638
  userType: import_joi101.default.string().required(),
70596
70639
  type: import_joi101.default.string().valid("all", ...Object.values(EAccessCardTypes)).required(),
70597
- search: import_joi101.default.string().optional().allow("", null),
70598
- userId: import_joi101.default.string().hex().optional().allow("", null)
70640
+ search: import_joi101.default.string().optional().allow("", null)
70599
70641
  });
70600
70642
  const { error } = schema2.validate({
70601
70643
  site,
70602
70644
  unitId,
70603
70645
  userType,
70604
70646
  type,
70605
- search,
70606
- userId
70647
+ search
70607
70648
  });
70608
70649
  if (error) {
70609
70650
  return res.status(400).json({ message: error.message });
@@ -70615,8 +70656,7 @@ function useAccessManagementController() {
70615
70656
  unitId,
70616
70657
  userType,
70617
70658
  type,
70618
- search,
70619
- userId
70659
+ search
70620
70660
  });
70621
70661
  return res.status(200).json({ message: "Success", data: result });
70622
70662
  } catch (error) {