@7365admin1/core 3.73.1 → 3.75.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 +12 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +91 -50
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +91 -50
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/test/backfill-org-owner-roles.test.mjs +6 -1
- package/test/e2e/client-invite-service365.e2e.test.mjs +168 -0
- package/test/org-default-roles.test.mjs +11 -0
package/dist/index.mjs
CHANGED
|
@@ -3166,6 +3166,16 @@ import {
|
|
|
3166
3166
|
} from "@7365admin1/node-server-utils";
|
|
3167
3167
|
import { v4 as uuidv4 } from "uuid";
|
|
3168
3168
|
|
|
3169
|
+
// src/utils/service-app-type.util.ts
|
|
3170
|
+
var APP_TYPE_ALIASES = Object.freeze({
|
|
3171
|
+
real_estate_developer: "property_management_agency"
|
|
3172
|
+
});
|
|
3173
|
+
function serviceAppType(type) {
|
|
3174
|
+
if (typeof type !== "string")
|
|
3175
|
+
return type;
|
|
3176
|
+
return APP_TYPE_ALIASES[type] ?? type;
|
|
3177
|
+
}
|
|
3178
|
+
|
|
3169
3179
|
// src/repositories/member.repo.ts
|
|
3170
3180
|
import {
|
|
3171
3181
|
AppError as AppError2,
|
|
@@ -3328,7 +3338,7 @@ function useMemberRepo() {
|
|
|
3328
3338
|
}
|
|
3329
3339
|
async function add(value, session) {
|
|
3330
3340
|
try {
|
|
3331
|
-
value = MMember(value);
|
|
3341
|
+
value = MMember({ ...value, type: serviceAppType(value.type) });
|
|
3332
3342
|
await collection.insertOne(value, { session });
|
|
3333
3343
|
delNamespace().then(() => {
|
|
3334
3344
|
logger7.info(`Cache cleared for namespace: ${namespace_collection3}`);
|
|
@@ -3456,6 +3466,7 @@ function useMemberRepo() {
|
|
|
3456
3466
|
}
|
|
3457
3467
|
}
|
|
3458
3468
|
async function getByUserIdType(user, type) {
|
|
3469
|
+
type = serviceAppType(type);
|
|
3459
3470
|
try {
|
|
3460
3471
|
user = new ObjectId13(user);
|
|
3461
3472
|
} catch (error) {
|
|
@@ -3615,7 +3626,7 @@ function useMemberRepo() {
|
|
|
3615
3626
|
search = search || "";
|
|
3616
3627
|
page = page > 0 ? page - 1 : 0;
|
|
3617
3628
|
const query2 = {
|
|
3618
|
-
type,
|
|
3629
|
+
type: serviceAppType(type),
|
|
3619
3630
|
status
|
|
3620
3631
|
};
|
|
3621
3632
|
const cacheOptions = {
|
|
@@ -4137,6 +4148,7 @@ function useMemberRepo() {
|
|
|
4137
4148
|
}
|
|
4138
4149
|
}
|
|
4139
4150
|
async function getByUserIdTypeOrg(user, type, org) {
|
|
4151
|
+
type = serviceAppType(type);
|
|
4140
4152
|
try {
|
|
4141
4153
|
user = new ObjectId13(user);
|
|
4142
4154
|
} catch {
|
|
@@ -11751,7 +11763,7 @@ function useRoleRepo() {
|
|
|
11751
11763
|
const { delNamespace, setCache, getCache } = useCache14(namespace_collection3);
|
|
11752
11764
|
const { delNamespace: _delDashboardNameSpace } = useCache14("dashboard");
|
|
11753
11765
|
async function addRole(value, session) {
|
|
11754
|
-
value = new MRole(value);
|
|
11766
|
+
value = new MRole({ ...value, type: serviceAppType(value.type) });
|
|
11755
11767
|
try {
|
|
11756
11768
|
const res = await collection.insertOne(value, { session });
|
|
11757
11769
|
delNamespace().then(() => {
|
|
@@ -11854,6 +11866,7 @@ function useRoleRepo() {
|
|
|
11854
11866
|
if (!name) {
|
|
11855
11867
|
throw new BadRequestError33("Role name is required.");
|
|
11856
11868
|
}
|
|
11869
|
+
type = serviceAppType(type);
|
|
11857
11870
|
const query2 = type ? { name, type } : { name };
|
|
11858
11871
|
try {
|
|
11859
11872
|
const cacheKey = makeCacheKey11(namespace_collection3, query2);
|
|
@@ -11910,8 +11923,8 @@ function useRoleRepo() {
|
|
|
11910
11923
|
cacheOptions.search = search;
|
|
11911
11924
|
}
|
|
11912
11925
|
if (type) {
|
|
11913
|
-
query2.type = type;
|
|
11914
|
-
cacheOptions.type = type;
|
|
11926
|
+
query2.type = serviceAppType(type);
|
|
11927
|
+
cacheOptions.type = query2.type;
|
|
11915
11928
|
}
|
|
11916
11929
|
const cacheKey = makeCacheKey11(namespace_collection3, cacheOptions);
|
|
11917
11930
|
const cachedData = await getCache(cacheKey);
|
|
@@ -14552,13 +14565,14 @@ function defaultOwnerRoles(org, nature, appOwnerPermissions) {
|
|
|
14552
14565
|
{ ...owner, type: "organization" },
|
|
14553
14566
|
{ ...administrator, type: "organization" }
|
|
14554
14567
|
];
|
|
14555
|
-
|
|
14568
|
+
const appType = serviceAppType(nature);
|
|
14569
|
+
if (appType && APP_SERVICE_TYPES.has(appType)) {
|
|
14556
14570
|
roles.push({
|
|
14557
14571
|
...owner,
|
|
14558
|
-
type:
|
|
14572
|
+
type: appType,
|
|
14559
14573
|
...appOwnerPermissions?.length && { permissions: [...appOwnerPermissions] }
|
|
14560
14574
|
});
|
|
14561
|
-
roles.push({ ...administrator, type:
|
|
14575
|
+
roles.push({ ...administrator, type: appType });
|
|
14562
14576
|
}
|
|
14563
14577
|
return roles;
|
|
14564
14578
|
}
|
|
@@ -14643,15 +14657,17 @@ async function moduleCeiling(org, site) {
|
|
|
14643
14657
|
}
|
|
14644
14658
|
async function ownerGrantCeiling(org, type) {
|
|
14645
14659
|
const orgId = org?.toString() ?? "";
|
|
14660
|
+
type = serviceAppType(type);
|
|
14646
14661
|
if (!orgId || !type)
|
|
14647
14662
|
return null;
|
|
14648
14663
|
try {
|
|
14649
14664
|
const orgDoc = await useOrgRepo().getById(orgId).catch(() => null);
|
|
14650
|
-
|
|
14665
|
+
const appType = serviceAppType(orgDoc?.nature);
|
|
14666
|
+
if (!orgDoc?.invitedRole || appType !== type)
|
|
14651
14667
|
return null;
|
|
14652
14668
|
const role = await useRoleRepo().getRoleById(String(orgDoc.invitedRole)).catch(() => null);
|
|
14653
14669
|
const permissions = role?.permissions;
|
|
14654
|
-
const chosen = role && String(role.org ?? "") === orgId && role.default === true && role.type ===
|
|
14670
|
+
const chosen = role && String(role.org ?? "") === orgId && role.default === true && role.type === appType && Array.isArray(permissions) && permissions.length > 0 && !permissions.includes("*");
|
|
14655
14671
|
return chosen ? permissions.map(String) : null;
|
|
14656
14672
|
} catch {
|
|
14657
14673
|
return null;
|
|
@@ -23571,6 +23587,44 @@ function useMemberController() {
|
|
|
23571
23587
|
// src/controllers/verification.controller.ts
|
|
23572
23588
|
import { BadRequestError as BadRequestError51, logger as logger38 } from "@7365admin1/node-server-utils";
|
|
23573
23589
|
import Joi28 from "joi";
|
|
23590
|
+
|
|
23591
|
+
// src/utils/resident-app-modules.util.ts
|
|
23592
|
+
var RESIDENT_APP_NATURES = Object.freeze([
|
|
23593
|
+
"property_management_agency",
|
|
23594
|
+
"real_estate_developer"
|
|
23595
|
+
]);
|
|
23596
|
+
function effectiveResidentAppModules(orgModules, siteModules) {
|
|
23597
|
+
const org = asModules(orgModules);
|
|
23598
|
+
const site = asModules(siteModules);
|
|
23599
|
+
const effective = {};
|
|
23600
|
+
for (const key of residentAppModuleKeys) {
|
|
23601
|
+
effective[key] = org[key] !== false && site[key] !== false;
|
|
23602
|
+
}
|
|
23603
|
+
return effective;
|
|
23604
|
+
}
|
|
23605
|
+
function residentAppModulesAboveCeiling(orgModules, requested, stored) {
|
|
23606
|
+
const org = asModules(orgModules);
|
|
23607
|
+
const wanted = asModules(requested);
|
|
23608
|
+
const had = asModules(stored);
|
|
23609
|
+
return residentAppModuleKeys.filter(
|
|
23610
|
+
(key) => org[key] === false && wanted[key] === true && had[key] === false
|
|
23611
|
+
);
|
|
23612
|
+
}
|
|
23613
|
+
function residentAppModulesAboveCeilingMessage(rejected) {
|
|
23614
|
+
return `This client has not been given ${rejected.join(", ")} in the resident app, so a site cannot switch it on. Ask Seven365 to add it to the organisation first.`;
|
|
23615
|
+
}
|
|
23616
|
+
function withoutDerivedSiteMetadata(metadata) {
|
|
23617
|
+
if (!metadata || typeof metadata !== "object" || Array.isArray(metadata)) {
|
|
23618
|
+
return metadata;
|
|
23619
|
+
}
|
|
23620
|
+
const { residentAppModulesEffective: _derived, ...stored } = metadata;
|
|
23621
|
+
return stored;
|
|
23622
|
+
}
|
|
23623
|
+
function asModules(value) {
|
|
23624
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
23625
|
+
}
|
|
23626
|
+
|
|
23627
|
+
// src/controllers/verification.controller.ts
|
|
23574
23628
|
var withoutEmail = (payload) => JSON.stringify({ ...payload, email: payload?.email ? "[redacted]" : void 0 });
|
|
23575
23629
|
function callerId2(req) {
|
|
23576
23630
|
const user = req.user;
|
|
@@ -23652,7 +23706,11 @@ function useVerificationController() {
|
|
|
23652
23706
|
modules: modulesSchema.optional(),
|
|
23653
23707
|
// The new client owner's permissions in that application, chosen with
|
|
23654
23708
|
// the modules (owner, 2026-09-15). Only accepted together with modules.
|
|
23655
|
-
permissions: permissionsSchema.optional()
|
|
23709
|
+
permissions: permissionsSchema.optional(),
|
|
23710
|
+
// The Service365 resident app switches for a Property Management (or
|
|
23711
|
+
// real estate developer) client, same shape the Client details card saves
|
|
23712
|
+
// to `organizations.residentAppModules`. Only accepted with modules.
|
|
23713
|
+
residentAppModules: residentAppModulesSchema.optional()
|
|
23656
23714
|
});
|
|
23657
23715
|
const { error, value } = validation.validate(payload);
|
|
23658
23716
|
if (error) {
|
|
@@ -23672,7 +23730,8 @@ function useVerificationController() {
|
|
|
23672
23730
|
siteId,
|
|
23673
23731
|
siteName,
|
|
23674
23732
|
modules,
|
|
23675
|
-
permissions
|
|
23733
|
+
permissions,
|
|
23734
|
+
residentAppModules
|
|
23676
23735
|
} = value;
|
|
23677
23736
|
const newClient = !org && !app.includes(PLATFORM_STAFF_MEMBER_TYPE);
|
|
23678
23737
|
const role = newClient ? "" : value.role;
|
|
@@ -23702,6 +23761,14 @@ function useVerificationController() {
|
|
|
23702
23761
|
}
|
|
23703
23762
|
clientModules.permissions = chosen;
|
|
23704
23763
|
}
|
|
23764
|
+
if (residentAppModules !== void 0) {
|
|
23765
|
+
if (!clientModules || !RESIDENT_APP_NATURES.includes(app[0])) {
|
|
23766
|
+
throw new BadRequestError51(
|
|
23767
|
+
"Service365 resident app modules can only be chosen with the modules of a new Property Management client."
|
|
23768
|
+
);
|
|
23769
|
+
}
|
|
23770
|
+
clientModules.residentAppModules = residentAppModules;
|
|
23771
|
+
}
|
|
23705
23772
|
await requireInviteReach(req, org, app, role);
|
|
23706
23773
|
const verificationIds = await _createSimpleUserInvite({
|
|
23707
23774
|
email,
|
|
@@ -25535,10 +25602,12 @@ function useOrgController() {
|
|
|
25535
25602
|
const invitedBy = invite.metadata?.invitedBy;
|
|
25536
25603
|
if (chosen.length && invitedBy && invite.metadata?.app === body.nature) {
|
|
25537
25604
|
const permissions = clientInviteOwnerPermissionsOn() ? normalisePermissions(invite.metadata?.permissions) : [];
|
|
25605
|
+
const residentApp = RESIDENT_APP_NATURES.includes(body.nature) ? residentAppModulesSchema.validate(invite.metadata?.residentAppModules) : null;
|
|
25538
25606
|
invitedModules = {
|
|
25539
25607
|
modules: chosen,
|
|
25540
25608
|
invitedBy: String(invitedBy),
|
|
25541
|
-
permissions: permissionsOutsideModules(permissions, chosen).length ? [] : permissions
|
|
25609
|
+
permissions: permissionsOutsideModules(permissions, chosen).length ? [] : permissions,
|
|
25610
|
+
...residentApp?.value && !residentApp.error ? { residentAppModules: residentApp.value } : {}
|
|
25542
25611
|
};
|
|
25543
25612
|
}
|
|
25544
25613
|
} else {
|
|
@@ -25561,7 +25630,10 @@ function useOrgController() {
|
|
|
25561
25630
|
createdBy,
|
|
25562
25631
|
invitedFrom,
|
|
25563
25632
|
invitedRole,
|
|
25564
|
-
...invitedModules && { modules: invitedModules.modules }
|
|
25633
|
+
...invitedModules && { modules: invitedModules.modules },
|
|
25634
|
+
...invitedModules?.residentAppModules && {
|
|
25635
|
+
residentAppModules: invitedModules.residentAppModules
|
|
25636
|
+
}
|
|
25565
25637
|
});
|
|
25566
25638
|
if (invitedModules) {
|
|
25567
25639
|
await recordConsoleAction({
|
|
@@ -25575,7 +25647,10 @@ function useOrgController() {
|
|
|
25575
25647
|
moduleCount: invitedModules.modules.length,
|
|
25576
25648
|
gateListHash: moduleListHash(invitedModules.modules),
|
|
25577
25649
|
acknowledged: false,
|
|
25578
|
-
impactMemberCount: 0
|
|
25650
|
+
impactMemberCount: 0,
|
|
25651
|
+
...invitedModules.residentAppModules && {
|
|
25652
|
+
residentAppModulesOff: Object.keys(invitedModules.residentAppModules).filter((key) => invitedModules.residentAppModules[key] === false).join(", ")
|
|
25653
|
+
}
|
|
25579
25654
|
})
|
|
25580
25655
|
});
|
|
25581
25656
|
}
|
|
@@ -25598,7 +25673,7 @@ function useOrgController() {
|
|
|
25598
25673
|
invitedModules?.permissions
|
|
25599
25674
|
);
|
|
25600
25675
|
const appOwnerRole = invitedModules?.permissions.length ? seededRoles.find(
|
|
25601
|
-
(role) => role.type === body.nature && role.name === "owner"
|
|
25676
|
+
(role) => role.type === serviceAppType(body.nature) && role.name === "owner"
|
|
25602
25677
|
) : void 0;
|
|
25603
25678
|
if (appOwnerRole) {
|
|
25604
25679
|
try {
|
|
@@ -34301,40 +34376,6 @@ import {
|
|
|
34301
34376
|
UnauthorizedError as UnauthorizedError14,
|
|
34302
34377
|
useAtlas as useAtlas56
|
|
34303
34378
|
} from "@7365admin1/node-server-utils";
|
|
34304
|
-
|
|
34305
|
-
// src/utils/resident-app-modules.util.ts
|
|
34306
|
-
function effectiveResidentAppModules(orgModules, siteModules) {
|
|
34307
|
-
const org = asModules(orgModules);
|
|
34308
|
-
const site = asModules(siteModules);
|
|
34309
|
-
const effective = {};
|
|
34310
|
-
for (const key of residentAppModuleKeys) {
|
|
34311
|
-
effective[key] = org[key] !== false && site[key] !== false;
|
|
34312
|
-
}
|
|
34313
|
-
return effective;
|
|
34314
|
-
}
|
|
34315
|
-
function residentAppModulesAboveCeiling(orgModules, requested, stored) {
|
|
34316
|
-
const org = asModules(orgModules);
|
|
34317
|
-
const wanted = asModules(requested);
|
|
34318
|
-
const had = asModules(stored);
|
|
34319
|
-
return residentAppModuleKeys.filter(
|
|
34320
|
-
(key) => org[key] === false && wanted[key] === true && had[key] === false
|
|
34321
|
-
);
|
|
34322
|
-
}
|
|
34323
|
-
function residentAppModulesAboveCeilingMessage(rejected) {
|
|
34324
|
-
return `This client has not been given ${rejected.join(", ")} in the resident app, so a site cannot switch it on. Ask Seven365 to add it to the organisation first.`;
|
|
34325
|
-
}
|
|
34326
|
-
function withoutDerivedSiteMetadata(metadata) {
|
|
34327
|
-
if (!metadata || typeof metadata !== "object" || Array.isArray(metadata)) {
|
|
34328
|
-
return metadata;
|
|
34329
|
-
}
|
|
34330
|
-
const { residentAppModulesEffective: _derived, ...stored } = metadata;
|
|
34331
|
-
return stored;
|
|
34332
|
-
}
|
|
34333
|
-
function asModules(value) {
|
|
34334
|
-
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
34335
|
-
}
|
|
34336
|
-
|
|
34337
|
-
// src/controllers/site.controller.ts
|
|
34338
34379
|
import Joi51 from "joi";
|
|
34339
34380
|
import { ObjectId as ObjectId74 } from "mongodb";
|
|
34340
34381
|
async function requireSiteWrite(req, siteId) {
|