@7365admin1/core 3.63.0 → 3.64.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 +32 -0
- package/dist/index.d.ts +134 -119
- package/dist/index.js +5512 -5341
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +576 -406
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
- package/test/org-modules.test.mjs +22 -3
- package/test/resident-app-modules.test.mjs +197 -0
- package/test/site-modules.test.mjs +151 -0
package/dist/index.mjs
CHANGED
|
@@ -10513,7 +10513,7 @@ import {
|
|
|
10513
10513
|
BadRequestError as BadRequestError31,
|
|
10514
10514
|
NotFoundError as NotFoundError11,
|
|
10515
10515
|
InternalServerError as InternalServerError15,
|
|
10516
|
-
UnauthorizedError as
|
|
10516
|
+
UnauthorizedError as UnauthorizedError4,
|
|
10517
10517
|
useAtlas as useAtlas21
|
|
10518
10518
|
} from "@7365admin1/node-server-utils";
|
|
10519
10519
|
|
|
@@ -10948,9 +10948,9 @@ var SUBSCRIPTION_AUTO_SUSPEND_ENABLED = process.env.SUBSCRIPTION_AUTO_SUSPEND_EN
|
|
|
10948
10948
|
// src/repositories/organization.repo.ts
|
|
10949
10949
|
import {
|
|
10950
10950
|
AppError as AppError3,
|
|
10951
|
-
BadRequestError as
|
|
10951
|
+
BadRequestError as BadRequestError20,
|
|
10952
10952
|
InternalServerError as InternalServerError9,
|
|
10953
|
-
logger as
|
|
10953
|
+
logger as logger10,
|
|
10954
10954
|
makeCacheKey as makeCacheKey7,
|
|
10955
10955
|
NotFoundError as NotFoundError7,
|
|
10956
10956
|
paginate as paginate7,
|
|
@@ -10959,9 +10959,9 @@ import {
|
|
|
10959
10959
|
} from "@7365admin1/node-server-utils";
|
|
10960
10960
|
|
|
10961
10961
|
// src/models/organization.model.ts
|
|
10962
|
-
import { BadRequestError as
|
|
10963
|
-
import
|
|
10964
|
-
import { ObjectId as
|
|
10962
|
+
import { BadRequestError as BadRequestError19 } from "@7365admin1/node-server-utils";
|
|
10963
|
+
import Joi10 from "joi";
|
|
10964
|
+
import { ObjectId as ObjectId19 } from "mongodb";
|
|
10965
10965
|
|
|
10966
10966
|
// src/utils/org-modules.util.ts
|
|
10967
10967
|
import Joi8 from "joi";
|
|
@@ -10981,6 +10981,285 @@ function normaliseModules(modules) {
|
|
|
10981
10981
|
}
|
|
10982
10982
|
return out;
|
|
10983
10983
|
}
|
|
10984
|
+
function orgAllowsModule(modules, resource) {
|
|
10985
|
+
if (!Array.isArray(modules) || modules.length === 0)
|
|
10986
|
+
return true;
|
|
10987
|
+
const key = typeof resource === "string" ? resource.trim() : "";
|
|
10988
|
+
if (!key)
|
|
10989
|
+
return true;
|
|
10990
|
+
return modules.includes(key);
|
|
10991
|
+
}
|
|
10992
|
+
|
|
10993
|
+
// src/models/site.model.ts
|
|
10994
|
+
import {
|
|
10995
|
+
BadRequestError as BadRequestError18,
|
|
10996
|
+
logger as logger9
|
|
10997
|
+
} from "@7365admin1/node-server-utils";
|
|
10998
|
+
import Joi9 from "joi";
|
|
10999
|
+
|
|
11000
|
+
// src/utils/site-modules.util.ts
|
|
11001
|
+
import { UnauthorizedError } from "@7365admin1/node-server-utils";
|
|
11002
|
+
var siteModulesSchema = modulesSchema;
|
|
11003
|
+
function narrows(modules) {
|
|
11004
|
+
return Array.isArray(modules) && modules.length > 0;
|
|
11005
|
+
}
|
|
11006
|
+
function effectiveSiteModules(orgModules, siteModules) {
|
|
11007
|
+
const org = narrows(orgModules) ? normaliseModules(orgModules) : [];
|
|
11008
|
+
const site = narrows(siteModules) ? normaliseModules(siteModules) : [];
|
|
11009
|
+
if (!org.length)
|
|
11010
|
+
return site;
|
|
11011
|
+
if (!site.length)
|
|
11012
|
+
return org;
|
|
11013
|
+
const allowed = site.filter((key) => org.includes(key));
|
|
11014
|
+
return allowed.length ? allowed : org;
|
|
11015
|
+
}
|
|
11016
|
+
function rejectedSiteModules(orgModules, requested) {
|
|
11017
|
+
if (!narrows(orgModules))
|
|
11018
|
+
return [];
|
|
11019
|
+
const org = normaliseModules(orgModules);
|
|
11020
|
+
return normaliseModules(requested).filter((key) => !org.includes(key));
|
|
11021
|
+
}
|
|
11022
|
+
function rejectedSiteModulesMessage(rejected) {
|
|
11023
|
+
return `This organisation has not been given ${rejected.join(", ")}, so a site cannot offer it. Ask Seven365 to add it to the organisation first.`;
|
|
11024
|
+
}
|
|
11025
|
+
function permissionsOutsideModules(effective, next, previous) {
|
|
11026
|
+
if (!narrows(effective))
|
|
11027
|
+
return [];
|
|
11028
|
+
if (!Array.isArray(next) || !next.length || next.includes("*"))
|
|
11029
|
+
return [];
|
|
11030
|
+
const held = new Set(Array.isArray(previous) ? previous : []);
|
|
11031
|
+
const allowed = normaliseModules(effective);
|
|
11032
|
+
const refused = [];
|
|
11033
|
+
for (const permission of next) {
|
|
11034
|
+
if (typeof permission !== "string")
|
|
11035
|
+
continue;
|
|
11036
|
+
if (held.has(permission))
|
|
11037
|
+
continue;
|
|
11038
|
+
const resource = permission.split(":")[0]?.trim();
|
|
11039
|
+
if (!resource)
|
|
11040
|
+
continue;
|
|
11041
|
+
if (!orgAllowsModule(allowed, resource))
|
|
11042
|
+
refused.push(permission);
|
|
11043
|
+
}
|
|
11044
|
+
return refused;
|
|
11045
|
+
}
|
|
11046
|
+
function requireWithinModules(effective, next, previous) {
|
|
11047
|
+
const refused = permissionsOutsideModules(effective, next, previous);
|
|
11048
|
+
if (!refused.length)
|
|
11049
|
+
return;
|
|
11050
|
+
throw new UnauthorizedError(
|
|
11051
|
+
"This site has not been given " + refused.join(", ") + ". Ask Seven365 to add the module to the organisation first."
|
|
11052
|
+
);
|
|
11053
|
+
}
|
|
11054
|
+
|
|
11055
|
+
// src/models/site.model.ts
|
|
11056
|
+
import { ObjectId as ObjectId18 } from "mongodb";
|
|
11057
|
+
|
|
11058
|
+
// src/utils/site-timezone.util.ts
|
|
11059
|
+
import moment from "moment-timezone";
|
|
11060
|
+
var DEFAULT_SITE_TIMEZONE = "Asia/Singapore";
|
|
11061
|
+
function isValidTimezone(tz) {
|
|
11062
|
+
return typeof tz === "string" && tz.length > 0 && !!moment.tz.zone(tz);
|
|
11063
|
+
}
|
|
11064
|
+
function resolveSiteTimezone(site) {
|
|
11065
|
+
const tz = site?.timezone;
|
|
11066
|
+
return isValidTimezone(tz) ? tz : DEFAULT_SITE_TIMEZONE;
|
|
11067
|
+
}
|
|
11068
|
+
function siteDayBounds(value, timezone) {
|
|
11069
|
+
if (!value)
|
|
11070
|
+
return null;
|
|
11071
|
+
const tz = isValidTimezone(timezone) ? timezone : DEFAULT_SITE_TIMEZONE;
|
|
11072
|
+
const isDateOnly = typeof value === "string" && /^\d{4}-\d{2}-\d{2}$/.test(value);
|
|
11073
|
+
const m = isDateOnly ? moment.tz(value, "YYYY-MM-DD", tz) : typeof value === "string" ? moment.tz(value, moment.ISO_8601, tz) : moment.tz(value, tz);
|
|
11074
|
+
if (!m.isValid())
|
|
11075
|
+
return null;
|
|
11076
|
+
return {
|
|
11077
|
+
start: m.clone().startOf("day").toDate(),
|
|
11078
|
+
end: m.clone().endOf("day").toDate(),
|
|
11079
|
+
date: m.format("YYYY-MM-DD")
|
|
11080
|
+
};
|
|
11081
|
+
}
|
|
11082
|
+
|
|
11083
|
+
// src/models/site.model.ts
|
|
11084
|
+
function siteTimezoneValidator(value, helpers) {
|
|
11085
|
+
return isValidTimezone(value) ? value : helpers.error("any.invalid");
|
|
11086
|
+
}
|
|
11087
|
+
var addressSchema = Joi9.object({
|
|
11088
|
+
line1: Joi9.string().min(3).required(),
|
|
11089
|
+
line2: Joi9.string().allow("", null),
|
|
11090
|
+
city: Joi9.string().min(2).required(),
|
|
11091
|
+
state: Joi9.string().allow("", null),
|
|
11092
|
+
postalCode: Joi9.string().min(3).required(),
|
|
11093
|
+
country: Joi9.string().min(2).required()
|
|
11094
|
+
});
|
|
11095
|
+
var hidQrCodePassSchema = Joi9.object({
|
|
11096
|
+
enabled: Joi9.boolean().required(),
|
|
11097
|
+
onlineMode: Joi9.boolean().optional(),
|
|
11098
|
+
readerId: Joi9.string().hex().length(24).allow("").required(),
|
|
11099
|
+
qrGatePermissions: Joi9.object({
|
|
11100
|
+
resident: Joi9.array().items(Joi9.string().hex().length(24)).unique().default([]),
|
|
11101
|
+
propertyManagement: Joi9.array().items(Joi9.string().hex().length(24)).unique().default([])
|
|
11102
|
+
}).optional(),
|
|
11103
|
+
qrFormat: Joi9.string().valid("0", "1", "2").required(),
|
|
11104
|
+
identificationMethods: Joi9.object({
|
|
11105
|
+
facial: Joi9.boolean().required(),
|
|
11106
|
+
card: Joi9.boolean().required(),
|
|
11107
|
+
qrCode: Joi9.boolean().required(),
|
|
11108
|
+
idPassword: Joi9.boolean().required(),
|
|
11109
|
+
pin: Joi9.boolean().required(),
|
|
11110
|
+
bluetooth: Joi9.boolean().required()
|
|
11111
|
+
}).optional(),
|
|
11112
|
+
printer: Joi9.object({
|
|
11113
|
+
vendorId: Joi9.string().allow("").required(),
|
|
11114
|
+
productId: Joi9.string().allow("").required()
|
|
11115
|
+
}).required(),
|
|
11116
|
+
template: Joi9.object({
|
|
11117
|
+
header: Joi9.string().allow("").required(),
|
|
11118
|
+
subtext: Joi9.string().allow("").required()
|
|
11119
|
+
}).required(),
|
|
11120
|
+
validityMinutes: Joi9.number().integer().min(1).allow(null).required(),
|
|
11121
|
+
updatedAt: Joi9.string().isoDate().required()
|
|
11122
|
+
});
|
|
11123
|
+
var residentAppModulesSchema = Joi9.object({
|
|
11124
|
+
feedback: Joi9.boolean(),
|
|
11125
|
+
bulletin: Joi9.boolean(),
|
|
11126
|
+
bulletinVideos: Joi9.boolean(),
|
|
11127
|
+
inviteVisitors: Joi9.boolean(),
|
|
11128
|
+
myVisitors: Joi9.boolean(),
|
|
11129
|
+
billingAndSoa: Joi9.boolean(),
|
|
11130
|
+
access: Joi9.boolean(),
|
|
11131
|
+
facilityBooking: Joi9.boolean(),
|
|
11132
|
+
onlineForm: Joi9.boolean(),
|
|
11133
|
+
emergencyContact: Joi9.boolean()
|
|
11134
|
+
});
|
|
11135
|
+
var metadataSchema3 = Joi9.object({
|
|
11136
|
+
/**
|
|
11137
|
+
* The catalogue modules THIS SITE offers — tier two of the cascade.
|
|
11138
|
+
*
|
|
11139
|
+
* Owner requirement R1: whatever Seven365 gives an organisation is the only
|
|
11140
|
+
* set that organisation may then give to each site it creates. The
|
|
11141
|
+
* organisation's own list is `organizations.modules`; this narrows it per
|
|
11142
|
+
* site, and `site-modules.util.ts` holds both the read (fails open) and the
|
|
11143
|
+
* write rule (refuses a key the organisation was never given).
|
|
11144
|
+
*
|
|
11145
|
+
* ABSENT AND EMPTY BOTH MEAN "everything the organisation has" — never
|
|
11146
|
+
* "nothing". No site has this field today.
|
|
11147
|
+
*
|
|
11148
|
+
* It lives in `metadata` because `MSite` is a whitelist: a new top-level
|
|
11149
|
+
* field is silently dropped, while `metadata` is passed through wholesale.
|
|
11150
|
+
* `residentAppModules` below is the same pattern for the same reason.
|
|
11151
|
+
*/
|
|
11152
|
+
modules: siteModulesSchema.optional(),
|
|
11153
|
+
block: Joi9.number().optional(),
|
|
11154
|
+
guardPosts: Joi9.number().optional(),
|
|
11155
|
+
gracePeriod: Joi9.number().optional(),
|
|
11156
|
+
incidentCounter: Joi9.number().optional(),
|
|
11157
|
+
incidentLogo: Joi9.string().optional().allow("", null),
|
|
11158
|
+
services: Joi9.array().items(Joi9.object().unknown(true)).optional(),
|
|
11159
|
+
residentAppModules: residentAppModulesSchema.optional(),
|
|
11160
|
+
hidQrCodePass: hidQrCodePassSchema.optional()
|
|
11161
|
+
});
|
|
11162
|
+
var residentAppModuleKeys = [
|
|
11163
|
+
"feedback",
|
|
11164
|
+
"bulletin",
|
|
11165
|
+
"bulletinVideos",
|
|
11166
|
+
"inviteVisitors",
|
|
11167
|
+
"myVisitors",
|
|
11168
|
+
"billingAndSoa",
|
|
11169
|
+
"access",
|
|
11170
|
+
"facilityBooking",
|
|
11171
|
+
"onlineForm",
|
|
11172
|
+
"emergencyContact"
|
|
11173
|
+
];
|
|
11174
|
+
var updateAddressSchema = Joi9.object({
|
|
11175
|
+
line1: Joi9.string().min(3).allow("", null),
|
|
11176
|
+
line2: Joi9.string().allow("", null),
|
|
11177
|
+
city: Joi9.string().min(2).allow("", null),
|
|
11178
|
+
state: Joi9.string().allow("", null),
|
|
11179
|
+
postalCode: Joi9.string().min(3).allow("", null),
|
|
11180
|
+
country: Joi9.string().min(2).allow("", null)
|
|
11181
|
+
});
|
|
11182
|
+
var SiteStatus = /* @__PURE__ */ ((SiteStatus2) => {
|
|
11183
|
+
SiteStatus2["ACTIVE"] = "active";
|
|
11184
|
+
SiteStatus2["PENDING"] = "pending";
|
|
11185
|
+
return SiteStatus2;
|
|
11186
|
+
})(SiteStatus || {});
|
|
11187
|
+
var SiteCategories = /* @__PURE__ */ ((SiteCategories2) => {
|
|
11188
|
+
SiteCategories2["RESIDENTIAL"] = "residential";
|
|
11189
|
+
SiteCategories2["COMMERCIAL"] = "commercial";
|
|
11190
|
+
SiteCategories2["INDUSTRIAL"] = "industrial";
|
|
11191
|
+
SiteCategories2["MIXED_DEVELOPMENT"] = "mixed_development";
|
|
11192
|
+
SiteCategories2["INSTITUTIONAL"] = "institutional";
|
|
11193
|
+
SiteCategories2["INFRASTRUCTURE"] = "infrastructure";
|
|
11194
|
+
SiteCategories2["HOSPITALITY"] = "hospitality";
|
|
11195
|
+
return SiteCategories2;
|
|
11196
|
+
})(SiteCategories || {});
|
|
11197
|
+
var allowedFieldsSite = [
|
|
11198
|
+
"metadata.block",
|
|
11199
|
+
"metadata.guardPosts",
|
|
11200
|
+
"metadata.gracePeriod",
|
|
11201
|
+
"metadata.incidentCounter",
|
|
11202
|
+
"metadata.incidentLogo",
|
|
11203
|
+
"metadata.services",
|
|
11204
|
+
"metadata.hidQrCodePass"
|
|
11205
|
+
];
|
|
11206
|
+
var siteSchema = Joi9.object({
|
|
11207
|
+
name: Joi9.string().required(),
|
|
11208
|
+
description: Joi9.string().optional().allow("", null),
|
|
11209
|
+
orgId: Joi9.string().hex().required(),
|
|
11210
|
+
customerId: Joi9.string().hex().optional().allow("", null),
|
|
11211
|
+
address: addressSchema.optional(),
|
|
11212
|
+
timezone: Joi9.string().custom(siteTimezoneValidator, "IANA timezone").optional().default(DEFAULT_SITE_TIMEZONE),
|
|
11213
|
+
category: Joi9.string().valid(...Object.values(SiteCategories)).default("commercial" /* COMMERCIAL */),
|
|
11214
|
+
deliveryCompanyList: Joi9.array().items(Joi9.string()).optional().allow(null),
|
|
11215
|
+
isOpenGate: Joi9.boolean().optional().default(false),
|
|
11216
|
+
dahuaTimeExpiration: Joi9.number().optional().allow(null),
|
|
11217
|
+
siteDocs: Joi9.array().items(Joi9.string().hex().length(24).allow(null, "")),
|
|
11218
|
+
unitDocs: Joi9.array().items(Joi9.string().hex().length(24).allow(null, ""))
|
|
11219
|
+
});
|
|
11220
|
+
var updateSiteSchema = Joi9.object({
|
|
11221
|
+
_id: Joi9.string().hex().length(24).required(),
|
|
11222
|
+
address: updateAddressSchema.optional(),
|
|
11223
|
+
timezone: Joi9.string().custom(siteTimezoneValidator, "IANA timezone").optional(),
|
|
11224
|
+
metadata: metadataSchema3.optional(),
|
|
11225
|
+
deliveryCompanyList: Joi9.array().items(Joi9.string().trim()).optional().allow(null),
|
|
11226
|
+
isOpenGate: Joi9.boolean().optional().allow(null),
|
|
11227
|
+
dahuaTimeExpiration: Joi9.number().optional().allow(null),
|
|
11228
|
+
siteDocs: Joi9.array().items(Joi9.string().hex().length(24).allow(null, "")),
|
|
11229
|
+
unitDocs: Joi9.array().items(Joi9.string().hex().length(24).allow(null, ""))
|
|
11230
|
+
});
|
|
11231
|
+
function MSite(value) {
|
|
11232
|
+
const { error } = siteSchema.validate(value);
|
|
11233
|
+
if (error) {
|
|
11234
|
+
logger9.log({ level: "error", message: error.message });
|
|
11235
|
+
throw new BadRequestError18(error.message);
|
|
11236
|
+
}
|
|
11237
|
+
if (value.orgId) {
|
|
11238
|
+
try {
|
|
11239
|
+
value.orgId = new ObjectId18(value.orgId);
|
|
11240
|
+
} catch (error2) {
|
|
11241
|
+
throw new BadRequestError18("Invalid org ID format.");
|
|
11242
|
+
}
|
|
11243
|
+
}
|
|
11244
|
+
return {
|
|
11245
|
+
name: value.name ?? "",
|
|
11246
|
+
description: value.description ?? "",
|
|
11247
|
+
orgId: value.orgId ?? "",
|
|
11248
|
+
metadata: value.metadata ?? {},
|
|
11249
|
+
status: "active",
|
|
11250
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
11251
|
+
address: value.address,
|
|
11252
|
+
timezone: value.timezone ?? DEFAULT_SITE_TIMEZONE,
|
|
11253
|
+
category: value.category,
|
|
11254
|
+
deliveryCompanyList: value.deliveryCompanyList ?? [],
|
|
11255
|
+
isOpenGate: value.isOpenGate,
|
|
11256
|
+
dahuaTimeExpiration: value.dahuaTimeExpiration,
|
|
11257
|
+
siteDocs: value.siteDocs ?? [],
|
|
11258
|
+
unitDocs: value.unitDocs ?? [],
|
|
11259
|
+
updatedAt: value.updatedAt ?? "",
|
|
11260
|
+
deletedAt: value.deletedAt ?? ""
|
|
11261
|
+
};
|
|
11262
|
+
}
|
|
10984
11263
|
|
|
10985
11264
|
// src/models/organization.model.ts
|
|
10986
11265
|
var allowedNatures = [
|
|
@@ -10993,38 +11272,39 @@ var allowedNatures = [
|
|
|
10993
11272
|
"pest_control_services",
|
|
10994
11273
|
"pool_maintenance_services"
|
|
10995
11274
|
];
|
|
10996
|
-
var orgSchema =
|
|
10997
|
-
_id:
|
|
10998
|
-
name:
|
|
10999
|
-
description:
|
|
11000
|
-
type:
|
|
11001
|
-
nature:
|
|
11275
|
+
var orgSchema = Joi10.object({
|
|
11276
|
+
_id: Joi10.string().hex().optional().allow("", null),
|
|
11277
|
+
name: Joi10.string().required(),
|
|
11278
|
+
description: Joi10.string().optional().allow("", null),
|
|
11279
|
+
type: Joi10.string().required(),
|
|
11280
|
+
nature: Joi10.string().valid(...allowedNatures).required(),
|
|
11002
11281
|
modules: modulesSchema.optional(),
|
|
11003
|
-
|
|
11004
|
-
|
|
11005
|
-
|
|
11006
|
-
|
|
11007
|
-
|
|
11008
|
-
|
|
11009
|
-
|
|
11010
|
-
|
|
11011
|
-
|
|
11012
|
-
|
|
11013
|
-
|
|
11014
|
-
|
|
11015
|
-
|
|
11016
|
-
|
|
11282
|
+
residentAppModules: residentAppModulesSchema.optional(),
|
|
11283
|
+
email: Joi10.string().email().required(),
|
|
11284
|
+
contact: Joi10.string().optional().allow("", null),
|
|
11285
|
+
country: Joi10.string().optional().allow("", null),
|
|
11286
|
+
busInst: Joi10.string().optional().allow("", null),
|
|
11287
|
+
status: Joi10.string().optional().allow("", null),
|
|
11288
|
+
defaultSite: Joi10.string().hex().optional().allow("", null),
|
|
11289
|
+
createdBy: Joi10.string().hex().optional().allow("", null),
|
|
11290
|
+
invitedFrom: Joi10.string().hex().optional().allow("", null),
|
|
11291
|
+
invitedRole: Joi10.string().hex().optional().allow("", null),
|
|
11292
|
+
terms: Joi10.string().optional().allow("", null),
|
|
11293
|
+
policies: Joi10.string().optional().allow("", null),
|
|
11294
|
+
createdAt: Joi10.string().optional().allow("", null),
|
|
11295
|
+
updatedAt: Joi10.string().optional().allow("", null),
|
|
11296
|
+
deletedAt: Joi10.string().optional().allow("", null)
|
|
11017
11297
|
});
|
|
11018
11298
|
function MOrg(value) {
|
|
11019
11299
|
const { error } = orgSchema.validate(value);
|
|
11020
11300
|
if (error) {
|
|
11021
|
-
throw new
|
|
11301
|
+
throw new BadRequestError19(error.message);
|
|
11022
11302
|
}
|
|
11023
11303
|
if (value._id) {
|
|
11024
11304
|
try {
|
|
11025
|
-
value._id = new
|
|
11305
|
+
value._id = new ObjectId19(value._id);
|
|
11026
11306
|
} catch (error2) {
|
|
11027
|
-
throw new
|
|
11307
|
+
throw new BadRequestError19("Invalid organization ID format.");
|
|
11028
11308
|
}
|
|
11029
11309
|
}
|
|
11030
11310
|
return {
|
|
@@ -11037,15 +11317,20 @@ function MOrg(value) {
|
|
|
11037
11317
|
// deliberate empty list to any later reader, and would put the field on all
|
|
11038
11318
|
// 168 live organisations for no reason.
|
|
11039
11319
|
...value.modules === void 0 ? {} : { modules: normaliseModules(value.modules) },
|
|
11320
|
+
// Same rule for the resident app's own tier (R4). Absent stays ABSENT, and
|
|
11321
|
+
// MOrg is a WHITELIST — a new top-level field that is not named here is
|
|
11322
|
+
// silently dropped on the way to Mongo, which is how this one has to be
|
|
11323
|
+
// added at all.
|
|
11324
|
+
...value.residentAppModules === void 0 ? {} : { residentAppModules: value.residentAppModules },
|
|
11040
11325
|
email: value.email,
|
|
11041
11326
|
contact: value.contact,
|
|
11042
11327
|
country: value.country ?? "",
|
|
11043
11328
|
busInst: value.busInst,
|
|
11044
11329
|
status: value.status || "active",
|
|
11045
11330
|
defaultSite: value.defaultSite,
|
|
11046
|
-
createdBy: value.createdBy &&
|
|
11047
|
-
invitedFrom: value.invitedFrom &&
|
|
11048
|
-
invitedRole: value.invitedRole &&
|
|
11331
|
+
createdBy: value.createdBy && ObjectId19.isValid(value.createdBy.toString()) ? new ObjectId19(value.createdBy.toString()) : void 0,
|
|
11332
|
+
invitedFrom: value.invitedFrom && ObjectId19.isValid(value.invitedFrom.toString()) ? new ObjectId19(value.invitedFrom.toString()) : null,
|
|
11333
|
+
invitedRole: value.invitedRole && ObjectId19.isValid(value.invitedRole.toString()) ? new ObjectId19(value.invitedRole.toString()) : null,
|
|
11049
11334
|
terms: value.terms ?? "",
|
|
11050
11335
|
policies: value.policies ?? "",
|
|
11051
11336
|
createdAt: value.createdAt || /* @__PURE__ */ new Date(),
|
|
@@ -11055,7 +11340,7 @@ function MOrg(value) {
|
|
|
11055
11340
|
}
|
|
11056
11341
|
|
|
11057
11342
|
// src/repositories/organization.repo.ts
|
|
11058
|
-
import { ObjectId as
|
|
11343
|
+
import { ObjectId as ObjectId20 } from "mongodb";
|
|
11059
11344
|
var DUPLICATE_FIELD_LABEL = {
|
|
11060
11345
|
name: "name",
|
|
11061
11346
|
email: "email address"
|
|
@@ -11114,12 +11399,12 @@ function useOrgRepo() {
|
|
|
11114
11399
|
return null;
|
|
11115
11400
|
try {
|
|
11116
11401
|
return await collection.findOne({
|
|
11117
|
-
createdBy: new
|
|
11402
|
+
createdBy: new ObjectId20(createdBy.toString()),
|
|
11118
11403
|
nature,
|
|
11119
11404
|
status: { $ne: "deleted" }
|
|
11120
11405
|
});
|
|
11121
11406
|
} catch (error) {
|
|
11122
|
-
|
|
11407
|
+
logger10.log({
|
|
11123
11408
|
level: "error",
|
|
11124
11409
|
message: `Failed to look up an existing ${nature} organisation for ${createdBy}`
|
|
11125
11410
|
});
|
|
@@ -11131,22 +11416,22 @@ function useOrgRepo() {
|
|
|
11131
11416
|
value = MOrg(value);
|
|
11132
11417
|
const res = await collection.insertOne(value, { session });
|
|
11133
11418
|
delNamespace().then(() => {
|
|
11134
|
-
|
|
11419
|
+
logger10.info(`Cache cleared for namespace: ${namespace_collection2}`);
|
|
11135
11420
|
}).catch((err) => {
|
|
11136
|
-
|
|
11421
|
+
logger10.error(
|
|
11137
11422
|
`Failed to clear cache for namespace: ${namespace_collection2}`,
|
|
11138
11423
|
err
|
|
11139
11424
|
);
|
|
11140
11425
|
});
|
|
11141
11426
|
return res.insertedId;
|
|
11142
11427
|
} catch (error) {
|
|
11143
|
-
|
|
11428
|
+
logger10.log({ level: "error", message: error.message });
|
|
11144
11429
|
if (error instanceof AppError3) {
|
|
11145
11430
|
throw error;
|
|
11146
11431
|
} else {
|
|
11147
11432
|
const isDuplicated = error.message.includes("duplicate");
|
|
11148
11433
|
if (isDuplicated) {
|
|
11149
|
-
throw new
|
|
11434
|
+
throw new BadRequestError20("Organization already exist.");
|
|
11150
11435
|
}
|
|
11151
11436
|
throw new InternalServerError9("Failed to create organization.");
|
|
11152
11437
|
}
|
|
@@ -11155,7 +11440,7 @@ function useOrgRepo() {
|
|
|
11155
11440
|
async function update(id, value, session) {
|
|
11156
11441
|
try {
|
|
11157
11442
|
await collection.updateOne(
|
|
11158
|
-
{ _id: new
|
|
11443
|
+
{ _id: new ObjectId20(id) },
|
|
11159
11444
|
{
|
|
11160
11445
|
$set: {
|
|
11161
11446
|
...value,
|
|
@@ -11167,7 +11452,7 @@ function useOrgRepo() {
|
|
|
11167
11452
|
await delNamespace();
|
|
11168
11453
|
return id;
|
|
11169
11454
|
} catch (error) {
|
|
11170
|
-
|
|
11455
|
+
logger10.log({
|
|
11171
11456
|
level: "error",
|
|
11172
11457
|
message: `Organization update failed for ${id}: ${error?.name ?? "Error"}${error?.code !== void 0 ? ` code=${error.code}` : ""}${duplicatedField(error) ? ` key=${duplicatedField(error)}` : ""} fields=[${Object.keys(value ?? {}).join(",")}] ${error?.message ?? ""}`
|
|
11173
11458
|
});
|
|
@@ -11175,7 +11460,7 @@ function useOrgRepo() {
|
|
|
11175
11460
|
throw error;
|
|
11176
11461
|
const field = duplicatedField(error);
|
|
11177
11462
|
if (field) {
|
|
11178
|
-
throw new
|
|
11463
|
+
throw new BadRequestError20(
|
|
11179
11464
|
`Another organization already uses this ${DUPLICATE_FIELD_LABEL[field] ?? field}. Please use a different one.`
|
|
11180
11465
|
);
|
|
11181
11466
|
}
|
|
@@ -11203,12 +11488,12 @@ function useOrgRepo() {
|
|
|
11203
11488
|
};
|
|
11204
11489
|
if (scope) {
|
|
11205
11490
|
const ids2 = Array.from(
|
|
11206
|
-
new Set((scope.orgIds ?? []).filter((id) =>
|
|
11491
|
+
new Set((scope.orgIds ?? []).filter((id) => ObjectId20.isValid(id)))
|
|
11207
11492
|
).sort();
|
|
11208
11493
|
const email = (scope.email ?? "").trim();
|
|
11209
11494
|
const reach = [];
|
|
11210
11495
|
if (ids2.length)
|
|
11211
|
-
reach.push({ _id: { $in: ids2.map((id) => new
|
|
11496
|
+
reach.push({ _id: { $in: ids2.map((id) => new ObjectId20(id)) } });
|
|
11212
11497
|
if (email)
|
|
11213
11498
|
reach.push({ email });
|
|
11214
11499
|
if (!reach.length) {
|
|
@@ -11228,7 +11513,7 @@ function useOrgRepo() {
|
|
|
11228
11513
|
const cacheKey = makeCacheKey7(namespace_collection2, cacheOptions);
|
|
11229
11514
|
const cachedData = await getCache(cacheKey);
|
|
11230
11515
|
if (cachedData) {
|
|
11231
|
-
|
|
11516
|
+
logger10.info(`Cache hit for key: ${cacheKey}`);
|
|
11232
11517
|
return cachedData;
|
|
11233
11518
|
}
|
|
11234
11519
|
const normalizedPage = page > 0 ? page - 1 : 0;
|
|
@@ -11253,21 +11538,21 @@ function useOrgRepo() {
|
|
|
11253
11538
|
const length = await collection.countDocuments(query2);
|
|
11254
11539
|
const data = paginate7(items, normalizedPage, limit, length);
|
|
11255
11540
|
setCache(cacheKey, data, 15 * 60).then(() => {
|
|
11256
|
-
|
|
11541
|
+
logger10.info(`Cache set for key: ${cacheKey}`);
|
|
11257
11542
|
}).catch((err) => {
|
|
11258
|
-
|
|
11543
|
+
logger10.error(`Failed to set cache for key: ${cacheKey}`, err);
|
|
11259
11544
|
});
|
|
11260
11545
|
return data;
|
|
11261
11546
|
} catch (error) {
|
|
11262
|
-
|
|
11547
|
+
logger10.log({ level: "error", message: `${error}` });
|
|
11263
11548
|
throw error;
|
|
11264
11549
|
}
|
|
11265
11550
|
}
|
|
11266
11551
|
async function getNameById(_id) {
|
|
11267
11552
|
try {
|
|
11268
|
-
_id = new
|
|
11553
|
+
_id = new ObjectId20(_id);
|
|
11269
11554
|
} catch (error) {
|
|
11270
|
-
throw new
|
|
11555
|
+
throw new BadRequestError20("Invalid organization ID format.");
|
|
11271
11556
|
}
|
|
11272
11557
|
const data = await collection.findOne(
|
|
11273
11558
|
{ _id },
|
|
@@ -11280,16 +11565,16 @@ function useOrgRepo() {
|
|
|
11280
11565
|
}
|
|
11281
11566
|
async function getById(_id) {
|
|
11282
11567
|
try {
|
|
11283
|
-
_id = new
|
|
11568
|
+
_id = new ObjectId20(_id);
|
|
11284
11569
|
} catch (error) {
|
|
11285
|
-
throw new
|
|
11570
|
+
throw new BadRequestError20("Invalid organization ID format.");
|
|
11286
11571
|
}
|
|
11287
11572
|
const cacheKey = makeCacheKey7(namespace_collection2, {
|
|
11288
11573
|
_id: _id.toString()
|
|
11289
11574
|
});
|
|
11290
11575
|
const cachedData = await getCache(cacheKey);
|
|
11291
11576
|
if (cachedData) {
|
|
11292
|
-
|
|
11577
|
+
logger10.info(`Cache hit for key: ${cacheKey}`);
|
|
11293
11578
|
return cachedData;
|
|
11294
11579
|
}
|
|
11295
11580
|
try {
|
|
@@ -11298,9 +11583,9 @@ function useOrgRepo() {
|
|
|
11298
11583
|
throw new NotFoundError7("Organization not found.");
|
|
11299
11584
|
}
|
|
11300
11585
|
setCache(cacheKey, data, 15 * 60).then(() => {
|
|
11301
|
-
|
|
11586
|
+
logger10.info(`Cache set for key: ${cacheKey}`);
|
|
11302
11587
|
}).catch((err) => {
|
|
11303
|
-
|
|
11588
|
+
logger10.error(`Failed to set cache for key: ${cacheKey}`, err);
|
|
11304
11589
|
});
|
|
11305
11590
|
return data;
|
|
11306
11591
|
} catch (error) {
|
|
@@ -11320,13 +11605,13 @@ function useOrgRepo() {
|
|
|
11320
11605
|
const cacheKey = makeCacheKey7(namespace_collection2, { name });
|
|
11321
11606
|
const cachedData = await getCache(cacheKey);
|
|
11322
11607
|
if (cachedData) {
|
|
11323
|
-
|
|
11608
|
+
logger10.info(`Cache hit for key: ${cacheKey}`);
|
|
11324
11609
|
return cachedData;
|
|
11325
11610
|
}
|
|
11326
11611
|
setCache(cacheKey, data, 15 * 60).then(() => {
|
|
11327
|
-
|
|
11612
|
+
logger10.info(`Cache set for key: ${cacheKey}`);
|
|
11328
11613
|
}).catch((err) => {
|
|
11329
|
-
|
|
11614
|
+
logger10.error(`Failed to set cache for key: ${cacheKey}`, err);
|
|
11330
11615
|
});
|
|
11331
11616
|
return data;
|
|
11332
11617
|
} catch (error) {
|
|
@@ -11341,15 +11626,15 @@ function useOrgRepo() {
|
|
|
11341
11626
|
const cacheKey = makeCacheKey7(namespace_collection2, { email });
|
|
11342
11627
|
const cachedData = await getCache(cacheKey);
|
|
11343
11628
|
if (cachedData) {
|
|
11344
|
-
|
|
11629
|
+
logger10.info(`Cache hit for key: ${cacheKey}`);
|
|
11345
11630
|
return cachedData;
|
|
11346
11631
|
}
|
|
11347
11632
|
try {
|
|
11348
11633
|
const data = await collection.findOne({ email });
|
|
11349
11634
|
setCache(cacheKey, data, 15 * 60).then(() => {
|
|
11350
|
-
|
|
11635
|
+
logger10.info(`Cache set for key: ${cacheKey}`);
|
|
11351
11636
|
}).catch((err) => {
|
|
11352
|
-
|
|
11637
|
+
logger10.error(`Failed to set cache for key: ${cacheKey}`, err);
|
|
11353
11638
|
});
|
|
11354
11639
|
return data;
|
|
11355
11640
|
} catch (error) {
|
|
@@ -11362,7 +11647,7 @@ function useOrgRepo() {
|
|
|
11362
11647
|
});
|
|
11363
11648
|
const cachedData = await getCache(cacheKey);
|
|
11364
11649
|
if (cachedData) {
|
|
11365
|
-
|
|
11650
|
+
logger10.info(`Cache hit for key: ${cacheKey}`);
|
|
11366
11651
|
return cachedData;
|
|
11367
11652
|
}
|
|
11368
11653
|
try {
|
|
@@ -11370,9 +11655,9 @@ function useOrgRepo() {
|
|
|
11370
11655
|
email
|
|
11371
11656
|
}).toArray();
|
|
11372
11657
|
setCache(cacheKey, data, 15 * 60).then(() => {
|
|
11373
|
-
|
|
11658
|
+
logger10.info(`Cache set for key: ${cacheKey}`);
|
|
11374
11659
|
}).catch((err) => {
|
|
11375
|
-
|
|
11660
|
+
logger10.error(`Failed to set cache for key: ${cacheKey}`, err);
|
|
11376
11661
|
});
|
|
11377
11662
|
return data;
|
|
11378
11663
|
} catch (error) {
|
|
@@ -11386,14 +11671,14 @@ function useOrgRepo() {
|
|
|
11386
11671
|
}, session) {
|
|
11387
11672
|
const allowedFields = ["name", "description", "defaultSite"];
|
|
11388
11673
|
if (!allowedFields.includes(field)) {
|
|
11389
|
-
throw new
|
|
11674
|
+
throw new BadRequestError20(
|
|
11390
11675
|
`Field "${field}" is not allowed to be updated.`
|
|
11391
11676
|
);
|
|
11392
11677
|
}
|
|
11393
11678
|
try {
|
|
11394
|
-
_id = new
|
|
11679
|
+
_id = new ObjectId20(_id);
|
|
11395
11680
|
} catch (error) {
|
|
11396
|
-
throw new
|
|
11681
|
+
throw new BadRequestError20("Invalid organization ID format.");
|
|
11397
11682
|
}
|
|
11398
11683
|
try {
|
|
11399
11684
|
await collection.updateOne(
|
|
@@ -11403,9 +11688,9 @@ function useOrgRepo() {
|
|
|
11403
11688
|
{ session }
|
|
11404
11689
|
);
|
|
11405
11690
|
delNamespace().then(() => {
|
|
11406
|
-
|
|
11691
|
+
logger10.info(`Cache cleared for namespace: ${namespace_collection2}`);
|
|
11407
11692
|
}).catch((err) => {
|
|
11408
|
-
|
|
11693
|
+
logger10.error(
|
|
11409
11694
|
`Failed to clear cache for namespace: ${namespace_collection2}`,
|
|
11410
11695
|
err
|
|
11411
11696
|
);
|
|
@@ -11417,9 +11702,9 @@ function useOrgRepo() {
|
|
|
11417
11702
|
}
|
|
11418
11703
|
async function deleteById(_id) {
|
|
11419
11704
|
try {
|
|
11420
|
-
_id = new
|
|
11705
|
+
_id = new ObjectId20(_id);
|
|
11421
11706
|
} catch (error) {
|
|
11422
|
-
throw new
|
|
11707
|
+
throw new BadRequestError20("Invalid organization ID format.");
|
|
11423
11708
|
}
|
|
11424
11709
|
try {
|
|
11425
11710
|
await collection.updateOne(
|
|
@@ -11427,9 +11712,9 @@ function useOrgRepo() {
|
|
|
11427
11712
|
{ $set: { status: "deleted", deletedAt: (/* @__PURE__ */ new Date()).toISOString() } }
|
|
11428
11713
|
);
|
|
11429
11714
|
delNamespace().then(() => {
|
|
11430
|
-
|
|
11715
|
+
logger10.info(`Cache cleared for namespace: ${namespace_collection2}`);
|
|
11431
11716
|
}).catch((err) => {
|
|
11432
|
-
|
|
11717
|
+
logger10.error(
|
|
11433
11718
|
`Failed to clear cache for namespace: ${namespace_collection2}`,
|
|
11434
11719
|
err
|
|
11435
11720
|
);
|
|
@@ -11441,9 +11726,9 @@ function useOrgRepo() {
|
|
|
11441
11726
|
}
|
|
11442
11727
|
async function updateStatusById(_id, status) {
|
|
11443
11728
|
try {
|
|
11444
|
-
_id = new
|
|
11729
|
+
_id = new ObjectId20(_id);
|
|
11445
11730
|
} catch (error) {
|
|
11446
|
-
throw new
|
|
11731
|
+
throw new BadRequestError20("Invalid organization ID format.");
|
|
11447
11732
|
}
|
|
11448
11733
|
try {
|
|
11449
11734
|
const result = await collection.updateOne(
|
|
@@ -11454,9 +11739,9 @@ function useOrgRepo() {
|
|
|
11454
11739
|
throw new NotFoundError7("Organization not found.");
|
|
11455
11740
|
}
|
|
11456
11741
|
delNamespace().then(() => {
|
|
11457
|
-
|
|
11742
|
+
logger10.info(`Cache cleared for namespace: ${namespace_collection2}`);
|
|
11458
11743
|
}).catch((err) => {
|
|
11459
|
-
|
|
11744
|
+
logger10.error(
|
|
11460
11745
|
`Failed to clear cache for namespace: ${namespace_collection2}`,
|
|
11461
11746
|
err
|
|
11462
11747
|
);
|
|
@@ -11611,7 +11896,7 @@ function useOrgRepo() {
|
|
|
11611
11896
|
totalItems
|
|
11612
11897
|
);
|
|
11613
11898
|
} catch (error) {
|
|
11614
|
-
|
|
11899
|
+
logger10.log({
|
|
11615
11900
|
level: "error",
|
|
11616
11901
|
message: `${error}`
|
|
11617
11902
|
});
|
|
@@ -11624,7 +11909,7 @@ function useOrgRepo() {
|
|
|
11624
11909
|
throw new NotFoundError7("Admin role not found.");
|
|
11625
11910
|
let orgId;
|
|
11626
11911
|
try {
|
|
11627
|
-
orgId = new
|
|
11912
|
+
orgId = new ObjectId20(role.org);
|
|
11628
11913
|
} catch {
|
|
11629
11914
|
throw new InternalServerError9(
|
|
11630
11915
|
"Invalid organization reference in admin role."
|
|
@@ -11640,9 +11925,9 @@ function useOrgRepo() {
|
|
|
11640
11925
|
}
|
|
11641
11926
|
async function completeOnboardingById(_id, session) {
|
|
11642
11927
|
try {
|
|
11643
|
-
_id = new
|
|
11928
|
+
_id = new ObjectId20(_id);
|
|
11644
11929
|
} catch (error) {
|
|
11645
|
-
throw new
|
|
11930
|
+
throw new BadRequestError20("Invalid organization ID format.");
|
|
11646
11931
|
}
|
|
11647
11932
|
try {
|
|
11648
11933
|
const result = await collection.updateOne(
|
|
@@ -11694,238 +11979,6 @@ function useOrgRepo() {
|
|
|
11694
11979
|
|
|
11695
11980
|
// src/repositories/site.repo.ts
|
|
11696
11981
|
import { ObjectId as ObjectId21 } from "mongodb";
|
|
11697
|
-
|
|
11698
|
-
// src/models/site.model.ts
|
|
11699
|
-
import {
|
|
11700
|
-
BadRequestError as BadRequestError20,
|
|
11701
|
-
logger as logger10
|
|
11702
|
-
} from "@7365admin1/node-server-utils";
|
|
11703
|
-
import Joi10 from "joi";
|
|
11704
|
-
|
|
11705
|
-
// src/utils/site-modules.util.ts
|
|
11706
|
-
var siteModulesSchema = modulesSchema;
|
|
11707
|
-
function narrows(modules) {
|
|
11708
|
-
return Array.isArray(modules) && modules.length > 0;
|
|
11709
|
-
}
|
|
11710
|
-
function rejectedSiteModules(orgModules, requested) {
|
|
11711
|
-
if (!narrows(orgModules))
|
|
11712
|
-
return [];
|
|
11713
|
-
const org = normaliseModules(orgModules);
|
|
11714
|
-
return normaliseModules(requested).filter((key) => !org.includes(key));
|
|
11715
|
-
}
|
|
11716
|
-
function rejectedSiteModulesMessage(rejected) {
|
|
11717
|
-
return `This organisation has not been given ${rejected.join(", ")}, so a site cannot offer it. Ask Seven365 to add it to the organisation first.`;
|
|
11718
|
-
}
|
|
11719
|
-
|
|
11720
|
-
// src/models/site.model.ts
|
|
11721
|
-
import { ObjectId as ObjectId20 } from "mongodb";
|
|
11722
|
-
|
|
11723
|
-
// src/utils/site-timezone.util.ts
|
|
11724
|
-
import moment from "moment-timezone";
|
|
11725
|
-
var DEFAULT_SITE_TIMEZONE = "Asia/Singapore";
|
|
11726
|
-
function isValidTimezone(tz) {
|
|
11727
|
-
return typeof tz === "string" && tz.length > 0 && !!moment.tz.zone(tz);
|
|
11728
|
-
}
|
|
11729
|
-
function resolveSiteTimezone(site) {
|
|
11730
|
-
const tz = site?.timezone;
|
|
11731
|
-
return isValidTimezone(tz) ? tz : DEFAULT_SITE_TIMEZONE;
|
|
11732
|
-
}
|
|
11733
|
-
function siteDayBounds(value, timezone) {
|
|
11734
|
-
if (!value)
|
|
11735
|
-
return null;
|
|
11736
|
-
const tz = isValidTimezone(timezone) ? timezone : DEFAULT_SITE_TIMEZONE;
|
|
11737
|
-
const isDateOnly = typeof value === "string" && /^\d{4}-\d{2}-\d{2}$/.test(value);
|
|
11738
|
-
const m = isDateOnly ? moment.tz(value, "YYYY-MM-DD", tz) : typeof value === "string" ? moment.tz(value, moment.ISO_8601, tz) : moment.tz(value, tz);
|
|
11739
|
-
if (!m.isValid())
|
|
11740
|
-
return null;
|
|
11741
|
-
return {
|
|
11742
|
-
start: m.clone().startOf("day").toDate(),
|
|
11743
|
-
end: m.clone().endOf("day").toDate(),
|
|
11744
|
-
date: m.format("YYYY-MM-DD")
|
|
11745
|
-
};
|
|
11746
|
-
}
|
|
11747
|
-
|
|
11748
|
-
// src/models/site.model.ts
|
|
11749
|
-
function siteTimezoneValidator(value, helpers) {
|
|
11750
|
-
return isValidTimezone(value) ? value : helpers.error("any.invalid");
|
|
11751
|
-
}
|
|
11752
|
-
var addressSchema = Joi10.object({
|
|
11753
|
-
line1: Joi10.string().min(3).required(),
|
|
11754
|
-
line2: Joi10.string().allow("", null),
|
|
11755
|
-
city: Joi10.string().min(2).required(),
|
|
11756
|
-
state: Joi10.string().allow("", null),
|
|
11757
|
-
postalCode: Joi10.string().min(3).required(),
|
|
11758
|
-
country: Joi10.string().min(2).required()
|
|
11759
|
-
});
|
|
11760
|
-
var hidQrCodePassSchema = Joi10.object({
|
|
11761
|
-
enabled: Joi10.boolean().required(),
|
|
11762
|
-
onlineMode: Joi10.boolean().optional(),
|
|
11763
|
-
readerId: Joi10.string().hex().length(24).allow("").required(),
|
|
11764
|
-
qrGatePermissions: Joi10.object({
|
|
11765
|
-
resident: Joi10.array().items(Joi10.string().hex().length(24)).unique().default([]),
|
|
11766
|
-
propertyManagement: Joi10.array().items(Joi10.string().hex().length(24)).unique().default([])
|
|
11767
|
-
}).optional(),
|
|
11768
|
-
qrFormat: Joi10.string().valid("0", "1", "2").required(),
|
|
11769
|
-
identificationMethods: Joi10.object({
|
|
11770
|
-
facial: Joi10.boolean().required(),
|
|
11771
|
-
card: Joi10.boolean().required(),
|
|
11772
|
-
qrCode: Joi10.boolean().required(),
|
|
11773
|
-
idPassword: Joi10.boolean().required(),
|
|
11774
|
-
pin: Joi10.boolean().required(),
|
|
11775
|
-
bluetooth: Joi10.boolean().required()
|
|
11776
|
-
}).optional(),
|
|
11777
|
-
printer: Joi10.object({
|
|
11778
|
-
vendorId: Joi10.string().allow("").required(),
|
|
11779
|
-
productId: Joi10.string().allow("").required()
|
|
11780
|
-
}).required(),
|
|
11781
|
-
template: Joi10.object({
|
|
11782
|
-
header: Joi10.string().allow("").required(),
|
|
11783
|
-
subtext: Joi10.string().allow("").required()
|
|
11784
|
-
}).required(),
|
|
11785
|
-
validityMinutes: Joi10.number().integer().min(1).allow(null).required(),
|
|
11786
|
-
updatedAt: Joi10.string().isoDate().required()
|
|
11787
|
-
});
|
|
11788
|
-
var metadataSchema3 = Joi10.object({
|
|
11789
|
-
/**
|
|
11790
|
-
* The catalogue modules THIS SITE offers — tier two of the cascade.
|
|
11791
|
-
*
|
|
11792
|
-
* Owner requirement R1: whatever Seven365 gives an organisation is the only
|
|
11793
|
-
* set that organisation may then give to each site it creates. The
|
|
11794
|
-
* organisation's own list is `organizations.modules`; this narrows it per
|
|
11795
|
-
* site, and `site-modules.util.ts` holds both the read (fails open) and the
|
|
11796
|
-
* write rule (refuses a key the organisation was never given).
|
|
11797
|
-
*
|
|
11798
|
-
* ABSENT AND EMPTY BOTH MEAN "everything the organisation has" — never
|
|
11799
|
-
* "nothing". No site has this field today.
|
|
11800
|
-
*
|
|
11801
|
-
* It lives in `metadata` because `MSite` is a whitelist: a new top-level
|
|
11802
|
-
* field is silently dropped, while `metadata` is passed through wholesale.
|
|
11803
|
-
* `residentAppModules` below is the same pattern for the same reason.
|
|
11804
|
-
*/
|
|
11805
|
-
modules: siteModulesSchema.optional(),
|
|
11806
|
-
block: Joi10.number().optional(),
|
|
11807
|
-
guardPosts: Joi10.number().optional(),
|
|
11808
|
-
gracePeriod: Joi10.number().optional(),
|
|
11809
|
-
incidentCounter: Joi10.number().optional(),
|
|
11810
|
-
incidentLogo: Joi10.string().optional().allow("", null),
|
|
11811
|
-
services: Joi10.array().items(Joi10.object().unknown(true)).optional(),
|
|
11812
|
-
residentAppModules: Joi10.object({
|
|
11813
|
-
feedback: Joi10.boolean(),
|
|
11814
|
-
bulletin: Joi10.boolean(),
|
|
11815
|
-
bulletinVideos: Joi10.boolean(),
|
|
11816
|
-
inviteVisitors: Joi10.boolean(),
|
|
11817
|
-
myVisitors: Joi10.boolean(),
|
|
11818
|
-
billingAndSoa: Joi10.boolean(),
|
|
11819
|
-
access: Joi10.boolean(),
|
|
11820
|
-
facilityBooking: Joi10.boolean(),
|
|
11821
|
-
onlineForm: Joi10.boolean(),
|
|
11822
|
-
emergencyContact: Joi10.boolean()
|
|
11823
|
-
}).optional(),
|
|
11824
|
-
hidQrCodePass: hidQrCodePassSchema.optional()
|
|
11825
|
-
});
|
|
11826
|
-
var residentAppModuleKeys = [
|
|
11827
|
-
"feedback",
|
|
11828
|
-
"bulletin",
|
|
11829
|
-
"bulletinVideos",
|
|
11830
|
-
"inviteVisitors",
|
|
11831
|
-
"myVisitors",
|
|
11832
|
-
"billingAndSoa",
|
|
11833
|
-
"access",
|
|
11834
|
-
"facilityBooking",
|
|
11835
|
-
"onlineForm",
|
|
11836
|
-
"emergencyContact"
|
|
11837
|
-
];
|
|
11838
|
-
var updateAddressSchema = Joi10.object({
|
|
11839
|
-
line1: Joi10.string().min(3).allow("", null),
|
|
11840
|
-
line2: Joi10.string().allow("", null),
|
|
11841
|
-
city: Joi10.string().min(2).allow("", null),
|
|
11842
|
-
state: Joi10.string().allow("", null),
|
|
11843
|
-
postalCode: Joi10.string().min(3).allow("", null),
|
|
11844
|
-
country: Joi10.string().min(2).allow("", null)
|
|
11845
|
-
});
|
|
11846
|
-
var SiteStatus = /* @__PURE__ */ ((SiteStatus2) => {
|
|
11847
|
-
SiteStatus2["ACTIVE"] = "active";
|
|
11848
|
-
SiteStatus2["PENDING"] = "pending";
|
|
11849
|
-
return SiteStatus2;
|
|
11850
|
-
})(SiteStatus || {});
|
|
11851
|
-
var SiteCategories = /* @__PURE__ */ ((SiteCategories2) => {
|
|
11852
|
-
SiteCategories2["RESIDENTIAL"] = "residential";
|
|
11853
|
-
SiteCategories2["COMMERCIAL"] = "commercial";
|
|
11854
|
-
SiteCategories2["INDUSTRIAL"] = "industrial";
|
|
11855
|
-
SiteCategories2["MIXED_DEVELOPMENT"] = "mixed_development";
|
|
11856
|
-
SiteCategories2["INSTITUTIONAL"] = "institutional";
|
|
11857
|
-
SiteCategories2["INFRASTRUCTURE"] = "infrastructure";
|
|
11858
|
-
SiteCategories2["HOSPITALITY"] = "hospitality";
|
|
11859
|
-
return SiteCategories2;
|
|
11860
|
-
})(SiteCategories || {});
|
|
11861
|
-
var allowedFieldsSite = [
|
|
11862
|
-
"metadata.block",
|
|
11863
|
-
"metadata.guardPosts",
|
|
11864
|
-
"metadata.gracePeriod",
|
|
11865
|
-
"metadata.incidentCounter",
|
|
11866
|
-
"metadata.incidentLogo",
|
|
11867
|
-
"metadata.services",
|
|
11868
|
-
"metadata.hidQrCodePass"
|
|
11869
|
-
];
|
|
11870
|
-
var siteSchema = Joi10.object({
|
|
11871
|
-
name: Joi10.string().required(),
|
|
11872
|
-
description: Joi10.string().optional().allow("", null),
|
|
11873
|
-
orgId: Joi10.string().hex().required(),
|
|
11874
|
-
customerId: Joi10.string().hex().optional().allow("", null),
|
|
11875
|
-
address: addressSchema.optional(),
|
|
11876
|
-
timezone: Joi10.string().custom(siteTimezoneValidator, "IANA timezone").optional().default(DEFAULT_SITE_TIMEZONE),
|
|
11877
|
-
category: Joi10.string().valid(...Object.values(SiteCategories)).default("commercial" /* COMMERCIAL */),
|
|
11878
|
-
deliveryCompanyList: Joi10.array().items(Joi10.string()).optional().allow(null),
|
|
11879
|
-
isOpenGate: Joi10.boolean().optional().default(false),
|
|
11880
|
-
dahuaTimeExpiration: Joi10.number().optional().allow(null),
|
|
11881
|
-
siteDocs: Joi10.array().items(Joi10.string().hex().length(24).allow(null, "")),
|
|
11882
|
-
unitDocs: Joi10.array().items(Joi10.string().hex().length(24).allow(null, ""))
|
|
11883
|
-
});
|
|
11884
|
-
var updateSiteSchema = Joi10.object({
|
|
11885
|
-
_id: Joi10.string().hex().length(24).required(),
|
|
11886
|
-
address: updateAddressSchema.optional(),
|
|
11887
|
-
timezone: Joi10.string().custom(siteTimezoneValidator, "IANA timezone").optional(),
|
|
11888
|
-
metadata: metadataSchema3.optional(),
|
|
11889
|
-
deliveryCompanyList: Joi10.array().items(Joi10.string().trim()).optional().allow(null),
|
|
11890
|
-
isOpenGate: Joi10.boolean().optional().allow(null),
|
|
11891
|
-
dahuaTimeExpiration: Joi10.number().optional().allow(null),
|
|
11892
|
-
siteDocs: Joi10.array().items(Joi10.string().hex().length(24).allow(null, "")),
|
|
11893
|
-
unitDocs: Joi10.array().items(Joi10.string().hex().length(24).allow(null, ""))
|
|
11894
|
-
});
|
|
11895
|
-
function MSite(value) {
|
|
11896
|
-
const { error } = siteSchema.validate(value);
|
|
11897
|
-
if (error) {
|
|
11898
|
-
logger10.log({ level: "error", message: error.message });
|
|
11899
|
-
throw new BadRequestError20(error.message);
|
|
11900
|
-
}
|
|
11901
|
-
if (value.orgId) {
|
|
11902
|
-
try {
|
|
11903
|
-
value.orgId = new ObjectId20(value.orgId);
|
|
11904
|
-
} catch (error2) {
|
|
11905
|
-
throw new BadRequestError20("Invalid org ID format.");
|
|
11906
|
-
}
|
|
11907
|
-
}
|
|
11908
|
-
return {
|
|
11909
|
-
name: value.name ?? "",
|
|
11910
|
-
description: value.description ?? "",
|
|
11911
|
-
orgId: value.orgId ?? "",
|
|
11912
|
-
metadata: value.metadata ?? {},
|
|
11913
|
-
status: "active",
|
|
11914
|
-
createdAt: /* @__PURE__ */ new Date(),
|
|
11915
|
-
address: value.address,
|
|
11916
|
-
timezone: value.timezone ?? DEFAULT_SITE_TIMEZONE,
|
|
11917
|
-
category: value.category,
|
|
11918
|
-
deliveryCompanyList: value.deliveryCompanyList ?? [],
|
|
11919
|
-
isOpenGate: value.isOpenGate,
|
|
11920
|
-
dahuaTimeExpiration: value.dahuaTimeExpiration,
|
|
11921
|
-
siteDocs: value.siteDocs ?? [],
|
|
11922
|
-
unitDocs: value.unitDocs ?? [],
|
|
11923
|
-
updatedAt: value.updatedAt ?? "",
|
|
11924
|
-
deletedAt: value.deletedAt ?? ""
|
|
11925
|
-
};
|
|
11926
|
-
}
|
|
11927
|
-
|
|
11928
|
-
// src/repositories/site.repo.ts
|
|
11929
11982
|
import {
|
|
11930
11983
|
useAtlas as useAtlas13,
|
|
11931
11984
|
InternalServerError as InternalServerError10,
|
|
@@ -13875,7 +13928,7 @@ async function holdsRole(userId, roleId) {
|
|
|
13875
13928
|
// src/services/service-provider-invite.service.ts
|
|
13876
13929
|
import {
|
|
13877
13930
|
BadRequestError as BadRequestError30,
|
|
13878
|
-
UnauthorizedError as
|
|
13931
|
+
UnauthorizedError as UnauthorizedError3,
|
|
13879
13932
|
NotFoundError as NotFoundError10,
|
|
13880
13933
|
compileHandlebar,
|
|
13881
13934
|
getDirectory,
|
|
@@ -15130,7 +15183,7 @@ var NotificationService = class {
|
|
|
15130
15183
|
// src/utils/console-authz.util.ts
|
|
15131
15184
|
import {
|
|
15132
15185
|
BadRequestError as BadRequestError29,
|
|
15133
|
-
UnauthorizedError,
|
|
15186
|
+
UnauthorizedError as UnauthorizedError2,
|
|
15134
15187
|
useAtlas as useAtlas20
|
|
15135
15188
|
} from "@7365admin1/node-server-utils";
|
|
15136
15189
|
import { ObjectId as ObjectId31 } from "mongodb";
|
|
@@ -15307,10 +15360,10 @@ async function requirePlatformStaff(req, grant) {
|
|
|
15307
15360
|
const id = callerId(req);
|
|
15308
15361
|
const role = id ? await resolveStaffRole(id) : null;
|
|
15309
15362
|
if (!role) {
|
|
15310
|
-
throw new
|
|
15363
|
+
throw new UnauthorizedError2("Not authorized.");
|
|
15311
15364
|
}
|
|
15312
15365
|
if (grant && !consoleRoleAllows(role.permissions, grant.resource, grant.action)) {
|
|
15313
|
-
throw new
|
|
15366
|
+
throw new UnauthorizedError2("Not authorized.");
|
|
15314
15367
|
}
|
|
15315
15368
|
return id;
|
|
15316
15369
|
}
|
|
@@ -15330,7 +15383,7 @@ async function requireOrgAccess(req, orgId) {
|
|
|
15330
15383
|
async function requirePlatformOwner(req, grant) {
|
|
15331
15384
|
const id = callerId(req);
|
|
15332
15385
|
if (!id || !await isPlatformOwner(id)) {
|
|
15333
|
-
throw new
|
|
15386
|
+
throw new UnauthorizedError2("Not authorized.");
|
|
15334
15387
|
}
|
|
15335
15388
|
if (grant)
|
|
15336
15389
|
await requirePlatformStaff(req, grant);
|
|
@@ -15339,7 +15392,7 @@ async function requirePlatformOwner(req, grant) {
|
|
|
15339
15392
|
async function requireOrgReach(req, orgId) {
|
|
15340
15393
|
const id = callerId(req);
|
|
15341
15394
|
if (!id)
|
|
15342
|
-
throw new
|
|
15395
|
+
throw new UnauthorizedError2("Not authorized.");
|
|
15343
15396
|
const org = orgId?.toString() ?? "";
|
|
15344
15397
|
const actor = await resolveInviteActor(id);
|
|
15345
15398
|
if (staffMayBypass(actor))
|
|
@@ -15350,7 +15403,7 @@ async function requireOrgReach(req, orgId) {
|
|
|
15350
15403
|
return id;
|
|
15351
15404
|
if (org && await hasOrgOwnership(id, org))
|
|
15352
15405
|
return id;
|
|
15353
|
-
throw new
|
|
15406
|
+
throw new UnauthorizedError2("Not authorized.");
|
|
15354
15407
|
}
|
|
15355
15408
|
async function requireRoleInvitable(role, org, apps) {
|
|
15356
15409
|
const roleId = role?.toString() ?? "";
|
|
@@ -15490,13 +15543,13 @@ function useServiceProviderInviteService() {
|
|
|
15490
15543
|
const org = inviteOrgId(invite);
|
|
15491
15544
|
if (org && actor.orgIds.includes(org))
|
|
15492
15545
|
return;
|
|
15493
|
-
throw new
|
|
15546
|
+
throw new UnauthorizedError3(
|
|
15494
15547
|
"This invitation belongs to another organisation."
|
|
15495
15548
|
);
|
|
15496
15549
|
}
|
|
15497
15550
|
function assertSuperAdmin(actor) {
|
|
15498
15551
|
if (!staffMayBypass(actor)) {
|
|
15499
|
-
throw new
|
|
15552
|
+
throw new UnauthorizedError3(
|
|
15500
15553
|
"Only a Seven365 administrator can approve or reject invitations."
|
|
15501
15554
|
);
|
|
15502
15555
|
}
|
|
@@ -15509,7 +15562,7 @@ function useServiceProviderInviteService() {
|
|
|
15509
15562
|
async function actorFor(userId) {
|
|
15510
15563
|
const actor = await resolveInviteActor(userId);
|
|
15511
15564
|
if (!actor.id)
|
|
15512
|
-
throw new
|
|
15565
|
+
throw new UnauthorizedError3("Sign in to continue.");
|
|
15513
15566
|
return actor;
|
|
15514
15567
|
}
|
|
15515
15568
|
async function listApprovals({
|
|
@@ -15546,7 +15599,7 @@ function useServiceProviderInviteService() {
|
|
|
15546
15599
|
let scope = actor.orgIds;
|
|
15547
15600
|
if (orgId) {
|
|
15548
15601
|
if (!staffMayBypass(actor) && !actor.orgIds.includes(orgId)) {
|
|
15549
|
-
throw new
|
|
15602
|
+
throw new UnauthorizedError3(
|
|
15550
15603
|
"You are not a member of that organisation."
|
|
15551
15604
|
);
|
|
15552
15605
|
}
|
|
@@ -15705,7 +15758,7 @@ function useServiceProviderInviteService() {
|
|
|
15705
15758
|
);
|
|
15706
15759
|
const invitedUserId = user?._id?.toString();
|
|
15707
15760
|
if (!invitedUserId || invitedUserId !== userId) {
|
|
15708
|
-
throw new
|
|
15761
|
+
throw new UnauthorizedError3("This invitation was sent to someone else.");
|
|
15709
15762
|
}
|
|
15710
15763
|
}
|
|
15711
15764
|
async function decline({ id, userId }) {
|
|
@@ -16006,10 +16059,10 @@ function useVerificationService() {
|
|
|
16006
16059
|
const org = await getOrgById(orgId);
|
|
16007
16060
|
const actor = await resolveInviteActor(invitedBy);
|
|
16008
16061
|
if (!actor.id) {
|
|
16009
|
-
throw new
|
|
16062
|
+
throw new UnauthorizedError4("Sign in to continue.");
|
|
16010
16063
|
}
|
|
16011
16064
|
if (!staffMayBypass(actor) && !actor.orgIds.includes(orgId)) {
|
|
16012
|
-
throw new
|
|
16065
|
+
throw new UnauthorizedError4(
|
|
16013
16066
|
"You can only invite service providers to your own organisation's sites."
|
|
16014
16067
|
);
|
|
16015
16068
|
}
|
|
@@ -19080,7 +19133,7 @@ function useRoleService() {
|
|
|
19080
19133
|
}
|
|
19081
19134
|
|
|
19082
19135
|
// src/utils/role-scope.util.ts
|
|
19083
|
-
import { UnauthorizedError as
|
|
19136
|
+
import { UnauthorizedError as UnauthorizedError5 } from "@7365admin1/node-server-utils";
|
|
19084
19137
|
function roleScope(role) {
|
|
19085
19138
|
if ((role?.type ?? "") === PLATFORM_STAFF_ROLE_TYPE)
|
|
19086
19139
|
return "platform";
|
|
@@ -19114,7 +19167,7 @@ function requireNoPlatformGrant(role, next) {
|
|
|
19114
19167
|
const added = platformPermissionsAdded(next, role?.permissions);
|
|
19115
19168
|
if (added.length === 0)
|
|
19116
19169
|
return;
|
|
19117
|
-
throw new
|
|
19170
|
+
throw new UnauthorizedError5(
|
|
19118
19171
|
"These are Seven365 console permissions and cannot be granted on a client role: " + added.join(", ")
|
|
19119
19172
|
);
|
|
19120
19173
|
}
|
|
@@ -19128,14 +19181,14 @@ function refuseSharedClientTemplate(role, next) {
|
|
|
19128
19181
|
return true;
|
|
19129
19182
|
if (Array.isArray(next) && restorableFromHistory(role, next))
|
|
19130
19183
|
return true;
|
|
19131
|
-
throw new
|
|
19184
|
+
throw new UnauthorizedError5(
|
|
19132
19185
|
"This role is shared by every client that was invited with it and cannot be changed here."
|
|
19133
19186
|
);
|
|
19134
19187
|
}
|
|
19135
19188
|
function refuseNewSharedClientTemplate(role) {
|
|
19136
19189
|
if (roleScope(role) !== "client-template")
|
|
19137
19190
|
return;
|
|
19138
|
-
throw new
|
|
19191
|
+
throw new UnauthorizedError5(
|
|
19139
19192
|
"A role with no organisation would be shared by every client and cannot be created. Choose the organisation this role belongs to."
|
|
19140
19193
|
);
|
|
19141
19194
|
}
|
|
@@ -19270,6 +19323,28 @@ async function healOrgDefaultRoles(deps, org) {
|
|
|
19270
19323
|
}
|
|
19271
19324
|
}
|
|
19272
19325
|
|
|
19326
|
+
// src/utils/module-ceiling.util.ts
|
|
19327
|
+
async function moduleCeiling(org, site) {
|
|
19328
|
+
const orgId = org?.toString() ?? "";
|
|
19329
|
+
const siteId = site?.toString() ?? "";
|
|
19330
|
+
if (!orgId && !siteId)
|
|
19331
|
+
return [];
|
|
19332
|
+
try {
|
|
19333
|
+
const { getById: getOrgById } = useOrgRepo();
|
|
19334
|
+
const { getSiteById } = useSiteRepo();
|
|
19335
|
+
const [orgDoc, siteDoc] = await Promise.all([
|
|
19336
|
+
orgId ? getOrgById(orgId).catch(() => null) : null,
|
|
19337
|
+
siteId ? getSiteById(siteId).catch(() => null) : null
|
|
19338
|
+
]);
|
|
19339
|
+
return effectiveSiteModules(
|
|
19340
|
+
orgDoc?.modules,
|
|
19341
|
+
siteDoc?.metadata?.modules
|
|
19342
|
+
);
|
|
19343
|
+
} catch {
|
|
19344
|
+
return [];
|
|
19345
|
+
}
|
|
19346
|
+
}
|
|
19347
|
+
|
|
19273
19348
|
// src/controllers/role.controller.ts
|
|
19274
19349
|
async function requireRoleOrg(req, org) {
|
|
19275
19350
|
const orgId = org?.toString() ?? "";
|
|
@@ -19371,6 +19446,10 @@ function useRoleController() {
|
|
|
19371
19446
|
{ type: payload.type, org: payload.org, permissions: [] },
|
|
19372
19447
|
payload.permissions
|
|
19373
19448
|
);
|
|
19449
|
+
requireWithinModules(
|
|
19450
|
+
await moduleCeiling(payload.org, payload.site),
|
|
19451
|
+
payload.permissions
|
|
19452
|
+
);
|
|
19374
19453
|
const role = await _createRole(payload);
|
|
19375
19454
|
res.status(201).json({ message: "Successfully created role.", data: { role } });
|
|
19376
19455
|
return;
|
|
@@ -19488,6 +19567,11 @@ function useRoleController() {
|
|
|
19488
19567
|
throw new NotFoundError14("Role not found.");
|
|
19489
19568
|
const isRestore = await requireRoleWrite(req, existing, permissions);
|
|
19490
19569
|
requireNoPlatformGrant(existing, permissions);
|
|
19570
|
+
requireWithinModules(
|
|
19571
|
+
await moduleCeiling(existing?.org, existing?.site),
|
|
19572
|
+
permissions,
|
|
19573
|
+
existing?.permissions
|
|
19574
|
+
);
|
|
19491
19575
|
if (isRestore)
|
|
19492
19576
|
await recordPriorPermissions(
|
|
19493
19577
|
_appendPermissionHistory,
|
|
@@ -19526,6 +19610,11 @@ function useRoleController() {
|
|
|
19526
19610
|
throw new NotFoundError14("Role not found.");
|
|
19527
19611
|
const isRestore = await requireRoleWrite(req, existing, permissions);
|
|
19528
19612
|
requireNoPlatformGrant(existing, permissions);
|
|
19613
|
+
requireWithinModules(
|
|
19614
|
+
await moduleCeiling(existing?.org, existing?.site),
|
|
19615
|
+
permissions,
|
|
19616
|
+
existing?.permissions
|
|
19617
|
+
);
|
|
19529
19618
|
if (isRestore)
|
|
19530
19619
|
await recordPriorPermissions(
|
|
19531
19620
|
_appendPermissionHistory,
|
|
@@ -19646,12 +19735,12 @@ import { BadRequestError as BadRequestError49, logger as logger34 } from "@7365a
|
|
|
19646
19735
|
// src/services/member.service.ts
|
|
19647
19736
|
import {
|
|
19648
19737
|
BadRequestError as BadRequestError41,
|
|
19649
|
-
UnauthorizedError as
|
|
19738
|
+
UnauthorizedError as UnauthorizedError6,
|
|
19650
19739
|
useAtlas as useAtlas28
|
|
19651
19740
|
} from "@7365admin1/node-server-utils";
|
|
19652
19741
|
async function requireStaffCaller(callerId5, what) {
|
|
19653
19742
|
if (!await isSuperAdmin(callerId5)) {
|
|
19654
|
-
throw new
|
|
19743
|
+
throw new UnauthorizedError6(
|
|
19655
19744
|
`Only Seven365 staff can ${what}.`
|
|
19656
19745
|
);
|
|
19657
19746
|
}
|
|
@@ -19868,14 +19957,14 @@ function useMemberService() {
|
|
|
19868
19957
|
}
|
|
19869
19958
|
|
|
19870
19959
|
// src/utils/site-reach.util.ts
|
|
19871
|
-
import { useAtlas as useAtlas35, UnauthorizedError as
|
|
19960
|
+
import { useAtlas as useAtlas35, UnauthorizedError as UnauthorizedError8 } from "@7365admin1/node-server-utils";
|
|
19872
19961
|
import { ObjectId as ObjectId48 } from "mongodb";
|
|
19873
19962
|
|
|
19874
19963
|
// src/services/camera-view.service.ts
|
|
19875
19964
|
import {
|
|
19876
19965
|
BadRequestError as BadRequestError48,
|
|
19877
19966
|
NotFoundError as NotFoundError16,
|
|
19878
|
-
UnauthorizedError as
|
|
19967
|
+
UnauthorizedError as UnauthorizedError7,
|
|
19879
19968
|
logger as logger33,
|
|
19880
19969
|
useAtlas as useAtlas34,
|
|
19881
19970
|
useCache as useCache19
|
|
@@ -26322,7 +26411,7 @@ function useCameraViewService() {
|
|
|
26322
26411
|
}
|
|
26323
26412
|
async function authorizeCamera(params) {
|
|
26324
26413
|
if (!params.userId || !ObjectId47.isValid(params.userId)) {
|
|
26325
|
-
throw new
|
|
26414
|
+
throw new UnauthorizedError7("Not signed in.");
|
|
26326
26415
|
}
|
|
26327
26416
|
if (!ObjectId47.isValid(params.cameraId)) {
|
|
26328
26417
|
throw new BadRequestError48("Invalid camera id.");
|
|
@@ -26351,7 +26440,7 @@ function useCameraViewService() {
|
|
|
26351
26440
|
memberships
|
|
26352
26441
|
);
|
|
26353
26442
|
if (!hasAnyPermission(permissions, params.permissions)) {
|
|
26354
|
-
throw new
|
|
26443
|
+
throw new UnauthorizedError7(
|
|
26355
26444
|
"You do not have permission to use this camera."
|
|
26356
26445
|
);
|
|
26357
26446
|
}
|
|
@@ -26459,7 +26548,7 @@ function useCameraViewService() {
|
|
|
26459
26548
|
}
|
|
26460
26549
|
async function entitleSite(params) {
|
|
26461
26550
|
if (!params.userId || !ObjectId47.isValid(params.userId)) {
|
|
26462
|
-
throw new
|
|
26551
|
+
throw new UnauthorizedError7("Not signed in.");
|
|
26463
26552
|
}
|
|
26464
26553
|
if (!ObjectId47.isValid(params.siteId)) {
|
|
26465
26554
|
throw new BadRequestError48("Invalid site id.");
|
|
@@ -26491,7 +26580,7 @@ function useCameraViewService() {
|
|
|
26491
26580
|
);
|
|
26492
26581
|
const required = params.permissions ?? CAMERA_VIEW_PERMISSIONS;
|
|
26493
26582
|
if (!hasAnyPermission(permissions, required)) {
|
|
26494
|
-
throw new
|
|
26583
|
+
throw new UnauthorizedError7(
|
|
26495
26584
|
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.")
|
|
26496
26585
|
);
|
|
26497
26586
|
}
|
|
@@ -26900,7 +26989,7 @@ function idText(value) {
|
|
|
26900
26989
|
async function requireSiteReach(req, site, permission) {
|
|
26901
26990
|
const siteId = site?.toString?.() ?? "";
|
|
26902
26991
|
if (!siteId)
|
|
26903
|
-
throw new
|
|
26992
|
+
throw new UnauthorizedError8("Not authorized.");
|
|
26904
26993
|
const actor = await resolveInviteActor(callerId(req));
|
|
26905
26994
|
if (staffMayBypass(actor))
|
|
26906
26995
|
return;
|
|
@@ -27886,7 +27975,7 @@ function useVerificationController() {
|
|
|
27886
27975
|
// src/controllers/file.controller.ts
|
|
27887
27976
|
import {
|
|
27888
27977
|
BadRequestError as BadRequestError51,
|
|
27889
|
-
UnauthorizedError as
|
|
27978
|
+
UnauthorizedError as UnauthorizedError9,
|
|
27890
27979
|
logger as logger36
|
|
27891
27980
|
} from "@7365admin1/node-server-utils";
|
|
27892
27981
|
import Joi28 from "joi";
|
|
@@ -27915,7 +28004,7 @@ async function requireFileDelete(req, file) {
|
|
|
27915
28004
|
return;
|
|
27916
28005
|
const caller = callerId(req);
|
|
27917
28006
|
if (!caller)
|
|
27918
|
-
throw new
|
|
28007
|
+
throw new UnauthorizedError9("Not authorized.");
|
|
27919
28008
|
if (caller === ownerId)
|
|
27920
28009
|
return;
|
|
27921
28010
|
const [me, owner] = await Promise.all([
|
|
@@ -27931,7 +28020,7 @@ async function requireFileDelete(req, file) {
|
|
|
27931
28020
|
})) {
|
|
27932
28021
|
return;
|
|
27933
28022
|
}
|
|
27934
|
-
throw new
|
|
28023
|
+
throw new UnauthorizedError9("Not authorized.");
|
|
27935
28024
|
}
|
|
27936
28025
|
function useFileController() {
|
|
27937
28026
|
const { createFile, deleteFile: _deleteFile } = useFileService();
|
|
@@ -28004,7 +28093,7 @@ import {
|
|
|
28004
28093
|
BadRequestError as BadRequestError55,
|
|
28005
28094
|
logger as logger39,
|
|
28006
28095
|
NotFoundError as NotFoundError17,
|
|
28007
|
-
UnauthorizedError as
|
|
28096
|
+
UnauthorizedError as UnauthorizedError10
|
|
28008
28097
|
} from "@7365admin1/node-server-utils";
|
|
28009
28098
|
import Joi32 from "joi";
|
|
28010
28099
|
|
|
@@ -29291,7 +29380,7 @@ function useOrgController() {
|
|
|
29291
29380
|
try {
|
|
29292
29381
|
const createdBy = callerId(req);
|
|
29293
29382
|
if (!createdBy)
|
|
29294
|
-
throw new
|
|
29383
|
+
throw new UnauthorizedError10("Not authorized.");
|
|
29295
29384
|
const { inviteId, ...body } = req.body ?? {};
|
|
29296
29385
|
let invitedFrom = null;
|
|
29297
29386
|
let invitedRole = null;
|
|
@@ -29543,7 +29632,8 @@ function useOrgController() {
|
|
|
29543
29632
|
}
|
|
29544
29633
|
async function updateModules(req, res, next) {
|
|
29545
29634
|
const validation = Joi32.object({
|
|
29546
|
-
modules: modulesSchema.required()
|
|
29635
|
+
modules: modulesSchema.required(),
|
|
29636
|
+
residentAppModules: residentAppModulesSchema.optional()
|
|
29547
29637
|
});
|
|
29548
29638
|
const { error } = validation.validate(req.body);
|
|
29549
29639
|
if (error) {
|
|
@@ -29554,7 +29644,14 @@ function useOrgController() {
|
|
|
29554
29644
|
try {
|
|
29555
29645
|
const staffId = await requireConsolePermission(req, "organizations");
|
|
29556
29646
|
const modules = normaliseModules(req.body.modules);
|
|
29557
|
-
|
|
29647
|
+
const residentAppModules = req.body.residentAppModules;
|
|
29648
|
+
await _update(orgId, {
|
|
29649
|
+
modules,
|
|
29650
|
+
// Absent stays ABSENT. Writing `{}` would put the field on an
|
|
29651
|
+
// organisation nobody has configured, and `{}` is not distinguishable
|
|
29652
|
+
// later from a deliberate "all ten on".
|
|
29653
|
+
...residentAppModules === void 0 ? {} : { residentAppModules }
|
|
29654
|
+
});
|
|
29558
29655
|
const data = await _getById(orgId);
|
|
29559
29656
|
await recordConsoleAction({
|
|
29560
29657
|
action: "client.modules-changed" /* CLIENT_MODULES_CHANGED */,
|
|
@@ -29564,7 +29661,14 @@ function useOrgController() {
|
|
|
29564
29661
|
targetType: "organization" /* ORGANIZATION */,
|
|
29565
29662
|
after: pickAuditFields("client.modules-changed" /* CLIENT_MODULES_CHANGED */, {
|
|
29566
29663
|
modules: modules.join(", "),
|
|
29567
|
-
moduleCount: modules.length
|
|
29664
|
+
moduleCount: modules.length,
|
|
29665
|
+
// Joined for the same reason as `modules`: `auditValue` drops an
|
|
29666
|
+
// object, which would leave a row saying something changed without
|
|
29667
|
+
// saying what to. Only the switches turned OFF are worth recording -
|
|
29668
|
+
// absent means on, so the off list IS the decision.
|
|
29669
|
+
...residentAppModules === void 0 ? {} : {
|
|
29670
|
+
residentAppModulesOff: Object.keys(residentAppModules).filter((key) => residentAppModules[key] === false).join(", ")
|
|
29671
|
+
}
|
|
29568
29672
|
})
|
|
29569
29673
|
});
|
|
29570
29674
|
res.json({
|
|
@@ -31887,7 +31991,7 @@ function useSubscriptionService() {
|
|
|
31887
31991
|
import Joi37 from "joi";
|
|
31888
31992
|
import {
|
|
31889
31993
|
BadRequestError as BadRequestError71,
|
|
31890
|
-
UnauthorizedError as
|
|
31994
|
+
UnauthorizedError as UnauthorizedError11,
|
|
31891
31995
|
logger as logger49
|
|
31892
31996
|
} from "@7365admin1/node-server-utils";
|
|
31893
31997
|
|
|
@@ -32308,7 +32412,7 @@ function useSubscriptionController() {
|
|
|
32308
32412
|
try {
|
|
32309
32413
|
const subscription = await _getSubscriptionById(subscriptionId);
|
|
32310
32414
|
if (!subscription) {
|
|
32311
|
-
throw new
|
|
32415
|
+
throw new UnauthorizedError11("Not authorized.");
|
|
32312
32416
|
}
|
|
32313
32417
|
await requireOrgAccess(req, subscription.org?.toString());
|
|
32314
32418
|
const _res = await _updateSubscriptionSeats({
|
|
@@ -37771,6 +37875,32 @@ function useSiteService() {
|
|
|
37771
37875
|
|
|
37772
37876
|
// src/controllers/site.controller.ts
|
|
37773
37877
|
import { BadRequestError as BadRequestError92, logger as logger66 } from "@7365admin1/node-server-utils";
|
|
37878
|
+
|
|
37879
|
+
// src/utils/resident-app-modules.util.ts
|
|
37880
|
+
function effectiveResidentAppModules(orgModules, siteModules) {
|
|
37881
|
+
const org = asModules(orgModules);
|
|
37882
|
+
const site = asModules(siteModules);
|
|
37883
|
+
const effective = {};
|
|
37884
|
+
for (const key of residentAppModuleKeys) {
|
|
37885
|
+
effective[key] = org[key] !== false && site[key] !== false;
|
|
37886
|
+
}
|
|
37887
|
+
return effective;
|
|
37888
|
+
}
|
|
37889
|
+
function residentAppModulesAboveCeiling(orgModules, requested) {
|
|
37890
|
+
const org = asModules(orgModules);
|
|
37891
|
+
const wanted = asModules(requested);
|
|
37892
|
+
return residentAppModuleKeys.filter(
|
|
37893
|
+
(key) => org[key] === false && wanted[key] === true
|
|
37894
|
+
);
|
|
37895
|
+
}
|
|
37896
|
+
function residentAppModulesAboveCeilingMessage(rejected) {
|
|
37897
|
+
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.`;
|
|
37898
|
+
}
|
|
37899
|
+
function asModules(value) {
|
|
37900
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
37901
|
+
}
|
|
37902
|
+
|
|
37903
|
+
// src/controllers/site.controller.ts
|
|
37774
37904
|
import Joi51 from "joi";
|
|
37775
37905
|
async function requireSiteWrite(req, siteId) {
|
|
37776
37906
|
const actor = await resolveInviteActor(callerId(req));
|
|
@@ -37868,6 +37998,25 @@ function useSiteController() {
|
|
|
37868
37998
|
return;
|
|
37869
37999
|
}
|
|
37870
38000
|
}
|
|
38001
|
+
async function withResidentAppCeiling(site) {
|
|
38002
|
+
if (!site || typeof site !== "object")
|
|
38003
|
+
return site;
|
|
38004
|
+
try {
|
|
38005
|
+
const org = site.orgId ? await _getOrgById(site.orgId.toString()).catch(() => null) : null;
|
|
38006
|
+
return {
|
|
38007
|
+
...site,
|
|
38008
|
+
metadata: {
|
|
38009
|
+
...site.metadata ?? {},
|
|
38010
|
+
residentAppModulesEffective: effectiveResidentAppModules(
|
|
38011
|
+
org?.residentAppModules,
|
|
38012
|
+
site.metadata?.residentAppModules
|
|
38013
|
+
)
|
|
38014
|
+
}
|
|
38015
|
+
};
|
|
38016
|
+
} catch {
|
|
38017
|
+
return site;
|
|
38018
|
+
}
|
|
38019
|
+
}
|
|
37871
38020
|
async function getSiteById(req, res, next) {
|
|
37872
38021
|
const validation = Joi51.string().hex().required();
|
|
37873
38022
|
const _id = req.params.id;
|
|
@@ -37880,7 +38029,7 @@ function useSiteController() {
|
|
|
37880
38029
|
try {
|
|
37881
38030
|
const site = await _getSiteById(_id);
|
|
37882
38031
|
await requireSiteRead(req, _id, site);
|
|
37883
|
-
res.json(site);
|
|
38032
|
+
res.json(await withResidentAppCeiling(site));
|
|
37884
38033
|
return;
|
|
37885
38034
|
} catch (error2) {
|
|
37886
38035
|
logger66.log({ level: "error", message: error2.message });
|
|
@@ -37946,6 +38095,22 @@ function useSiteController() {
|
|
|
37946
38095
|
}
|
|
37947
38096
|
const { _id, ...rest } = value;
|
|
37948
38097
|
await requireSiteWrite(req, _id);
|
|
38098
|
+
if (rest?.metadata?.residentAppModules) {
|
|
38099
|
+
const existing = await _getSiteById(_id);
|
|
38100
|
+
const org = existing?.orgId ? await _getOrgById(existing.orgId.toString()).catch(
|
|
38101
|
+
() => null
|
|
38102
|
+
) : null;
|
|
38103
|
+
const above = residentAppModulesAboveCeiling(
|
|
38104
|
+
org?.residentAppModules,
|
|
38105
|
+
rest.metadata.residentAppModules
|
|
38106
|
+
);
|
|
38107
|
+
if (above.length) {
|
|
38108
|
+
next(
|
|
38109
|
+
new BadRequestError92(residentAppModulesAboveCeilingMessage(above))
|
|
38110
|
+
);
|
|
38111
|
+
return;
|
|
38112
|
+
}
|
|
38113
|
+
}
|
|
37949
38114
|
await _updateById(_id, rest);
|
|
37950
38115
|
res.json({ message: "Successfully updated site." });
|
|
37951
38116
|
return;
|
|
@@ -42685,7 +42850,7 @@ function useCustomerSiteService() {
|
|
|
42685
42850
|
import Joi60 from "joi";
|
|
42686
42851
|
import {
|
|
42687
42852
|
BadRequestError as BadRequestError107,
|
|
42688
|
-
UnauthorizedError as
|
|
42853
|
+
UnauthorizedError as UnauthorizedError12,
|
|
42689
42854
|
logger as logger79
|
|
42690
42855
|
} from "@7365admin1/node-server-utils";
|
|
42691
42856
|
async function requireEitherOrg(req, ...orgs) {
|
|
@@ -42694,7 +42859,7 @@ async function requireEitherOrg(req, ...orgs) {
|
|
|
42694
42859
|
if (staffMayBypass(actor))
|
|
42695
42860
|
return;
|
|
42696
42861
|
if (wanted.length === 0 || !wanted.some((org) => actor.orgIds.includes(org))) {
|
|
42697
|
-
throw new
|
|
42862
|
+
throw new UnauthorizedError12("Not authorized.");
|
|
42698
42863
|
}
|
|
42699
42864
|
}
|
|
42700
42865
|
function useCustomerSiteController() {
|
|
@@ -45906,7 +46071,7 @@ import {
|
|
|
45906
46071
|
BadRequestError as BadRequestError121,
|
|
45907
46072
|
InternalServerError as InternalServerError43,
|
|
45908
46073
|
NotFoundError as NotFoundError34,
|
|
45909
|
-
UnauthorizedError as
|
|
46074
|
+
UnauthorizedError as UnauthorizedError13,
|
|
45910
46075
|
logger as logger91
|
|
45911
46076
|
} from "@7365admin1/node-server-utils";
|
|
45912
46077
|
|
|
@@ -49440,8 +49605,8 @@ function useHidAmicoService() {
|
|
|
49440
49605
|
await siteAccess.authorizeSite({ siteId, userId: value.userId, permissions });
|
|
49441
49606
|
}
|
|
49442
49607
|
} catch (error) {
|
|
49443
|
-
if (error instanceof
|
|
49444
|
-
throw new
|
|
49608
|
+
if (error instanceof UnauthorizedError13) {
|
|
49609
|
+
throw new UnauthorizedError13("You do not have permission to manage HID access at this site.");
|
|
49445
49610
|
}
|
|
49446
49611
|
throw error;
|
|
49447
49612
|
}
|
|
@@ -49465,7 +49630,7 @@ function useHidAmicoService() {
|
|
|
49465
49630
|
try {
|
|
49466
49631
|
await authorizeSiteForCaller(String(reader.site || ""), userId);
|
|
49467
49632
|
} catch (error) {
|
|
49468
|
-
if (error instanceof
|
|
49633
|
+
if (error instanceof UnauthorizedError13)
|
|
49469
49634
|
throw error;
|
|
49470
49635
|
throw new NotFoundError34(HID_READER_NOT_FOUND);
|
|
49471
49636
|
}
|
|
@@ -49517,10 +49682,10 @@ function useHidAmicoService() {
|
|
|
49517
49682
|
async function assertUserReaderPermission(reader, userId, options = {}) {
|
|
49518
49683
|
const permission = await getReaderPermissionForUser(reader, userId);
|
|
49519
49684
|
if (!permission.granted) {
|
|
49520
|
-
throw new
|
|
49685
|
+
throw new UnauthorizedError13("You do not have HID access permission for this reader.");
|
|
49521
49686
|
}
|
|
49522
49687
|
if (options.intercom && !permission.intercom) {
|
|
49523
|
-
throw new
|
|
49688
|
+
throw new UnauthorizedError13("You do not have HID intercom permission for this reader.");
|
|
49524
49689
|
}
|
|
49525
49690
|
return permission;
|
|
49526
49691
|
}
|
|
@@ -50858,10 +51023,10 @@ function useHidAmicoService() {
|
|
|
50858
51023
|
const hasActiveMembership = memberships.some((membership) => membership.status !== "deleted" && membership.status !== "inactive");
|
|
50859
51024
|
const resolvedIssuer = issuer || (hasResidentMembership ? "resident" : "property_management");
|
|
50860
51025
|
if (resolvedIssuer === "resident" && !hasResidentMembership) {
|
|
50861
|
-
throw new
|
|
51026
|
+
throw new UnauthorizedError13("Only a Resident account can issue a Resident visitor QR code.");
|
|
50862
51027
|
}
|
|
50863
51028
|
if (resolvedIssuer === "property_management" && !hasActiveMembership) {
|
|
50864
|
-
throw new
|
|
51029
|
+
throw new UnauthorizedError13("Only a site Property Management account can issue this visitor QR code.");
|
|
50865
51030
|
}
|
|
50866
51031
|
const configuredGateIds = site?.metadata?.hidQrCodePass?.qrGatePermissions?.[resolvedIssuer === "resident" ? "resident" : "propertyManagement"] || [];
|
|
50867
51032
|
const gateReaderIds = configuredGateIds.filter((readerId) => typeof readerId === "string" && /^[a-f0-9]{24}$/i.test(readerId)).map(String);
|
|
@@ -53454,7 +53619,7 @@ function useVisitorTransactionService() {
|
|
|
53454
53619
|
import Joi69 from "joi";
|
|
53455
53620
|
import moment5 from "moment-timezone";
|
|
53456
53621
|
import { ObjectId as ObjectId91 } from "mongodb";
|
|
53457
|
-
import { BadRequestError as BadRequestError124, NotFoundError as NotFoundError36, InternalServerError as InternalServerError45, UnauthorizedError as
|
|
53622
|
+
import { BadRequestError as BadRequestError124, NotFoundError as NotFoundError36, InternalServerError as InternalServerError45, UnauthorizedError as UnauthorizedError14, logger as logger95 } from "@7365admin1/node-server-utils";
|
|
53458
53623
|
|
|
53459
53624
|
// src/models/visitor-invite.model.ts
|
|
53460
53625
|
import Joi67 from "joi";
|
|
@@ -58238,7 +58403,7 @@ function useVisitorTransactionController() {
|
|
|
58238
58403
|
}
|
|
58239
58404
|
const inviterUserId = callerId(req);
|
|
58240
58405
|
if (!inviterUserId) {
|
|
58241
|
-
next(new
|
|
58406
|
+
next(new UnauthorizedError14("Not authorized."));
|
|
58242
58407
|
return;
|
|
58243
58408
|
}
|
|
58244
58409
|
try {
|
|
@@ -58802,7 +58967,7 @@ function useGuestManagementController() {
|
|
|
58802
58967
|
// src/controllers/person.controller.ts
|
|
58803
58968
|
import Joi72 from "joi";
|
|
58804
58969
|
import { BadRequestError as BadRequestError127, logger as logger98 } from "@7365admin1/node-server-utils";
|
|
58805
|
-
import { NotFoundError as NotFoundError37, UnauthorizedError as
|
|
58970
|
+
import { NotFoundError as NotFoundError37, UnauthorizedError as UnauthorizedError15 } from "@7365admin1/node-server-utils";
|
|
58806
58971
|
|
|
58807
58972
|
// src/utils/self-signup-files.util.ts
|
|
58808
58973
|
function dropUnsentFiles(body) {
|
|
@@ -58854,7 +59019,7 @@ function usePersonController() {
|
|
|
58854
59019
|
}
|
|
58855
59020
|
const org = person.org?.toString() ?? "";
|
|
58856
59021
|
if (!org || !actor.orgIds.includes(org)) {
|
|
58857
|
-
throw new
|
|
59022
|
+
throw new UnauthorizedError15("Not authorized.");
|
|
58858
59023
|
}
|
|
58859
59024
|
return actor;
|
|
58860
59025
|
}
|
|
@@ -58985,7 +59150,7 @@ function usePersonController() {
|
|
|
58985
59150
|
});
|
|
58986
59151
|
} else if (org) {
|
|
58987
59152
|
if (!actor.orgIds.includes(org)) {
|
|
58988
|
-
throw new
|
|
59153
|
+
throw new UnauthorizedError15("Not authorized.");
|
|
58989
59154
|
}
|
|
58990
59155
|
} else {
|
|
58991
59156
|
orgs = actor.orgIds;
|
|
@@ -63305,7 +63470,7 @@ function useSiteFacilityService() {
|
|
|
63305
63470
|
import {
|
|
63306
63471
|
BadRequestError as BadRequestError149,
|
|
63307
63472
|
NotFoundError as NotFoundError43,
|
|
63308
|
-
UnauthorizedError as
|
|
63473
|
+
UnauthorizedError as UnauthorizedError16,
|
|
63309
63474
|
logger as logger117
|
|
63310
63475
|
} from "@7365admin1/node-server-utils";
|
|
63311
63476
|
import Joi84 from "joi";
|
|
@@ -63392,7 +63557,7 @@ function useSiteFacilityController() {
|
|
|
63392
63557
|
} else {
|
|
63393
63558
|
const { actor } = await siteReachOf(req);
|
|
63394
63559
|
if (!actor.isSuperAdmin)
|
|
63395
|
-
throw new
|
|
63560
|
+
throw new UnauthorizedError16("Not authorized.");
|
|
63396
63561
|
}
|
|
63397
63562
|
const data = await _getAll({
|
|
63398
63563
|
search,
|
|
@@ -66427,7 +66592,7 @@ function useBulletinBoardService() {
|
|
|
66427
66592
|
import {
|
|
66428
66593
|
BadRequestError as BadRequestError158,
|
|
66429
66594
|
NotFoundError as NotFoundError51,
|
|
66430
|
-
UnauthorizedError as
|
|
66595
|
+
UnauthorizedError as UnauthorizedError17,
|
|
66431
66596
|
logger as logger126
|
|
66432
66597
|
} from "@7365admin1/node-server-utils";
|
|
66433
66598
|
import Joi92 from "joi";
|
|
@@ -66463,7 +66628,7 @@ function useBulletinBoardController() {
|
|
|
66463
66628
|
} else {
|
|
66464
66629
|
const { canReach } = await siteReachOf(req);
|
|
66465
66630
|
if (!await canReach(scopeOf(value))) {
|
|
66466
|
-
throw new
|
|
66631
|
+
throw new UnauthorizedError17("Not authorized.");
|
|
66467
66632
|
}
|
|
66468
66633
|
}
|
|
66469
66634
|
const data = await _add(value);
|
|
@@ -67020,7 +67185,7 @@ import { BadRequestError as BadRequestError164, logger as logger132 } from "@736
|
|
|
67020
67185
|
import {
|
|
67021
67186
|
BadRequestError as BadRequestError163,
|
|
67022
67187
|
logger as logger131,
|
|
67023
|
-
UnauthorizedError as
|
|
67188
|
+
UnauthorizedError as UnauthorizedError18,
|
|
67024
67189
|
useAtlas as useAtlas90
|
|
67025
67190
|
} from "@7365admin1/node-server-utils";
|
|
67026
67191
|
|
|
@@ -67404,7 +67569,7 @@ function useSiteBillingItemService() {
|
|
|
67404
67569
|
function assertUnitAtSite(buildingUnit, site) {
|
|
67405
67570
|
const siteId = site?.toString?.() ?? "";
|
|
67406
67571
|
if (!siteId || (buildingUnit?.site?.toString() ?? "") !== siteId) {
|
|
67407
|
-
throw new
|
|
67572
|
+
throw new UnauthorizedError18("Not authorized.");
|
|
67408
67573
|
}
|
|
67409
67574
|
}
|
|
67410
67575
|
async function add(value) {
|
|
@@ -67575,7 +67740,7 @@ function useSiteBillingItemService() {
|
|
|
67575
67740
|
message: error.message
|
|
67576
67741
|
});
|
|
67577
67742
|
await session.abortTransaction();
|
|
67578
|
-
if (error instanceof BadRequestError163 || error instanceof
|
|
67743
|
+
if (error instanceof BadRequestError163 || error instanceof UnauthorizedError18) {
|
|
67579
67744
|
throw error;
|
|
67580
67745
|
}
|
|
67581
67746
|
throw new BadRequestError163("Failed to update Site Billing Item.");
|
|
@@ -69544,7 +69709,7 @@ import Joi100 from "joi";
|
|
|
69544
69709
|
|
|
69545
69710
|
// src/utils/resident-unit.util.ts
|
|
69546
69711
|
import { ObjectId as ObjectId125 } from "mongodb";
|
|
69547
|
-
import { UnauthorizedError as
|
|
69712
|
+
import { UnauthorizedError as UnauthorizedError19, useAtlas as useAtlas96 } from "@7365admin1/node-server-utils";
|
|
69548
69713
|
async function requireOwnUnit(req, site, unitId) {
|
|
69549
69714
|
const actor = await resolveInviteActor(callerId(req));
|
|
69550
69715
|
if (staffMayBypass(actor))
|
|
@@ -69552,7 +69717,7 @@ async function requireOwnUnit(req, site, unitId) {
|
|
|
69552
69717
|
const unit = unitId?.toString?.() ?? "";
|
|
69553
69718
|
const siteId = site?.toString?.() ?? "";
|
|
69554
69719
|
if (!ObjectId125.isValid(unit) || !ObjectId125.isValid(siteId) || !actor.id) {
|
|
69555
|
-
throw new
|
|
69720
|
+
throw new UnauthorizedError19("Not authorized.");
|
|
69556
69721
|
}
|
|
69557
69722
|
const db2 = useAtlas96.getDb();
|
|
69558
69723
|
if (!db2)
|
|
@@ -69564,7 +69729,7 @@ async function requireOwnUnit(req, site, unitId) {
|
|
|
69564
69729
|
status: { $ne: "deleted" }
|
|
69565
69730
|
});
|
|
69566
69731
|
if (!own)
|
|
69567
|
-
throw new
|
|
69732
|
+
throw new UnauthorizedError19("Not authorized.");
|
|
69568
69733
|
}
|
|
69569
69734
|
|
|
69570
69735
|
// src/controllers/site-unit-billing.controller.ts
|
|
@@ -78116,7 +78281,7 @@ function useOnlineFormRepo() {
|
|
|
78116
78281
|
import {
|
|
78117
78282
|
BadRequestError as BadRequestError204,
|
|
78118
78283
|
NotFoundError as NotFoundError71,
|
|
78119
|
-
UnauthorizedError as
|
|
78284
|
+
UnauthorizedError as UnauthorizedError20,
|
|
78120
78285
|
logger as logger170
|
|
78121
78286
|
} from "@7365admin1/node-server-utils";
|
|
78122
78287
|
import Joi123 from "joi";
|
|
@@ -78188,10 +78353,10 @@ function useOnlineFormController() {
|
|
|
78188
78353
|
if (site) {
|
|
78189
78354
|
await requireSiteReach(req, site);
|
|
78190
78355
|
} else if (!staffMayBypass(actor)) {
|
|
78191
|
-
throw new
|
|
78356
|
+
throw new UnauthorizedError20("Not authorized.");
|
|
78192
78357
|
}
|
|
78193
78358
|
if (org && !staffMayBypass(actor) && !actor.orgIds.includes(org)) {
|
|
78194
|
-
throw new
|
|
78359
|
+
throw new UnauthorizedError20("Not authorized.");
|
|
78195
78360
|
}
|
|
78196
78361
|
const data = await _getAll({ search, page, limit, status, org, site });
|
|
78197
78362
|
res.json(data);
|
|
@@ -78256,7 +78421,7 @@ function useOnlineFormController() {
|
|
|
78256
78421
|
if (payload.org) {
|
|
78257
78422
|
const { actor } = await siteReachOf(req);
|
|
78258
78423
|
if (!staffMayBypass(actor) && !actor.orgIds.includes(payload.org)) {
|
|
78259
|
-
throw new
|
|
78424
|
+
throw new UnauthorizedError20("Not authorized.");
|
|
78260
78425
|
}
|
|
78261
78426
|
}
|
|
78262
78427
|
await _updateOnlineFormById(_id, payload);
|
|
@@ -85502,7 +85667,7 @@ import {
|
|
|
85502
85667
|
BadRequestError as BadRequestError231,
|
|
85503
85668
|
NotFoundError as NotFoundError80,
|
|
85504
85669
|
InternalServerError as InternalServerError84,
|
|
85505
|
-
UnauthorizedError as
|
|
85670
|
+
UnauthorizedError as UnauthorizedError21,
|
|
85506
85671
|
useAtlas as useAtlas135,
|
|
85507
85672
|
hashPassword as hashPassword4
|
|
85508
85673
|
} from "@7365admin1/node-server-utils";
|
|
@@ -86425,10 +86590,10 @@ function useVerificationServiceV2() {
|
|
|
86425
86590
|
const org = await getOrgById(orgId);
|
|
86426
86591
|
const actor = await resolveInviteActor(invitedBy);
|
|
86427
86592
|
if (!actor.id) {
|
|
86428
|
-
throw new
|
|
86593
|
+
throw new UnauthorizedError21("Sign in to continue.");
|
|
86429
86594
|
}
|
|
86430
86595
|
if (!staffMayBypass(actor) && !actor.orgIds.includes(orgId)) {
|
|
86431
|
-
throw new
|
|
86596
|
+
throw new UnauthorizedError21(
|
|
86432
86597
|
"You can only invite service providers to your own organisation's sites."
|
|
86433
86598
|
);
|
|
86434
86599
|
}
|
|
@@ -86741,7 +86906,7 @@ function useVerificationServiceV2() {
|
|
|
86741
86906
|
const user = await _getUserById(userId).catch(() => null);
|
|
86742
86907
|
const email = user?.email;
|
|
86743
86908
|
if (!email) {
|
|
86744
|
-
throw new
|
|
86909
|
+
throw new UnauthorizedError21("Not authorized.");
|
|
86745
86910
|
}
|
|
86746
86911
|
return email;
|
|
86747
86912
|
}
|
|
@@ -86779,7 +86944,7 @@ function useVerificationServiceV2() {
|
|
|
86779
86944
|
throw new NotFoundError80("Invitation not found.");
|
|
86780
86945
|
}
|
|
86781
86946
|
if (invite.email.toLowerCase() !== email.toLowerCase()) {
|
|
86782
|
-
throw new
|
|
86947
|
+
throw new UnauthorizedError21("Not authorized.");
|
|
86783
86948
|
}
|
|
86784
86949
|
await _setOnboardingById({
|
|
86785
86950
|
_id: inviteId,
|
|
@@ -88366,6 +88531,10 @@ function useRoleControllerV2() {
|
|
|
88366
88531
|
if (value.org) {
|
|
88367
88532
|
await requireOrgReach(req, value.org);
|
|
88368
88533
|
}
|
|
88534
|
+
requireWithinModules(
|
|
88535
|
+
await moduleCeiling(value.org, value.site),
|
|
88536
|
+
value.permissions
|
|
88537
|
+
);
|
|
88369
88538
|
const role = await _createRole(value);
|
|
88370
88539
|
res.status(201).json({ message: "Successfully created role.", data: { role } });
|
|
88371
88540
|
return;
|
|
@@ -90677,7 +90846,7 @@ import Joi159 from "joi";
|
|
|
90677
90846
|
import { ObjectId as ObjectId180 } from "mongodb";
|
|
90678
90847
|
import {
|
|
90679
90848
|
InternalServerError as InternalServerError94,
|
|
90680
|
-
UnauthorizedError as
|
|
90849
|
+
UnauthorizedError as UnauthorizedError22,
|
|
90681
90850
|
useAtlas as useAtlas147
|
|
90682
90851
|
} from "@7365admin1/node-server-utils";
|
|
90683
90852
|
function db() {
|
|
@@ -90693,7 +90862,7 @@ function asObjectId(value) {
|
|
|
90693
90862
|
function requireCaller(req) {
|
|
90694
90863
|
const id = callerId(req);
|
|
90695
90864
|
if (!id || !ObjectId180.isValid(id)) {
|
|
90696
|
-
throw new
|
|
90865
|
+
throw new UnauthorizedError22("Not authorized.");
|
|
90697
90866
|
}
|
|
90698
90867
|
return id;
|
|
90699
90868
|
}
|
|
@@ -90701,7 +90870,7 @@ function requireSelfId(req, claimed) {
|
|
|
90701
90870
|
const caller = requireCaller(req);
|
|
90702
90871
|
const asked = claimed?.toString?.() ?? "";
|
|
90703
90872
|
if (!asked || asked !== caller) {
|
|
90704
|
-
throw new
|
|
90873
|
+
throw new UnauthorizedError22("Not authorized.");
|
|
90705
90874
|
}
|
|
90706
90875
|
return caller;
|
|
90707
90876
|
}
|
|
@@ -90709,11 +90878,11 @@ async function requireOwnChannel(req, channelId) {
|
|
|
90709
90878
|
const caller = requireCaller(req);
|
|
90710
90879
|
const _id = asObjectId(channelId);
|
|
90711
90880
|
if (!_id)
|
|
90712
|
-
throw new
|
|
90881
|
+
throw new UnauthorizedError22("Not authorized.");
|
|
90713
90882
|
const channel = await db().collection("channel-preloved").findOne({ _id }, { projection: { senderId: 1, receiverId: 1, postId: 1 } });
|
|
90714
90883
|
const participants = [channel?.senderId, channel?.receiverId].map((id) => id?.toString?.() ?? "").filter(Boolean);
|
|
90715
90884
|
if (!channel || !participants.includes(caller)) {
|
|
90716
|
-
throw new
|
|
90885
|
+
throw new UnauthorizedError22("Not authorized.");
|
|
90717
90886
|
}
|
|
90718
90887
|
return { caller, channel };
|
|
90719
90888
|
}
|
|
@@ -90721,10 +90890,10 @@ async function requireOwnChatMessage(req, chatId) {
|
|
|
90721
90890
|
const caller = requireCaller(req);
|
|
90722
90891
|
const _id = asObjectId(chatId);
|
|
90723
90892
|
if (!_id)
|
|
90724
|
-
throw new
|
|
90893
|
+
throw new UnauthorizedError22("Not authorized.");
|
|
90725
90894
|
const message = await db().collection("chat-preloved").findOne({ _id }, { projection: { senderId: 1 } });
|
|
90726
90895
|
if (!message || message.senderId?.toString?.() !== caller) {
|
|
90727
|
-
throw new
|
|
90896
|
+
throw new UnauthorizedError22("Not authorized.");
|
|
90728
90897
|
}
|
|
90729
90898
|
return caller;
|
|
90730
90899
|
}
|
|
@@ -91150,7 +91319,7 @@ function useBidPrelovedService() {
|
|
|
91150
91319
|
// src/controllers/bid-preloved.controller.ts
|
|
91151
91320
|
import {
|
|
91152
91321
|
BadRequestError as BadRequestError252,
|
|
91153
|
-
UnauthorizedError as
|
|
91322
|
+
UnauthorizedError as UnauthorizedError23,
|
|
91154
91323
|
logger as logger213
|
|
91155
91324
|
} from "@7365admin1/node-server-utils";
|
|
91156
91325
|
import Joi162 from "joi";
|
|
@@ -91202,7 +91371,7 @@ function useBidPrelovedController() {
|
|
|
91202
91371
|
const bid = await _getById(params.id);
|
|
91203
91372
|
const seller = await sellerOfPost(bid?.postId);
|
|
91204
91373
|
if (!seller || seller !== caller) {
|
|
91205
|
-
throw new
|
|
91374
|
+
throw new UnauthorizedError23("Not authorized.");
|
|
91206
91375
|
}
|
|
91207
91376
|
const data = await _updateStatus(params.id, body.status);
|
|
91208
91377
|
res.status(200).json(data);
|
|
@@ -91226,7 +91395,7 @@ function useBidPrelovedController() {
|
|
|
91226
91395
|
const data = await _getById(params.id);
|
|
91227
91396
|
const seller = await sellerOfPost(data?.postId);
|
|
91228
91397
|
if (data?.buyerId?.toString?.() !== caller && seller !== caller) {
|
|
91229
|
-
throw new
|
|
91398
|
+
throw new UnauthorizedError23("Not authorized.");
|
|
91230
91399
|
}
|
|
91231
91400
|
res.status(200).json(data);
|
|
91232
91401
|
} catch (error2) {
|
|
@@ -91624,7 +91793,7 @@ function useFormEntryRepo() {
|
|
|
91624
91793
|
import {
|
|
91625
91794
|
BadRequestError as BadRequestError254,
|
|
91626
91795
|
NotFoundError as NotFoundError89,
|
|
91627
|
-
UnauthorizedError as
|
|
91796
|
+
UnauthorizedError as UnauthorizedError24,
|
|
91628
91797
|
logger as logger215
|
|
91629
91798
|
} from "@7365admin1/node-server-utils";
|
|
91630
91799
|
import Joi164 from "joi";
|
|
@@ -91734,7 +91903,7 @@ function useFormEntryController() {
|
|
|
91734
91903
|
} else {
|
|
91735
91904
|
const { actor } = await siteReachOf(req);
|
|
91736
91905
|
if (!actor.isSuperAdmin)
|
|
91737
|
-
throw new
|
|
91906
|
+
throw new UnauthorizedError24("Not authorized.");
|
|
91738
91907
|
}
|
|
91739
91908
|
const data = await _getAll({ search, page, limit, status, org, site });
|
|
91740
91909
|
res.json(data);
|
|
@@ -91785,7 +91954,7 @@ function useFormEntryController() {
|
|
|
91785
91954
|
await requireSiteReach(req, rest.site);
|
|
91786
91955
|
delete rest.userId;
|
|
91787
91956
|
if (isOwner && rest.status && rest.status !== "pending") {
|
|
91788
|
-
throw new
|
|
91957
|
+
throw new UnauthorizedError24("Not authorized.");
|
|
91789
91958
|
}
|
|
91790
91959
|
await _updateFormEntryById(_id, rest);
|
|
91791
91960
|
res.json({ message: "Successfully updated online form." });
|
|
@@ -91842,7 +92011,7 @@ function useFormEntryController() {
|
|
|
91842
92011
|
async function residentFormSubmission(req, res, next) {
|
|
91843
92012
|
const userId = callerId(req);
|
|
91844
92013
|
if (!userId) {
|
|
91845
|
-
next(new
|
|
92014
|
+
next(new UnauthorizedError24("Not authorized."));
|
|
91846
92015
|
return;
|
|
91847
92016
|
}
|
|
91848
92017
|
const payload = { ...req.body, userId };
|
|
@@ -93073,7 +93242,7 @@ function useHidAmicoController() {
|
|
|
93073
93242
|
import {
|
|
93074
93243
|
BadRequestError as BadRequestError257,
|
|
93075
93244
|
logger as logger217,
|
|
93076
|
-
UnauthorizedError as
|
|
93245
|
+
UnauthorizedError as UnauthorizedError25
|
|
93077
93246
|
} from "@7365admin1/node-server-utils";
|
|
93078
93247
|
import Joi166 from "joi";
|
|
93079
93248
|
function usePlatformTermsController() {
|
|
@@ -93089,7 +93258,7 @@ function usePlatformTermsController() {
|
|
|
93089
93258
|
async function requireOwnRecord(req, user, { staffMayRead = false } = {}) {
|
|
93090
93259
|
const id = callerId(req);
|
|
93091
93260
|
if (!id) {
|
|
93092
|
-
throw new
|
|
93261
|
+
throw new UnauthorizedError25("Not authorized.");
|
|
93093
93262
|
}
|
|
93094
93263
|
if (id === user) {
|
|
93095
93264
|
return id;
|
|
@@ -93097,7 +93266,7 @@ function usePlatformTermsController() {
|
|
|
93097
93266
|
if (staffMayRead) {
|
|
93098
93267
|
return await requireConsolePermission(req, "platform-terms", "see-platform-terms");
|
|
93099
93268
|
}
|
|
93100
|
-
throw new
|
|
93269
|
+
throw new UnauthorizedError25("Not authorized.");
|
|
93101
93270
|
}
|
|
93102
93271
|
async function getLatest(_req, res, next) {
|
|
93103
93272
|
try {
|
|
@@ -93306,7 +93475,7 @@ function useConsoleAuditController() {
|
|
|
93306
93475
|
// src/controllers/notification.controller.ts
|
|
93307
93476
|
import {
|
|
93308
93477
|
BadRequestError as BadRequestError259,
|
|
93309
|
-
UnauthorizedError as
|
|
93478
|
+
UnauthorizedError as UnauthorizedError26,
|
|
93310
93479
|
logger as logger219
|
|
93311
93480
|
} from "@7365admin1/node-server-utils";
|
|
93312
93481
|
function useNotificationController() {
|
|
@@ -93324,10 +93493,10 @@ function useNotificationController() {
|
|
|
93324
93493
|
function inboxOwner(req, claimed) {
|
|
93325
93494
|
const caller = callerId5(req);
|
|
93326
93495
|
if (!caller) {
|
|
93327
|
-
throw new
|
|
93496
|
+
throw new UnauthorizedError26("Not authorized.");
|
|
93328
93497
|
}
|
|
93329
93498
|
if (claimed && claimed !== caller) {
|
|
93330
|
-
throw new
|
|
93499
|
+
throw new UnauthorizedError26("Not authorized.");
|
|
93331
93500
|
}
|
|
93332
93501
|
return caller;
|
|
93333
93502
|
}
|
|
@@ -93339,7 +93508,7 @@ function useNotificationController() {
|
|
|
93339
93508
|
}
|
|
93340
93509
|
try {
|
|
93341
93510
|
if (!await isSuperAdmin(callerId5(req))) {
|
|
93342
|
-
throw new
|
|
93511
|
+
throw new UnauthorizedError26("Not authorized.");
|
|
93343
93512
|
}
|
|
93344
93513
|
const inserted = await _addMany(value.to, {
|
|
93345
93514
|
title: value.title,
|
|
@@ -94174,6 +94343,7 @@ export {
|
|
|
94174
94343
|
requirePlatformStaff,
|
|
94175
94344
|
resetCameraTransports,
|
|
94176
94345
|
residentAppModuleKeys,
|
|
94346
|
+
residentAppModulesSchema,
|
|
94177
94347
|
residentFormEntry,
|
|
94178
94348
|
resolutionRefusalReason,
|
|
94179
94349
|
resolveCamera,
|