@7365admin1/core 3.79.1-staging.335 → 3.79.1-staging.337
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/.changeset/vehicle-one-plate-per-site.md +6 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +171 -26
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +171 -26
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
|
@@ -35,6 +35,12 @@ and moves it to the form's tab (off every camera list, onto the list of its new
|
|
|
35
35
|
kind; pending for a security agency without the approve permission). Its
|
|
36
36
|
history records "moved".
|
|
37
37
|
|
|
38
|
+
**Sync.** `syncVehicleToCameras` puts a live vehicle's plate on every active
|
|
39
|
+
camera of its list that lacks it (a plate saved while its cameras were
|
|
40
|
+
inactive), camera by camera, and records the cameras that took it;
|
|
41
|
+
`activeCameraCounts` gives the active cameras per list so the screens can offer
|
|
42
|
+
it.
|
|
43
|
+
|
|
38
44
|
**One plate per site.** Adding a plate the site already has - deleted,
|
|
39
45
|
pending, temporary, blocked or unblocked included - is refused with its status
|
|
40
46
|
and "edit the existing vehicle instead" (restore, for a deleted one). The same
|
package/dist/index.d.ts
CHANGED
|
@@ -4123,6 +4123,15 @@ declare function useVehicleService(): {
|
|
|
4123
4123
|
view: string;
|
|
4124
4124
|
}[]>;
|
|
4125
4125
|
moveVehicle: (_id: string, value: TVehicle, user?: string) => Promise<string>;
|
|
4126
|
+
activeCameraCounts: (site: string) => Promise<{
|
|
4127
|
+
allowed: number;
|
|
4128
|
+
blocked: number;
|
|
4129
|
+
}>;
|
|
4130
|
+
syncVehicleToCameras: (_id: string, user?: string) => Promise<{
|
|
4131
|
+
message: string;
|
|
4132
|
+
synced: number;
|
|
4133
|
+
failed: number;
|
|
4134
|
+
}>;
|
|
4126
4135
|
fillCameraLists: (site: unknown, camera: {
|
|
4127
4136
|
id?: string;
|
|
4128
4137
|
host?: string;
|
|
@@ -4188,6 +4197,8 @@ declare function useVehicleController(): {
|
|
|
4188
4197
|
removeFromBlocklist: (req: Request, res: Response, next: NextFunction) => Promise<void>;
|
|
4189
4198
|
existingPlates: (req: Request, res: Response, next: NextFunction) => Promise<void>;
|
|
4190
4199
|
moveVehicle: (req: Request, res: Response, next: NextFunction) => Promise<void>;
|
|
4200
|
+
activeCameraCounts: (req: Request, res: Response, next: NextFunction) => Promise<void>;
|
|
4201
|
+
syncVehicleToCameras: (req: Request, res: Response, next: NextFunction) => Promise<void>;
|
|
4191
4202
|
};
|
|
4192
4203
|
|
|
4193
4204
|
declare function useSiteCameraRepo(): {
|
package/dist/index.js
CHANGED
|
@@ -18525,6 +18525,7 @@ function useVehicleRepo() {
|
|
|
18525
18525
|
seasonPassType: 1,
|
|
18526
18526
|
start: 1,
|
|
18527
18527
|
end: 1,
|
|
18528
|
+
remarks: 1,
|
|
18528
18529
|
nric: 1,
|
|
18529
18530
|
plateNumber: 1,
|
|
18530
18531
|
site: 1,
|
|
@@ -18571,7 +18572,9 @@ function useVehicleRepo() {
|
|
|
18571
18572
|
// A season pass's expiry, shown under its number.
|
|
18572
18573
|
seasonPassType: "$seasonPassType",
|
|
18573
18574
|
start: "$start",
|
|
18574
|
-
end: "$end"
|
|
18575
|
+
end: "$end",
|
|
18576
|
+
// Each record's own remarks (e.g. why it was blocked).
|
|
18577
|
+
remarks: "$remarks"
|
|
18575
18578
|
}
|
|
18576
18579
|
}
|
|
18577
18580
|
}
|
|
@@ -35160,25 +35163,44 @@ function useVehicleService() {
|
|
|
35160
35163
|
}
|
|
35161
35164
|
return failures;
|
|
35162
35165
|
}
|
|
35166
|
+
const CAMERA_WRITE_ROUNDS = 3;
|
|
35167
|
+
const CAMERA_WRITE_RETRY_DELAY_MS = 3e3;
|
|
35163
35168
|
async function writeToCameras(cameras, write, journal, options = {}) {
|
|
35164
35169
|
const failures = [];
|
|
35165
35170
|
let recNo = "";
|
|
35166
35171
|
for (const camera of cameras) {
|
|
35167
|
-
|
|
35168
|
-
|
|
35169
|
-
|
|
35170
|
-
|
|
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;
|
|
35171
35180
|
journal.inserted(camera, write.mode, write.plateNumber);
|
|
35172
|
-
|
|
35173
|
-
|
|
35174
|
-
|
|
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;
|
|
35175
35203
|
}
|
|
35176
|
-
} catch (error) {
|
|
35177
|
-
logCameraFailure(`write ${write.mode}`, camera, error);
|
|
35178
|
-
failures.push({
|
|
35179
|
-
camera: cameraLabel(camera),
|
|
35180
|
-
reason: anprWriteReason(error)
|
|
35181
|
-
});
|
|
35182
35204
|
}
|
|
35183
35205
|
}
|
|
35184
35206
|
return { failures, recNo };
|
|
@@ -35565,6 +35587,9 @@ function useVehicleService() {
|
|
|
35565
35587
|
}
|
|
35566
35588
|
const _plateNumber = String(vehicle.plateNumber ?? "");
|
|
35567
35589
|
const owner = vehicle.name;
|
|
35590
|
+
const approveOutcome = ["deleted", "inactive", "rejected"].includes(
|
|
35591
|
+
String(vehicle.status ?? "")
|
|
35592
|
+
) ? "restored" : "approved";
|
|
35568
35593
|
const isBlocklist = vehicle.type === "blocklist" /* BLOCKLIST */;
|
|
35569
35594
|
const mode = listMode(vehicle.type);
|
|
35570
35595
|
const hasStart = typeof vehicle.start === "string" ? vehicle.start.trim() !== "" : !!vehicle.start;
|
|
@@ -35593,7 +35618,7 @@ function useVehicleService() {
|
|
|
35593
35618
|
const cameraSite = vehicle.site ?? siteId;
|
|
35594
35619
|
const siteCameras = isBlocklist ? await blockCamerasAt(cameraSite) : await allowCamerasAt(cameraSite);
|
|
35595
35620
|
if (!siteCameras.length) {
|
|
35596
|
-
throw new import_node_server_utils109.BadRequestError(anprNoCameraRefusal(
|
|
35621
|
+
throw new import_node_server_utils109.BadRequestError(anprNoCameraRefusal(approveOutcome));
|
|
35597
35622
|
}
|
|
35598
35623
|
const { failures } = await writeToCameras(
|
|
35599
35624
|
siteCameras,
|
|
@@ -35611,7 +35636,7 @@ function useVehicleService() {
|
|
|
35611
35636
|
cameraFailureSentence({
|
|
35612
35637
|
failures,
|
|
35613
35638
|
operation: "added to",
|
|
35614
|
-
outcome:
|
|
35639
|
+
outcome: approveOutcome,
|
|
35615
35640
|
consequence: isBlocklist ? "it would still open the barrier" : "it would not open the barrier"
|
|
35616
35641
|
})
|
|
35617
35642
|
);
|
|
@@ -35722,7 +35747,11 @@ function useVehicleService() {
|
|
|
35722
35747
|
const ownEnd = validDate(vehicle.end);
|
|
35723
35748
|
let startDate;
|
|
35724
35749
|
let endDate;
|
|
35725
|
-
if (
|
|
35750
|
+
if (isBlocklist) {
|
|
35751
|
+
const block = blockWindow();
|
|
35752
|
+
startDate = new Date(block.start);
|
|
35753
|
+
endDate = new Date(block.end);
|
|
35754
|
+
} else if (ownEnd) {
|
|
35726
35755
|
if (ownEnd.getTime() <= Date.now()) {
|
|
35727
35756
|
throw new import_node_server_utils109.BadRequestError(
|
|
35728
35757
|
`This vehicle's expiry date (${dahuaWallClock(
|
|
@@ -36128,6 +36157,76 @@ function useVehicleService() {
|
|
|
36128
36157
|
throw error;
|
|
36129
36158
|
}
|
|
36130
36159
|
}
|
|
36160
|
+
async function activeCameraCounts(site) {
|
|
36161
|
+
const [allowed, blocked] = await Promise.all([
|
|
36162
|
+
allowCamerasAt(site),
|
|
36163
|
+
blockCamerasAt(site)
|
|
36164
|
+
]);
|
|
36165
|
+
return { allowed: allowed.length, blocked: blocked.length };
|
|
36166
|
+
}
|
|
36167
|
+
async function syncVehicleToCameras(_id, user) {
|
|
36168
|
+
const vehicle = await findOne({ _id: new import_mongodb76.ObjectId(_id) });
|
|
36169
|
+
if (!vehicle)
|
|
36170
|
+
throw new import_node_server_utils109.NotFoundError("Vehicle not found.");
|
|
36171
|
+
if (!isLiveVehicleStatus(vehicle.status)) {
|
|
36172
|
+
throw new import_node_server_utils109.BadRequestError(
|
|
36173
|
+
`This vehicle is ${String(vehicle.status || "not live")}, so it is not put on any camera.`
|
|
36174
|
+
);
|
|
36175
|
+
}
|
|
36176
|
+
const blocked = vehicle.type === "blocklist" /* BLOCKLIST */;
|
|
36177
|
+
const cameras = blocked ? await blockCamerasAt(vehicle.site) : await allowCamerasAt(vehicle.site);
|
|
36178
|
+
if (!cameras.length) {
|
|
36179
|
+
throw new import_node_server_utils109.BadRequestError(
|
|
36180
|
+
"There is no active ANPR camera to sync to. Switch a camera on first."
|
|
36181
|
+
);
|
|
36182
|
+
}
|
|
36183
|
+
const plateNumber = String(vehicle.plateNumber ?? "");
|
|
36184
|
+
const write = {
|
|
36185
|
+
plateNumber,
|
|
36186
|
+
mode: blocked ? "TrafficBlackList" /* TRAFFIC_BLACKLIST */ : "TrafficRedList" /* TRAFFIC_REDLIST */,
|
|
36187
|
+
owner: String(vehicle.name ?? ""),
|
|
36188
|
+
...blocked ? blockWindow() : cameraWindow(vehicle.start, vehicle.end)
|
|
36189
|
+
};
|
|
36190
|
+
const journal = cameraJournal();
|
|
36191
|
+
const took = [];
|
|
36192
|
+
const failures = [];
|
|
36193
|
+
for (const camera of cameras) {
|
|
36194
|
+
const result = await writeToCameras([camera], write, journal, {
|
|
36195
|
+
onlyIfMissing: true
|
|
36196
|
+
});
|
|
36197
|
+
if (result.failures.length)
|
|
36198
|
+
failures.push(...result.failures);
|
|
36199
|
+
else
|
|
36200
|
+
took.push(camera);
|
|
36201
|
+
}
|
|
36202
|
+
const hostKey = (value) => String(value ?? "").trim().toLowerCase().replace(/\/+$/, "");
|
|
36203
|
+
const tookHosts = new Set(took.map((camera) => hostKey(camera.host)));
|
|
36204
|
+
const current = Array.isArray(vehicle.anprCameras) ? vehicle.anprCameras : [];
|
|
36205
|
+
try {
|
|
36206
|
+
await _setAnprCameras(_id, [
|
|
36207
|
+
...current.filter((entry) => !tookHosts.has(hostKey(entry.host))),
|
|
36208
|
+
...anprCameraSummary(took, blocked ? "blocked" : "allowed")
|
|
36209
|
+
]);
|
|
36210
|
+
if (took.length) {
|
|
36211
|
+
await _pushHistory(_id, vehicleHistoryEntry("synced", user, ["anprCameras"]));
|
|
36212
|
+
}
|
|
36213
|
+
} catch (error) {
|
|
36214
|
+
import_node_server_utils109.logger.error(`syncVehicleToCameras could not record ${_id}: ${error?.message ?? error}`);
|
|
36215
|
+
}
|
|
36216
|
+
const list = blocked ? "blocklist" : "allowed list";
|
|
36217
|
+
if (!failures.length) {
|
|
36218
|
+
return {
|
|
36219
|
+
message: `Vehicle ${plateNumber} is on the ${list} of all ${took.length} active camera${took.length === 1 ? "" : "s"}.`,
|
|
36220
|
+
synced: took.length,
|
|
36221
|
+
failed: 0
|
|
36222
|
+
};
|
|
36223
|
+
}
|
|
36224
|
+
return {
|
|
36225
|
+
message: `Vehicle ${plateNumber} was synced to ${took.length} of ${cameras.length} active cameras. Not synced: ${failures.map((f) => `${f.camera} (${f.reason})`).join("; ")}.`,
|
|
36226
|
+
synced: took.length,
|
|
36227
|
+
failed: failures.length
|
|
36228
|
+
};
|
|
36229
|
+
}
|
|
36131
36230
|
async function existingPlatesAtSite(site, plateNumbers) {
|
|
36132
36231
|
const seen = /* @__PURE__ */ new Set();
|
|
36133
36232
|
const items = [];
|
|
@@ -36253,12 +36352,12 @@ function useVehicleService() {
|
|
|
36253
36352
|
...value.phoneNumber !== void 0 && { phoneNumber: value.phoneNumber },
|
|
36254
36353
|
...value.nric !== void 0 && { nric: value.nric },
|
|
36255
36354
|
...value.remarks !== void 0 && { remarks: value.remarks },
|
|
36256
|
-
|
|
36257
|
-
|
|
36258
|
-
|
|
36259
|
-
|
|
36260
|
-
|
|
36261
|
-
}
|
|
36355
|
+
// Block / level / unit whenever the form sends them - a Block Listed
|
|
36356
|
+
// vehicle blocked from a visitor transaction brings the visit's.
|
|
36357
|
+
...value.block && { block: asId(value.block) },
|
|
36358
|
+
...value.level && { level: asId(value.level) },
|
|
36359
|
+
...value.unit && { unit: asId(value.unit) },
|
|
36360
|
+
...toBlocklist ? {} : { ...value.peopleId && { peopleId: asId(value.peopleId) } }
|
|
36262
36361
|
};
|
|
36263
36362
|
const changed = changedFields(vehicle, formattedValue);
|
|
36264
36363
|
await _updateVehicleById(_id, formattedValue, session);
|
|
@@ -36564,6 +36663,8 @@ function useVehicleService() {
|
|
|
36564
36663
|
removeFromBlocklist,
|
|
36565
36664
|
existingPlatesAtSite,
|
|
36566
36665
|
moveVehicle,
|
|
36666
|
+
activeCameraCounts,
|
|
36667
|
+
syncVehicleToCameras,
|
|
36567
36668
|
fillCameraLists,
|
|
36568
36669
|
newCameraPlateRows,
|
|
36569
36670
|
writeVehicleToNewCamera
|
|
@@ -40139,7 +40240,9 @@ function useVehicleController() {
|
|
|
40139
40240
|
blocklistVehicles: _blocklistVehicles,
|
|
40140
40241
|
removeFromBlocklist: _removeFromBlocklist,
|
|
40141
40242
|
existingPlatesAtSite: _existingPlatesAtSite,
|
|
40142
|
-
moveVehicle: _moveVehicle
|
|
40243
|
+
moveVehicle: _moveVehicle,
|
|
40244
|
+
activeCameraCounts: _activeCameraCounts,
|
|
40245
|
+
syncVehicleToCameras: _syncVehicleToCameras
|
|
40143
40246
|
} = useVehicleService();
|
|
40144
40247
|
const {
|
|
40145
40248
|
getSeasonPassTypes: _getSeasonPassTypes,
|
|
@@ -40168,6 +40271,10 @@ function useVehicleController() {
|
|
|
40168
40271
|
message: "You do not have permission to view vehicles."
|
|
40169
40272
|
};
|
|
40170
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 = {
|
|
40171
40278
|
permissions: [
|
|
40172
40279
|
"vehicle-mgmt:see-vehicle-details",
|
|
40173
40280
|
"vehicle-mgmt:update-vehicle"
|
|
@@ -40574,7 +40681,7 @@ function useVehicleController() {
|
|
|
40574
40681
|
}
|
|
40575
40682
|
const { _id } = value;
|
|
40576
40683
|
try {
|
|
40577
|
-
await requireVehicleReach(req, _id,
|
|
40684
|
+
await requireVehicleReach(req, _id, VEHICLE_EDIT_READ);
|
|
40578
40685
|
const site = await _getSpecificVehicleById(_id);
|
|
40579
40686
|
res.json(site);
|
|
40580
40687
|
return;
|
|
@@ -40970,6 +41077,42 @@ function useVehicleController() {
|
|
|
40970
41077
|
return;
|
|
40971
41078
|
}
|
|
40972
41079
|
}
|
|
41080
|
+
async function activeCameraCounts(req, res, next) {
|
|
41081
|
+
const { error, value } = import_joi58.default.object({
|
|
41082
|
+
site: import_joi58.default.string().hex().length(24).required()
|
|
41083
|
+
}).validate({ site: req.params.site });
|
|
41084
|
+
if (error) {
|
|
41085
|
+
next(new import_node_server_utils123.BadRequestError(error.message));
|
|
41086
|
+
return;
|
|
41087
|
+
}
|
|
41088
|
+
try {
|
|
41089
|
+
await requireSiteReach(req, value.site, VEHICLE_LIST);
|
|
41090
|
+
res.json(await _activeCameraCounts(value.site));
|
|
41091
|
+
return;
|
|
41092
|
+
} catch (error2) {
|
|
41093
|
+
import_node_server_utils123.logger.log({ level: "error", message: error2.message });
|
|
41094
|
+
next(error2);
|
|
41095
|
+
return;
|
|
41096
|
+
}
|
|
41097
|
+
}
|
|
41098
|
+
async function syncVehicleToCameras(req, res, next) {
|
|
41099
|
+
const { error, value } = import_joi58.default.object({
|
|
41100
|
+
_id: import_joi58.default.string().hex().length(24).required()
|
|
41101
|
+
}).validate({ _id: req.params.id });
|
|
41102
|
+
if (error) {
|
|
41103
|
+
next(new import_node_server_utils123.BadRequestError(error.message));
|
|
41104
|
+
return;
|
|
41105
|
+
}
|
|
41106
|
+
try {
|
|
41107
|
+
await requireVehicleReach(req, value._id, VEHICLE_UPDATE);
|
|
41108
|
+
res.json(await _syncVehicleToCameras(value._id, actingUser(req)));
|
|
41109
|
+
return;
|
|
41110
|
+
} catch (error2) {
|
|
41111
|
+
import_node_server_utils123.logger.log({ level: "error", message: error2.message });
|
|
41112
|
+
next(error2);
|
|
41113
|
+
return;
|
|
41114
|
+
}
|
|
41115
|
+
}
|
|
40973
41116
|
return {
|
|
40974
41117
|
add,
|
|
40975
41118
|
addBulk,
|
|
@@ -40988,7 +41131,9 @@ function useVehicleController() {
|
|
|
40988
41131
|
blocklistVehicles,
|
|
40989
41132
|
removeFromBlocklist,
|
|
40990
41133
|
existingPlates,
|
|
40991
|
-
moveVehicle
|
|
41134
|
+
moveVehicle,
|
|
41135
|
+
activeCameraCounts,
|
|
41136
|
+
syncVehicleToCameras
|
|
40992
41137
|
};
|
|
40993
41138
|
}
|
|
40994
41139
|
|