@7365admin1/core 3.53.8 → 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",
@@ -24520,22 +24523,6 @@ function selectHealthTargets(cameraIds, cap, isValidId) {
24520
24523
  truncated: unique.length > limit
24521
24524
  };
24522
24525
  }
24523
- function orgSiteScope(params) {
24524
- const org = idOf(params.org);
24525
- if (!org)
24526
- return null;
24527
- const inOrg = params.memberships.filter(
24528
- (membership) => idOf(membership.org) === org
24529
- );
24530
- if (inOrg.some(
24531
- (membership) => !idOf(membership.siteId) || membership.orgLevelRole === true
24532
- )) {
24533
- return null;
24534
- }
24535
- return Array.from(
24536
- new Set(inOrg.map((membership) => idOf(membership.siteId)).filter(Boolean))
24537
- );
24538
- }
24539
24526
  function entitledSiteScope(params) {
24540
24527
  const sites = /* @__PURE__ */ new Set();
24541
24528
  for (const membership of params.memberships) {
@@ -29408,7 +29395,7 @@ function usePromoCodeRepo() {
29408
29395
  }
29409
29396
  }
29410
29397
  async function updateById(_id, value) {
29411
- const objectId2 = toObjectId25(_id);
29398
+ const objectId2 = toObjectId26(_id);
29412
29399
  const changes = promoCodeUpdate(value);
29413
29400
  const res = await collection.updateOne(
29414
29401
  { _id: objectId2, deletedAt: null },
@@ -29421,7 +29408,7 @@ function usePromoCodeRepo() {
29421
29408
  return res;
29422
29409
  }
29423
29410
  async function updateStatusById(_id, status) {
29424
- const objectId2 = toObjectId25(_id);
29411
+ const objectId2 = toObjectId26(_id);
29425
29412
  const res = await collection.updateOne(
29426
29413
  { _id: objectId2, deletedAt: null },
29427
29414
  { $set: { status } }
@@ -29433,7 +29420,7 @@ function usePromoCodeRepo() {
29433
29420
  return res;
29434
29421
  }
29435
29422
  async function softDeleteById(_id, deletedBy) {
29436
- const objectId2 = toObjectId25(_id);
29423
+ const objectId2 = toObjectId26(_id);
29437
29424
  const res = await collection.updateOne(
29438
29425
  { _id: objectId2, deletedAt: null },
29439
29426
  {
@@ -29450,7 +29437,7 @@ function usePromoCodeRepo() {
29450
29437
  await invalidate(`delete ${objectId2}`);
29451
29438
  return res;
29452
29439
  }
29453
- function toObjectId25(_id) {
29440
+ function toObjectId26(_id) {
29454
29441
  const { error } = Joi34.object({
29455
29442
  _id: Joi34.string().hex().length(24).required()
29456
29443
  }).validate({ _id: String(_id) });
@@ -41987,8 +41974,7 @@ function useCustomerSiteController() {
41987
41974
  });
41988
41975
  try {
41989
41976
  await requireOrgAccess(req, org);
41990
- const actor = await resolveInviteActor(callerId(req));
41991
- const siteScope = staffMayBypass(actor) ? null : orgSiteScope({ memberships: actor.memberships, org });
41977
+ const siteScope = await entitledSites(req);
41992
41978
  const data = await _getAll({
41993
41979
  search,
41994
41980
  page,
@@ -92278,6 +92264,228 @@ function useNotificationPreferenceController() {
92278
92264
  }
92279
92265
  return { get, update };
92280
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
+ }
92281
92489
  export {
92282
92490
  ANPRMode,
92283
92491
  APP_BASE_URLS,
@@ -92323,11 +92531,13 @@ export {
92323
92531
  CameraType,
92324
92532
  ConsoleAuditAction,
92325
92533
  ConsoleAuditTarget,
92534
+ DEFAULT_RING_SECONDS,
92326
92535
  DEFAULT_SITE_TIMEZONE,
92327
92536
  DEVICE_STATUS,
92328
92537
  DOBStatus,
92329
92538
  DUPLICATE_TERMS_VERSION_MESSAGE,
92330
92539
  DayOfWeek,
92540
+ E164,
92331
92541
  EAccessCardTypes,
92332
92542
  EAccessCardUserTypes,
92333
92543
  EmailSender,
@@ -92350,6 +92560,8 @@ export {
92350
92560
  MAX_BULK_CAMERA_ROWS,
92351
92561
  MAX_CAMERA_CHANNEL,
92352
92562
  MAX_CAMERA_NAME_LENGTH,
92563
+ MAX_PERSONAL_EMERGENCY_CONTACTS,
92564
+ MAX_RING_SECONDS,
92353
92565
  MAccessCard,
92354
92566
  MAccessCardTransaction,
92355
92567
  MAddress,
@@ -92383,6 +92595,7 @@ export {
92383
92595
  MHidAmicoReader,
92384
92596
  MHidSipAccount,
92385
92597
  MHidSitePermissions,
92598
+ MIN_RING_SECONDS,
92386
92599
  MIncidentReport,
92387
92600
  MManpowerDesignations,
92388
92601
  MManpowerMonitoring,
@@ -92408,6 +92621,7 @@ export {
92408
92621
  MPatrolQuestion,
92409
92622
  MPatrolRoute,
92410
92623
  MPerson,
92624
+ MPersonalEmergencyChain,
92411
92625
  MPlatformTerms,
92412
92626
  MPost,
92413
92627
  MPostFavorite,
@@ -92550,6 +92764,7 @@ export {
92550
92764
  deviceHttpTimeoutMs,
92551
92765
  deviceProbeTtlSeconds,
92552
92766
  emitNotificationCreated,
92767
+ emptyPersonalEmergencyChain,
92553
92768
  encodeHidPacsCard,
92554
92769
  entitledSiteScope,
92555
92770
  events_namespace_collection,
@@ -92606,7 +92821,6 @@ export {
92606
92821
  online_forms_namespace_collection,
92607
92822
  orgLevelRoles,
92608
92823
  orgSchema,
92609
- orgSiteScope,
92610
92824
  overnight_parking_requests_namespace_collection,
92611
92825
  parseCameraChannel,
92612
92826
  parseCameraHost,
@@ -92733,6 +92947,7 @@ export {
92733
92947
  schemaPatrolQuestion,
92734
92948
  schemaPatrolRoute,
92735
92949
  schemaPerson,
92950
+ schemaPersonalEmergencyContact,
92736
92951
  schemaPlate,
92737
92952
  schemaPlatformTerms,
92738
92953
  schemaPost,
@@ -92778,6 +92993,7 @@ export {
92778
92993
  schemaUpdatePatrolQuestion,
92779
92994
  schemaUpdatePatrolRoute,
92780
92995
  schemaUpdatePerson,
92996
+ schemaUpdatePersonalEmergencyChain,
92781
92997
  schemaUpdatePost,
92782
92998
  schemaUpdatePostFavorite,
92783
92999
  schemaUpdateServiceProviderBilling,
@@ -92958,6 +93174,9 @@ export {
92958
93174
  usePatrolRouteRepo,
92959
93175
  usePersonController,
92960
93176
  usePersonRepo,
93177
+ usePersonalEmergencyChainController,
93178
+ usePersonalEmergencyChainRepo,
93179
+ usePersonalEmergencyChainService,
92961
93180
  usePlatformTermsController,
92962
93181
  usePlatformTermsRepo,
92963
93182
  usePlatformTermsService,