@7365admin1/core 3.52.2-staging.239 → 3.52.2-staging.240

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.
@@ -0,0 +1,11 @@
1
+ ---
2
+ "@7365admin1/core": patch
3
+ ---
4
+
5
+ Write camera failures in words an operator can act on, and stop offering plates whose vehicle has been deleted.
6
+
7
+ Camera failure messages no longer carry a camera's `_id`, its host, an IP, a port or a raw driver error. A camera is named by its display name and direction ("North Gate (entry)", or "the entry camera" when it has no name), and the reason is plain language ("the camera could not be reached"). The full technical detail — record id, host, and the device's or driver's own text — goes to the server log instead. The same treatment is applied to create, approve, restore, block, unblock, edit and delete; `blocklistVehicles`, `removeFromBlocklist` and `reactivateVehicleById` previously said only "Failed to delete/update/add plate number to ANPR", which named no camera and said nothing about what happened to the record. Blocking now states the opposite consequence to saving, because a failed blacklist write leaves the car able to get in.
8
+
9
+ `site.people.plates[]` no longer keeps a plate after its vehicle is deleted. The pull matched on `recNo`, which Dahua numbers per device, so the copy on the vehicle and the copy on the person diverged and the update silently matched nothing; it now matches on the plate number, and leaves the plate alone when a second live vehicle still holds it. Unblocking, which soft-deletes the record, now pulls the plate too. `GET /api/people/unit/:unit` and `GET /api/people` also drop any plate whose vehicle records are all deleted, so rows that had already drifted are not offered either. A plate the resident merely declared, with no vehicle record at all, is unchanged.
10
+
11
+ Unblocking a vehicle at a site with no ANPR camera now succeeds instead of failing with "No site cameras found." — there is nothing to revoke, which is the state the unblock is trying to reach. Approve, Restore, Block and Edit still refuse in that state, now saying why and what did not happen.
package/dist/index.d.ts CHANGED
@@ -2528,6 +2528,14 @@ type AnprRevokeOutcome = {
2528
2528
  statusCode?: number;
2529
2529
  /** Why it failed, in words an operator can act on. Empty when `ok`. */
2530
2530
  reason: string;
2531
+ /**
2532
+ * The technical cause, for the SERVER LOG only. Never rendered.
2533
+ *
2534
+ * `reason` used to BE this string, so a driver error went straight to the
2535
+ * browser: "connect EHOSTUNREACH 129.126.212.21:8081" put an internal IP and
2536
+ * port in front of a condo manager, who can do nothing with either.
2537
+ */
2538
+ detail?: string;
2531
2539
  };
2532
2540
 
