@7365admin1/core 3.79.1-staging.336 → 3.79.1-staging.338

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 CHANGED
@@ -1,5 +1,64 @@
1
1
  # @iservice365/core
2
2
 
3
+ ## 3.80.0
4
+
5
+ ### Minor Changes
6
+
7
+ - a267a47: Vehicle statuses say which list a vehicle is on, a site holds a plate once
8
+ whatever its status, and every vehicle keeps a history.
9
+
10
+ **Statuses.** A whitelist vehicle is `active`, a season pass `temporary`, a
11
+ blocked vehicle `blocked`. Blocking a resident or season pass puts its plate on
12
+ the blocklist of every active camera except Exit for ten years (whatever the
13
+ pass's own dates) and takes it OFF every camera's allowed list, so no camera
14
+ holds a plate on both lists; approving a pending blocklist vehicle uses the
15
+ same ten years, and a new camera is filled the same way. Unblocking sets `unblocked` and takes the plate off every active camera's
16
+ blocklist by plate number - and off the allowed list too, so an unblocked car
17
+ is on no list rather than quietly whitelisted again. A blocked, unblocked,
18
+ deleted or inactive vehicle can be blocked again in place. Everything that
19
+ writes, syncs, revokes or recognises a plate at the gate now treats `active`,
20
+ `temporary` and `blocked` as live; the gate still recognises a season pass as a
21
+ registered resident until its expiry, and an unblocked plate no longer counts
22
+ as blocked at visitor check-in.
23
+
24
+ **Categories and tabs.** A whitelist vehicle is filed `resident`, a season
25
+ pass `season_pass` (new), and a vehicle added to the blocklist `visitor` or
26
+ `contractor` (new in the vehicle enum; default `visitor`); blocked from a visitor transaction it keeps that type - `delivery`, `pick-up` or `drop-off` (new), a guest as `visitor`. A season pass seen at the gate is logged as a `resident` transaction. Blocking keeps the
27
+ category, so a blocked resident is still `resident`; editing a season pass type
28
+ on or off moves a whitelist vehicle between `resident` and `season_pass`. The
29
+ vehicle list takes `view` - `residents`, `season-passes` or `block-listed` -
30
+ for the three tabs; older season passes saved as `resident` show under Season
31
+ Passes. The gate counts `resident` and `season_pass` as registered.
32
+
33
+ **Update an existing plate from the add form.** `existingPlatesAtSite`
34
+ answers which of the plates being added the site already holds, with each
35
+ record's tab and status; `moveVehicle` saves the form's details on that record
36
+ and moves it to the form's tab (off every camera list, onto the list of its new
37
+ kind; pending for a security agency without the approve permission). Its
38
+ history records "moved".
39
+
40
+ **Sync.** `syncVehicleToCameras` puts a live vehicle's plate on every active
41
+ camera of its list that lacks it (a plate saved while its cameras were
42
+ inactive), camera by camera, and records the cameras that took it;
43
+ `activeCameraCounts` gives the active cameras per list so the screens can offer
44
+ it.
45
+
46
+ **One plate per site.** Adding a plate the site already has - deleted,
47
+ pending, temporary, blocked or unblocked included - is refused with its status
48
+ and "edit the existing vehicle instead" (restore, for a deleted one). The same
49
+ check guards approve, restore and plate edits. Matching ignores case and
50
+ spacing.
51
+
52
+ **History.** `history` holds the last 20 changes - created, updated,
53
+ approved, restored, blocked, unblocked, deleted - each with the user's id, the
54
+ time and the fields that changed. `getSpecificVehicleById` adds each user's
55
+ name.
56
+
57
+ **Also:** the vehicle list returns every status unless one is asked for; the
58
+ camera's record id (`recNo`) is no longer stored; `processDeletingExpiredVehicles`
59
+ is no longer needed (the camera enforces each pass's own expiry) and should be
60
+ unscheduled by the API.
61
+
3
62
  ## 3.79.0
4
63
 
5
64
  ### Minor Changes
package/dist/index.js CHANGED
@@ -35163,25 +35163,44 @@ function useVehicleService() {
35163
35163
  }
35164
35164
  return failures;
35165
35165
  }
