@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/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 logger220 = {
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(logger220.methodMap, level.toLowerCase());
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 = logger220.lookupLevel(level);
558
- if (typeof console !== "undefined" && logger220.lookupLevel(logger220.level) <= level) {
559
- var method = logger220.methodMap[level];
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"] = logger220;
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
- // Nothing sends this. The sender below exists, but the only place emergency
14247
- // contacts are written is `iservice365-API-core`, and the change that would
14248
- // have called it from there (API-core #997) was closed, not merged. Until a
14249
- // caller exists this stays out of the screen rather than offering a switch
14250
- // over a message that never arrives.
14251
- rendered: false
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 = toObjectId25(_id);
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 = toObjectId25(_id);
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 = toObjectId25(_id);
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 toObjectId25(_id) {
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,