2533
2541
  declare enum CameraType {
package/dist/index.js CHANGED
@@ -17573,9 +17573,39 @@ function usePersonRepo() {
17573
17573
  throw new import_node_server_utils41.InternalServerError("Unable to connect to server.");
17574
17574
  }
17575
17575
  const collection = db2.collection(site_people_namespace_collection);
17576
+ const vehiclesCollection = db2.collection("vehicles");
17576
17577
  const { delNamespace, getCache, setCache } = (0, import_node_server_utils41.useCache)(
17577
17578
  site_people_namespace_collection
17578
17579
  );
17580
+ async function withoutDeletedPlates(people) {
17581
+ const numbers = Array.from(
17582
+ new Set(
17583
+ people.flatMap(
17584
+ (person) => (Array.isArray(person?.plates) ? person.plates : []).map((plate) => plate?.plateNumber).filter((value) => typeof value === "string" && !!value)
17585
+ )
17586
+ )
17587
+ );
17588
+ if (!numbers.length)
17589
+ return people;
17590
+ const records = await vehiclesCollection.find(
17591
+ { plateNumber: { $in: numbers } },
17592
+ { projection: { plateNumber: 1, status: 1, site: 1 } }
17593
+ ).toArray();
17594
+ const live = /* @__PURE__ */ new Map();
17595
+ for (const record of records) {
17596
+ const key = `${String(record.site)}|${record.plateNumber}`;
17597
+ live.set(key, live.get(key) === true || record.status !== "deleted");
17598
+ }
17599
+ return people.map((person) => {
17600
+ if (!Array.isArray(person?.plates) || !person.plates.length)
17601
+ return person;
17602
+ const plates = person.plates.filter((plate) => {
17603
+ const key = `${String(person.site)}|${plate?.plateNumber}`;
17604
+ return !live.has(key) || live.get(key) === true;
17605
+ });
17606
+ return plates.length === person.plates.length ? person : { ...person, plates };
17607
+ });
17608
+ }
17579
17609
  async function createIndexes() {
17580
17610
  try {
17581
17611
  await collection.createIndexes([
@@ -17751,7 +17781,12 @@ function usePersonRepo() {
17751
17781
  collection.aggregate([{ $match: query2 }, { $count: "total" }], { session }).toArray()
17752
17782
  ]);
17753
17783
  const totalCount = countResult[0]?.total || 0;
17754
- const data = (0, import_node_server_utils41.paginate)(items, page, limit, totalCount);
17784
+ const data = (0, import_node_server_utils41.paginate)(
17785
+ await withoutDeletedPlates(items),
17786
+ page,
17787
+ limit,
17788
+ totalCount
17789
+ );
17755
17790
  return data;
17756
17791
  } catch (error) {
17757
17792
  throw error;
@@ -18004,7 +18039,7 @@ function usePersonRepo() {
18004
18039
  },
18005
18040
  { $unwind: { path: "$unit", preserveNullAndEmptyArrays: true } }
18006
18041
  ]).toArray();
18007
- return data;
18042
+ return await withoutDeletedPlates(data);
18008
18043
  } catch (error) {
18009
18044
  throw new import_node_server_utils41.InternalServerError(
18010
18045
  "Failed to fetch people type residents by unit."
@@ -18203,10 +18238,22 @@ function usePersonRepo() {
18203
18238
  "Cannot remove a vehicle plate without a person or a site."
18204
18239
  );
18205
18240
  }
18206
- const match = recNo ? { recNo } : scope?.plateNumber ? { plateNumber: scope.plateNumber } : {};
18241
+ const match = scope?.plateNumber ? { plateNumber: scope.plateNumber } : recNo ? { recNo } : {};
18207
18242
  if (!Object.keys(match).length)
18208
18243
  return;
18209
18244
  const matchKey = Object.keys(match)[0];
18245
+ if (matchKey === "plateNumber" && site) {
18246
+ const survivor = await vehiclesCollection.findOne(
18247
+ {
18248
+ site,
18249
+ plateNumber: scope.plateNumber,
18250
+ status: { $ne: "deleted" }
18251
+ },
18252
+ { projection: { _id: 1 }, session }
18253
+ );
18254
+ if (survivor)
18255
+ return;
18256
+ }
18210
18257
  const filter = {
18211
18258
  [`plates.${matchKey}`]: match[matchKey]
18212
18259
  };
@@ -22443,34 +22490,119 @@ function anprRevokeOutcome(response) {
22443
22490
  if (statusCode === 200)
22444
22491
  return { ok: true, statusCode, reason: "" };
22445
22492
  if (statusCode === void 0) {
22446
- return { ok: false, reason: "the camera did not answer" };
22493
+ return {
22494
+ ok: false,
22495
+ reason: CAMERA_FAILURE_REASONS.transient,
22496
+ detail: "the device reply carried no status code"
22497
+ };
22447
22498
  }
22448
- return { ok: false, statusCode, reason: `the camera answered ${statusCode}` };
22499
+ return {
22500
+ ok: false,
22501
+ statusCode,
22502
+ reason: reasonForStatus(statusCode),
22503
+ detail: `the camera answered HTTP ${statusCode}`
22504
+ };
22449
22505
  }
22450
22506
  function anprRevokeThrew(error) {
22451
- const message = error?.message ?? String(error ?? "");
22452
- return { ok: false, reason: message || "the camera could not be reached" };
22507
+ const message = String(error?.message ?? error ?? "");
22508
+ return {
22509
+ ok: false,
22510
+ reason: cameraFailureReason(message),
22511
+ detail: message || "the request threw with no message"
22512
+ };
22513
+ }
22514
+ function cameraFailureSentence(options) {
22515
+ const { failures, operation, outcome, consequence } = options;
22516
+ return `The plate number could not be ${operation} ${cameraCount(failures.length)}: ${cameraDetail(failures)}. The vehicle has NOT been ${outcome} \u2014 ${consequence}. Please check the ${failures.length === 1 ? "camera" : "cameras"} and try again.`;
22453
22517
  }
22454
22518
  function anprRevokeFailureMessage(failures, action = "deleted") {
22455
- const detail = failures.map((failure) => `${failure.camera} (${failure.reason})`).join("; ");
22456
- return `The plate number could not be removed from ${failures.length} ANPR camera(s): ${detail}. The vehicle has NOT been ${action} \u2014 it would still open the barrier. Check the camera, then try again.`;
22519
+ return cameraFailureSentence({
22520
+ failures,
22521
+ operation: "removed from",
22522
+ outcome: action,
22523
+ consequence: "it would still open the barrier"
22524
+ });
22457
22525
  }
22458
- var WRITE_REASONS = {
22526
+ function anprRestoreFailureMessage(failures) {
22527
+ return cameraFailureSentence({
22528
+ failures,
22529
+ operation: "added to",
22530
+ outcome: "restored",
22531
+ consequence: "it would not open the barrier"
22532
+ });
22533
+ }
22534
+ function anprBlockFailureMessage(failures) {
22535
+ return cameraFailureSentence({
22536
+ failures,
22537
+ operation: "added to",
22538
+ outcome: "blocked",
22539
+ consequence: "it would still open the barrier"
22540
+ });
22541
+ }
22542
+ function anprUnblockFailureMessage(failures) {
22543
+ return cameraFailureSentence({
22544
+ failures,
22545
+ operation: "removed from",
22546
+ outcome: "unblocked",
22547
+ consequence: "it would still be turned away at the barrier"
22548
+ });
22549
+ }
22550
+ var CAMERA_FAILURE_REASONS = {
22459
22551
  challenge: "the camera refused the login saved for it (its username or password is wrong, or the account is temporarily locked after failed logins)",
22460
22552
  rejected: "the camera rejected the request \u2014 its account is not allowed to change the plate list",
22461
22553
  transient: "the camera could not be reached"
22462
22554
  };
22463
- function anprWriteReason(detail) {
22555
+ function cameraFailureReason(detail) {
22464
22556
  const message = detail?.message ?? String(detail ?? "");
22465
- return WRITE_REASONS[classifyDahuaFailure(message)];
22557
+ return CAMERA_FAILURE_REASONS[classifyDahuaFailure(message)];
22558
+ }
22559
+ function reasonForStatus(statusCode) {
22560
+ if (statusCode === 401)
22561
+ return CAMERA_FAILURE_REASONS.challenge;
22562
+ if (statusCode === 403)
22563
+ return CAMERA_FAILURE_REASONS.rejected;
22564
+ return "the camera answered with an error";
22466
22565
  }
22566
+ var DIRECTION_WORDS2 = {
22567
+ entry: "entry",
22568
+ exit: "exit",
22569
+ both: "entry/exit",
22570
+ visitors: "visitor lane",
22571
+ residents: "resident lane"
22572
+ };
22573
+ function cameraLabel(camera) {
22574
+ const name = String(camera?.name ?? "").trim();
22575
+ const direction = DIRECTION_WORDS2[String(camera?.direction ?? "").trim().toLowerCase()];
22576
+ if (name && direction)
22577
+ return `${name} (${direction})`;
22578
+ if (name)
22579
+ return name;
22580
+ if (direction)
22581
+ return `the ${direction} camera`;
22582
+ return "an ANPR camera";
22583
+ }
22584
+ function cameraCount(total) {
22585
+ return total === 1 ? "1 ANPR camera" : `${total} ANPR cameras`;
22586
+ }
22587
+ function cameraDetail(failures) {
22588
+ const parts = failures.map((f) => `${f.camera} \u2014 ${f.reason}`);
22589
+ return Array.from(new Set(parts)).join("; ");
22590
+ }
22591
+ var anprWriteReason = cameraFailureReason;
22467
22592
  function anprWriteFailureMessage(failures, action = "added") {
22468
- const detail = failures.map((failure) => `${failure.camera} (${failure.reason})`).join("; ");
22469
- return `The plate number could not be ${action} to ${failures.length} ANPR camera(s): ${detail}. The vehicle has NOT been saved \u2014 it would not open the barrier. Check the camera, then try again.`;
22593
+ return cameraFailureSentence({
22594
+ failures,
22595
+ operation: "added to",
22596
+ outcome: action === "added" ? "saved" : action,
22597
+ consequence: "it would not open the barrier"
22598
+ });
22470
22599
  }
22471
22600
  function anprNoCameraMessage(type) {
22472
22601
  return `Vehicle saved to the ${type} record. There is no active ANPR camera at this site, so the plate has NOT been registered on a barrier yet and the gate will not open for it. It will need to be added once a camera is set up and switched on.`;
22473
22602
  }
22603
+ function anprNoCameraRefusal(outcome) {
22604
+ return `There is no ANPR camera set up at this site, so the plate cannot be registered on a barrier. The vehicle has NOT been ${outcome}. Add a camera in Settings first, then try again.`;
22605
+ }
22474
22606
 
22475
22607
  // src/services/dahua.service.ts
22476
22608
  function notifyCameraFault(camera, message) {
@@ -35188,8 +35320,11 @@ function parseDahuaFind(text2) {
35188
35320
  plateNumber: text2.match(/records\[0\]\.PlateNumber=(.*)/)?.[1]?.trim()
35189
35321
  };
35190
35322
  }
35191
- function cameraLabel(camera) {
35192
- return String(camera?.name || camera?._id || "ANPR camera");
35323
+ function logCameraFailure(where, camera, detail) {
35324
+ const raw = String(detail?.message ?? detail ?? "");
35325
+ import_node_server_utils99.logger.error(
35326
+ `${where}: ANPR camera ${String(camera?._id ?? "unknown id")} host ${String(camera?.host ?? "unknown host")} name ${JSON.stringify(String(camera?.name ?? ""))} direction ${JSON.stringify(String(camera?.direction ?? ""))} failed: ${raw || "no detail"}`
35327
+ );
35193
35328
  }
35194
35329
  function useVehicleService() {
35195
35330
  const {
@@ -35336,9 +35471,7 @@ function useVehicleService() {
35336
35471
  for (const camera of siteCameras) {
35337
35472
  const { host, username, password } = camera;
35338
35473
  const plateNumber2 = vehicleValue.plateNumber;
35339
- const label = String(
35340
- camera?.name || camera?._id || "ANPR camera"
35341
- );
35474
+ const label = cameraLabel(camera);
35342
35475
  const dahuaPayload = {
35343
35476
  host,
35344
35477
  username,
@@ -35354,6 +35487,11 @@ function useVehicleService() {
35354
35487
  const responseStatus = dahuaResponse?.statusCode || dahuaResponse?.status || dahuaResponse?.res?.status || "unknown";
35355
35488
  const responseData = dahuaResponse?.data?.toString("utf-8") ?? "";
35356
35489
  if (responseStatus !== 200) {
35490
+ logCameraFailure(
35491
+ "add",
35492
+ camera,
35493
+ `HTTP ${responseStatus}: ${responseData.slice(0, 500)}`
35494
+ );
35357
35495
  failures.push({
35358
35496
  camera: label,
35359
35497
  reason: anprWriteReason(`HTTP ${responseStatus}`)
@@ -35375,6 +35513,7 @@ function useVehicleService() {
35375
35513
  );
35376
35514
  }
35377
35515
  } catch (error) {
35516
+ logCameraFailure("add", camera, error);
35378
35517
  failures.push({ camera: label, reason: anprWriteReason(error) });
35379
35518
  }
35380
35519
  }
@@ -35452,9 +35591,7 @@ function useVehicleService() {
35452
35591
  const host = camera.host;
35453
35592
  const username = camera.username;
35454
35593
  const password = camera.password;
35455
- const label = String(
35456
- camera?.name || camera?._id || "ANPR camera"
35457
- );
35594
+ const label = cameraLabel(camera);
35458
35595
  let recnoForCamera = recno;
35459
35596
  if (plateNumber) {
35460
35597
  try {
@@ -35473,10 +35610,9 @@ function useVehicleService() {
35473
35610
  if (found.recNo)
35474
35611
  recnoForCamera = found.recNo;
35475
35612
  } catch (error) {
35476
- failures.push({
35477
- camera: label,
35478
- reason: anprRevokeThrew(error).reason
35479
- });
35613
+ const outcome2 = anprRevokeThrew(error);
35614
+ logCameraFailure("deleteVehicle lookup", camera, outcome2.detail);
35615
+ failures.push({ camera: label, reason: outcome2.reason });
35480
35616
  continue;
35481
35617
  }
35482
35618
  }
@@ -35490,9 +35626,14 @@ function useVehicleService() {
35490
35626
  mode: _mode
35491
35627
  });
35492
35628
  if (!outcome?.ok) {
35629
+ logCameraFailure(
35630
+ "deleteVehicle revoke",
35631
+ camera,
35632
+ outcome?.detail ?? "no outcome returned"
35633
+ );
35493
35634
  failures.push({
35494
35635
  camera: label,
35495
- reason: outcome?.reason || "the camera did not answer"
35636
+ reason: outcome?.reason || "the camera could not be reached"
35496
35637
  });
35497
35638
  }
35498
35639
  }
@@ -35572,7 +35713,7 @@ function useVehicleService() {
35572
35713
  page++;
35573
35714
  } while (page <= pages);
35574
35715
  if (!siteCameras.length) {
35575
- throw new import_node_server_utils99.BadRequestError("No site cameras found.");
35716
+ throw new import_node_server_utils99.BadRequestError(anprNoCameraRefusal("approved"));
35576
35717
  }
35577
35718
  for (const camera of siteCameras) {
35578
35719
  const { host, username, password } = camera;
@@ -35588,6 +35729,11 @@ function useVehicleService() {
35588
35729
  };
35589
35730
  const dahuaResponse = await _addPlateNumber(dahuaPayload);
35590
35731
  if (dahuaResponse?.statusCode != 200) {
35732
+ logCameraFailure(
35733
+ "approveVehicleById",
35734
+ camera,
35735
+ `HTTP ${dahuaResponse?.statusCode ?? "none"}`
35736
+ );
35591
35737
  throw new import_node_server_utils99.BadRequestError(
35592
35738
  anprWriteFailureMessage([
35593
35739
  {
@@ -35735,7 +35881,7 @@ function useVehicleService() {
35735
35881
  page++;
35736
35882
  } while (page <= pages);
35737
35883
  if (!siteCameras.length) {
35738
- throw new import_node_server_utils99.BadRequestError("No site cameras found.");
35884
+ throw new import_node_server_utils99.BadRequestError(anprNoCameraRefusal("restored"));
35739
35885
  }
35740
35886
  for (const camera of siteCameras) {
35741
35887
  const { host, username, password } = camera;
@@ -35751,7 +35897,21 @@ function useVehicleService() {
35751
35897
  };
35752
35898
  const dahuaResponse = await _addPlateNumber(dahuaPayload);
35753
35899
  if (dahuaResponse?.statusCode !== 200) {
35754
- throw new import_node_server_utils99.BadRequestError("Failed to add plate number to ANPR");
35900
+ logCameraFailure(
35901
+ "reactivateVehicleById",
35902
+ camera,
35903
+ `HTTP ${dahuaResponse?.statusCode ?? "none"}`
35904
+ );
35905
+ throw new import_node_server_utils99.BadRequestError(
35906
+ anprRestoreFailureMessage([
35907
+ {
35908
+ camera: cameraLabel(camera),
35909
+ reason: anprWriteReason(
35910
+ `HTTP ${dahuaResponse?.statusCode ?? "none"}`
35911
+ )
35912
+ }
35913
+ ])
35914
+ );
35755
35915
  }
35756
35916
  const responseData = dahuaResponse?.data.toString("utf-8");
35757
35917
  value.recNo = responseData.split("=")[1]?.trim();
@@ -35835,7 +35995,7 @@ function useVehicleService() {
35835
35995
  page++;
35836
35996
  } while (page <= pages);
35837
35997
  if (!siteCameras.length) {
35838
- throw new import_node_server_utils99.BadRequestError("No site cameras found.");
35998
+ throw new import_node_server_utils99.BadRequestError(anprNoCameraRefusal("updated"));
35839
35999
  }
35840
36000
  for (const camera of siteCameras) {
35841
36001
  const { host, username, password } = camera;
@@ -35852,12 +36012,17 @@ function useVehicleService() {
35852
36012
  removePlateNumber
35853
36013
  );
35854
36014
  if (!responseForDeletion?.ok) {
36015
+ logCameraFailure(
36016
+ "updateVehicleById revoke",
36017
+ camera,
36018
+ responseForDeletion?.detail ?? "no outcome returned"
36019
+ );
35855
36020
  throw new import_node_server_utils99.BadRequestError(
35856
36021
  anprRevokeFailureMessage(
35857
36022
  [
35858
36023
  {
35859
36024
  camera: cameraLabel(camera),
35860
- reason: responseForDeletion?.reason || "the camera did not answer"
36025
+ reason: responseForDeletion?.reason || "the camera could not be reached"
35861
36026
  }
35862
36027
  ],
35863
36028
  "updated"
@@ -35879,6 +36044,7 @@ function useVehicleService() {
35879
36044
  try {
35880
36045
  dahuaResponse = await _addPlateNumber(dahuaPayload);
35881
36046
  } catch (error) {
36047
+ logCameraFailure("updateVehicleById write", camera, error);
35882
36048
  const restored = await restorePlateOnCamera({
35883
36049
  ...removePlateNumber,
35884
36050
  plateNumber: _plateNumber,
@@ -35894,6 +36060,11 @@ function useVehicleService() {
35894
36060
  );
35895
36061
  }
35896
36062
  if (dahuaResponse?.statusCode !== 200) {
36063
+ logCameraFailure(
36064
+ "updateVehicleById write",
36065
+ camera,
36066
+ `HTTP ${dahuaResponse?.statusCode ?? "none"}`
36067
+ );
35897
36068
  throw new import_node_server_utils99.BadRequestError(
35898
36069
  anprWriteFailureMessage(
35899
36070
  [
@@ -35940,11 +36111,16 @@ function useVehicleService() {
35940
36111
  recno: existing.recNo || _recNo
35941
36112
  });
35942
36113
  if (!outcome?.ok) {
36114
+ logCameraFailure(
36115
+ "updateVehicleById revoke",
36116
+ camera,
36117
+ outcome?.detail ?? "no outcome returned"
36118
+ );
35943
36119
  throw new import_node_server_utils99.BadRequestError(
35944
36120
  anprRevokeFailureMessage([
35945
36121
  {
35946
36122
  camera: cameraLabel(camera),
35947
- reason: outcome?.reason || "the camera did not answer"
36123
+ reason: outcome?.reason || "the camera could not be reached"
35948
36124
  }
35949
36125
  ], "updated")
35950
36126
  );
@@ -35965,6 +36141,7 @@ function useVehicleService() {
35965
36141
  try {
35966
36142
  dahuaResponse = await _addPlateNumber(dahuaPayload);
35967
36143
  } catch (error) {
36144
+ logCameraFailure("updateVehicleById write", camera, error);
35968
36145
  const restored = existing.exists ? await restorePlateOnCamera({
35969
36146
  ...dahuaPayload,
35970
36147
  owner: _name
@@ -35977,6 +36154,11 @@ function useVehicleService() {
35977
36154
  );
35978
36155
  }
35979
36156
  if (dahuaResponse?.statusCode !== 200) {
36157
+ logCameraFailure(
36158
+ "updateVehicleById write",
36159
+ camera,
36160
+ `HTTP ${dahuaResponse?.statusCode ?? "none"}`
36161
+ );
35980
36162
  throw new import_node_server_utils99.BadRequestError(
35981
36163
  anprWriteFailureMessage(
35982
36164
  [
@@ -36157,7 +36339,7 @@ function useVehicleService() {
36157
36339
  page++;
36158
36340
  } while (page <= pages);
36159
36341
  if (!siteCameras.length) {
36160
- throw new import_node_server_utils99.BadRequestError("No site cameras found.");
36342
+ throw new import_node_server_utils99.BadRequestError(anprNoCameraRefusal("blocked"));
36161
36343
  }
36162
36344
  for (const camera of siteCameras) {
36163
36345
  const { host, username, password } = camera;
@@ -36171,8 +36353,23 @@ function useVehicleService() {
36171
36353
  const responseForDeletion = await _removePlateNumber(
36172
36354
  removePlateNumber
36173
36355
  );
36174
- if (responseForDeletion?.statusCode !== 200) {
36175
- throw new import_node_server_utils99.BadRequestError("Failed to delete plate number to ANPR");
36356
+ if (!responseForDeletion?.ok) {
36357
+ logCameraFailure(
36358
+ "blocklistVehicles revoke",
36359
+ camera,
36360
+ responseForDeletion?.detail ?? "no outcome returned"
36361
+ );
36362
+ throw new import_node_server_utils99.BadRequestError(
36363
+ anprRevokeFailureMessage(
36364
+ [
36365
+ {
36366
+ camera: cameraLabel(camera),
36367
+ reason: responseForDeletion?.reason || "the camera could not be reached"
36368
+ }
36369
+ ],
36370
+ "updated"
36371
+ )
36372
+ );
36176
36373
  }
36177
36374
  const dahuaPayload = {
36178
36375
  host,
@@ -36186,7 +36383,21 @@ function useVehicleService() {
36186
36383
  };
36187
36384
  const dahuaResponse = await _addPlateNumber(dahuaPayload);
36188
36385
  if (dahuaResponse?.statusCode !== 200) {
36189
- throw new import_node_server_utils99.BadRequestError("Failed to update plate number to ANPR");
36386
+ logCameraFailure(
36387
+ "blocklistVehicles write",
36388
+ camera,
36389
+ `HTTP ${dahuaResponse?.statusCode ?? "none"}`
36390
+ );
36391
+ throw new import_node_server_utils99.BadRequestError(
36392
+ anprBlockFailureMessage([
36393
+ {
36394
+ camera: cameraLabel(camera),
36395
+ reason: anprWriteReason(
36396
+ `HTTP ${dahuaResponse?.statusCode ?? "none"}`
36397
+ )
36398
+ }
36399
+ ])
36400
+ );
36190
36401
  }
36191
36402
  const responseData = dahuaResponse?.data?.toString("utf-8") ?? "";
36192
36403
  value.recNo = responseData.split("=")[1]?.trim();
@@ -36248,9 +36459,9 @@ function useVehicleService() {
36248
36459
  siteCameras.push(...siteCameraReq.items);
36249
36460
  page++;
36250
36461
  } while (page <= pages);
36251
- if (!siteCameras.length) {
36252
- throw new import_node_server_utils99.BadRequestError("No site cameras found.");
36253
- }
36462
+ import_node_server_utils99.logger.info(
36463
+ `removeFromBlocklist resolved ${siteCameras.length} ANPR camera(s)`
36464
+ );
36254
36465
  for (const camera of siteCameras) {
36255
36466
  const { host, username, password } = camera;
36256
36467
  const removePlateNumber = {
@@ -36263,12 +36474,35 @@ function useVehicleService() {
36263
36474
  const responseForDeletion = await _removePlateNumber(
36264
36475
  removePlateNumber
36265
36476
  );
36266
- if (responseForDeletion?.statusCode !== 200) {
36267
- throw new import_node_server_utils99.BadRequestError("Failed to delete plate number to ANPR");
36477
+ if (!responseForDeletion?.ok) {
36478
+ logCameraFailure(
36479
+ "removeFromBlocklist revoke",
36480
+ camera,
36481
+ responseForDeletion?.detail ?? "no outcome returned"
36482
+ );
36483
+ throw new import_node_server_utils99.BadRequestError(
36484
+ anprUnblockFailureMessage([
36485
+ {
36486
+ camera: cameraLabel(camera),
36487
+ reason: responseForDeletion?.reason || "the camera could not be reached"
36488
+ }
36489
+ ])
36490
+ );
36268
36491
  }
36269
36492
  }
36270
36493
  }
36271
36494
  await _deleteVehicle(_id, session);
36495
+ if ((value.peopleId || site) && (_plateNumber || _recNo)) {
36496
+ await _pullVehicleByRecNo(
36497
+ String(_recNo ?? ""),
36498
+ {
36499
+ peopleId: value.peopleId,
36500
+ site,
36501
+ plateNumber: _plateNumber
36502
+ },
36503
+ session
36504
+ );
36505
+ }
36272
36506
  await session.commitTransaction();
36273
36507
  } catch (error) {
36274
36508
  await session.abortTransaction();