35166
+ const CAMERA_WRITE_ROUNDS = 3;
35167
+ const CAMERA_WRITE_RETRY_DELAY_MS = 3e3;
35166
35168
  async function writeToCameras(cameras, write, journal, options = {}) {
35167
35169
  const failures = [];
35168
35170
  let recNo = "";
35169
35171
  for (const camera of cameras) {
35170
- try {
35171
- if (options.onlyIfMissing) {
35172
- const { inserted } = await ensurePlateOnCamera(camera, write);
35173
- if (inserted)
35172
+ for (let round = 1; ; round++) {
35173
+ try {
35174
+ if (options.onlyIfMissing || round > 1) {
35175
+ const { inserted } = await ensurePlateOnCamera(camera, write);
35176
+ if (inserted)
35177
+ journal.inserted(camera, write.mode, write.plateNumber);
35178
+ } else {
35179
+ recNo = await insertPlateOnCamera(camera, write) || recNo;
35174
35180
  journal.inserted(camera, write.mode, write.plateNumber);
35175
- } else {
35176
- recNo = await insertPlateOnCamera(camera, write) || recNo;
35177
- journal.inserted(camera, write.mode, write.plateNumber);
35181
+ }
35182
+ if (round > 1) {
35183
+ import_node_server_utils109.logger.info(
35184
+ `[${cameraLabel(camera)}] write ${write.mode} went through on round ${round}`
35185
+ );
35186
+ }
35187
+ break;
35188
+ } catch (error) {
35189
+ const transient = classifyDahuaFailure(error?.message ?? error) === "transient";
35190
+ if (transient && round < CAMERA_WRITE_ROUNDS) {
35191
+ logCameraFailure(`write ${write.mode} (round ${round}, retrying)`, camera, error);
35192
+ await new Promise(
35193
+ (resolve) => setTimeout(resolve, CAMERA_WRITE_RETRY_DELAY_MS * round)
35194
+ );
35195
+ continue;
35196
+ }
35197
+ logCameraFailure(`write ${write.mode}`, camera, error);
35198
+ failures.push({
35199
+ camera: cameraLabel(camera),
35200
+ reason: anprWriteReason(error)
35201
+ });
35202
+ break;
35178
35203
  }
35179
- } catch (error) {
35180
- logCameraFailure(`write ${write.mode}`, camera, error);
35181
- failures.push({
35182
- camera: cameraLabel(camera),
35183
- reason: anprWriteReason(error)
35184
- });
35185
35204
  }
35186
35205
  }
35187
35206
  return { failures, recNo };
@@ -35568,6 +35587,9 @@ function useVehicleService() {
35568
35587
  }
35569
35588
  const _plateNumber = String(vehicle.plateNumber ?? "");
35570
35589
  const owner = vehicle.name;
35590
+ const approveOutcome = ["deleted", "inactive", "rejected"].includes(
35591
+ String(vehicle.status ?? "")
35592
+ ) ? "restored" : "approved";
35571
35593
  const isBlocklist = vehicle.type === "blocklist" /* BLOCKLIST */;
35572
35594
  const mode = listMode(vehicle.type);
35573
35595
  const hasStart = typeof vehicle.start === "string" ? vehicle.start.trim() !== "" : !!vehicle.start;
@@ -35596,7 +35618,7 @@ function useVehicleService() {
35596
35618
  const cameraSite = vehicle.site ?? siteId;
35597
35619
  const siteCameras = isBlocklist ? await blockCamerasAt(cameraSite) : await allowCamerasAt(cameraSite);
35598
35620
  if (!siteCameras.length) {
35599
- throw new import_node_server_utils109.BadRequestError(anprNoCameraRefusal("approved"));
35621
+ throw new import_node_server_utils109.BadRequestError(anprNoCameraRefusal(approveOutcome));
35600
35622
  }
35601
35623
  const { failures } = await writeToCameras(
35602
35624
  siteCameras,
@@ -35614,7 +35636,7 @@ function useVehicleService() {
35614
35636
  cameraFailureSentence({
35615
35637
  failures,
35616
35638
  operation: "added to",
35617
- outcome: "approved",
35639
+ outcome: approveOutcome,
35618
35640
  consequence: isBlocklist ? "it would still open the barrier" : "it would not open the barrier"
35619
35641
  })
35620
35642
  );
@@ -35725,7 +35747,11 @@ function useVehicleService() {
35725
35747
  const ownEnd = validDate(vehicle.end);
35726
35748
  let startDate;
35727
35749
  let endDate;
35728
- if (ownEnd) {
35750
+ if (isBlocklist) {
35751
+ const block = blockWindow();
35752
+ startDate = new Date(block.start);
35753
+ endDate = new Date(block.end);
35754
+ } else if (ownEnd) {
35729
35755
  if (ownEnd.getTime() <= Date.now()) {
35730
35756
  throw new import_node_server_utils109.BadRequestError(
35731
35757
  `This vehicle's expiry date (${dahuaWallClock(
@@ -40245,6 +40271,10 @@ function useVehicleController() {
40245
40271
  message: "You do not have permission to view vehicles."
40246
40272
  };
40247
40273
  const VEHICLE_DETAILS = {
40274
+ permissions: ["vehicle-mgmt:see-vehicle-details"],
40275
+ message: "You do not have permission to view vehicle details."
40276
+ };
40277
+ const VEHICLE_EDIT_READ = {
40248
40278
  permissions: [
40249
40279
  "vehicle-mgmt:see-vehicle-details",
40250
40280
  "vehicle-mgmt:update-vehicle"
@@ -40651,7 +40681,7 @@ function useVehicleController() {
40651
40681
  }
40652
40682
  const { _id } = value;
40653
40683
  try {
40654
- await requireVehicleReach(req, _id, VEHICLE_DETAILS);
40684
+ await requireVehicleReach(req, _id, VEHICLE_EDIT_READ);
40655
40685
  const site = await _getSpecificVehicleById(_id);
40656
40686
  res.json(site);
40657
40687
  return;