@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.
- package/.changeset/site-module-allowlist.md +5 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +75 -0
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +75 -0
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
- package/test/site-modules.test.mjs +149 -0
|
@@ -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
|