@7365admin1/core 3.54.0 → 3.56.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 +49 -0
- package/dist/index.d.ts +17 -7
- package/dist/index.js +4434 -4375
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +146 -87
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
- package/test/e2e/notification-matrix.e2e.test.mjs +420 -0
- package/test/e2e/notification-preference.e2e.test.mjs +21 -8
- package/test/e2e/onboarding-org-save-diagnosis.e2e.test.mjs +76 -3
- package/test/e2e/safety-notification.e2e.test.mjs +1 -1
- package/test/role-scope-separation.test.mjs +195 -0
- package/test/role-scope.test.mjs +55 -3
- package/tools/role-scope-lockout-check/check.js +316 -0
- package/tools/role-scope-lockout-check/platform-only-permissions.json +21 -0
- package/tools/role-scope-lockout-check/srv.js +39 -0
package/dist/index.mjs
CHANGED
|
@@ -13658,6 +13658,8 @@ var NotificationModule = /* @__PURE__ */ ((NotificationModule2) => {
|
|
|
13658
13658
|
NotificationModule2["FACILITY_BOOKING"] = "facilityBooking";
|
|
13659
13659
|
NotificationModule2["ONLINE_FORM"] = "onlineForm";
|
|
13660
13660
|
NotificationModule2["PRELOVED_MARKETPLACE"] = "prelovedMarketplace";
|
|
13661
|
+
NotificationModule2["MARKETPLACE_ORDER"] = "marketplace-order";
|
|
13662
|
+
NotificationModule2["MARKETPLACE_VENDOR_ORDER"] = "marketplace-vendor-order";
|
|
13661
13663
|
NotificationModule2["INCIDENT_REPORT"] = "incidentReport";
|
|
13662
13664
|
NotificationModule2["EMERGENCY_CONTACT"] = "emergencyContact";
|
|
13663
13665
|
NotificationModule2["VIRTUAL_PATROL"] = "virtualPatrol";
|
|
@@ -14126,7 +14128,7 @@ var NOTIFICATION_CATEGORIES = [
|
|
|
14126
14128
|
{
|
|
14127
14129
|
key: "facilityBooking",
|
|
14128
14130
|
label: "Facility Bookings",
|
|
14129
|
-
description: "When a facility booking is made or its status changes.",
|
|
14131
|
+
description: "When a facility booking is made or its status changes. The confirmation email goes to the address entered on the booking form itself.",
|
|
14130
14132
|
channels: { inApp: true, push: true, email: false },
|
|
14131
14133
|
channelNote: {
|
|
14132
14134
|
email: "Booking confirmations go to the email address entered on the booking form itself. To change where a confirmation lands, change that address when the booking is made."
|
|
@@ -14138,7 +14140,7 @@ var NOTIFICATION_CATEGORIES = [
|
|
|
14138
14140
|
{
|
|
14139
14141
|
key: "visitors",
|
|
14140
14142
|
label: "Visitors",
|
|
14141
|
-
description: "When a visitor you invited checks in or checks out.",
|
|
14143
|
+
description: "When a visitor you invited checks in or checks out. The invitation email goes to the address entered for the visitor.",
|
|
14142
14144
|
channels: { inApp: true, push: true, email: false },
|
|
14143
14145
|
channelNote: {
|
|
14144
14146
|
email: "Visitor invitations go to the email address entered for the visitor when they are invited. To change where one lands, change that address on the invitation."
|
|
@@ -14212,7 +14214,7 @@ var NOTIFICATION_CATEGORIES = [
|
|
|
14212
14214
|
{
|
|
14213
14215
|
key: "serviceProviderInvite",
|
|
14214
14216
|
label: "Service Provider Invitations",
|
|
14215
|
-
description: "When a property manager invites your company to provide services at a site.",
|
|
14217
|
+
description: "When a property manager invites your company to provide services at a site. The invitation email is the invitation itself and is always sent to the address it was addressed to; these settings control the copy that reaches you inside the app.",
|
|
14216
14218
|
channels: { inApp: true, push: true, email: false },
|
|
14217
14219
|
channelNote: {
|
|
14218
14220
|
email: "The invitation email is the invitation itself \u2014 for a company that has no account here yet it is the only copy, so it is always sent to the address the invitation was addressed to. The switches above control the copy that reaches you inside the app."
|
|
@@ -18694,6 +18696,53 @@ function useRoleService() {
|
|
|
18694
18696
|
return { deleteRole, getDeletionPreview, deleteWithReassignments };
|
|
18695
18697
|
}
|
|
18696
18698
|
|
|
18699
|
+
// src/utils/role-scope.util.ts
|
|
18700
|
+
import { UnauthorizedError as UnauthorizedError4 } from "@7365admin1/node-server-utils";
|
|
18701
|
+
function roleScope(role) {
|
|
18702
|
+
if ((role?.type ?? "") === PLATFORM_STAFF_ROLE_TYPE)
|
|
18703
|
+
return "platform";
|
|
18704
|
+
return role?.org?.toString() ? "client" : "client-template";
|
|
18705
|
+
}
|
|
18706
|
+
var PLATFORM_ONLY_RESOURCES = [
|
|
18707
|
+
"organizations",
|
|
18708
|
+
"promo-codes",
|
|
18709
|
+
"subscriptions",
|
|
18710
|
+
"sp-approvals",
|
|
18711
|
+
"marketplace-vendors",
|
|
18712
|
+
"platform-terms",
|
|
18713
|
+
"activity-history"
|
|
18714
|
+
];
|
|
18715
|
+
var PLATFORM_ONLY_PERMISSIONS = new Set(
|
|
18716
|
+
PLATFORM_ONLY_RESOURCES.flatMap(
|
|
18717
|
+
(resource) => (CONSOLE_PERMISSIONS[resource] ?? []).map(
|
|
18718
|
+
(action) => `${resource}:${action}`
|
|
18719
|
+
)
|
|
18720
|
+
)
|
|
18721
|
+
);
|
|
18722
|
+
function platformPermissionsAdded(next, held) {
|
|
18723
|
+
const already = new Set(held ?? []);
|
|
18724
|
+
return (next ?? []).filter(
|
|
18725
|
+
(permission) => PLATFORM_ONLY_PERMISSIONS.has(permission) && !already.has(permission)
|
|
18726
|
+
);
|
|
18727
|
+
}
|
|
18728
|
+
function requireNoPlatformGrant(role, next) {
|
|
18729
|
+
if (roleScope(role) === "platform")
|
|
18730
|
+
return;
|
|
18731
|
+
const added = platformPermissionsAdded(next, role?.permissions);
|
|
18732
|
+
if (added.length === 0)
|
|
18733
|
+
return;
|
|
18734
|
+
throw new UnauthorizedError4(
|
|
18735
|
+
"These are Seven365 console permissions and cannot be granted on a client role: " + added.join(", ")
|
|
18736
|
+
);
|
|
18737
|
+
}
|
|
18738
|
+
function refuseSharedClientTemplate(role) {
|
|
18739
|
+
if (roleScope(role) !== "client-template")
|
|
18740
|
+
return;
|
|
18741
|
+
throw new UnauthorizedError4(
|
|
18742
|
+
"This role is shared by every client that was invited with it and cannot be changed here."
|
|
18743
|
+
);
|
|
18744
|
+
}
|
|
18745
|
+
|
|
18697
18746
|
// src/controllers/role.controller.ts
|
|
18698
18747
|
async function requireRoleOrg(req, org) {
|
|
18699
18748
|
const orgId = org?.toString() ?? "";
|
|
@@ -18708,6 +18757,10 @@ async function requireRoleRead(req, roleId, org) {
|
|
|
18708
18757
|
return;
|
|
18709
18758
|
await requireRoleOrg(req, org);
|
|
18710
18759
|
}
|
|
18760
|
+
async function requireRoleWrite(req, role) {
|
|
18761
|
+
refuseSharedClientTemplate(role);
|
|
18762
|
+
await requireRoleOrg(req, role?.org);
|
|
18763
|
+
}
|
|
18711
18764
|
function useRoleController() {
|
|
18712
18765
|
const {
|
|
18713
18766
|
addRole: _createRole,
|
|
@@ -18745,6 +18798,10 @@ function useRoleController() {
|
|
|
18745
18798
|
if (payload.org) {
|
|
18746
18799
|
await requireOrgReach(req, payload.org);
|
|
18747
18800
|
}
|
|
18801
|
+
requireNoPlatformGrant(
|
|
18802
|
+
{ type: payload.type, org: payload.org, permissions: [] },
|
|
18803
|
+
payload.permissions
|
|
18804
|
+
);
|
|
18748
18805
|
const role = await _createRole(payload);
|
|
18749
18806
|
res.status(201).json({ message: "Successfully created role.", data: { role } });
|
|
18750
18807
|
return;
|
|
@@ -18848,7 +18905,8 @@ function useRoleController() {
|
|
|
18848
18905
|
const existing = await _getRoleById(_id);
|
|
18849
18906
|
if (!existing)
|
|
18850
18907
|
throw new NotFoundError14("Role not found.");
|
|
18851
|
-
await
|
|
18908
|
+
await requireRoleWrite(req, existing);
|
|
18909
|
+
requireNoPlatformGrant(existing, permissions);
|
|
18852
18910
|
const role = await _updateRole(_id, { name, permissions });
|
|
18853
18911
|
res.json({ message: "Successfully updated role.", data: { role } });
|
|
18854
18912
|
return;
|
|
@@ -18876,7 +18934,8 @@ function useRoleController() {
|
|
|
18876
18934
|
const existing = await _getRoleById(_id);
|
|
18877
18935
|
if (!existing)
|
|
18878
18936
|
throw new NotFoundError14("Role not found.");
|
|
18879
|
-
await
|
|
18937
|
+
await requireRoleWrite(req, existing);
|
|
18938
|
+
requireNoPlatformGrant(existing, permissions);
|
|
18880
18939
|
await _updatePermissionsById(_id, permissions);
|
|
18881
18940
|
res.json({ message: "Successfully updated role permissions." });
|
|
18882
18941
|
return;
|
|
@@ -18899,7 +18958,7 @@ function useRoleController() {
|
|
|
18899
18958
|
const existing = await _getRoleById(_id);
|
|
18900
18959
|
if (!existing)
|
|
18901
18960
|
throw new NotFoundError14("Role not found.");
|
|
18902
|
-
await
|
|
18961
|
+
await requireRoleWrite(req, existing);
|
|
18903
18962
|
const message = await _deleteRole(_id);
|
|
18904
18963
|
res.json({ message });
|
|
18905
18964
|
return;
|
|
@@ -18955,7 +19014,7 @@ function useRoleController() {
|
|
|
18955
19014
|
const existing = await _getRoleById(req.params.id);
|
|
18956
19015
|
if (!existing)
|
|
18957
19016
|
throw new NotFoundError14("Role not found.");
|
|
18958
|
-
await
|
|
19017
|
+
await requireRoleWrite(req, existing);
|
|
18959
19018
|
const message = await _deleteWithReassignments(
|
|
18960
19019
|
req.params.id,
|
|
18961
19020
|
req.body.reassignments
|
|
@@ -18988,12 +19047,12 @@ import { BadRequestError as BadRequestError49, logger as logger33 } from "@7365a
|
|
|
18988
19047
|
// src/services/member.service.ts
|
|
18989
19048
|
import {
|
|
18990
19049
|
BadRequestError as BadRequestError41,
|
|
18991
|
-
UnauthorizedError as
|
|
19050
|
+
UnauthorizedError as UnauthorizedError5,
|
|
18992
19051
|
useAtlas as useAtlas27
|
|
18993
19052
|
} from "@7365admin1/node-server-utils";
|
|
18994
19053
|
async function requireStaffCaller(callerId5, what) {
|
|
18995
19054
|
if (!await isSuperAdmin(callerId5)) {
|
|
18996
|
-
throw new
|
|
19055
|
+
throw new UnauthorizedError5(
|
|
18997
19056
|
`Only Seven365 staff can ${what}.`
|
|
18998
19057
|
);
|
|
18999
19058
|
}
|
|
@@ -19210,14 +19269,14 @@ function useMemberService() {
|
|
|
19210
19269
|
}
|
|
19211
19270
|
|
|
19212
19271
|
// src/utils/site-reach.util.ts
|
|
19213
|
-
import { useAtlas as useAtlas34, UnauthorizedError as
|
|
19272
|
+
import { useAtlas as useAtlas34, UnauthorizedError as UnauthorizedError7 } from "@7365admin1/node-server-utils";
|
|
19214
19273
|
import { ObjectId as ObjectId47 } from "mongodb";
|
|
19215
19274
|
|
|
19216
19275
|
// src/services/camera-view.service.ts
|
|
19217
19276
|
import {
|
|
19218
19277
|
BadRequestError as BadRequestError48,
|
|
19219
19278
|
NotFoundError as NotFoundError16,
|
|
19220
|
-
UnauthorizedError as
|
|
19279
|
+
UnauthorizedError as UnauthorizedError6,
|
|
19221
19280
|
logger as logger32,
|
|
19222
19281
|
useAtlas as useAtlas33,
|
|
19223
19282
|
useCache as useCache19
|
|
@@ -25664,7 +25723,7 @@ function useCameraViewService() {
|
|
|
25664
25723
|
}
|
|
25665
25724
|
async function authorizeCamera(params) {
|
|
25666
25725
|
if (!params.userId || !ObjectId46.isValid(params.userId)) {
|
|
25667
|
-
throw new
|
|
25726
|
+
throw new UnauthorizedError6("Not signed in.");
|
|
25668
25727
|
}
|
|
25669
25728
|
if (!ObjectId46.isValid(params.cameraId)) {
|
|
25670
25729
|
throw new BadRequestError48("Invalid camera id.");
|
|
@@ -25693,7 +25752,7 @@ function useCameraViewService() {
|
|
|
25693
25752
|
memberships
|
|
25694
25753
|
);
|
|
25695
25754
|
if (!hasAnyPermission(permissions, params.permissions)) {
|
|
25696
|
-
throw new
|
|
25755
|
+
throw new UnauthorizedError6(
|
|
25697
25756
|
"You do not have permission to use this camera."
|
|
25698
25757
|
);
|
|
25699
25758
|
}
|
|
@@ -25801,7 +25860,7 @@ function useCameraViewService() {
|
|
|
25801
25860
|
}
|
|
25802
25861
|
async function entitleSite(params) {
|
|
25803
25862
|
if (!params.userId || !ObjectId46.isValid(params.userId)) {
|
|
25804
|
-
throw new
|
|
25863
|
+
throw new UnauthorizedError6("Not signed in.");
|
|
25805
25864
|
}
|
|
25806
25865
|
if (!ObjectId46.isValid(params.siteId)) {
|
|
25807
25866
|
throw new BadRequestError48("Invalid site id.");
|
|
@@ -25833,7 +25892,7 @@ function useCameraViewService() {
|
|
|
25833
25892
|
);
|
|
25834
25893
|
const required = params.permissions ?? CAMERA_VIEW_PERMISSIONS;
|
|
25835
25894
|
if (!hasAnyPermission(permissions, required)) {
|
|
25836
|
-
throw new
|
|
25895
|
+
throw new UnauthorizedError6(
|
|
25837
25896
|
params.message ?? (required === CAMERA_VIEW_PERMISSIONS ? "You do not have permission to view these cameras." : "You do not have permission to set up cameras on this site.")
|
|
25838
25897
|
);
|
|
25839
25898
|
}
|
|
@@ -26242,7 +26301,7 @@ function idText(value) {
|
|
|
26242
26301
|
async function requireSiteReach(req, site, permission) {
|
|
26243
26302
|
const siteId = site?.toString?.() ?? "";
|
|
26244
26303
|
if (!siteId)
|
|
26245
|
-
throw new
|
|
26304
|
+
throw new UnauthorizedError7("Not authorized.");
|
|
26246
26305
|
const actor = await resolveInviteActor(callerId(req));
|
|
26247
26306
|
if (staffMayBypass(actor))
|
|
26248
26307
|
return;
|
|
@@ -27228,7 +27287,7 @@ function useVerificationController() {
|
|
|
27228
27287
|
// src/controllers/file.controller.ts
|
|
27229
27288
|
import {
|
|
27230
27289
|
BadRequestError as BadRequestError51,
|
|
27231
|
-
UnauthorizedError as
|
|
27290
|
+
UnauthorizedError as UnauthorizedError8,
|
|
27232
27291
|
logger as logger35
|
|
27233
27292
|
} from "@7365admin1/node-server-utils";
|
|
27234
27293
|
import Joi27 from "joi";
|
|
@@ -27257,7 +27316,7 @@ async function requireFileDelete(req, file) {
|
|
|
27257
27316
|
return;
|
|
27258
27317
|
const caller = callerId(req);
|
|
27259
27318
|
if (!caller)
|
|
27260
|
-
throw new
|
|
27319
|
+
throw new UnauthorizedError8("Not authorized.");
|
|
27261
27320
|
if (caller === ownerId)
|
|
27262
27321
|
return;
|
|
27263
27322
|
const [me, owner] = await Promise.all([
|
|
@@ -27273,7 +27332,7 @@ async function requireFileDelete(req, file) {
|
|
|
27273
27332
|
})) {
|
|
27274
27333
|
return;
|
|
27275
27334
|
}
|
|
27276
|
-
throw new
|
|
27335
|
+
throw new UnauthorizedError8("Not authorized.");
|
|
27277
27336
|
}
|
|
27278
27337
|
function useFileController() {
|
|
27279
27338
|
const { createFile, deleteFile: _deleteFile } = useFileService();
|
|
@@ -27346,7 +27405,7 @@ import {
|
|
|
27346
27405
|
BadRequestError as BadRequestError55,
|
|
27347
27406
|
logger as logger38,
|
|
27348
27407
|
NotFoundError as NotFoundError17,
|
|
27349
|
-
UnauthorizedError as
|
|
27408
|
+
UnauthorizedError as UnauthorizedError9
|
|
27350
27409
|
} from "@7365admin1/node-server-utils";
|
|
27351
27410
|
import Joi31 from "joi";
|
|
27352
27411
|
|
|
@@ -28619,7 +28678,7 @@ function useOrgController() {
|
|
|
28619
28678
|
try {
|
|
28620
28679
|
const createdBy = callerId(req);
|
|
28621
28680
|
if (!createdBy)
|
|
28622
|
-
throw new
|
|
28681
|
+
throw new UnauthorizedError9("Not authorized.");
|
|
28623
28682
|
const _id = await _add({ ...req.body, createdBy });
|
|
28624
28683
|
const data = await _getById(_id);
|
|
28625
28684
|
res.status(201).json({
|
|
@@ -31120,7 +31179,7 @@ function useSubscriptionService() {
|
|
|
31120
31179
|
import Joi36 from "joi";
|
|
31121
31180
|
import {
|
|
31122
31181
|
BadRequestError as BadRequestError71,
|
|
31123
|
-
UnauthorizedError as
|
|
31182
|
+
UnauthorizedError as UnauthorizedError10,
|
|
31124
31183
|
logger as logger48
|
|
31125
31184
|
} from "@7365admin1/node-server-utils";
|
|
31126
31185
|
|
|
@@ -31541,7 +31600,7 @@ function useSubscriptionController() {
|
|
|
31541
31600
|
try {
|
|
31542
31601
|
const subscription = await _getSubscriptionById(subscriptionId);
|
|
31543
31602
|
if (!subscription) {
|
|
31544
|
-
throw new
|
|
31603
|
+
throw new UnauthorizedError10("Not authorized.");
|
|
31545
31604
|
}
|
|
31546
31605
|
await requireOrgAccess(req, subscription.org?.toString());
|
|
31547
31606
|
const _res = await _updateSubscriptionSeats({
|
|
@@ -41873,7 +41932,7 @@ function useCustomerSiteService() {
|
|
|
41873
41932
|
import Joi59 from "joi";
|
|
41874
41933
|
import {
|
|
41875
41934
|
BadRequestError as BadRequestError107,
|
|
41876
|
-
UnauthorizedError as
|
|
41935
|
+
UnauthorizedError as UnauthorizedError11,
|
|
41877
41936
|
logger as logger78
|
|
41878
41937
|
} from "@7365admin1/node-server-utils";
|
|
41879
41938
|
async function requireEitherOrg(req, ...orgs) {
|
|
@@ -41882,7 +41941,7 @@ async function requireEitherOrg(req, ...orgs) {
|
|
|
41882
41941
|
if (staffMayBypass(actor))
|
|
41883
41942
|
return;
|
|
41884
41943
|
if (wanted.length === 0 || !wanted.some((org) => actor.orgIds.includes(org))) {
|
|
41885
|
-
throw new
|
|
41944
|
+
throw new UnauthorizedError11("Not authorized.");
|
|
41886
41945
|
}
|
|
41887
41946
|
}
|
|
41888
41947
|
function useCustomerSiteController() {
|
|
@@ -45094,7 +45153,7 @@ import {
|
|
|
45094
45153
|
BadRequestError as BadRequestError121,
|
|
45095
45154
|
InternalServerError as InternalServerError42,
|
|
45096
45155
|
NotFoundError as NotFoundError34,
|
|
45097
|
-
UnauthorizedError as
|
|
45156
|
+
UnauthorizedError as UnauthorizedError12,
|
|
45098
45157
|
logger as logger90
|
|
45099
45158
|
} from "@7365admin1/node-server-utils";
|
|
45100
45159
|
|
|
@@ -48628,8 +48687,8 @@ function useHidAmicoService() {
|
|
|
48628
48687
|
await siteAccess.authorizeSite({ siteId, userId: value.userId, permissions });
|
|
48629
48688
|
}
|
|
48630
48689
|
} catch (error) {
|
|
48631
|
-
if (error instanceof
|
|
48632
|
-
throw new
|
|
48690
|
+
if (error instanceof UnauthorizedError12) {
|
|
48691
|
+
throw new UnauthorizedError12("You do not have permission to manage HID access at this site.");
|
|
48633
48692
|
}
|
|
48634
48693
|
throw error;
|
|
48635
48694
|
}
|
|
@@ -48653,7 +48712,7 @@ function useHidAmicoService() {
|
|
|
48653
48712
|
try {
|
|
48654
48713
|
await authorizeSiteForCaller(String(reader.site || ""), userId);
|
|
48655
48714
|
} catch (error) {
|
|
48656
|
-
if (error instanceof
|
|
48715
|
+
if (error instanceof UnauthorizedError12)
|
|
48657
48716
|
throw error;
|
|
48658
48717
|
throw new NotFoundError34(HID_READER_NOT_FOUND);
|
|
48659
48718
|
}
|
|
@@ -48705,10 +48764,10 @@ function useHidAmicoService() {
|
|
|
48705
48764
|
async function assertUserReaderPermission(reader, userId, options = {}) {
|
|
48706
48765
|
const permission = await getReaderPermissionForUser(reader, userId);
|
|
48707
48766
|
if (!permission.granted) {
|
|
48708
|
-
throw new
|
|
48767
|
+
throw new UnauthorizedError12("You do not have HID access permission for this reader.");
|
|
48709
48768
|
}
|
|
48710
48769
|
if (options.intercom && !permission.intercom) {
|
|
48711
|
-
throw new
|
|
48770
|
+
throw new UnauthorizedError12("You do not have HID intercom permission for this reader.");
|
|
48712
48771
|
}
|
|
48713
48772
|
return permission;
|
|
48714
48773
|
}
|
|
@@ -50046,10 +50105,10 @@ function useHidAmicoService() {
|
|
|
50046
50105
|
const hasActiveMembership = memberships.some((membership) => membership.status !== "deleted" && membership.status !== "inactive");
|
|
50047
50106
|
const resolvedIssuer = issuer || (hasResidentMembership ? "resident" : "property_management");
|
|
50048
50107
|
if (resolvedIssuer === "resident" && !hasResidentMembership) {
|
|
50049
|
-
throw new
|
|
50108
|
+
throw new UnauthorizedError12("Only a Resident account can issue a Resident visitor QR code.");
|
|
50050
50109
|
}
|
|
50051
50110
|
if (resolvedIssuer === "property_management" && !hasActiveMembership) {
|
|
50052
|
-
throw new
|
|
50111
|
+
throw new UnauthorizedError12("Only a site Property Management account can issue this visitor QR code.");
|
|
50053
50112
|
}
|
|
50054
50113
|
const configuredGateIds = site?.metadata?.hidQrCodePass?.qrGatePermissions?.[resolvedIssuer === "resident" ? "resident" : "propertyManagement"] || [];
|
|
50055
50114
|
const gateReaderIds = configuredGateIds.filter((readerId) => typeof readerId === "string" && /^[a-f0-9]{24}$/i.test(readerId)).map(String);
|
|
@@ -52642,7 +52701,7 @@ function useVisitorTransactionService() {
|
|
|
52642
52701
|
import Joi68 from "joi";
|
|
52643
52702
|
import moment5 from "moment-timezone";
|
|
52644
52703
|
import { ObjectId as ObjectId90 } from "mongodb";
|
|
52645
|
-
import { BadRequestError as BadRequestError124, NotFoundError as NotFoundError36, InternalServerError as InternalServerError44, UnauthorizedError as
|
|
52704
|
+
import { BadRequestError as BadRequestError124, NotFoundError as NotFoundError36, InternalServerError as InternalServerError44, UnauthorizedError as UnauthorizedError13, logger as logger94 } from "@7365admin1/node-server-utils";
|
|
52646
52705
|
|
|
52647
52706
|
// src/models/visitor-invite.model.ts
|
|
52648
52707
|
import Joi66 from "joi";
|
|
@@ -57426,7 +57485,7 @@ function useVisitorTransactionController() {
|
|
|
57426
57485
|
}
|
|
57427
57486
|
const inviterUserId = callerId(req);
|
|
57428
57487
|
if (!inviterUserId) {
|
|
57429
|
-
next(new
|
|
57488
|
+
next(new UnauthorizedError13("Not authorized."));
|
|
57430
57489
|
return;
|
|
57431
57490
|
}
|
|
57432
57491
|
try {
|
|
@@ -57990,7 +58049,7 @@ function useGuestManagementController() {
|
|
|
57990
58049
|
// src/controllers/person.controller.ts
|
|
57991
58050
|
import Joi71 from "joi";
|
|
57992
58051
|
import { BadRequestError as BadRequestError127, logger as logger97 } from "@7365admin1/node-server-utils";
|
|
57993
|
-
import { NotFoundError as NotFoundError37, UnauthorizedError as
|
|
58052
|
+
import { NotFoundError as NotFoundError37, UnauthorizedError as UnauthorizedError14 } from "@7365admin1/node-server-utils";
|
|
57994
58053
|
function usePersonController() {
|
|
57995
58054
|
const {
|
|
57996
58055
|
getAll: _getAll,
|
|
@@ -58024,7 +58083,7 @@ function usePersonController() {
|
|
|
58024
58083
|
}
|
|
58025
58084
|
const org = person.org?.toString() ?? "";
|
|
58026
58085
|
if (!org || !actor.orgIds.includes(org)) {
|
|
58027
|
-
throw new
|
|
58086
|
+
throw new UnauthorizedError14("Not authorized.");
|
|
58028
58087
|
}
|
|
58029
58088
|
return actor;
|
|
58030
58089
|
}
|
|
@@ -58150,7 +58209,7 @@ function usePersonController() {
|
|
|
58150
58209
|
});
|
|
58151
58210
|
} else if (org) {
|
|
58152
58211
|
if (!actor.orgIds.includes(org)) {
|
|
58153
|
-
throw new
|
|
58212
|
+
throw new UnauthorizedError14("Not authorized.");
|
|
58154
58213
|
}
|
|
58155
58214
|
} else {
|
|
58156
58215
|
orgs = actor.orgIds;
|
|
@@ -62470,7 +62529,7 @@ function useSiteFacilityService() {
|
|
|
62470
62529
|
import {
|
|
62471
62530
|
BadRequestError as BadRequestError149,
|
|
62472
62531
|
NotFoundError as NotFoundError43,
|
|
62473
|
-
UnauthorizedError as
|
|
62532
|
+
UnauthorizedError as UnauthorizedError15,
|
|
62474
62533
|
logger as logger116
|
|
62475
62534
|
} from "@7365admin1/node-server-utils";
|
|
62476
62535
|
import Joi83 from "joi";
|
|
@@ -62557,7 +62616,7 @@ function useSiteFacilityController() {
|
|
|
62557
62616
|
} else {
|
|
62558
62617
|
const { actor } = await siteReachOf(req);
|
|
62559
62618
|
if (!actor.isSuperAdmin)
|
|
62560
|
-
throw new
|
|
62619
|
+
throw new UnauthorizedError15("Not authorized.");
|
|
62561
62620
|
}
|
|
62562
62621
|
const data = await _getAll({
|
|
62563
62622
|
search,
|
|
@@ -65592,7 +65651,7 @@ function useBulletinBoardService() {
|
|
|
65592
65651
|
import {
|
|
65593
65652
|
BadRequestError as BadRequestError158,
|
|
65594
65653
|
NotFoundError as NotFoundError51,
|
|
65595
|
-
UnauthorizedError as
|
|
65654
|
+
UnauthorizedError as UnauthorizedError16,
|
|
65596
65655
|
logger as logger125
|
|
65597
65656
|
} from "@7365admin1/node-server-utils";
|
|
65598
65657
|
import Joi91 from "joi";
|
|
@@ -65628,7 +65687,7 @@ function useBulletinBoardController() {
|
|
|
65628
65687
|
} else {
|
|
65629
65688
|
const { canReach } = await siteReachOf(req);
|
|
65630
65689
|
if (!await canReach(scopeOf(value))) {
|
|
65631
|
-
throw new
|
|
65690
|
+
throw new UnauthorizedError16("Not authorized.");
|
|
65632
65691
|
}
|
|
65633
65692
|
}
|
|
65634
65693
|
const data = await _add(value);
|
|
@@ -66185,7 +66244,7 @@ import { BadRequestError as BadRequestError164, logger as logger131 } from "@736
|
|
|
66185
66244
|
import {
|
|
66186
66245
|
BadRequestError as BadRequestError163,
|
|
66187
66246
|
logger as logger130,
|
|
66188
|
-
UnauthorizedError as
|
|
66247
|
+
UnauthorizedError as UnauthorizedError17,
|
|
66189
66248
|
useAtlas as useAtlas89
|
|
66190
66249
|
} from "@7365admin1/node-server-utils";
|
|
66191
66250
|
|
|
@@ -66569,7 +66628,7 @@ function useSiteBillingItemService() {
|
|
|
66569
66628
|
function assertUnitAtSite(buildingUnit, site) {
|
|
66570
66629
|
const siteId = site?.toString?.() ?? "";
|
|
66571
66630
|
if (!siteId || (buildingUnit?.site?.toString() ?? "") !== siteId) {
|
|
66572
|
-
throw new
|
|
66631
|
+
throw new UnauthorizedError17("Not authorized.");
|
|
66573
66632
|
}
|
|
66574
66633
|
}
|
|
66575
66634
|
async function add(value) {
|
|
@@ -66740,7 +66799,7 @@ function useSiteBillingItemService() {
|
|
|
66740
66799
|
message: error.message
|
|
66741
66800
|
});
|
|
66742
66801
|
await session.abortTransaction();
|
|
66743
|
-
if (error instanceof BadRequestError163 || error instanceof
|
|
66802
|
+
if (error instanceof BadRequestError163 || error instanceof UnauthorizedError17) {
|
|
66744
66803
|
throw error;
|
|
66745
66804
|
}
|
|
66746
66805
|
throw new BadRequestError163("Failed to update Site Billing Item.");
|
|
@@ -68709,7 +68768,7 @@ import Joi99 from "joi";
|
|
|
68709
68768
|
|
|
68710
68769
|
// src/utils/resident-unit.util.ts
|
|
68711
68770
|
import { ObjectId as ObjectId124 } from "mongodb";
|
|
68712
|
-
import { UnauthorizedError as
|
|
68771
|
+
import { UnauthorizedError as UnauthorizedError18, useAtlas as useAtlas95 } from "@7365admin1/node-server-utils";
|
|
68713
68772
|
async function requireOwnUnit(req, site, unitId) {
|
|
68714
68773
|
const actor = await resolveInviteActor(callerId(req));
|
|
68715
68774
|
if (staffMayBypass(actor))
|
|
@@ -68717,7 +68776,7 @@ async function requireOwnUnit(req, site, unitId) {
|
|
|
68717
68776
|
const unit = unitId?.toString?.() ?? "";
|
|
68718
68777
|
const siteId = site?.toString?.() ?? "";
|
|
68719
68778
|
if (!ObjectId124.isValid(unit) || !ObjectId124.isValid(siteId) || !actor.id) {
|
|
68720
|
-
throw new
|
|
68779
|
+
throw new UnauthorizedError18("Not authorized.");
|
|
68721
68780
|
}
|
|
68722
68781
|
const db2 = useAtlas95.getDb();
|
|
68723
68782
|
if (!db2)
|
|
@@ -68729,7 +68788,7 @@ async function requireOwnUnit(req, site, unitId) {
|
|
|
68729
68788
|
status: { $ne: "deleted" }
|
|
68730
68789
|
});
|
|
68731
68790
|
if (!own)
|
|
68732
|
-
throw new
|
|
68791
|
+
throw new UnauthorizedError18("Not authorized.");
|
|
68733
68792
|
}
|
|
68734
68793
|
|
|
68735
68794
|
// src/controllers/site-unit-billing.controller.ts
|
|
@@ -77281,7 +77340,7 @@ function useOnlineFormRepo() {
|
|
|
77281
77340
|
import {
|
|
77282
77341
|
BadRequestError as BadRequestError204,
|
|
77283
77342
|
NotFoundError as NotFoundError71,
|
|
77284
|
-
UnauthorizedError as
|
|
77343
|
+
UnauthorizedError as UnauthorizedError19,
|
|
77285
77344
|
logger as logger169
|
|
77286
77345
|
} from "@7365admin1/node-server-utils";
|
|
77287
77346
|
import Joi122 from "joi";
|
|
@@ -77353,10 +77412,10 @@ function useOnlineFormController() {
|
|
|
77353
77412
|
if (site) {
|
|
77354
77413
|
await requireSiteReach(req, site);
|
|
77355
77414
|
} else if (!staffMayBypass(actor)) {
|
|
77356
|
-
throw new
|
|
77415
|
+
throw new UnauthorizedError19("Not authorized.");
|
|
77357
77416
|
}
|
|
77358
77417
|
if (org && !staffMayBypass(actor) && !actor.orgIds.includes(org)) {
|
|
77359
|
-
throw new
|
|
77418
|
+
throw new UnauthorizedError19("Not authorized.");
|
|
77360
77419
|
}
|
|
77361
77420
|
const data = await _getAll({ search, page, limit, status, org, site });
|
|
77362
77421
|
res.json(data);
|
|
@@ -77421,7 +77480,7 @@ function useOnlineFormController() {
|
|
|
77421
77480
|
if (payload.org) {
|
|
77422
77481
|
const { actor } = await siteReachOf(req);
|
|
77423
77482
|
if (!staffMayBypass(actor) && !actor.orgIds.includes(payload.org)) {
|
|
77424
|
-
throw new
|
|
77483
|
+
throw new UnauthorizedError19("Not authorized.");
|
|
77425
77484
|
}
|
|
77426
77485
|
}
|
|
77427
77486
|
await _updateOnlineFormById(_id, payload);
|
|
@@ -84667,7 +84726,7 @@ import {
|
|
|
84667
84726
|
BadRequestError as BadRequestError229,
|
|
84668
84727
|
NotFoundError as NotFoundError78,
|
|
84669
84728
|
InternalServerError as InternalServerError81,
|
|
84670
|
-
UnauthorizedError as
|
|
84729
|
+
UnauthorizedError as UnauthorizedError20,
|
|
84671
84730
|
useAtlas as useAtlas132,
|
|
84672
84731
|
hashPassword as hashPassword4
|
|
84673
84732
|
} from "@7365admin1/node-server-utils";
|
|
@@ -84936,10 +84995,10 @@ function useVerificationServiceV2() {
|
|
|
84936
84995
|
const org = await getOrgById(orgId);
|
|
84937
84996
|
const actor = await resolveInviteActor(invitedBy);
|
|
84938
84997
|
if (!actor.id) {
|
|
84939
|
-
throw new
|
|
84998
|
+
throw new UnauthorizedError20("Sign in to continue.");
|
|
84940
84999
|
}
|
|
84941
85000
|
if (!staffMayBypass(actor) && !actor.orgIds.includes(orgId)) {
|
|
84942
|
-
throw new
|
|
85001
|
+
throw new UnauthorizedError20(
|
|
84943
85002
|
"You can only invite service providers to your own organisation's sites."
|
|
84944
85003
|
);
|
|
84945
85004
|
}
|
|
@@ -89356,7 +89415,7 @@ import Joi158 from "joi";
|
|
|
89356
89415
|
import { ObjectId as ObjectId179 } from "mongodb";
|
|
89357
89416
|
import {
|
|
89358
89417
|
InternalServerError as InternalServerError93,
|
|
89359
|
-
UnauthorizedError as
|
|
89418
|
+
UnauthorizedError as UnauthorizedError21,
|
|
89360
89419
|
useAtlas as useAtlas146
|
|
89361
89420
|
} from "@7365admin1/node-server-utils";
|
|
89362
89421
|
function db() {
|
|
@@ -89372,7 +89431,7 @@ function asObjectId(value) {
|
|
|
89372
89431
|
function requireCaller(req) {
|
|
89373
89432
|
const id = callerId(req);
|
|
89374
89433
|
if (!id || !ObjectId179.isValid(id)) {
|
|
89375
|
-
throw new
|
|
89434
|
+
throw new UnauthorizedError21("Not authorized.");
|
|
89376
89435
|
}
|
|
89377
89436
|
return id;
|
|
89378
89437
|
}
|
|
@@ -89380,7 +89439,7 @@ function requireSelfId(req, claimed) {
|
|
|
89380
89439
|
const caller = requireCaller(req);
|
|
89381
89440
|
const asked = claimed?.toString?.() ?? "";
|
|
89382
89441
|
if (!asked || asked !== caller) {
|
|
89383
|
-
throw new
|
|
89442
|
+
throw new UnauthorizedError21("Not authorized.");
|
|
89384
89443
|
}
|
|
89385
89444
|
return caller;
|
|
89386
89445
|
}
|
|
@@ -89388,11 +89447,11 @@ async function requireOwnChannel(req, channelId) {
|
|
|
89388
89447
|
const caller = requireCaller(req);
|
|
89389
89448
|
const _id = asObjectId(channelId);
|
|
89390
89449
|
if (!_id)
|
|
89391
|
-
throw new
|
|
89450
|
+
throw new UnauthorizedError21("Not authorized.");
|
|
89392
89451
|
const channel = await db().collection("channel-preloved").findOne({ _id }, { projection: { senderId: 1, receiverId: 1, postId: 1 } });
|
|
89393
89452
|
const participants = [channel?.senderId, channel?.receiverId].map((id) => id?.toString?.() ?? "").filter(Boolean);
|
|
89394
89453
|
if (!channel || !participants.includes(caller)) {
|
|
89395
|
-
throw new
|
|
89454
|
+
throw new UnauthorizedError21("Not authorized.");
|
|
89396
89455
|
}
|
|
89397
89456
|
return { caller, channel };
|
|
89398
89457
|
}
|
|
@@ -89400,10 +89459,10 @@ async function requireOwnChatMessage(req, chatId) {
|
|
|
89400
89459
|
const caller = requireCaller(req);
|
|
89401
89460
|
const _id = asObjectId(chatId);
|
|
89402
89461
|
if (!_id)
|
|
89403
|
-
throw new
|
|
89462
|
+
throw new UnauthorizedError21("Not authorized.");
|
|
89404
89463
|
const message = await db().collection("chat-preloved").findOne({ _id }, { projection: { senderId: 1 } });
|
|
89405
89464
|
if (!message || message.senderId?.toString?.() !== caller) {
|
|
89406
|
-
throw new
|
|
89465
|
+
throw new UnauthorizedError21("Not authorized.");
|
|
89407
89466
|
}
|
|
89408
89467
|
return caller;
|
|
89409
89468
|
}
|
|
@@ -89829,7 +89888,7 @@ function useBidPrelovedService() {
|
|
|
89829
89888
|
// src/controllers/bid-preloved.controller.ts
|
|
89830
89889
|
import {
|
|
89831
89890
|
BadRequestError as BadRequestError252,
|
|
89832
|
-
UnauthorizedError as
|
|
89891
|
+
UnauthorizedError as UnauthorizedError22,
|
|
89833
89892
|
logger as logger212
|
|
89834
89893
|
} from "@7365admin1/node-server-utils";
|
|
89835
89894
|
import Joi161 from "joi";
|
|
@@ -89881,7 +89940,7 @@ function useBidPrelovedController() {
|
|
|
89881
89940
|
const bid = await _getById(params.id);
|
|
89882
89941
|
const seller = await sellerOfPost(bid?.postId);
|
|
89883
89942
|
if (!seller || seller !== caller) {
|
|
89884
|
-
throw new
|
|
89943
|
+
throw new UnauthorizedError22("Not authorized.");
|
|
89885
89944
|
}
|
|
89886
89945
|
const data = await _updateStatus(params.id, body.status);
|
|
89887
89946
|
res.status(200).json(data);
|
|
@@ -89905,7 +89964,7 @@ function useBidPrelovedController() {
|
|
|
89905
89964
|
const data = await _getById(params.id);
|
|
89906
89965
|
const seller = await sellerOfPost(data?.postId);
|
|
89907
89966
|
if (data?.buyerId?.toString?.() !== caller && seller !== caller) {
|
|
89908
|
-
throw new
|
|
89967
|
+
throw new UnauthorizedError22("Not authorized.");
|
|
89909
89968
|
}
|
|
89910
89969
|
res.status(200).json(data);
|
|
89911
89970
|
} catch (error2) {
|
|
@@ -90303,7 +90362,7 @@ function useFormEntryRepo() {
|
|
|
90303
90362
|
import {
|
|
90304
90363
|
BadRequestError as BadRequestError254,
|
|
90305
90364
|
NotFoundError as NotFoundError89,
|
|
90306
|
-
UnauthorizedError as
|
|
90365
|
+
UnauthorizedError as UnauthorizedError23,
|
|
90307
90366
|
logger as logger214
|
|
90308
90367
|
} from "@7365admin1/node-server-utils";
|
|
90309
90368
|
import Joi163 from "joi";
|
|
@@ -90413,7 +90472,7 @@ function useFormEntryController() {
|
|
|
90413
90472
|
} else {
|
|
90414
90473
|
const { actor } = await siteReachOf(req);
|
|
90415
90474
|
if (!actor.isSuperAdmin)
|
|
90416
|
-
throw new
|
|
90475
|
+
throw new UnauthorizedError23("Not authorized.");
|
|
90417
90476
|
}
|
|
90418
90477
|
const data = await _getAll({ search, page, limit, status, org, site });
|
|
90419
90478
|
res.json(data);
|
|
@@ -90464,7 +90523,7 @@ function useFormEntryController() {
|
|
|
90464
90523
|
await requireSiteReach(req, rest.site);
|
|
90465
90524
|
delete rest.userId;
|
|
90466
90525
|
if (isOwner && rest.status && rest.status !== "pending") {
|
|
90467
|
-
throw new
|
|
90526
|
+
throw new UnauthorizedError23("Not authorized.");
|
|
90468
90527
|
}
|
|
90469
90528
|
await _updateFormEntryById(_id, rest);
|
|
90470
90529
|
res.json({ message: "Successfully updated online form." });
|
|
@@ -90521,7 +90580,7 @@ function useFormEntryController() {
|
|
|
90521
90580
|
async function residentFormSubmission(req, res, next) {
|
|
90522
90581
|
const userId = callerId(req);
|
|
90523
90582
|
if (!userId) {
|
|
90524
|
-
next(new
|
|
90583
|
+
next(new UnauthorizedError23("Not authorized."));
|
|
90525
90584
|
return;
|
|
90526
90585
|
}
|
|
90527
90586
|
const payload = { ...req.body, userId };
|
|
@@ -91752,7 +91811,7 @@ function useHidAmicoController() {
|
|
|
91752
91811
|
import {
|
|
91753
91812
|
BadRequestError as BadRequestError257,
|
|
91754
91813
|
logger as logger216,
|
|
91755
|
-
UnauthorizedError as
|
|
91814
|
+
UnauthorizedError as UnauthorizedError24
|
|
91756
91815
|
} from "@7365admin1/node-server-utils";
|
|
91757
91816
|
import Joi165 from "joi";
|
|
91758
91817
|
function usePlatformTermsController() {
|
|
@@ -91768,7 +91827,7 @@ function usePlatformTermsController() {
|
|
|
91768
91827
|
async function requireOwnRecord(req, user, { staffMayRead = false } = {}) {
|
|
91769
91828
|
const id = callerId(req);
|
|
91770
91829
|
if (!id) {
|
|
91771
|
-
throw new
|
|
91830
|
+
throw new UnauthorizedError24("Not authorized.");
|
|
91772
91831
|
}
|
|
91773
91832
|
if (id === user) {
|
|
91774
91833
|
return id;
|
|
@@ -91776,7 +91835,7 @@ function usePlatformTermsController() {
|
|
|
91776
91835
|
if (staffMayRead) {
|
|
91777
91836
|
return await requireConsolePermission(req, "platform-terms", "see-platform-terms");
|
|
91778
91837
|
}
|
|
91779
|
-
throw new
|
|
91838
|
+
throw new UnauthorizedError24("Not authorized.");
|
|
91780
91839
|
}
|
|
91781
91840
|
async function getLatest(_req, res, next) {
|
|
91782
91841
|
try {
|
|
@@ -91982,7 +92041,7 @@ function useConsoleAuditController() {
|
|
|
91982
92041
|
// src/controllers/notification.controller.ts
|
|
91983
92042
|
import {
|
|
91984
92043
|
BadRequestError as BadRequestError259,
|
|
91985
|
-
UnauthorizedError as
|
|
92044
|
+
UnauthorizedError as UnauthorizedError25,
|
|
91986
92045
|
logger as logger218
|
|
91987
92046
|
} from "@7365admin1/node-server-utils";
|
|
91988
92047
|
function useNotificationController() {
|
|
@@ -92000,10 +92059,10 @@ function useNotificationController() {
|
|
|
92000
92059
|
function inboxOwner(req, claimed) {
|
|
92001
92060
|
const caller = callerId5(req);
|
|
92002
92061
|
if (!caller) {
|
|
92003
|
-
throw new
|
|
92062
|
+
throw new UnauthorizedError25("Not authorized.");
|
|
92004
92063
|
}
|
|
92005
92064
|
if (claimed && claimed !== caller) {
|
|
92006
|
-
throw new
|
|
92065
|
+
throw new UnauthorizedError25("Not authorized.");
|
|
92007
92066
|
}
|
|
92008
92067
|
return caller;
|
|
92009
92068
|
}
|
|
@@ -92015,7 +92074,7 @@ function useNotificationController() {
|
|
|
92015
92074
|
}
|
|
92016
92075
|
try {
|
|
92017
92076
|
if (!await isSuperAdmin(callerId5(req))) {
|
|
92018
|
-
throw new
|
|
92077
|
+
throw new UnauthorizedError25("Not authorized.");
|
|
92019
92078
|
}
|
|
92020
92079
|
const inserted = await _addMany(value.to, {
|
|
92021
92080
|
title: value.title,
|
|
@@ -92182,8 +92241,10 @@ function useNotificationPreferenceService() {
|
|
|
92182
92241
|
categoriesForUser(id)
|
|
92183
92242
|
]);
|
|
92184
92243
|
const isOff = (category, channel) => off.some((o) => o.category === category && o.channel === channel);
|
|
92244
|
+
const supportedIn = (category) => NOTIFICATION_CHANNELS.filter((channel) => category.channels[channel]);
|
|
92245
|
+
const offered = new Set(categories.flatMap((c) => supportedIn(c)));
|
|
92185
92246
|
return {
|
|
92186
|
-
channels: NOTIFICATION_CHANNELS.map((c) => ({
|
|
92247
|
+
channels: NOTIFICATION_CHANNELS.filter((c) => offered.has(c)).map((c) => ({
|
|
92187
92248
|
key: c,
|
|
92188
92249
|
label: NOTIFICATION_CHANNEL_LABELS[c]
|
|
92189
92250
|
})),
|
|
@@ -92192,16 +92253,14 @@ function useNotificationPreferenceService() {
|
|
|
92192
92253
|
label: category.label,
|
|
92193
92254
|
description: category.description,
|
|
92194
92255
|
safety: Boolean(category.safety),
|
|
92195
|
-
channels:
|
|
92196
|
-
|
|
92197
|
-
|
|
92198
|
-
|
|
92199
|
-
|
|
92200
|
-
|
|
92201
|
-
|
|
92202
|
-
|
|
92203
|
-
};
|
|
92204
|
-
})
|
|
92256
|
+
channels: supportedIn(category).map((channel) => ({
|
|
92257
|
+
key: channel,
|
|
92258
|
+
label: NOTIFICATION_CHANNEL_LABELS[channel],
|
|
92259
|
+
// kept so a client written against the old shape still filters
|
|
92260
|
+
// correctly; every channel sent is now a supported one.
|
|
92261
|
+
supported: true,
|
|
92262
|
+
enabled: !isOff(category.key, channel)
|
|
92263
|
+
}))
|
|
92205
92264
|
}))
|
|
92206
92265
|
};
|
|
92207
92266
|
}
|