@7365admin1/core 3.62.1-staging.277 → 3.62.1-staging.278

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.
@@ -0,0 +1,5 @@
1
+ ---
2
+ "@7365admin1/core": minor
3
+ ---
4
+
5
+ Tier two of the module cascade: `sites.metadata.modules`, the modules a site offers, plus `PATCH /sites/:id/modules` for the organisation to set it. The write refuses any key Seven365 never gave the organisation (owner requirement R1); the read fails open at every tier, so absent and empty both mean "everything the organisation has" and no site or role editor changes until somebody sets a list.
package/dist/index.d.ts CHANGED
@@ -2126,6 +2126,8 @@ declare enum SiteCategories {
2126
2126
  HOSPITALITY = "hospitality"
2127
2127
  }
2128
2128
  type TSiteMetadata = {
2129
+ /** Tier two of the module cascade — see `site-modules.util.ts`. */
2130
+ modules?: string[];
2129
2131
  block?: number;
2130
2132
  guardPosts?: number;
2131
2133
  gracePeriod?: number;
@@ -2310,6 +2312,7 @@ declare function useSiteController(): {
2310
2312
  updateSiteBlock: (req: Request, res: Response, next: NextFunction) => Promise<void>;
2311
2313
  deleteSite: (req: Request, res: Response, next: NextFunction) => Promise<void>;
2312
2314
  updateById: (req: Request, res: Response, next: NextFunction) => Promise<void>;
2315
+ updateSiteModules: (req: Request, res: Response, next: NextFunction) => Promise<void>;
2313
2316
  updateGuardPostsById: (req: Request, res: Response, next: NextFunction) => Promise<void>;
2314
2317
  siteInformation: (req: Request, res: Response, next: NextFunction) => Promise<void>;
2315
2318
  getAllSitesForResidentCreation: (req: Request, res: Response, next: NextFunction) => Promise<void>;
package/dist/index.js CHANGED
@@ -12397,6 +12397,23 @@ var import_mongodb21 = require("mongodb");
12397
12397
  // src/models/site.model.ts
12398
12398
  var import_node_server_utils22 = require("@7365admin1/node-server-utils");
12399
12399
  var import_joi10 = __toESM(require("joi"));
12400
+
12401
+ // src/utils/site-modules.util.ts
12402
+ var siteModulesSchema = modulesSchema;
12403
+ function narrows(modules) {
12404
+ return Array.isArray(modules) && modules.length > 0;
12405
+ }
12406
+ function rejectedSiteModules(orgModules, requested) {
12407
+ if (!narrows(orgModules))
12408
+ return [];
12409
+ const org = normaliseModules(orgModules);
12410
+ return normaliseModules(requested).filter((key) => !org.includes(key));
12411
+ }
12412
+ function rejectedSiteModulesMessage(rejected) {
12413
+ return `This organisation has not been given ${rejected.join(", ")}, so a site cannot offer it. Ask Seven365 to add it to the organisation first.`;
12414
+ }
12415
+
12416
+ // src/models/site.model.ts
12400
12417
  var import_mongodb20 = require("mongodb");
12401
12418
 
12402
12419
  // src/utils/site-timezone.util.ts
@@ -12465,6 +12482,23 @@ var hidQrCodePassSchema = import_joi10.default.object({
12465
12482
  updatedAt: import_joi10.default.string().isoDate().required()
12466
12483
  });
12467
12484
  var metadataSchema3 = import_joi10.default.object({
12485
+ /**
12486
+ * The catalogue modules THIS SITE offers — tier two of the cascade.
12487
+ *
12488
+ * Owner requirement R1: whatever Seven365 gives an organisation is the only
12489
+ * set that organisation may then give to each site it creates. The
12490
+ * organisation's own list is `organizations.modules`; this narrows it per
12491
+ * site, and `site-modules.util.ts` holds both the read (fails open) and the
12492
+ * write rule (refuses a key the organisation was never given).
12493
+ *
12494
+ * ABSENT AND EMPTY BOTH MEAN "everything the organisation has" — never
12495
+ * "nothing". No site has this field today.
12496
+ *
12497
+ * It lives in `metadata` because `MSite` is a whitelist: a new top-level
12498
+ * field is silently dropped, while `metadata` is passed through wholesale.
12499
+ * `residentAppModules` below is the same pattern for the same reason.
12500
+ */
12501
+ modules: siteModulesSchema.optional(),
12468
12502
  block: import_joi10.default.number().optional(),
12469
12503
  guardPosts: import_joi10.default.number().optional(),
12470
12504
  gracePeriod: import_joi10.default.number().optional(),
@@ -38364,6 +38398,7 @@ function useSiteController() {
38364
38398
  deleteSite: _deleteSite,
38365
38399
  getAllSitesForResidentCreation: _getAllSitesForResidentCreation
38366
38400
  } = useSiteRepo();
38401
+ const { getById: _getOrgById } = useOrgRepo();
38367
38402
  const { updateGuardPostById, siteInformation: _siteInformation } = useSiteService();
38368
38403
  async function createSite(req, res, next) {
38369
38404
  const payload = { ...req.body };
@@ -38522,6 +38557,45 @@ function useSiteController() {
38522
38557
  return;
38523
38558
  }
38524
38559
  }
38560
+ async function updateSiteModules(req, res, next) {
38561
+ const validation = import_joi51.default.object({ modules: siteModulesSchema.required() });
38562
+ const { error } = validation.validate(req.body);
38563
+ if (error) {
38564
+ import_node_server_utils104.logger.log({ level: "error", message: error.message });
38565
+ next(new import_node_server_utils104.BadRequestError(error.message));
38566
+ return;
38567
+ }
38568
+ const siteId = req.params.id ?? "";
38569
+ try {
38570
+ await requireSiteWrite(req, siteId);
38571
+ const site = await _getSiteById(siteId);
38572
+ if (!site) {
38573
+ next(new import_node_server_utils104.BadRequestError("Site not found."));
38574
+ return;
38575
+ }
38576
+ const org = site.orgId ? await _getOrgById(site.orgId.toString()) : null;
38577
+ const rejected = rejectedSiteModules(
38578
+ org?.modules,
38579
+ req.body.modules
38580
+ );
38581
+ if (rejected.length) {
38582
+ next(new import_node_server_utils104.BadRequestError(rejectedSiteModulesMessage(rejected)));
38583
+ return;
38584
+ }
38585
+ const modules = normaliseModules(req.body.modules);
38586
+ await _updateById(siteId, {
38587
+ ...site,
38588
+ metadata: { ...site.metadata ?? {}, modules }
38589
+ });
38590
+ const data = await _getSiteById(siteId);
38591
+ res.json({ message: "Site modules updated successfully.", data });
38592
+ return;
38593
+ } catch (error2) {
38594
+ import_node_server_utils104.logger.log({ level: "error", message: error2.message });
38595
+ next(error2);
38596
+ return;
38597
+ }
38598
+ }
38525
38599
  async function updateGuardPostsById(req, res, next) {
38526
38600
  const validation = import_joi51.default.object({
38527
38601
  _id: import_joi51.default.string().hex().required(),
@@ -38614,6 +38688,7 @@ function useSiteController() {
38614
38688
  updateSiteBlock,
38615
38689
  deleteSite,
38616
38690
  updateById,
38691
+ updateSiteModules,
38617
38692
  updateGuardPostsById,
38618
38693
  siteInformation,
38619
38694
  getAllSitesForResidentCreation