@7365admin1/core 3.53.9 → 3.54.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 +24 -0
- package/dist/index.d.ts +229 -5
- package/dist/index.js +259 -16
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +253 -16
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
- package/test/e2e/harness.mjs +9 -0
- package/test/e2e/personal-emergency-chain-scope.e2e.test.mjs +373 -0
- package/test/notification-category.util.test.mjs +29 -11
- package/test/personal-emergency-chain.test.mjs +261 -0
package/dist/index.mjs
CHANGED
|
@@ -537,13 +537,13 @@ var require_logger = __commonJS({
|
|
|
537
537
|
"use strict";
|
|
538
538
|
exports.__esModule = true;
|
|
539
539
|
var _utils = require_utils();
|
|
540
|
-
var
|
|
540
|
+
var logger222 = {
|
|
541
541
|
methodMap: ["debug", "info", "warn", "error"],
|
|
542
542
|
level: "info",
|
|
543
543
|
// Maps a given level value to the `methodMap` indexes above.
|
|
544
544
|
lookupLevel: function lookupLevel(level) {
|
|
545
545
|
if (typeof level === "string") {
|
|
546
|
-
var levelMap = _utils.indexOf(
|
|
546
|
+
var levelMap = _utils.indexOf(logger222.methodMap, level.toLowerCase());
|
|
547
547
|
if (levelMap >= 0) {
|
|
548
548
|
level = levelMap;
|
|
549
549
|
} else {
|
|
@@ -554,9 +554,9 @@ var require_logger = __commonJS({
|
|
|
554
554
|
},
|
|
555
555
|
// Can be overridden in the host environment
|
|
556
556
|
log: function log(level) {
|
|
557
|
-
level =
|
|
558
|
-
if (typeof console !== "undefined" &&
|
|
559
|
-
var method =
|
|
557
|
+
level = logger222.lookupLevel(level);
|
|
558
|
+
if (typeof console !== "undefined" && logger222.lookupLevel(logger222.level) <= level) {
|
|
559
|
+
var method = logger222.methodMap[level];
|
|
560
560
|
if (!console[method]) {
|
|
561
561
|
method = "log";
|
|
562
562
|
}
|
|
@@ -567,7 +567,7 @@ var require_logger = __commonJS({
|
|
|
567
567
|
}
|
|
568
568
|
}
|
|
569
569
|
};
|
|
570
|
-
exports["default"] =
|
|
570
|
+
exports["default"] = logger222;
|
|
571
571
|
module.exports = exports["default"];
|
|
572
572
|
}
|
|
573
573
|
});
|
|
@@ -14243,12 +14243,15 @@ var NOTIFICATION_CATEGORIES = [
|
|
|
14243
14243
|
channelNote: { email: NO_EMAIL_RECIPIENT },
|
|
14244
14244
|
permissionResources: ["emergency-contact"],
|
|
14245
14245
|
safety: true,
|
|
14246
|
-
//
|
|
14247
|
-
// contacts are written
|
|
14248
|
-
//
|
|
14249
|
-
//
|
|
14250
|
-
//
|
|
14251
|
-
|
|
14246
|
+
// A caller exists again. `iservice365-API-core` is still the only place a
|
|
14247
|
+
// site's emergency contacts are written, and its `emergency.service.ts`
|
|
14248
|
+
// now calls the sender below on each of the three writes. That caller
|
|
14249
|
+
// reads THIS flag before it sends (`notificationCategory("emergencyContact")
|
|
14250
|
+
// ?.rendered`), so the two halves cannot land out of order and leave a
|
|
14251
|
+
// safety alert arriving with no switch to turn it off: whichever repository
|
|
14252
|
+
// merges first, the alert only starts once a published core carrying this
|
|
14253
|
+
// line is installed there.
|
|
14254
|
+
rendered: true
|
|
14252
14255
|
},
|
|
14253
14256
|
{
|
|
14254
14257
|
key: "virtualPatrol",
|
|
@@ -29392,7 +29395,7 @@ function usePromoCodeRepo() {
|
|
|
29392
29395
|
}
|
|
29393
29396
|
}
|
|
29394
29397
|
async function updateById(_id, value) {
|
|
29395
|
-
const objectId2 =
|
|
29398
|
+
const objectId2 = toObjectId26(_id);
|
|
29396
29399
|
const changes = promoCodeUpdate(value);
|
|
29397
29400
|
const res = await collection.updateOne(
|
|
29398
29401
|
{ _id: objectId2, deletedAt: null },
|
|
@@ -29405,7 +29408,7 @@ function usePromoCodeRepo() {
|
|
|
29405
29408
|
return res;
|
|
29406
29409
|
}
|
|
29407
29410
|
async function updateStatusById(_id, status) {
|
|
29408
|
-
const objectId2 =
|
|
29411
|
+
const objectId2 = toObjectId26(_id);
|
|
29409
29412
|
const res = await collection.updateOne(
|
|
29410
29413
|
{ _id: objectId2, deletedAt: null },
|
|
29411
29414
|
{ $set: { status } }
|
|
@@ -29417,7 +29420,7 @@ function usePromoCodeRepo() {
|
|
|
29417
29420
|
return res;
|
|
29418
29421
|
}
|
|
29419
29422
|
async function softDeleteById(_id, deletedBy) {
|
|
29420
|
-
const objectId2 =
|
|
29423
|
+
const objectId2 = toObjectId26(_id);
|
|
29421
29424
|
const res = await collection.updateOne(
|
|
29422
29425
|
{ _id: objectId2, deletedAt: null },
|
|
29423
29426
|
{
|
|
@@ -29434,7 +29437,7 @@ function usePromoCodeRepo() {
|
|
|
29434
29437
|
await invalidate(`delete ${objectId2}`);
|
|
29435
29438
|
return res;
|
|
29436
29439
|
}
|
|
29437
|
-
function
|
|
29440
|
+
function toObjectId26(_id) {
|
|
29438
29441
|
const { error } = Joi34.object({
|
|
29439
29442
|
_id: Joi34.string().hex().length(24).required()
|
|
29440
29443
|
}).validate({ _id: String(_id) });
|
|
@@ -92261,6 +92264,228 @@ function useNotificationPreferenceController() {
|
|
|
92261
92264
|
}
|
|
92262
92265
|
return { get, update };
|
|
92263
92266
|
}
|
|
92267
|
+
|
|
92268
|
+
// src/models/personal-emergency-chain.model.ts
|
|
92269
|
+
import { BadRequestError as BadRequestError262 } from "@7365admin1/node-server-utils";
|
|
92270
|
+
import { ObjectId as ObjectId184 } from "mongodb";
|
|
92271
|
+
import Joi167 from "joi";
|
|
92272
|
+
var MAX_PERSONAL_EMERGENCY_CONTACTS = 10;
|
|
92273
|
+
var MIN_RING_SECONDS = 5;
|
|
92274
|
+
var MAX_RING_SECONDS = 120;
|
|
92275
|
+
var DEFAULT_RING_SECONDS = 20;
|
|
92276
|
+
var E164 = /^\+[1-9]\d{1,14}$/;
|
|
92277
|
+
var schemaPersonalEmergencyContact = Joi167.object({
|
|
92278
|
+
name: Joi167.string().trim().min(1).max(120).required(),
|
|
92279
|
+
phone: Joi167.string().trim().pattern(E164).required().messages({
|
|
92280
|
+
"string.pattern.base": "Phone number must be in international format, e.g. +6591234567."
|
|
92281
|
+
}),
|
|
92282
|
+
relationship: Joi167.string().trim().max(60).allow("", null).default(""),
|
|
92283
|
+
order: Joi167.number().integer().min(1).max(MAX_PERSONAL_EMERGENCY_CONTACTS).required(),
|
|
92284
|
+
active: Joi167.boolean().default(true)
|
|
92285
|
+
});
|
|
92286
|
+
var schemaUpdatePersonalEmergencyChain = Joi167.object({
|
|
92287
|
+
contacts: Joi167.array().items(schemaPersonalEmergencyContact).unique("order").max(MAX_PERSONAL_EMERGENCY_CONTACTS).required().messages({
|
|
92288
|
+
"array.unique": "Two contacts cannot share the same position in the chain."
|
|
92289
|
+
}),
|
|
92290
|
+
ringSeconds: Joi167.number().integer().min(MIN_RING_SECONDS).max(MAX_RING_SECONDS).default(DEFAULT_RING_SECONDS),
|
|
92291
|
+
siteDirectoryFallback: Joi167.boolean().default(true),
|
|
92292
|
+
org: Joi167.string().hex().length(24).allow("", null).default(null),
|
|
92293
|
+
site: Joi167.string().hex().length(24).allow("", null).default(null)
|
|
92294
|
+
});
|
|
92295
|
+
var toObjectId25 = (value, what) => {
|
|
92296
|
+
if (value === null || value === void 0 || value === "")
|
|
92297
|
+
return null;
|
|
92298
|
+
const id = value.toString();
|
|
92299
|
+
if (!ObjectId184.isValid(id))
|
|
92300
|
+
throw new BadRequestError262(`Invalid ${what}.`);
|
|
92301
|
+
return new ObjectId184(id);
|
|
92302
|
+
};
|
|
92303
|
+
function MPersonalEmergencyChain(value) {
|
|
92304
|
+
const user = toObjectId25(value.user, "user ID");
|
|
92305
|
+
if (!user)
|
|
92306
|
+
throw new BadRequestError262("Invalid user ID.");
|
|
92307
|
+
const contacts = [...value.contacts ?? []].map((c) => ({
|
|
92308
|
+
name: c.name.trim(),
|
|
92309
|
+
phone: c.phone.trim(),
|
|
92310
|
+
relationship: (c.relationship ?? "").trim(),
|
|
92311
|
+
order: c.order,
|
|
92312
|
+
active: c.active ?? true
|
|
92313
|
+
})).sort((a, b) => a.order - b.order);
|
|
92314
|
+
const seen = /* @__PURE__ */ new Set();
|
|
92315
|
+
for (const contact of contacts) {
|
|
92316
|
+
if (seen.has(contact.order)) {
|
|
92317
|
+
throw new BadRequestError262(
|
|
92318
|
+
"Two contacts cannot share the same position in the chain."
|
|
92319
|
+
);
|
|
92320
|
+
}
|
|
92321
|
+
seen.add(contact.order);
|
|
92322
|
+
}
|
|
92323
|
+
const now = /* @__PURE__ */ new Date();
|
|
92324
|
+
return {
|
|
92325
|
+
user,
|
|
92326
|
+
org: toObjectId25(value.org, "organization ID"),
|
|
92327
|
+
site: toObjectId25(value.site, "site ID"),
|
|
92328
|
+
contacts,
|
|
92329
|
+
ringSeconds: value.ringSeconds ?? DEFAULT_RING_SECONDS,
|
|
92330
|
+
siteDirectoryFallback: value.siteDirectoryFallback ?? true,
|
|
92331
|
+
createdAt: value.createdAt ?? now,
|
|
92332
|
+
updatedAt: now
|
|
92333
|
+
};
|
|
92334
|
+
}
|
|
92335
|
+
function emptyPersonalEmergencyChain(user) {
|
|
92336
|
+
return {
|
|
92337
|
+
user,
|
|
92338
|
+
org: null,
|
|
92339
|
+
site: null,
|
|
92340
|
+
contacts: [],
|
|
92341
|
+
ringSeconds: DEFAULT_RING_SECONDS,
|
|
92342
|
+
siteDirectoryFallback: true
|
|
92343
|
+
};
|
|
92344
|
+
}
|
|
92345
|
+
|
|
92346
|
+
// src/repositories/personal-emergency-chain.repo.ts
|
|
92347
|
+
import { ObjectId as ObjectId185 } from "mongodb";
|
|
92348
|
+
import {
|
|
92349
|
+
AppError as AppError34,
|
|
92350
|
+
BadRequestError as BadRequestError263,
|
|
92351
|
+
InternalServerError as InternalServerError97,
|
|
92352
|
+
logger as logger220,
|
|
92353
|
+
useAtlas as useAtlas152
|
|
92354
|
+
} from "@7365admin1/node-server-utils";
|
|
92355
|
+
function usePersonalEmergencyChainRepo() {
|
|
92356
|
+
const namespace_collection = "personal-emergency-contacts";
|
|
92357
|
+
const getDB2 = () => {
|
|
92358
|
+
const db2 = useAtlas152.getDb();
|
|
92359
|
+
if (!db2) {
|
|
92360
|
+
throw new InternalServerError97("Unable to connect to server.");
|
|
92361
|
+
}
|
|
92362
|
+
return db2;
|
|
92363
|
+
};
|
|
92364
|
+
const collection = () => getDB2().collection(namespace_collection);
|
|
92365
|
+
async function createIndexes() {
|
|
92366
|
+
try {
|
|
92367
|
+
await collection().createIndexes([
|
|
92368
|
+
{ key: { user: 1 }, name: "one_chain_per_user", unique: true }
|
|
92369
|
+
]);
|
|
92370
|
+
return `Successfully created indexes for ${namespace_collection}.`;
|
|
92371
|
+
} catch (error) {
|
|
92372
|
+
logger220.log({ level: "error", message: error.message });
|
|
92373
|
+
throw new InternalServerError97(
|
|
92374
|
+
`Failed to create indexes for ${namespace_collection}.`
|
|
92375
|
+
);
|
|
92376
|
+
}
|
|
92377
|
+
}
|
|
92378
|
+
async function getByUser(user) {
|
|
92379
|
+
if (!ObjectId185.isValid(user)) {
|
|
92380
|
+
throw new BadRequestError263("Invalid user ID format.");
|
|
92381
|
+
}
|
|
92382
|
+
try {
|
|
92383
|
+
const doc = await collection().findOne({ user: new ObjectId185(user) });
|
|
92384
|
+
return doc ?? emptyPersonalEmergencyChain(user);
|
|
92385
|
+
} catch (error) {
|
|
92386
|
+
if (error instanceof AppError34)
|
|
92387
|
+
throw error;
|
|
92388
|
+
logger220.log({ level: "error", message: error.message });
|
|
92389
|
+
throw new InternalServerError97("Failed to retrieve emergency contacts.");
|
|
92390
|
+
}
|
|
92391
|
+
}
|
|
92392
|
+
async function replaceForUser(user, value) {
|
|
92393
|
+
const doc = MPersonalEmergencyChain({ ...value, user });
|
|
92394
|
+
try {
|
|
92395
|
+
await collection().updateOne(
|
|
92396
|
+
{ user: doc.user },
|
|
92397
|
+
{
|
|
92398
|
+
$set: {
|
|
92399
|
+
contacts: doc.contacts,
|
|
92400
|
+
ringSeconds: doc.ringSeconds,
|
|
92401
|
+
siteDirectoryFallback: doc.siteDirectoryFallback,
|
|
92402
|
+
org: doc.org,
|
|
92403
|
+
site: doc.site,
|
|
92404
|
+
updatedAt: doc.updatedAt
|
|
92405
|
+
},
|
|
92406
|
+
$setOnInsert: { user: doc.user, createdAt: doc.createdAt }
|
|
92407
|
+
},
|
|
92408
|
+
{ upsert: true }
|
|
92409
|
+
);
|
|
92410
|
+
return doc;
|
|
92411
|
+
} catch (error) {
|
|
92412
|
+
if (error instanceof AppError34)
|
|
92413
|
+
throw error;
|
|
92414
|
+
logger220.log({ level: "error", message: error.message });
|
|
92415
|
+
throw new InternalServerError97("Failed to save emergency contacts.");
|
|
92416
|
+
}
|
|
92417
|
+
}
|
|
92418
|
+
return { createIndexes, getByUser, replaceForUser };
|
|
92419
|
+
}
|
|
92420
|
+
|
|
92421
|
+
// src/services/personal-emergency-chain.service.ts
|
|
92422
|
+
import { BadRequestError as BadRequestError264 } from "@7365admin1/node-server-utils";
|
|
92423
|
+
function usePersonalEmergencyChainService() {
|
|
92424
|
+
async function getForUser(user) {
|
|
92425
|
+
if (!user)
|
|
92426
|
+
throw new BadRequestError264("No user in session.");
|
|
92427
|
+
return await usePersonalEmergencyChainRepo().getByUser(user);
|
|
92428
|
+
}
|
|
92429
|
+
async function updateForUser(user, value) {
|
|
92430
|
+
if (!user)
|
|
92431
|
+
throw new BadRequestError264("No user in session.");
|
|
92432
|
+
return await usePersonalEmergencyChainRepo().replaceForUser(
|
|
92433
|
+
user,
|
|
92434
|
+
value
|
|
92435
|
+
);
|
|
92436
|
+
}
|
|
92437
|
+
return { getForUser, updateForUser };
|
|
92438
|
+
}
|
|
92439
|
+
|
|
92440
|
+
// src/controllers/personal-emergency-chain.controller.ts
|
|
92441
|
+
import { BadRequestError as BadRequestError265, logger as logger221 } from "@7365admin1/node-server-utils";
|
|
92442
|
+
function usePersonalEmergencyChainController() {
|
|
92443
|
+
const { getForUser, updateForUser } = usePersonalEmergencyChainService();
|
|
92444
|
+
async function get(req, res, next) {
|
|
92445
|
+
const userId = req.params.userId;
|
|
92446
|
+
try {
|
|
92447
|
+
await requireSelfOrPlatformStaff(req, userId, {
|
|
92448
|
+
resource: "users",
|
|
92449
|
+
action: "see-user-details"
|
|
92450
|
+
});
|
|
92451
|
+
res.status(200).json({ data: await getForUser(userId) });
|
|
92452
|
+
} catch (error) {
|
|
92453
|
+
logger221.log({ level: "error", message: error.message });
|
|
92454
|
+
next(error);
|
|
92455
|
+
}
|
|
92456
|
+
}
|
|
92457
|
+
async function update(req, res, next) {
|
|
92458
|
+
const userId = req.params.userId;
|
|
92459
|
+
try {
|
|
92460
|
+
await requireSelfOrPlatformStaff(req, userId, {
|
|
92461
|
+
resource: "users",
|
|
92462
|
+
action: "see-user-details"
|
|
92463
|
+
});
|
|
92464
|
+
} catch (error2) {
|
|
92465
|
+
logger221.log({ level: "error", message: error2.message });
|
|
92466
|
+
next(error2);
|
|
92467
|
+
return;
|
|
92468
|
+
}
|
|
92469
|
+
const { error, value } = schemaUpdatePersonalEmergencyChain.validate(
|
|
92470
|
+
req.body,
|
|
92471
|
+
{ convert: true }
|
|
92472
|
+
);
|
|
92473
|
+
if (error) {
|
|
92474
|
+
next(new BadRequestError265(error.details[0].message));
|
|
92475
|
+
return;
|
|
92476
|
+
}
|
|
92477
|
+
try {
|
|
92478
|
+
res.status(200).json({
|
|
92479
|
+
message: "success",
|
|
92480
|
+
data: await updateForUser(userId, value)
|
|
92481
|
+
});
|
|
92482
|
+
} catch (err) {
|
|
92483
|
+
logger221.log({ level: "error", message: err.message });
|
|
92484
|
+
next(err);
|
|
92485
|
+
}
|
|
92486
|
+
}
|
|
92487
|
+
return { get, update };
|
|
92488
|
+
}
|
|
92264
92489
|
export {
|
|
92265
92490
|
ANPRMode,
|
|
92266
92491
|
APP_BASE_URLS,
|
|
@@ -92306,11 +92531,13 @@ export {
|
|
|
92306
92531
|
CameraType,
|
|
92307
92532
|
ConsoleAuditAction,
|
|
92308
92533
|
ConsoleAuditTarget,
|
|
92534
|
+
DEFAULT_RING_SECONDS,
|
|
92309
92535
|
DEFAULT_SITE_TIMEZONE,
|
|
92310
92536
|
DEVICE_STATUS,
|
|
92311
92537
|
DOBStatus,
|
|
92312
92538
|
DUPLICATE_TERMS_VERSION_MESSAGE,
|
|
92313
92539
|
DayOfWeek,
|
|
92540
|
+
E164,
|
|
92314
92541
|
EAccessCardTypes,
|
|
92315
92542
|
EAccessCardUserTypes,
|
|
92316
92543
|
EmailSender,
|
|
@@ -92333,6 +92560,8 @@ export {
|
|
|
92333
92560
|
MAX_BULK_CAMERA_ROWS,
|
|
92334
92561
|
MAX_CAMERA_CHANNEL,
|
|
92335
92562
|
MAX_CAMERA_NAME_LENGTH,
|
|
92563
|
+
MAX_PERSONAL_EMERGENCY_CONTACTS,
|
|
92564
|
+
MAX_RING_SECONDS,
|
|
92336
92565
|
MAccessCard,
|
|
92337
92566
|
MAccessCardTransaction,
|
|
92338
92567
|
MAddress,
|
|
@@ -92366,6 +92595,7 @@ export {
|
|
|
92366
92595
|
MHidAmicoReader,
|
|
92367
92596
|
MHidSipAccount,
|
|
92368
92597
|
MHidSitePermissions,
|
|
92598
|
+
MIN_RING_SECONDS,
|
|
92369
92599
|
MIncidentReport,
|
|
92370
92600
|
MManpowerDesignations,
|
|
92371
92601
|
MManpowerMonitoring,
|
|
@@ -92391,6 +92621,7 @@ export {
|
|
|
92391
92621
|
MPatrolQuestion,
|
|
92392
92622
|
MPatrolRoute,
|
|
92393
92623
|
MPerson,
|
|
92624
|
+
MPersonalEmergencyChain,
|
|
92394
92625
|
MPlatformTerms,
|
|
92395
92626
|
MPost,
|
|
92396
92627
|
MPostFavorite,
|
|
@@ -92533,6 +92764,7 @@ export {
|
|
|
92533
92764
|
deviceHttpTimeoutMs,
|
|
92534
92765
|
deviceProbeTtlSeconds,
|
|
92535
92766
|
emitNotificationCreated,
|
|
92767
|
+
emptyPersonalEmergencyChain,
|
|
92536
92768
|
encodeHidPacsCard,
|
|
92537
92769
|
entitledSiteScope,
|
|
92538
92770
|
events_namespace_collection,
|
|
@@ -92715,6 +92947,7 @@ export {
|
|
|
92715
92947
|
schemaPatrolQuestion,
|
|
92716
92948
|
schemaPatrolRoute,
|
|
92717
92949
|
schemaPerson,
|
|
92950
|
+
schemaPersonalEmergencyContact,
|
|
92718
92951
|
schemaPlate,
|
|
92719
92952
|
schemaPlatformTerms,
|
|
92720
92953
|
schemaPost,
|
|
@@ -92760,6 +92993,7 @@ export {
|
|
|
92760
92993
|
schemaUpdatePatrolQuestion,
|
|
92761
92994
|
schemaUpdatePatrolRoute,
|
|
92762
92995
|
schemaUpdatePerson,
|
|
92996
|
+
schemaUpdatePersonalEmergencyChain,
|
|
92763
92997
|
schemaUpdatePost,
|
|
92764
92998
|
schemaUpdatePostFavorite,
|
|
92765
92999
|
schemaUpdateServiceProviderBilling,
|
|
@@ -92940,6 +93174,9 @@ export {
|
|
|
92940
93174
|
usePatrolRouteRepo,
|
|
92941
93175
|
usePersonController,
|
|
92942
93176
|
usePersonRepo,
|
|
93177
|
+
usePersonalEmergencyChainController,
|
|
93178
|
+
usePersonalEmergencyChainRepo,
|
|
93179
|
+
usePersonalEmergencyChainService,
|
|
92943
93180
|
usePlatformTermsController,
|
|
92944
93181
|
usePlatformTermsRepo,
|
|
92945
93182
|
usePlatformTermsService,
|