@7365admin1/core 3.68.0 → 3.68.2
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 +38 -0
- package/dist/index.d.ts +122 -6
- package/dist/index.js +2041 -858
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2219 -1035
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
- package/test/anpr-plate.test.mjs +272 -0
- package/test/dahua-protocol.util.test.mjs +137 -0
- package/test/member-duplicate-error.test.mjs +30 -0
- package/test/people-create-gate.test.mjs +71 -0
package/dist/index.mjs
CHANGED
|
@@ -9073,6 +9073,9 @@ import { BadRequestError as BadRequestError12 } from "@7365admin1/node-server-ut
|
|
|
9073
9073
|
import Joi7 from "joi";
|
|
9074
9074
|
import { ObjectId as ObjectId12 } from "mongodb";
|
|
9075
9075
|
var PLATFORM_STAFF_MEMBER_TYPE = "admin";
|
|
9076
|
+
function isDuplicateMemberError(error) {
|
|
9077
|
+
return error?.code === 11e3 || /E11000|duplicate key/i.test(error?.message ?? "");
|
|
9078
|
+
}
|
|
9076
9079
|
var MEMBER_TYPES = [
|
|
9077
9080
|
PLATFORM_STAFF_MEMBER_TYPE,
|
|
9078
9081
|
"organization",
|
|
@@ -9226,8 +9229,7 @@ function useMemberRepo() {
|
|
|
9226
9229
|
});
|
|
9227
9230
|
return "Successfully added member.";
|
|
9228
9231
|
} catch (error) {
|
|
9229
|
-
|
|
9230
|
-
if (isDuplicated && error.message.includes("org_1_user_1"))
|
|
9232
|
+
if (isDuplicateMemberError(error))
|
|
9231
9233
|
throw new BadRequestError13(
|
|
9232
9234
|
"The user is already a member of this organization."
|
|
9233
9235
|
);
|
|
@@ -21257,6 +21259,7 @@ import { promises as fsAsync } from "fs";
|
|
|
21257
21259
|
import Joi26 from "joi";
|
|
21258
21260
|
import * as path2 from "path";
|
|
21259
21261
|
import { request } from "urllib";
|
|
21262
|
+
import { createHash as createHash2 } from "crypto";
|
|
21260
21263
|
|
|
21261
21264
|
// src/repositories/visitor-transaction.repo.ts
|
|
21262
21265
|
import {
|
|
@@ -23773,6 +23776,69 @@ function useVehicleRepo() {
|
|
|
23773
23776
|
throw error;
|
|
23774
23777
|
}
|
|
23775
23778
|
}
|
|
23779
|
+
async function getLivePlatesBySite(site) {
|
|
23780
|
+
let siteId;
|
|
23781
|
+
try {
|
|
23782
|
+
siteId = new ObjectId47(site);
|
|
23783
|
+
} catch (error) {
|
|
23784
|
+
throw new BadRequestError46("Invalid site ID format.");
|
|
23785
|
+
}
|
|
23786
|
+
try {
|
|
23787
|
+
return await collection.aggregate([
|
|
23788
|
+
{
|
|
23789
|
+
$match: {
|
|
23790
|
+
site: siteId,
|
|
23791
|
+
status: "active" /* ACTIVE */,
|
|
23792
|
+
plateNumber: { $nin: [null, ""] }
|
|
23793
|
+
}
|
|
23794
|
+
},
|
|
23795
|
+
{ $sort: { name: 1, plateNumber: 1 } },
|
|
23796
|
+
{
|
|
23797
|
+
$lookup: {
|
|
23798
|
+
from: "buildings",
|
|
23799
|
+
localField: "block",
|
|
23800
|
+
foreignField: "_id",
|
|
23801
|
+
pipeline: [{ $project: { name: 1 } }],
|
|
23802
|
+
as: "blockDoc"
|
|
23803
|
+
}
|
|
23804
|
+
},
|
|
23805
|
+
{
|
|
23806
|
+
$lookup: {
|
|
23807
|
+
from: "building-levels",
|
|
23808
|
+
localField: "level",
|
|
23809
|
+
foreignField: "_id",
|
|
23810
|
+
pipeline: [{ $project: { name: 1 } }],
|
|
23811
|
+
as: "levelDoc"
|
|
23812
|
+
}
|
|
23813
|
+
},
|
|
23814
|
+
{
|
|
23815
|
+
$lookup: {
|
|
23816
|
+
from: "building-units",
|
|
23817
|
+
localField: "unit",
|
|
23818
|
+
foreignField: "_id",
|
|
23819
|
+
pipeline: [{ $project: { name: 1 } }],
|
|
23820
|
+
as: "unitDoc"
|
|
23821
|
+
}
|
|
23822
|
+
},
|
|
23823
|
+
{
|
|
23824
|
+
$project: {
|
|
23825
|
+
plateNumber: 1,
|
|
23826
|
+
name: 1,
|
|
23827
|
+
start: 1,
|
|
23828
|
+
end: 1,
|
|
23829
|
+
type: 1,
|
|
23830
|
+
blockedFrom: 1,
|
|
23831
|
+
seasonPassType: 1,
|
|
23832
|
+
blockName: { $ifNull: [{ $arrayElemAt: ["$blockDoc.name", 0] }, ""] },
|
|
23833
|
+
levelName: { $ifNull: [{ $arrayElemAt: ["$levelDoc.name", 0] }, ""] },
|
|
23834
|
+
unitName: { $ifNull: [{ $arrayElemAt: ["$unitDoc.name", 0] }, ""] }
|
|
23835
|
+
}
|
|
23836
|
+
}
|
|
23837
|
+
]).toArray();
|
|
23838
|
+
} catch (error) {
|
|
23839
|
+
throw new InternalServerError24("Failed to list the site's vehicles.");
|
|
23840
|
+
}
|
|
23841
|
+
}
|
|
23776
23842
|
async function getAllExpiredVehicles() {
|
|
23777
23843
|
try {
|
|
23778
23844
|
const items = await collection.find(expiredVehicleSweepFilter()).toArray();
|
|
@@ -23977,6 +24043,7 @@ function useVehicleRepo() {
|
|
|
23977
24043
|
getVehicleByPlateNumber,
|
|
23978
24044
|
getVehiclesByNRIC,
|
|
23979
24045
|
deleteExpiredVehicles,
|
|
24046
|
+
getLivePlatesBySite,
|
|
23980
24047
|
getAllVehiclesByUnitId,
|
|
23981
24048
|
getAllExpiredVehicles,
|
|
23982
24049
|
bulkUpsertVehicles,
|
|
@@ -24176,6 +24243,73 @@ function classifyDahuaFailure(message) {
|
|
|
24176
24243
|
}
|
|
24177
24244
|
return "transient";
|
|
24178
24245
|
}
|
|
24246
|
+
var DAHUA_COMMAND_ATTEMPTS = 10;
|
|
24247
|
+
var DAHUA_COMMAND_DEADLINE_MS = 45e3;
|
|
24248
|
+
var DAHUA_LOGIN_PROVEN_FOR_MS = 10 * 6e4;
|
|
24249
|
+
var COMMAND_RETRY_DELAYS_MS = [1e3, 2e3, 3e3, 4e3, 5e3];
|
|
24250
|
+
function dahuaCommandRetryDelayMs(failedAttempt) {
|
|
24251
|
+
const index = Math.min(
|
|
24252
|
+
Math.max(Math.floor(failedAttempt), 1),
|
|
24253
|
+
COMMAND_RETRY_DELAYS_MS.length
|
|
24254
|
+
);
|
|
24255
|
+
return COMMAND_RETRY_DELAYS_MS[index - 1];
|
|
24256
|
+
}
|
|
24257
|
+
var NOT_SENT_PATTERN = /ECONNREFUSED|EHOSTUNREACH|ENETUNREACH|EAI_AGAIN|ENOTFOUND|UND_ERR_CONNECT_TIMEOUT|connect ?timeout|ConnectTimeout/i;
|
|
24258
|
+
function classifyDahuaAttempt(attempt) {
|
|
24259
|
+
if (attempt.error !== void 0 && attempt.error !== null) {
|
|
24260
|
+
const error = attempt.error;
|
|
24261
|
+
const message = [error?.name, error?.code, error?.cause?.code, error?.message ?? error].filter(Boolean).join(" ");
|
|
24262
|
+
if (/invalid authority|\b401\b|unauthori[sz]ed/i.test(message)) {
|
|
24263
|
+
return "login-refused";
|
|
24264
|
+
}
|
|
24265
|
+
if (/\b403\b|forbidden/i.test(message))
|
|
24266
|
+
return "forbidden";
|
|
24267
|
+
if (NOT_SENT_PATTERN.test(message))
|
|
24268
|
+
return "not-sent";
|
|
24269
|
+
return "maybe-sent";
|
|
24270
|
+
}
|
|
24271
|
+
const status = attempt.statusCode;
|
|
24272
|
+
if (status === 401 || /invalid authority/i.test(String(attempt.text ?? ""))) {
|
|
24273
|
+
return "login-refused";
|
|
24274
|
+
}
|
|
24275
|
+
if (status === 403)
|
|
24276
|
+
return "forbidden";
|
|
24277
|
+
if (status === 502 || status === 503 || status === 504)
|
|
24278
|
+
return "busy";
|
|
24279
|
+
if (status === void 0)
|
|
24280
|
+
return "maybe-sent";
|
|
24281
|
+
return "answered";
|
|
24282
|
+
}
|
|
24283
|
+
function shouldRetryDahuaCommand(options) {
|
|
24284
|
+
const {
|
|
24285
|
+
kind,
|
|
24286
|
+
attempt,
|
|
24287
|
+
loginFailures,
|
|
24288
|
+
loginProven,
|
|
24289
|
+
repeatable,
|
|
24290
|
+
elapsedMs,
|
|
24291
|
+
nextDelayMs,
|
|
24292
|
+
maxAttempts = DAHUA_COMMAND_ATTEMPTS,
|
|
24293
|
+
deadlineMs = DAHUA_COMMAND_DEADLINE_MS
|
|
24294
|
+
} = options;
|
|
24295
|
+
if (kind === "answered" || kind === "forbidden")
|
|
24296
|
+
return false;
|
|
24297
|
+
if (attempt >= maxAttempts)
|
|
24298
|
+
return false;
|
|
24299
|
+
if (elapsedMs + nextDelayMs > deadlineMs)
|
|
24300
|
+
return false;
|
|
24301
|
+
switch (kind) {
|
|
24302
|
+
case "login-refused":
|
|
24303
|
+
return loginProven || loginFailures < DAHUA_AUTH_ATTEMPTS_BEFORE_ALERT;
|
|
24304
|
+
case "busy":
|
|
24305
|
+
case "not-sent":
|
|
24306
|
+
return true;
|
|
24307
|
+
case "maybe-sent":
|
|
24308
|
+
return repeatable;
|
|
24309
|
+
default:
|
|
24310
|
+
return false;
|
|
24311
|
+
}
|
|
24312
|
+
}
|
|
24179
24313
|
var enc = (value) => encodeURIComponent(String(value));
|
|
24180
24314
|
function buildPlateUpdateEndpoint(value) {
|
|
24181
24315
|
return `/cgi-bin/recordUpdater.cgi?action=update&name=${enc(value.mode)}&recno=${enc(value.recno)}&PlateNumber=${enc(value.plateNumber)}&BeginTime=${enc(value.start)}&CancelTime=${enc(value.end)}&+OpenGate=${value.openGate}&MasterOfCar=${enc(value.owner)}`;
|
|
@@ -24186,6 +24320,13 @@ function buildPlateInsertEndpoint(value) {
|
|
|
24186
24320
|
function buildPlateRemoveEndpoint(value) {
|
|
24187
24321
|
return `/cgi-bin/recordUpdater.cgi?action=remove&recno=${enc(value.recno)}&name=${enc(value.mode)}`;
|
|
24188
24322
|
}
|
|
24323
|
+
var CLEARABLE_PLATE_LISTS = ["TrafficRedList", "TrafficBlackList"];
|
|
24324
|
+
function buildPlateClearEndpoint(value) {
|
|
24325
|
+
if (!CLEARABLE_PLATE_LISTS.includes(value.mode)) {
|
|
24326
|
+
throw new Error(`Refusing to clear record set ${JSON.stringify(value.mode)}.`);
|
|
24327
|
+
}
|
|
24328
|
+
return `/cgi-bin/recordUpdater.cgi?action=clear&name=${enc(value.mode)}`;
|
|
24329
|
+
}
|
|
24189
24330
|
|
|
24190
24331
|
// src/utils/anpr-revoke.util.ts
|
|
24191
24332
|
function anprRevokeOutcome(response) {
|
|
@@ -24311,6 +24452,148 @@ function anprNoCameraRefusal(outcome) {
|
|
|
24311
24452
|
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.`;
|
|
24312
24453
|
}
|
|
24313
24454
|
|
|
24455
|
+
// src/utils/anpr-plate.util.ts
|
|
24456
|
+
var ALLOW_CAMERA_DIRECTIONS = ["entry", "both", "residents"];
|
|
24457
|
+
var BLOCK_CAMERA_DIRECTIONS = ["entry", "both", "residents", "visitors"];
|
|
24458
|
+
function isAllowCameraDirection(direction) {
|
|
24459
|
+
return ALLOW_CAMERA_DIRECTIONS.includes(
|
|
24460
|
+
String(direction ?? "").trim().toLowerCase()
|
|
24461
|
+
);
|
|
24462
|
+
}
|
|
24463
|
+
function isBlockCameraDirection(direction) {
|
|
24464
|
+
return BLOCK_CAMERA_DIRECTIONS.includes(
|
|
24465
|
+
String(direction ?? "").trim().toLowerCase()
|
|
24466
|
+
);
|
|
24467
|
+
}
|
|
24468
|
+
function listsFilledByDirectionChange(from, to) {
|
|
24469
|
+
const before = String(from ?? "").trim().toLowerCase();
|
|
24470
|
+
const after = String(to ?? "").trim().toLowerCase();
|
|
24471
|
+
if (!after || after === before)
|
|
24472
|
+
return { allow: false, block: false };
|
|
24473
|
+
return {
|
|
24474
|
+
allow: isAllowCameraDirection(after) && !isAllowCameraDirection(before),
|
|
24475
|
+
block: isBlockCameraDirection(after) && !isBlockCameraDirection(before)
|
|
24476
|
+
};
|
|
24477
|
+
}
|
|
24478
|
+
function listsClearedByDirectionChange(from, to) {
|
|
24479
|
+
const before = String(from ?? "").trim().toLowerCase();
|
|
24480
|
+
const after = String(to ?? "").trim().toLowerCase();
|
|
24481
|
+
if (!after || after === before)
|
|
24482
|
+
return { allow: false, block: false };
|
|
24483
|
+
if (after === "exit")
|
|
24484
|
+
return { allow: true, block: true };
|
|
24485
|
+
return {
|
|
24486
|
+
allow: isAllowCameraDirection(before) && !isAllowCameraDirection(after),
|
|
24487
|
+
block: false
|
|
24488
|
+
};
|
|
24489
|
+
}
|
|
24490
|
+
function mustRevokeFrom(direction, list, vehicleIsBlocked = false) {
|
|
24491
|
+
if (list === "allowed")
|
|
24492
|
+
return isAllowCameraDirection(direction);
|
|
24493
|
+
if (isBlockCameraDirection(direction))
|
|
24494
|
+
return true;
|
|
24495
|
+
return vehicleIsBlocked;
|
|
24496
|
+
}
|
|
24497
|
+
function normalisePlate(plate) {
|
|
24498
|
+
return String(plate ?? "").replace(/\s+/g, "").toUpperCase();
|
|
24499
|
+
}
|
|
24500
|
+
function samePlate(a, b) {
|
|
24501
|
+
const left = normalisePlate(a);
|
|
24502
|
+
return left !== "" && left === normalisePlate(b);
|
|
24503
|
+
}
|
|
24504
|
+
function parseDahuaFindRecords(text2) {
|
|
24505
|
+
const body = String(text2 ?? "");
|
|
24506
|
+
const found = Number(body.match(/found=(\d+)/i)?.[1] || 0);
|
|
24507
|
+
const byIndex = /* @__PURE__ */ new Map();
|
|
24508
|
+
const pattern = /records\[(\d+)\]\.(RecNo|PlateNumber|BeginTime|CancelTime|MasterOfCar)=([^\r\n]*)/gi;
|
|
24509
|
+
for (const match of body.matchAll(pattern)) {
|
|
24510
|
+
const index = Number(match[1]);
|
|
24511
|
+
const key = match[2].toLowerCase();
|
|
24512
|
+
const value = match[3].trim();
|
|
24513
|
+
const record = byIndex.get(index) ?? { recNo: "", plateNumber: "" };
|
|
24514
|
+
if (key === "recno")
|
|
24515
|
+
record.recNo = value;
|
|
24516
|
+
else if (key === "platenumber")
|
|
24517
|
+
record.plateNumber = value;
|
|
24518
|
+
else if (key === "begintime")
|
|
24519
|
+
record.beginTime = value;
|
|
24520
|
+
else if (key === "canceltime")
|
|
24521
|
+
record.cancelTime = value;
|
|
24522
|
+
else
|
|
24523
|
+
record.owner = value;
|
|
24524
|
+
byIndex.set(index, record);
|
|
24525
|
+
}
|
|
24526
|
+
const records = Array.from(byIndex.entries()).sort(([a], [b]) => a - b).map(([, record]) => record).filter((record) => record.recNo !== "");
|
|
24527
|
+
return { found, records };
|
|
24528
|
+
}
|
|
24529
|
+
function matchingPlateRecords(text2, plateNumber) {
|
|
24530
|
+
return parseDahuaFindRecords(text2).records.filter(
|
|
24531
|
+
(record) => record.plateNumber === "" || samePlate(record.plateNumber, plateNumber)
|
|
24532
|
+
);
|
|
24533
|
+
}
|
|
24534
|
+
function listsHeldBy(vehicle) {
|
|
24535
|
+
const type = String(vehicle?.type ?? "");
|
|
24536
|
+
return {
|
|
24537
|
+
allow: type === "whitelist" || String(vehicle?.blockedFrom ?? "") === "whitelist",
|
|
24538
|
+
block: type === "blocklist"
|
|
24539
|
+
};
|
|
24540
|
+
}
|
|
24541
|
+
function listsToRevoke(othersStillLive) {
|
|
24542
|
+
const others = (othersStillLive ?? []).map(listsHeldBy);
|
|
24543
|
+
return {
|
|
24544
|
+
allow: !others.some((held) => held.allow),
|
|
24545
|
+
block: !others.some((held) => held.block)
|
|
24546
|
+
};
|
|
24547
|
+
}
|
|
24548
|
+
var DEFAULT_CAMERA_TIMEZONE = "Asia/Singapore";
|
|
24549
|
+
function usableTimezone(timezone) {
|
|
24550
|
+
const zone = String(timezone ?? "").trim();
|
|
24551
|
+
if (!zone)
|
|
24552
|
+
return DEFAULT_CAMERA_TIMEZONE;
|
|
24553
|
+
try {
|
|
24554
|
+
new Intl.DateTimeFormat("en-US", { timeZone: zone });
|
|
24555
|
+
return zone;
|
|
24556
|
+
} catch {
|
|
24557
|
+
return DEFAULT_CAMERA_TIMEZONE;
|
|
24558
|
+
}
|
|
24559
|
+
}
|
|
24560
|
+
var WALL_CLOCK = /^(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2})(?::(\d{2}))?$/;
|
|
24561
|
+
function dahuaWallClock(value, timezone) {
|
|
24562
|
+
if (value === null || value === void 0 || value === "")
|
|
24563
|
+
return null;
|
|
24564
|
+
if (typeof value === "string") {
|
|
24565
|
+
const wall = value.trim().match(WALL_CLOCK);
|
|
24566
|
+
if (wall) {
|
|
24567
|
+
const [, y, mo, d, h, mi, se] = wall;
|
|
24568
|
+
return `${y}-${mo}-${d} ${h}:${mi}:${se ?? "00"}`;
|
|
24569
|
+
}
|
|
24570
|
+
}
|
|
24571
|
+
const date = value instanceof Date ? value : new Date(value);
|
|
24572
|
+
if (Number.isNaN(date.getTime()))
|
|
24573
|
+
return null;
|
|
24574
|
+
const parts = new Intl.DateTimeFormat("en-GB", {
|
|
24575
|
+
timeZone: usableTimezone(timezone),
|
|
24576
|
+
year: "numeric",
|
|
24577
|
+
month: "2-digit",
|
|
24578
|
+
day: "2-digit",
|
|
24579
|
+
hour: "2-digit",
|
|
24580
|
+
minute: "2-digit",
|
|
24581
|
+
second: "2-digit",
|
|
24582
|
+
hourCycle: "h23"
|
|
24583
|
+
}).formatToParts(date);
|
|
24584
|
+
const part = (type) => parts.find((entry) => entry.type === type)?.value ?? "00";
|
|
24585
|
+
return `${part("year")}-${part("month")}-${part("day")} ${part("hour")}:${part("minute")}:${part("second")}`;
|
|
24586
|
+
}
|
|
24587
|
+
function plateWindowMatches(record, wanted) {
|
|
24588
|
+
const same = (held, want) => {
|
|
24589
|
+
const a = dahuaWallClock(held);
|
|
24590
|
+
return a !== null && a === dahuaWallClock(want);
|
|
24591
|
+
};
|
|
24592
|
+
if (!wanted.start || !wanted.end)
|
|
24593
|
+
return false;
|
|
24594
|
+
return same(record.beginTime, wanted.start) && same(record.cancelTime, wanted.end);
|
|
24595
|
+
}
|
|
24596
|
+
|
|
24314
24597
|
// src/services/dahua.service.ts
|
|
24315
24598
|
function notifyCameraFault(camera, message) {
|
|
24316
24599
|
NotificationService.cameraFault({
|
|
@@ -24382,12 +24665,14 @@ async function useDahuaDigestWithRetry({
|
|
|
24382
24665
|
const statusCode = getDahuaStatusCode(response);
|
|
24383
24666
|
if (streaming) {
|
|
24384
24667
|
if (statusCode === 200) {
|
|
24668
|
+
markLoginProven(host, username, password);
|
|
24385
24669
|
return response;
|
|
24386
24670
|
}
|
|
24387
24671
|
} else {
|
|
24388
24672
|
const text2 = getDahuaResponseText(response);
|
|
24389
24673
|
const isOk = statusCode === 200 && text2.includes("OK");
|
|
24390
24674
|
if (isOk) {
|
|
24675
|
+
markLoginProven(host, username, password);
|
|
24391
24676
|
return response;
|
|
24392
24677
|
}
|
|
24393
24678
|
}
|
|
@@ -24421,6 +24706,123 @@ async function useDahuaDigestWithRetry({
|
|
|
24421
24706
|
}
|
|
24422
24707
|
throw lastError;
|
|
24423
24708
|
}
|
|
24709
|
+
var provenLogins = /* @__PURE__ */ new Map();
|
|
24710
|
+
function loginKey(host, username, password) {
|
|
24711
|
+
return createHash2("sha256").update(`${host ?? ""}
|
|
24712
|
+
${username ?? ""}
|
|
24713
|
+
${password ?? ""}`).digest("hex");
|
|
24714
|
+
}
|
|
24715
|
+
function markLoginProven(host, username, password) {
|
|
24716
|
+
provenLogins.set(loginKey(host, username, password), Date.now());
|
|
24717
|
+
}
|
|
24718
|
+
function forgetLogin(host, username, password) {
|
|
24719
|
+
provenLogins.delete(loginKey(host, username, password));
|
|
24720
|
+
}
|
|
24721
|
+
function isLoginProven(host, username, password) {
|
|
24722
|
+
const at2 = provenLogins.get(loginKey(host, username, password));
|
|
24723
|
+
return at2 !== void 0 && Date.now() - at2 < DAHUA_LOGIN_PROVEN_FOR_MS;
|
|
24724
|
+
}
|
|
24725
|
+
var ATTEMPT_WORDS = {
|
|
24726
|
+
"login-refused": "login refused",
|
|
24727
|
+
forbidden: "credentials refused",
|
|
24728
|
+
busy: "camera busy",
|
|
24729
|
+
"not-sent": "could not connect",
|
|
24730
|
+
"maybe-sent": "connection lost"
|
|
24731
|
+
};
|
|
24732
|
+
async function sendCameraCommand({
|
|
24733
|
+
host,
|
|
24734
|
+
username,
|
|
24735
|
+
password,
|
|
24736
|
+
endpoint,
|
|
24737
|
+
timeout = 1e4,
|
|
24738
|
+
repeatable = true,
|
|
24739
|
+
label = "command",
|
|
24740
|
+
deadlineMs = DAHUA_COMMAND_DEADLINE_MS
|
|
24741
|
+
}) {
|
|
24742
|
+
const startedAt = Date.now();
|
|
24743
|
+
let loginFailures = 0;
|
|
24744
|
+
for (let attempt = 1; ; attempt++) {
|
|
24745
|
+
let response;
|
|
24746
|
+
let error;
|
|
24747
|
+
try {
|
|
24748
|
+
response = await useDahuaDigest({
|
|
24749
|
+
host,
|
|
24750
|
+
username,
|
|
24751
|
+
password,
|
|
24752
|
+
endpoint,
|
|
24753
|
+
headers: { Connection: "close" },
|
|
24754
|
+
timeout
|
|
24755
|
+
});
|
|
24756
|
+
} catch (thrown) {
|
|
24757
|
+
error = thrown ?? new Error("the request failed with no error");
|
|
24758
|
+
}
|
|
24759
|
+
const kind = classifyDahuaAttempt(
|
|
24760
|
+
error ? { error } : {
|
|
24761
|
+
statusCode: getDahuaStatusCode(response),
|
|
24762
|
+
text: getDahuaResponseText(response)
|
|
24763
|
+
}
|
|
24764
|
+
);
|
|
24765
|
+
if (kind === "answered") {
|
|
24766
|
+
markLoginProven(host, username, password);
|
|
24767
|
+
if (attempt > 1) {
|
|
24768
|
+
loggerDahua.info(`[${host}] ${label}: went through on try ${attempt}`);
|
|
24769
|
+
}
|
|
24770
|
+
return response;
|
|
24771
|
+
}
|
|
24772
|
+
if (kind === "login-refused")
|
|
24773
|
+
loginFailures++;
|
|
24774
|
+
if (kind === "forbidden")
|
|
24775
|
+
forgetLogin(host, username, password);
|
|
24776
|
+
const nextDelayMs = dahuaCommandRetryDelayMs(attempt);
|
|
24777
|
+
const retry = shouldRetryDahuaCommand({
|
|
24778
|
+
kind,
|
|
24779
|
+
attempt,
|
|
24780
|
+
loginFailures,
|
|
24781
|
+
loginProven: isLoginProven(host, username, password),
|
|
24782
|
+
repeatable,
|
|
24783
|
+
elapsedMs: Date.now() - startedAt,
|
|
24784
|
+
nextDelayMs,
|
|
24785
|
+
deadlineMs
|
|
24786
|
+
});
|
|
24787
|
+
const why = ATTEMPT_WORDS[kind] ?? kind;
|
|
24788
|
+
if (!retry) {
|
|
24789
|
+
if (attempt > 1 || kind !== "forbidden") {
|
|
24790
|
+
loggerDahua.warn(
|
|
24791
|
+
`[${host}] ${label}: ${why} on try ${attempt}/${DAHUA_COMMAND_ATTEMPTS}; giving up`
|
|
24792
|
+
);
|
|
24793
|
+
}
|
|
24794
|
+
if (error)
|
|
24795
|
+
throw error;
|
|
24796
|
+
return response;
|
|
24797
|
+
}
|
|
24798
|
+
loggerDahua.warn(
|
|
24799
|
+
`[${host}] ${label}: ${why} on try ${attempt}/${DAHUA_COMMAND_ATTEMPTS}; trying again in ${nextDelayMs} ms`
|
|
24800
|
+
);
|
|
24801
|
+
await sleep(nextDelayMs);
|
|
24802
|
+
}
|
|
24803
|
+
}
|
|
24804
|
+
async function sendCameraCommandExpectingOk(options) {
|
|
24805
|
+
const response = await sendCameraCommand(options);
|
|
24806
|
+
const statusCode = getDahuaStatusCode(response);
|
|
24807
|
+
const text2 = getDahuaResponseText(response);
|
|
24808
|
+
if (statusCode === 200 && text2.includes("OK"))
|
|
24809
|
+
return response;
|
|
24810
|
+
const error = new Error(`Dahua response: ${text2 || `HTTP ${statusCode}`}`);
|
|
24811
|
+
error.answeredByCamera = true;
|
|
24812
|
+
throw error;
|
|
24813
|
+
}
|
|
24814
|
+
function anprOwnerName(owner) {
|
|
24815
|
+
return String(owner ?? "").replace(/['"]/g, "").replace(/[\/\\]/g, " - ").substring(0, 15).trim() || "unknown";
|
|
24816
|
+
}
|
|
24817
|
+
function plateWindowOnCamera(value) {
|
|
24818
|
+
const tenMinutesAgo = new Date(Date.now() - 10 * 6e4);
|
|
24819
|
+
const tenYearsOn = new Date(tenMinutesAgo);
|
|
24820
|
+
tenYearsOn.setFullYear(tenYearsOn.getFullYear() + 10);
|
|
24821
|
+
return {
|
|
24822
|
+
start: dahuaWallClock(value.start, value.timezone) ?? dahuaWallClock(tenMinutesAgo, value.timezone),
|
|
24823
|
+
end: dahuaWallClock(value.end, value.timezone) ?? dahuaWallClock(tenYearsOn, value.timezone)
|
|
24824
|
+
};
|
|
24825
|
+
}
|
|
24424
24826
|
function createFileNameFromEvent(utcSeconds, utcMilliseconds) {
|
|
24425
24827
|
const date = new Date(utcSeconds * 1e3 + utcMilliseconds);
|
|
24426
24828
|
const year = date.getUTCFullYear();
|
|
@@ -25022,111 +25424,115 @@ function useDahuaService() {
|
|
|
25022
25424
|
start: Joi26.string().isoDate().optional().allow("", null),
|
|
25023
25425
|
end: Joi26.string().isoDate().optional().allow("", null),
|
|
25024
25426
|
owner: Joi26.string().optional().allow("", null),
|
|
25025
|
-
isOpenGate: Joi26.boolean().optional().allow(null)
|
|
25427
|
+
isOpenGate: Joi26.boolean().optional().allow(null),
|
|
25428
|
+
timezone: Joi26.string().optional().allow("", null)
|
|
25026
25429
|
});
|
|
25027
25430
|
const { error: validationError } = validation.validate(value);
|
|
25028
25431
|
if (validationError) {
|
|
25029
25432
|
throw new BadRequestError47(`Validation error: ${validationError.message}`);
|
|
25030
25433
|
}
|
|
25031
25434
|
try {
|
|
25032
|
-
value.owner =
|
|
25033
|
-
const
|
|
25034
|
-
const date = dateStr ? new Date(dateStr) : /* @__PURE__ */ new Date();
|
|
25035
|
-
if (!dateStr) {
|
|
25036
|
-
date.setMinutes(date.getMinutes() - 10);
|
|
25037
|
-
}
|
|
25038
|
-
if (fallbackYearsAhead > 0 && !dateStr) {
|
|
25039
|
-
date.setFullYear(date.getFullYear() + fallbackYearsAhead);
|
|
25040
|
-
}
|
|
25041
|
-
const pad = (num) => String(num).padStart(2, "0");
|
|
25042
|
-
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`;
|
|
25043
|
-
};
|
|
25044
|
-
const formattedStart = formatDahuaDate3(value.start);
|
|
25045
|
-
const formattedEnd = formatDahuaDate3(value.end, 10);
|
|
25435
|
+
value.owner = anprOwnerName(value.owner);
|
|
25436
|
+
const { start: formattedStart, end: formattedEnd } = plateWindowOnCamera(value);
|
|
25046
25437
|
const beginTime = encodeURIComponent(formattedStart);
|
|
25047
25438
|
const cancelTime = encodeURIComponent(formattedEnd);
|
|
25048
25439
|
const plateNumber = encodeURIComponent(value.plateNumber);
|
|
25049
25440
|
const ownerName = encodeURIComponent(value.owner);
|
|
25050
25441
|
const endpoint = `/cgi-bin/recordUpdater.cgi?action=insert&name=${value.mode}&PlateNumber=${plateNumber}&BeginTime=${beginTime}&CancelTime=${cancelTime}&MasterOfCar=${ownerName}`;
|
|
25051
|
-
|
|
25052
|
-
|
|
25053
|
-
|
|
25054
|
-
|
|
25055
|
-
|
|
25056
|
-
|
|
25057
|
-
|
|
25058
|
-
|
|
25059
|
-
|
|
25060
|
-
|
|
25061
|
-
|
|
25062
|
-
|
|
25063
|
-
|
|
25064
|
-
statusCode: 200,
|
|
25065
|
-
data: Buffer.from(`recno=${insertedId}`)
|
|
25066
|
-
};
|
|
25067
|
-
} catch (insertError) {
|
|
25068
|
-
const errorMessage = insertError?.message || String(insertError);
|
|
25069
|
-
if (/recno=/i.test(errorMessage)) {
|
|
25070
|
-
loggerDahua.info(`[${value.host}] Insert confirmed successfully via raw RecNo response text.`);
|
|
25071
|
-
const insertedId = errorMessage.match(/\d+/)?.[0] || "unknown";
|
|
25442
|
+
const INSERT_ROUNDS = 3;
|
|
25443
|
+
for (let round = 1; ; round++) {
|
|
25444
|
+
try {
|
|
25445
|
+
const insertResponse = await sendCameraCommandExpectingOk({
|
|
25446
|
+
host: value.host,
|
|
25447
|
+
username: value.username,
|
|
25448
|
+
password: value.password,
|
|
25449
|
+
endpoint,
|
|
25450
|
+
repeatable: false,
|
|
25451
|
+
label: `add plate to ${value.mode}`
|
|
25452
|
+
});
|
|
25453
|
+
const insertText = getDahuaResponseText(insertResponse);
|
|
25454
|
+
const insertedId = insertText.match(/\d+/)?.[0] || "unknown";
|
|
25072
25455
|
return {
|
|
25456
|
+
...insertResponse,
|
|
25073
25457
|
statusCode: 200,
|
|
25074
25458
|
data: Buffer.from(`recno=${insertedId}`)
|
|
25075
25459
|
};
|
|
25076
|
-
}
|
|
25077
|
-
|
|
25078
|
-
|
|
25079
|
-
|
|
25080
|
-
|
|
25081
|
-
|
|
25082
|
-
|
|
25083
|
-
|
|
25084
|
-
|
|
25085
|
-
endpoint: findEndpoint,
|
|
25086
|
-
retries: 5
|
|
25087
|
-
});
|
|
25088
|
-
const textData = getDahuaResponseText(findResponse);
|
|
25089
|
-
recno = extractRecNo(textData);
|
|
25090
|
-
} catch (findError) {
|
|
25091
|
-
const findErrorMessage = findError?.message || String(findError);
|
|
25092
|
-
recno = extractRecNo(findErrorMessage);
|
|
25093
|
-
if (!recno) {
|
|
25094
|
-
loggerDahua.error(`[${value.host}] Failed handling existing duplicate plate flow`, findError);
|
|
25095
|
-
}
|
|
25460
|
+
} catch (insertError) {
|
|
25461
|
+
const errorMessage = insertError?.message || String(insertError);
|
|
25462
|
+
if (/recno=/i.test(errorMessage)) {
|
|
25463
|
+
loggerDahua.info(`[${value.host}] Insert confirmed successfully via raw RecNo response text.`);
|
|
25464
|
+
const insertedId = errorMessage.match(/\d+/)?.[0] || "unknown";
|
|
25465
|
+
return {
|
|
25466
|
+
statusCode: 200,
|
|
25467
|
+
data: Buffer.from(`recno=${insertedId}`)
|
|
25468
|
+
};
|
|
25096
25469
|
}
|
|
25097
|
-
|
|
25098
|
-
|
|
25099
|
-
|
|
25470
|
+
const duplicate = errorMessage.includes("Bad Request") || errorMessage.includes("Dahua response: Error");
|
|
25471
|
+
const lostInFlight = !insertError?.answeredByCamera && classifyDahuaAttempt({ error: insertError }) === "maybe-sent";
|
|
25472
|
+
if (duplicate || lostInFlight) {
|
|
25473
|
+
loggerDahua.info(
|
|
25474
|
+
`[${value.host}] Insert ${duplicate ? "failed (duplicate)" : "lost its connection"}. Checking if the plate is already on ${value.mode}...`
|
|
25475
|
+
);
|
|
25476
|
+
const findEndpoint = `/cgi-bin/recordFinder.cgi?action=find&name=${value.mode}&condition.PlateNumber=${plateNumber}`;
|
|
25100
25477
|
try {
|
|
25101
|
-
const
|
|
25478
|
+
const findResponse = await sendCameraCommand({
|
|
25102
25479
|
host: value.host,
|
|
25103
25480
|
username: value.username,
|
|
25104
25481
|
password: value.password,
|
|
25105
|
-
endpoint:
|
|
25106
|
-
|
|
25482
|
+
endpoint: findEndpoint,
|
|
25483
|
+
label: `find plate on ${value.mode}`
|
|
25107
25484
|
});
|
|
25108
|
-
|
|
25109
|
-
|
|
25110
|
-
|
|
25111
|
-
|
|
25112
|
-
|
|
25113
|
-
|
|
25114
|
-
|
|
25115
|
-
|
|
25116
|
-
|
|
25117
|
-
|
|
25118
|
-
|
|
25119
|
-
|
|
25485
|
+
if (getDahuaStatusCode(findResponse) === 200) {
|
|
25486
|
+
recno = extractRecNo(getDahuaResponseText(findResponse));
|
|
25487
|
+
} else {
|
|
25488
|
+
loggerDahua.error(
|
|
25489
|
+
`[${value.host}] Plate lookup after a failed insert answered HTTP ${getDahuaStatusCode(findResponse)}`
|
|
25490
|
+
);
|
|
25491
|
+
}
|
|
25492
|
+
} catch (findError) {
|
|
25493
|
+
loggerDahua.error(`[${value.host}] Failed handling existing duplicate plate flow`, findError);
|
|
25494
|
+
}
|
|
25495
|
+
if (recno) {
|
|
25496
|
+
loggerDahua.info(`[${value.host}] Found existing record ID: ${recno}. Updating registration instead...`);
|
|
25497
|
+
const updateEndpoint = `/cgi-bin/recordUpdater.cgi?action=update&name=${value.mode}&recno=${recno}&PlateNumber=${plateNumber}&BeginTime=${beginTime}&CancelTime=${cancelTime}&MasterOfCar=${ownerName}`;
|
|
25498
|
+
try {
|
|
25499
|
+
const updateResponse = await sendCameraCommandExpectingOk({
|
|
25500
|
+
host: value.host,
|
|
25501
|
+
username: value.username,
|
|
25502
|
+
password: value.password,
|
|
25503
|
+
endpoint: updateEndpoint,
|
|
25504
|
+
label: `update plate on ${value.mode}`
|
|
25505
|
+
});
|
|
25120
25506
|
return {
|
|
25507
|
+
...updateResponse,
|
|
25121
25508
|
statusCode: 200,
|
|
25122
25509
|
data: Buffer.from(`recno=${recno}`)
|
|
25123
25510
|
};
|
|
25511
|
+
} catch (updateError) {
|
|
25512
|
+
const updateErrorMessage = updateError?.message || String(updateError);
|
|
25513
|
+
if (/recno=/i.test(updateErrorMessage) || /ok/i.test(updateErrorMessage)) {
|
|
25514
|
+
loggerDahua.info(`[${value.host}] Update confirmed successfully despite wrapper omission.`);
|
|
25515
|
+
if (!recno) {
|
|
25516
|
+
recno = extractRecNo(updateErrorMessage);
|
|
25517
|
+
}
|
|
25518
|
+
return {
|
|
25519
|
+
statusCode: 200,
|
|
25520
|
+
data: Buffer.from(`recno=${recno}`)
|
|
25521
|
+
};
|
|
25522
|
+
}
|
|
25523
|
+
throw updateError;
|
|
25124
25524
|
}
|
|
25125
|
-
|
|
25525
|
+
}
|
|
25526
|
+
if (lostInFlight && round < INSERT_ROUNDS) {
|
|
25527
|
+
loggerDahua.warn(
|
|
25528
|
+
`[${value.host}] Plate not found after a lost insert; sending the insert again (round ${round + 1}/${INSERT_ROUNDS})`
|
|
25529
|
+
);
|
|
25530
|
+
await sleep(dahuaCommandRetryDelayMs(round));
|
|
25531
|
+
continue;
|
|
25126
25532
|
}
|
|
25127
25533
|
}
|
|
25534
|
+
throw insertError;
|
|
25128
25535
|
}
|
|
25129
|
-
throw insertError;
|
|
25130
25536
|
}
|
|
25131
25537
|
} catch (finalError) {
|
|
25132
25538
|
loggerDahua.error(
|
|
@@ -25146,7 +25552,8 @@ function useDahuaService() {
|
|
|
25146
25552
|
start: Joi26.string().isoDate().optional().allow("", null),
|
|
25147
25553
|
end: Joi26.string().isoDate().optional().allow("", null),
|
|
25148
25554
|
owner: Joi26.string().optional().allow("", null),
|
|
25149
|
-
isOpenGate: Joi26.boolean().optional()
|
|
25555
|
+
isOpenGate: Joi26.boolean().optional(),
|
|
25556
|
+
timezone: Joi26.string().optional().allow("", null)
|
|
25150
25557
|
});
|
|
25151
25558
|
const { error } = validation.validate(value);
|
|
25152
25559
|
if (error) {
|
|
@@ -25162,17 +25569,20 @@ function useDahuaService() {
|
|
|
25162
25569
|
mode: value.mode,
|
|
25163
25570
|
recno: value.recno,
|
|
25164
25571
|
plateNumber: value.plateNumber,
|
|
25165
|
-
|
|
25166
|
-
|
|
25572
|
+
// On the camera's clock (see `dahuaWallClock`); a time already written
|
|
25573
|
+
// for the camera passes through unchanged.
|
|
25574
|
+
start: dahuaWallClock(value.start, value.timezone) ?? value.start,
|
|
25575
|
+
end: dahuaWallClock(value.end, value.timezone) ?? value.end,
|
|
25167
25576
|
owner: value.owner,
|
|
25168
25577
|
openGate: _isOpenGate
|
|
25169
25578
|
});
|
|
25170
25579
|
try {
|
|
25171
|
-
const response = await
|
|
25580
|
+
const response = await sendCameraCommand({
|
|
25172
25581
|
host: value.host,
|
|
25173
25582
|
username: value.username,
|
|
25174
25583
|
password: value.password,
|
|
25175
|
-
endpoint
|
|
25584
|
+
endpoint,
|
|
25585
|
+
label: `update plate on ${value.mode}`
|
|
25176
25586
|
});
|
|
25177
25587
|
return response;
|
|
25178
25588
|
} catch (error2) {
|
|
@@ -25195,14 +25605,16 @@ function useDahuaService() {
|
|
|
25195
25605
|
throw new BadRequestError47(`Validation error: ${error.message}`);
|
|
25196
25606
|
}
|
|
25197
25607
|
try {
|
|
25198
|
-
const response = await
|
|
25608
|
+
const response = await sendCameraCommand({
|
|
25199
25609
|
host: value.host,
|
|
25200
25610
|
username: value.username,
|
|
25201
25611
|
password: value.password,
|
|
25202
25612
|
endpoint: buildPlateRemoveEndpoint({
|
|
25203
25613
|
recno: value.recno,
|
|
25204
25614
|
mode: value.mode
|
|
25205
|
-
})
|
|
25615
|
+
}),
|
|
25616
|
+
repeatable: false,
|
|
25617
|
+
label: `remove plate from ${value.mode}`
|
|
25206
25618
|
});
|
|
25207
25619
|
return anprRevokeOutcome(response);
|
|
25208
25620
|
} catch (error2) {
|
|
@@ -25224,11 +25636,18 @@ function useDahuaService() {
|
|
|
25224
25636
|
throw new BadRequestError47(`Validation error: ${error.message}`);
|
|
25225
25637
|
}
|
|
25226
25638
|
try {
|
|
25227
|
-
const
|
|
25639
|
+
const findEndpoint = `/cgi-bin/recordFinder.cgi?action=find&name=${value.mode}&condition.PlateNumber=${encodeURIComponent(value.plateNumber)}`;
|
|
25640
|
+
const response = value.requireOk ? await sendCameraCommand({
|
|
25641
|
+
host: value.host,
|
|
25642
|
+
username: value.username,
|
|
25643
|
+
password: value.password,
|
|
25644
|
+
endpoint: findEndpoint,
|
|
25645
|
+
label: `find plate on ${value.mode}`
|
|
25646
|
+
}) : await useDahuaDigest({
|
|
25228
25647
|
host: value.host,
|
|
25229
25648
|
username: value.username,
|
|
25230
25649
|
password: value.password,
|
|
25231
|
-
endpoint:
|
|
25650
|
+
endpoint: findEndpoint
|
|
25232
25651
|
});
|
|
25233
25652
|
if (value.requireOk) {
|
|
25234
25653
|
const statusCode = getDahuaStatusCode(response);
|
|
@@ -25255,7 +25674,8 @@ function useDahuaService() {
|
|
|
25255
25674
|
owner: Joi26.string().optional().allow("", null),
|
|
25256
25675
|
isOpenGate: Joi26.boolean().optional().allow(null),
|
|
25257
25676
|
vehicleType: Joi26.string().optional().allow("", null),
|
|
25258
|
-
vehicleColor: Joi26.string().optional().allow("", null)
|
|
25677
|
+
vehicleColor: Joi26.string().optional().allow("", null),
|
|
25678
|
+
timezone: Joi26.string().optional().allow("", null)
|
|
25259
25679
|
});
|
|
25260
25680
|
const { error } = validation.validate(value);
|
|
25261
25681
|
if (error) {
|
|
@@ -25271,17 +25691,20 @@ function useDahuaService() {
|
|
|
25271
25691
|
plateNumber: value.plateNumber,
|
|
25272
25692
|
vehicleType: value.vehicleType,
|
|
25273
25693
|
vehicleColor: value.vehicleColor,
|
|
25274
|
-
|
|
25275
|
-
|
|
25694
|
+
// On the camera's clock (see `dahuaWallClock`).
|
|
25695
|
+
start: dahuaWallClock(value.start, value.timezone) ?? value.start,
|
|
25696
|
+
end: dahuaWallClock(value.end, value.timezone) ?? value.end,
|
|
25276
25697
|
owner: value.owner,
|
|
25277
25698
|
openGate: isOpenGateString
|
|
25278
25699
|
});
|
|
25279
25700
|
try {
|
|
25280
|
-
const response = await
|
|
25701
|
+
const response = await sendCameraCommand({
|
|
25281
25702
|
host: value.host,
|
|
25282
25703
|
username: value.username,
|
|
25283
25704
|
password: value.password,
|
|
25284
|
-
endpoint
|
|
25705
|
+
endpoint,
|
|
25706
|
+
repeatable: false,
|
|
25707
|
+
label: `bulk add plate to ${value.mode}`
|
|
25285
25708
|
});
|
|
25286
25709
|
return response;
|
|
25287
25710
|
} catch (error2) {
|
|
@@ -25335,11 +25758,91 @@ function useDahuaService() {
|
|
|
25335
25758
|
throw error2;
|
|
25336
25759
|
}
|
|
25337
25760
|
}
|
|
25761
|
+
async function setPlateWindow(value) {
|
|
25762
|
+
const validation = Joi26.object({
|
|
25763
|
+
host: Joi26.string().required(),
|
|
25764
|
+
username: Joi26.string().required(),
|
|
25765
|
+
password: Joi26.string().required(),
|
|
25766
|
+
mode: Joi26.string().valid(...Object.values(ANPRMode)).required(),
|
|
25767
|
+
recno: Joi26.string().required(),
|
|
25768
|
+
plateNumber: Joi26.string().required(),
|
|
25769
|
+
owner: Joi26.string().optional().allow("", null),
|
|
25770
|
+
start: Joi26.string().isoDate().optional().allow("", null),
|
|
25771
|
+
end: Joi26.string().isoDate().optional().allow("", null),
|
|
25772
|
+
timezone: Joi26.string().optional().allow("", null)
|
|
25773
|
+
});
|
|
25774
|
+
const { error } = validation.validate(value);
|
|
25775
|
+
if (error) {
|
|
25776
|
+
throw new BadRequestError47(`Validation error: ${error.message}`);
|
|
25777
|
+
}
|
|
25778
|
+
const window = plateWindowOnCamera(value);
|
|
25779
|
+
const endpoint = `/cgi-bin/recordUpdater.cgi?action=update&name=${encodeURIComponent(value.mode)}&recno=${encodeURIComponent(value.recno)}&PlateNumber=${encodeURIComponent(value.plateNumber)}&BeginTime=${encodeURIComponent(window.start)}&CancelTime=${encodeURIComponent(window.end)}&MasterOfCar=${encodeURIComponent(anprOwnerName(value.owner))}`;
|
|
25780
|
+
try {
|
|
25781
|
+
return await sendCameraCommandExpectingOk({
|
|
25782
|
+
host: value.host,
|
|
25783
|
+
username: value.username,
|
|
25784
|
+
password: value.password,
|
|
25785
|
+
endpoint,
|
|
25786
|
+
label: `set plate window on ${value.mode}`
|
|
25787
|
+
});
|
|
25788
|
+
} catch (error2) {
|
|
25789
|
+
loggerDahua.error(
|
|
25790
|
+
`[${value.host}] Error setting plate window: ${error2?.message || error2}`
|
|
25791
|
+
);
|
|
25792
|
+
throw new BadRequestError47(anprWriteReason(error2));
|
|
25793
|
+
}
|
|
25794
|
+
}
|
|
25795
|
+
async function clearPlateList(value) {
|
|
25796
|
+
const validation = Joi26.object({
|
|
25797
|
+
host: Joi26.string().required(),
|
|
25798
|
+
username: Joi26.string().required(),
|
|
25799
|
+
password: Joi26.string().required(),
|
|
25800
|
+
mode: Joi26.string().valid(...Object.values(ANPRMode)).required()
|
|
25801
|
+
});
|
|
25802
|
+
const { error } = validation.validate(value);
|
|
25803
|
+
if (error) {
|
|
25804
|
+
return anprRevokeThrew(`Validation error: ${error.message}`);
|
|
25805
|
+
}
|
|
25806
|
+
loggerDahua.info(`clearPlateList called for host ${value.host} mode ${value.mode}`);
|
|
25807
|
+
try {
|
|
25808
|
+
const response = await sendCameraCommand({
|
|
25809
|
+
host: value.host,
|
|
25810
|
+
username: value.username,
|
|
25811
|
+
password: value.password,
|
|
25812
|
+
endpoint: buildPlateClearEndpoint({ mode: value.mode }),
|
|
25813
|
+
// A long list takes the device a while to empty.
|
|
25814
|
+
timeout: 6e4,
|
|
25815
|
+
deadlineMs: 9e4,
|
|
25816
|
+
label: `clear ${value.mode}`
|
|
25817
|
+
});
|
|
25818
|
+
const outcome = anprRevokeOutcome(response);
|
|
25819
|
+
if (!outcome.ok)
|
|
25820
|
+
return outcome;
|
|
25821
|
+
const text2 = getDahuaResponseText(response);
|
|
25822
|
+
if (!/\bOK\b/i.test(text2)) {
|
|
25823
|
+
loggerDahua.error(
|
|
25824
|
+
`[${value.host}] clearPlateList ${value.mode} answered 200 without OK: ${text2.slice(0, 200)}`
|
|
25825
|
+
);
|
|
25826
|
+
return {
|
|
25827
|
+
ok: false,
|
|
25828
|
+
statusCode: 200,
|
|
25829
|
+
reason: "the camera did not accept the command to clear its list",
|
|
25830
|
+
detail: `the camera answered 200 without OK: ${text2.slice(0, 200)}`
|
|
25831
|
+
};
|
|
25832
|
+
}
|
|
25833
|
+
return outcome;
|
|
25834
|
+
} catch (error2) {
|
|
25835
|
+
loggerDahua.error(`[${value.host}] Error clearing ${value.mode}:`, error2);
|
|
25836
|
+
return anprRevokeThrew(error2);
|
|
25837
|
+
}
|
|
25838
|
+
}
|
|
25338
25839
|
return {
|
|
25339
25840
|
getSnapshot,
|
|
25340
25841
|
listenToCamera,
|
|
25341
25842
|
addPlateNumber,
|
|
25342
25843
|
removePlateNumber,
|
|
25844
|
+
clearPlateList,
|
|
25845
|
+
setPlateWindow,
|
|
25343
25846
|
updatePlateNumber,
|
|
25344
25847
|
getPlateNumber,
|
|
25345
25848
|
bulkInsertPlateNumber,
|
|
@@ -25781,6 +26284,11 @@ function useSiteCameraRepo() {
|
|
|
25781
26284
|
{
|
|
25782
26285
|
$match: query2
|
|
25783
26286
|
},
|
|
26287
|
+
// A fixed order, so paging through a site's cameras 20 at a time
|
|
26288
|
+
// cannot skip one camera and return another twice.
|
|
26289
|
+
{
|
|
26290
|
+
$sort: { _id: 1 }
|
|
26291
|
+
},
|
|
25784
26292
|
{
|
|
25785
26293
|
$skip: value.page * value.limit
|
|
25786
26294
|
},
|
|
@@ -26743,7 +27251,7 @@ function formatCapabilityTrace(trace) {
|
|
|
26743
27251
|
}
|
|
26744
27252
|
|
|
26745
27253
|
// src/services/camera-device-http.service.ts
|
|
26746
|
-
import { createHash as
|
|
27254
|
+
import { createHash as createHash3, randomBytes } from "crypto";
|
|
26747
27255
|
var DIGEST_UNAUTHORIZED = 401;
|
|
26748
27256
|
var DIGEST_FORBIDDEN = 403;
|
|
26749
27257
|
function parseDigestChallenge(header) {
|
|
@@ -26759,7 +27267,7 @@ function parseDigestChallenge(header) {
|
|
|
26759
27267
|
return out;
|
|
26760
27268
|
}
|
|
26761
27269
|
function md5(input) {
|
|
26762
|
-
return
|
|
27270
|
+
return createHash3("md5").update(input, "utf8").digest("hex");
|
|
26763
27271
|
}
|
|
26764
27272
|
function buildDigestAuthorization(params) {
|
|
26765
27273
|
const realm = params.challenge.realm ?? "";
|
|
@@ -36421,6 +36929,7 @@ import Joi50 from "joi";
|
|
|
36421
36929
|
import {
|
|
36422
36930
|
BadRequestError as BadRequestError90,
|
|
36423
36931
|
logger as logger66,
|
|
36932
|
+
NotFoundError as NotFoundError25,
|
|
36424
36933
|
useAtlas as useAtlas54
|
|
36425
36934
|
} from "@7365admin1/node-server-utils";
|
|
36426
36935
|
import { ObjectId as ObjectId72 } from "mongodb";
|
|
@@ -37632,6 +38141,7 @@ function useVehicleService() {
|
|
|
37632
38141
|
getVehicleByPlateNumber: _getVehicleByPlateNumber,
|
|
37633
38142
|
getAllExpiredVehicles: _getAllExpiredVehicles,
|
|
37634
38143
|
bulkUpsertVehicles: _bulkUpsertVehicles,
|
|
38144
|
+
getLivePlatesBySite: _getLivePlatesBySite,
|
|
37635
38145
|
findOne
|
|
37636
38146
|
} = useVehicleRepo();
|
|
37637
38147
|
const {
|
|
@@ -37642,12 +38152,441 @@ function useVehicleService() {
|
|
|
37642
38152
|
bulkInsertPlateNumber: _bulkInsertPlateNumber
|
|
37643
38153
|
} = useDahuaService();
|
|
37644
38154
|
const { getAllCameraWithPassword: _getAllSiteCameras } = useSiteCameraRepo();
|
|
38155
|
+
const { setPlateWindow: _setPlateWindow } = useDahuaService();
|
|
37645
38156
|
const { getById: _getById } = useOrgRepo();
|
|
37646
38157
|
const {
|
|
37647
38158
|
pushVehicleById: _pushVehicleById,
|
|
37648
38159
|
pullVehicleByRecNo: _pullVehicleByRecNo
|
|
37649
38160
|
} = usePersonRepo();
|
|
37650
38161
|
const { getUnitByBlockLevelUnitNumber: _getUnitByBlockLevelUnitNumber } = useBuildingUnitRepo();
|
|
38162
|
+
async function camerasAt(site, options = {}) {
|
|
38163
|
+
const siteId = String(site ?? "");
|
|
38164
|
+
if (!siteId)
|
|
38165
|
+
return [];
|
|
38166
|
+
const cameras = [];
|
|
38167
|
+
let page = 1;
|
|
38168
|
+
let pages = 1;
|
|
38169
|
+
const limit = 20;
|
|
38170
|
+
do {
|
|
38171
|
+
const siteCameraReq = await _getAllSiteCameras({
|
|
38172
|
+
site: siteId,
|
|
38173
|
+
type: "anpr",
|
|
38174
|
+
...options.directions ? { direction: options.directions } : {},
|
|
38175
|
+
...options.activeOnly ? { status: "active" } : {},
|
|
38176
|
+
page,
|
|
38177
|
+
limit
|
|
38178
|
+
});
|
|
38179
|
+
pages = siteCameraReq.pages || 1;
|
|
38180
|
+
cameras.push(...siteCameraReq.items);
|
|
38181
|
+
page++;
|
|
38182
|
+
} while (page <= pages);
|
|
38183
|
+
return cameras;
|
|
38184
|
+
}
|
|
38185
|
+
function allowCamerasAt(site) {
|
|
38186
|
+
return camerasAt(site, {
|
|
38187
|
+
directions: ALLOW_CAMERA_DIRECTIONS,
|
|
38188
|
+
activeOnly: true
|
|
38189
|
+
});
|
|
38190
|
+
}
|
|
38191
|
+
function blockCamerasAt(site) {
|
|
38192
|
+
return camerasAt(site, {
|
|
38193
|
+
directions: BLOCK_CAMERA_DIRECTIONS,
|
|
38194
|
+
activeOnly: true
|
|
38195
|
+
});
|
|
38196
|
+
}
|
|
38197
|
+
function listOf(mode) {
|
|
38198
|
+
return mode === "TrafficBlackList" /* TRAFFIC_BLACKLIST */ ? "blocked" : "allowed";
|
|
38199
|
+
}
|
|
38200
|
+
function listMode(type) {
|
|
38201
|
+
return type === "blocklist" /* BLOCKLIST */ ? "TrafficBlackList" /* TRAFFIC_BLACKLIST */ : "TrafficRedList" /* TRAFFIC_REDLIST */;
|
|
38202
|
+
}
|
|
38203
|
+
function validDate(value) {
|
|
38204
|
+
if (value === null || value === void 0 || value === "")
|
|
38205
|
+
return null;
|
|
38206
|
+
const date = new Date(value);
|
|
38207
|
+
return Number.isNaN(date.getTime()) ? null : date;
|
|
38208
|
+
}
|
|
38209
|
+
function cameraWindow(start, end) {
|
|
38210
|
+
const startDate = validDate(start);
|
|
38211
|
+
const endDate = validDate(end);
|
|
38212
|
+
return {
|
|
38213
|
+
...startDate ? { start: startDate.toISOString() } : {},
|
|
38214
|
+
...endDate ? { end: endDate.toISOString() } : {}
|
|
38215
|
+
};
|
|
38216
|
+
}
|
|
38217
|
+
const siteZones = /* @__PURE__ */ new Map();
|
|
38218
|
+
async function siteTimezone(site) {
|
|
38219
|
+
const key = String(site ?? "");
|
|
38220
|
+
const cached = siteZones.get(key);
|
|
38221
|
+
if (cached && Date.now() - cached.at < 5 * 6e4)
|
|
38222
|
+
return cached.zone;
|
|
38223
|
+
let zone = resolveSiteTimezone(null);
|
|
38224
|
+
if (key) {
|
|
38225
|
+
try {
|
|
38226
|
+
const found = await useSiteRepo().getSiteById(key);
|
|
38227
|
+
zone = resolveSiteTimezone(found);
|
|
38228
|
+
} catch {
|
|
38229
|
+
zone = resolveSiteTimezone(null);
|
|
38230
|
+
}
|
|
38231
|
+
}
|
|
38232
|
+
siteZones.set(key, { zone, at: Date.now() });
|
|
38233
|
+
return zone;
|
|
38234
|
+
}
|
|
38235
|
+
function blockWindow(vehicleEnd) {
|
|
38236
|
+
const now = /* @__PURE__ */ new Date();
|
|
38237
|
+
let end = validDate(vehicleEnd);
|
|
38238
|
+
if (!end || end.getTime() <= now.getTime()) {
|
|
38239
|
+
end = new Date(now);
|
|
38240
|
+
end.setFullYear(end.getFullYear() + 10);
|
|
38241
|
+
}
|
|
38242
|
+
return { start: now.toISOString(), end: end.toISOString() };
|
|
38243
|
+
}
|
|
38244
|
+
async function findPlateOnCamera(camera, mode, plateNumber) {
|
|
38245
|
+
const text2 = await _getPlateNumber({
|
|
38246
|
+
host: camera.host,
|
|
38247
|
+
username: camera.username,
|
|
38248
|
+
password: camera.password,
|
|
38249
|
+
mode,
|
|
38250
|
+
plateNumber,
|
|
38251
|
+
requireOk: true
|
|
38252
|
+
});
|
|
38253
|
+
return matchingPlateRecords(text2 ?? "", plateNumber);
|
|
38254
|
+
}
|
|
38255
|
+
async function removePlateFromCamera(camera, mode, plateNumber) {
|
|
38256
|
+
if (!normalisePlate(plateNumber)) {
|
|
38257
|
+
return { ...ANPR_NOTHING_TO_REVOKE, changed: false };
|
|
38258
|
+
}
|
|
38259
|
+
let changed = false;
|
|
38260
|
+
const MAX_PASSES = 3;
|
|
38261
|
+
for (let pass = 0; pass <= MAX_PASSES; pass++) {
|
|
38262
|
+
let records;
|
|
38263
|
+
try {
|
|
38264
|
+
records = await findPlateOnCamera(camera, mode, plateNumber);
|
|
38265
|
+
} catch (error) {
|
|
38266
|
+
const outcome = anprRevokeThrew(error);
|
|
38267
|
+
logCameraFailure("plate lookup", camera, outcome.detail);
|
|
38268
|
+
return { ...outcome, changed };
|
|
38269
|
+
}
|
|
38270
|
+
if (!records.length)
|
|
38271
|
+
return { ok: true, reason: "", changed };
|
|
38272
|
+
if (pass === MAX_PASSES)
|
|
38273
|
+
break;
|
|
38274
|
+
for (const record of records) {
|
|
38275
|
+
const outcome = await _removePlateNumber({
|
|
38276
|
+
host: camera.host,
|
|
38277
|
+
username: camera.username,
|
|
38278
|
+
password: camera.password,
|
|
38279
|
+
recno: record.recNo,
|
|
38280
|
+
mode
|
|
38281
|
+
}).catch((error) => anprRevokeThrew(error));
|
|
38282
|
+
if (!outcome?.ok) {
|
|
38283
|
+
logCameraFailure(
|
|
38284
|
+
"plate remove",
|
|
38285
|
+
camera,
|
|
38286
|
+
outcome?.detail ?? "no outcome returned"
|
|
38287
|
+
);
|
|
38288
|
+
return {
|
|
38289
|
+
...outcome ?? anprRevokeThrew("no outcome returned"),
|
|
38290
|
+
changed
|
|
38291
|
+
};
|
|
38292
|
+
}
|
|
38293
|
+
changed = true;
|
|
38294
|
+
}
|
|
38295
|
+
}
|
|
38296
|
+
logCameraFailure(
|
|
38297
|
+
"plate remove",
|
|
38298
|
+
camera,
|
|
38299
|
+
`plate still listed after ${MAX_PASSES} rounds of removal`
|
|
38300
|
+
);
|
|
38301
|
+
return {
|
|
38302
|
+
ok: false,
|
|
38303
|
+
reason: "the camera did not remove the plate number",
|
|
38304
|
+
detail: `plate still listed after ${MAX_PASSES} rounds of removal`,
|
|
38305
|
+
changed
|
|
38306
|
+
};
|
|
38307
|
+
}
|
|
38308
|
+
async function insertPlateOnCamera(camera, write) {
|
|
38309
|
+
const response = await _addPlateNumber({
|
|
38310
|
+
host: camera.host,
|
|
38311
|
+
username: camera.username,
|
|
38312
|
+
password: camera.password,
|
|
38313
|
+
plateNumber: write.plateNumber,
|
|
38314
|
+
mode: write.mode,
|
|
38315
|
+
owner: write.owner,
|
|
38316
|
+
...write.start ? { start: write.start } : {},
|
|
38317
|
+
...write.end ? { end: write.end } : {},
|
|
38318
|
+
timezone: await siteTimezone(camera.site)
|
|
38319
|
+
});
|
|
38320
|
+
const status = response?.statusCode ?? response?.status ?? response?.res?.status;
|
|
38321
|
+
if (status !== 200)
|
|
38322
|
+
throw new Error(`HTTP ${status ?? "none"}`);
|
|
38323
|
+
return String(
|
|
38324
|
+
response?.data?.toString("utf-8")?.split("=")[1]?.trim() ?? ""
|
|
38325
|
+
);
|
|
38326
|
+
}
|
|
38327
|
+
async function ensurePlateOnCamera(camera, write) {
|
|
38328
|
+
const existing = await findPlateOnCamera(
|
|
38329
|
+
camera,
|
|
38330
|
+
write.mode,
|
|
38331
|
+
write.plateNumber
|
|
38332
|
+
);
|
|
38333
|
+
if (!existing.length) {
|
|
38334
|
+
await insertPlateOnCamera(camera, write);
|
|
38335
|
+
return { inserted: true };
|
|
38336
|
+
}
|
|
38337
|
+
const timezone = await siteTimezone(camera.site);
|
|
38338
|
+
const [keep, ...extra] = existing;
|
|
38339
|
+
const now = dahuaWallClock(/* @__PURE__ */ new Date(), timezone);
|
|
38340
|
+
const wanted = {
|
|
38341
|
+
start: dahuaWallClock(write.start, timezone),
|
|
38342
|
+
end: dahuaWallClock(write.end, timezone)
|
|
38343
|
+
};
|
|
38344
|
+
const windowIsRight = wanted.start && wanted.end ? plateWindowMatches(keep, wanted) : (
|
|
38345
|
+
// No dates of its own: any copy that lets it in now will do.
|
|
38346
|
+
Boolean(
|
|
38347
|
+
keep.beginTime && keep.cancelTime && now && (dahuaWallClock(keep.beginTime) ?? "") <= now && (dahuaWallClock(keep.cancelTime) ?? "") > now
|
|
38348
|
+
)
|
|
38349
|
+
);
|
|
38350
|
+
if (!windowIsRight) {
|
|
38351
|
+
await _setPlateWindow({
|
|
38352
|
+
host: camera.host,
|
|
38353
|
+
username: camera.username,
|
|
38354
|
+
password: camera.password,
|
|
38355
|
+
mode: write.mode,
|
|
38356
|
+
recno: keep.recNo,
|
|
38357
|
+
plateNumber: write.plateNumber,
|
|
38358
|
+
owner: write.owner,
|
|
38359
|
+
...write.start ? { start: write.start } : {},
|
|
38360
|
+
...write.end ? { end: write.end } : {},
|
|
38361
|
+
timezone
|
|
38362
|
+
});
|
|
38363
|
+
}
|
|
38364
|
+
for (const copy of extra) {
|
|
38365
|
+
const outcome = await _removePlateNumber({
|
|
38366
|
+
host: camera.host,
|
|
38367
|
+
username: camera.username,
|
|
38368
|
+
password: camera.password,
|
|
38369
|
+
recno: copy.recNo,
|
|
38370
|
+
mode: write.mode
|
|
38371
|
+
}).catch((error) => anprRevokeThrew(error));
|
|
38372
|
+
if (!outcome?.ok) {
|
|
38373
|
+
throw new Error(
|
|
38374
|
+
outcome?.reason || "the camera did not remove an old copy of the plate"
|
|
38375
|
+
);
|
|
38376
|
+
}
|
|
38377
|
+
}
|
|
38378
|
+
return { inserted: false };
|
|
38379
|
+
}
|
|
38380
|
+
function cameraJournal() {
|
|
38381
|
+
const steps = [];
|
|
38382
|
+
return {
|
|
38383
|
+
/** A plate was inserted; undo = take it off again. */
|
|
38384
|
+
inserted(camera, mode, plateNumber) {
|
|
38385
|
+
steps.push({
|
|
38386
|
+
camera,
|
|
38387
|
+
undo: () => removePlateFromCamera(camera, mode, plateNumber).then(
|
|
38388
|
+
(outcome) => outcome.ok,
|
|
38389
|
+
() => false
|
|
38390
|
+
)
|
|
38391
|
+
});
|
|
38392
|
+
},
|
|
38393
|
+
/** A plate was removed; undo = put it back as it was. */
|
|
38394
|
+
removed(camera, previous) {
|
|
38395
|
+
steps.push({
|
|
38396
|
+
camera,
|
|
38397
|
+
undo: () => insertPlateOnCamera(camera, previous).then(
|
|
38398
|
+
() => true,
|
|
38399
|
+
() => false
|
|
38400
|
+
)
|
|
38401
|
+
});
|
|
38402
|
+
},
|
|
38403
|
+
/**
|
|
38404
|
+
* Undo everything, newest first. Never throws. Returns the cameras that
|
|
38405
|
+
* could not be put back, so the operator can be told which barrier is
|
|
38406
|
+
* now wrong.
|
|
38407
|
+
*/
|
|
38408
|
+
async rollback() {
|
|
38409
|
+
const stranded = [];
|
|
38410
|
+
for (const step of steps.reverse()) {
|
|
38411
|
+
if (!await step.undo())
|
|
38412
|
+
stranded.push(cameraLabel(step.camera));
|
|
38413
|
+
}
|
|
38414
|
+
steps.length = 0;
|
|
38415
|
+
return Array.from(new Set(stranded));
|
|
38416
|
+
}
|
|
38417
|
+
};
|
|
38418
|
+
}
|
|
38419
|
+
function strandedWarning(stranded) {
|
|
38420
|
+
if (!stranded.length)
|
|
38421
|
+
return "";
|
|
38422
|
+
return ` WARNING: ${stranded.join(", ")} could not be put back the way ${stranded.length === 1 ? "it was" : "they were"} - check this plate on ${stranded.length === 1 ? "that camera" : "those cameras"} by hand.`;
|
|
38423
|
+
}
|
|
38424
|
+
async function rollbackCameras(journal, error) {
|
|
38425
|
+
const stranded = await journal.rollback().catch(() => []);
|
|
38426
|
+
if (stranded.length) {
|
|
38427
|
+
logger66.error(
|
|
38428
|
+
`vehicle camera rollback could not restore: ${stranded.join(", ")}`
|
|
38429
|
+
);
|
|
38430
|
+
if (error && typeof error === "object") {
|
|
38431
|
+
error.message = `${error.message ?? ""}${strandedWarning(stranded)}`;
|
|
38432
|
+
}
|
|
38433
|
+
}
|
|
38434
|
+
}
|
|
38435
|
+
async function revokableModes(site, plateNumber, exclude) {
|
|
38436
|
+
const others = [];
|
|
38437
|
+
let siteId = null;
|
|
38438
|
+
try {
|
|
38439
|
+
siteId = new ObjectId72(String(site));
|
|
38440
|
+
} catch {
|
|
38441
|
+
siteId = null;
|
|
38442
|
+
}
|
|
38443
|
+
if (siteId && plateNumber) {
|
|
38444
|
+
const excludeIds = exclude.map((id) => {
|
|
38445
|
+
try {
|
|
38446
|
+
return new ObjectId72(String(id));
|
|
38447
|
+
} catch {
|
|
38448
|
+
return null;
|
|
38449
|
+
}
|
|
38450
|
+
}).filter((id) => id !== null);
|
|
38451
|
+
const base = {
|
|
38452
|
+
site: siteId,
|
|
38453
|
+
plateNumber,
|
|
38454
|
+
status: "active" /* ACTIVE */,
|
|
38455
|
+
_id: { $nin: excludeIds }
|
|
38456
|
+
};
|
|
38457
|
+
const [allowHolder, blockHolder] = await Promise.all([
|
|
38458
|
+
findOne({
|
|
38459
|
+
...base,
|
|
38460
|
+
$or: [
|
|
38461
|
+
{ type: "whitelist" /* WHITELIST */ },
|
|
38462
|
+
{ blockedFrom: "whitelist" /* WHITELIST */ }
|
|
38463
|
+
]
|
|
38464
|
+
}),
|
|
38465
|
+
findOne({ ...base, type: "blocklist" /* BLOCKLIST */ })
|
|
38466
|
+
]);
|
|
38467
|
+
if (allowHolder)
|
|
38468
|
+
others.push(allowHolder);
|
|
38469
|
+
if (blockHolder)
|
|
38470
|
+
others.push(blockHolder);
|
|
38471
|
+
}
|
|
38472
|
+
const lists = listsToRevoke(others);
|
|
38473
|
+
return [
|
|
38474
|
+
...lists.allow ? ["TrafficRedList" /* TRAFFIC_REDLIST */] : [],
|
|
38475
|
+
...lists.block ? ["TrafficBlackList" /* TRAFFIC_BLACKLIST */] : []
|
|
38476
|
+
];
|
|
38477
|
+
}
|
|
38478
|
+
async function removeFromCameras(cameras, modes, plateNumber, previous, journal, vehicleIsBlocked = false) {
|
|
38479
|
+
const failures = [];
|
|
38480
|
+
for (const camera of cameras) {
|
|
38481
|
+
for (const mode of modes) {
|
|
38482
|
+
if (!mustRevokeFrom(camera.direction, listOf(mode), vehicleIsBlocked)) {
|
|
38483
|
+
continue;
|
|
38484
|
+
}
|
|
38485
|
+
const outcome = await removePlateFromCamera(camera, mode, plateNumber);
|
|
38486
|
+
if (outcome.changed) {
|
|
38487
|
+
journal.removed(camera, { ...previous, plateNumber, mode });
|
|
38488
|
+
}
|
|
38489
|
+
if (!outcome.ok) {
|
|
38490
|
+
failures.push({
|
|
38491
|
+
camera: cameraLabel(camera),
|
|
38492
|
+
reason: outcome.reason || "the camera could not be reached"
|
|
38493
|
+
});
|
|
38494
|
+
}
|
|
38495
|
+
}
|
|
38496
|
+
}
|
|
38497
|
+
return failures;
|
|
38498
|
+
}
|
|
38499
|
+
async function writeToCameras(cameras, write, journal, options = {}) {
|
|
38500
|
+
const failures = [];
|
|
38501
|
+
let recNo = "";
|
|
38502
|
+
for (const camera of cameras) {
|
|
38503
|
+
try {
|
|
38504
|
+
if (options.onlyIfMissing) {
|
|
38505
|
+
const { inserted } = await ensurePlateOnCamera(camera, write);
|
|
38506
|
+
if (inserted)
|
|
38507
|
+
journal.inserted(camera, write.mode, write.plateNumber);
|
|
38508
|
+
} else {
|
|
38509
|
+
recNo = await insertPlateOnCamera(camera, write) || recNo;
|
|
38510
|
+
journal.inserted(camera, write.mode, write.plateNumber);
|
|
38511
|
+
}
|
|
38512
|
+
} catch (error) {
|
|
38513
|
+
logCameraFailure(`write ${write.mode}`, camera, error);
|
|
38514
|
+
failures.push({
|
|
38515
|
+
camera: cameraLabel(camera),
|
|
38516
|
+
reason: anprWriteReason(error)
|
|
38517
|
+
});
|
|
38518
|
+
}
|
|
38519
|
+
}
|
|
38520
|
+
return { failures, recNo };
|
|
38521
|
+
}
|
|
38522
|
+
async function blockOnCameras(vehicle, site, owner, journal) {
|
|
38523
|
+
const plateNumber = String(vehicle.plateNumber ?? "");
|
|
38524
|
+
if (!plateNumber)
|
|
38525
|
+
return;
|
|
38526
|
+
const cameras = await blockCamerasAt(site);
|
|
38527
|
+
if (!cameras.length) {
|
|
38528
|
+
throw new BadRequestError90(anprNoCameraRefusal("blocked"));
|
|
38529
|
+
}
|
|
38530
|
+
const { failures } = await writeToCameras(
|
|
38531
|
+
cameras,
|
|
38532
|
+
{
|
|
38533
|
+
plateNumber,
|
|
38534
|
+
mode: "TrafficBlackList" /* TRAFFIC_BLACKLIST */,
|
|
38535
|
+
owner,
|
|
38536
|
+
...blockWindow(vehicle.end)
|
|
38537
|
+
},
|
|
38538
|
+
journal,
|
|
38539
|
+
{ onlyIfMissing: true }
|
|
38540
|
+
);
|
|
38541
|
+
if (failures.length) {
|
|
38542
|
+
throw new BadRequestError90(anprBlockFailureMessage(failures));
|
|
38543
|
+
}
|
|
38544
|
+
}
|
|
38545
|
+
async function unblockOnCameras(vehicle, site, options, journal) {
|
|
38546
|
+
const plateNumber = String(vehicle.plateNumber ?? "");
|
|
38547
|
+
if (!plateNumber)
|
|
38548
|
+
return;
|
|
38549
|
+
const window = cameraWindow(vehicle.start, vehicle.end);
|
|
38550
|
+
if (options.restore) {
|
|
38551
|
+
const allowCameras = await allowCamerasAt(site);
|
|
38552
|
+
const { failures: failures2 } = await writeToCameras(
|
|
38553
|
+
allowCameras,
|
|
38554
|
+
{
|
|
38555
|
+
plateNumber,
|
|
38556
|
+
mode: "TrafficRedList" /* TRAFFIC_REDLIST */,
|
|
38557
|
+
owner: options.owner,
|
|
38558
|
+
...window
|
|
38559
|
+
},
|
|
38560
|
+
journal,
|
|
38561
|
+
{ onlyIfMissing: true }
|
|
38562
|
+
);
|
|
38563
|
+
if (failures2.length) {
|
|
38564
|
+
throw new BadRequestError90(
|
|
38565
|
+
cameraFailureSentence({
|
|
38566
|
+
failures: failures2,
|
|
38567
|
+
operation: "added to",
|
|
38568
|
+
outcome: "unblocked",
|
|
38569
|
+
consequence: "it is still blocked at the barrier"
|
|
38570
|
+
})
|
|
38571
|
+
);
|
|
38572
|
+
}
|
|
38573
|
+
}
|
|
38574
|
+
const revokable = await revokableModes(site, plateNumber, [vehicle._id]);
|
|
38575
|
+
const modes = options.restore ? revokable.filter((mode) => mode === "TrafficBlackList" /* TRAFFIC_BLACKLIST */) : revokable;
|
|
38576
|
+
if (!modes.length)
|
|
38577
|
+
return;
|
|
38578
|
+
const failures = await removeFromCameras(
|
|
38579
|
+
await camerasAt(site, { activeOnly: true }),
|
|
38580
|
+
modes,
|
|
38581
|
+
plateNumber,
|
|
38582
|
+
{ owner: options.owner, ...window },
|
|
38583
|
+
journal,
|
|
38584
|
+
true
|
|
38585
|
+
);
|
|
38586
|
+
if (failures.length) {
|
|
38587
|
+
throw new BadRequestError90(anprUnblockFailureMessage(failures));
|
|
38588
|
+
}
|
|
38589
|
+
}
|
|
37651
38590
|
async function add(value, session, user) {
|
|
37652
38591
|
const isExternalSession = !!session;
|
|
37653
38592
|
if (!session) {
|
|
@@ -37702,18 +38641,18 @@ function useVehicleService() {
|
|
|
37702
38641
|
endDate.setFullYear(endDate.getFullYear() + 10);
|
|
37703
38642
|
start = startDate.toISOString();
|
|
37704
38643
|
end = endDate.toISOString();
|
|
37705
|
-
startDateDahua =
|
|
37706
|
-
endDateDahua =
|
|
38644
|
+
startDateDahua = start;
|
|
38645
|
+
endDateDahua = end;
|
|
37707
38646
|
} else {
|
|
37708
38647
|
if (hasStart) {
|
|
37709
38648
|
startDate = new Date(value.start);
|
|
37710
38649
|
start = startDate.toISOString();
|
|
37711
|
-
startDateDahua =
|
|
38650
|
+
startDateDahua = start;
|
|
37712
38651
|
}
|
|
37713
38652
|
if (hasEnd) {
|
|
37714
38653
|
endDate = new Date(value.end);
|
|
37715
38654
|
end = endDate.toISOString();
|
|
37716
|
-
endDateDahua =
|
|
38655
|
+
endDateDahua = end;
|
|
37717
38656
|
}
|
|
37718
38657
|
}
|
|
37719
38658
|
const owner = value.name;
|
|
@@ -37726,22 +38665,7 @@ function useVehicleService() {
|
|
|
37726
38665
|
let message = "Vehicle plate number needs approval from property management.";
|
|
37727
38666
|
let siteCameras = [];
|
|
37728
38667
|
if (value.status && value.status !== "pending" /* PENDING */) {
|
|
37729
|
-
|
|
37730
|
-
let pages = 1;
|
|
37731
|
-
const limit = 20;
|
|
37732
|
-
do {
|
|
37733
|
-
const siteCameraReq = await _getAllSiteCameras({
|
|
37734
|
-
site: value.site,
|
|
37735
|
-
type: "anpr",
|
|
37736
|
-
direction: ["both", "entry", "residents"],
|
|
37737
|
-
page,
|
|
37738
|
-
limit,
|
|
37739
|
-
status: "active"
|
|
37740
|
-
});
|
|
37741
|
-
pages = siteCameraReq.pages || 1;
|
|
37742
|
-
siteCameras.push(...siteCameraReq.items);
|
|
37743
|
-
page++;
|
|
37744
|
-
} while (page <= pages);
|
|
38668
|
+
siteCameras = isBlocklist ? await blockCamerasAt(value.site) : await allowCamerasAt(value.site);
|
|
37745
38669
|
logger66.info(
|
|
37746
38670
|
`addVehicle resolved ${siteCameras.length} site camera(s) for site ${value?.site}`
|
|
37747
38671
|
);
|
|
@@ -37776,7 +38700,10 @@ function useVehicleService() {
|
|
|
37776
38700
|
mode: _mode,
|
|
37777
38701
|
owner,
|
|
37778
38702
|
...startDateDahua ? { start: startDateDahua } : {},
|
|
37779
|
-
...endDateDahua ? { end: endDateDahua } : {}
|
|
38703
|
+
...endDateDahua ? { end: endDateDahua } : {},
|
|
38704
|
+
// The camera enforces the season pass window on its own clock,
|
|
38705
|
+
// in the site's zone.
|
|
38706
|
+
timezone: await siteTimezone(camera.site ?? value.site)
|
|
37780
38707
|
};
|
|
37781
38708
|
try {
|
|
37782
38709
|
const dahuaResponse = await _addPlateNumber(dahuaPayload);
|
|
@@ -37796,8 +38723,7 @@ function useVehicleService() {
|
|
|
37796
38723
|
}
|
|
37797
38724
|
const recNo = String(responseData.split("=")[1]?.trim() ?? "");
|
|
37798
38725
|
vehicleValue.recNo = recNo;
|
|
37799
|
-
|
|
37800
|
-
written.push({ camera, recno: recNo, mode: _mode });
|
|
38726
|
+
written.push({ camera, recno: recNo, mode: _mode, plateNumber: plateNumber2 });
|
|
37801
38727
|
if (value.peopleId && recNo) {
|
|
37802
38728
|
await _pushVehicleById(
|
|
37803
38729
|
value.peopleId,
|
|
@@ -37827,13 +38753,18 @@ function useVehicleService() {
|
|
|
37827
38753
|
} catch (error) {
|
|
37828
38754
|
const stranded = [];
|
|
37829
38755
|
for (const entry of written) {
|
|
37830
|
-
const
|
|
38756
|
+
const usableId = entry.recno && /^\d+$/.test(entry.recno);
|
|
38757
|
+
const outcome = usableId ? await _removePlateNumber({
|
|
37831
38758
|
host: entry.camera.host,
|
|
37832
38759
|
username: entry.camera.username,
|
|
37833
38760
|
password: entry.camera.password,
|
|
37834
38761
|
recno: entry.recno,
|
|
37835
38762
|
mode: entry.mode
|
|
37836
|
-
}).catch((removeError) => anprRevokeThrew(removeError))
|
|
38763
|
+
}).catch((removeError) => anprRevokeThrew(removeError)) : await removePlateFromCamera(
|
|
38764
|
+
entry.camera,
|
|
38765
|
+
entry.mode,
|
|
38766
|
+
entry.plateNumber
|
|
38767
|
+
);
|
|
37837
38768
|
if (!outcome?.ok)
|
|
37838
38769
|
stranded.push(cameraLabel(entry.camera));
|
|
37839
38770
|
}
|
|
@@ -37859,80 +38790,28 @@ function useVehicleService() {
|
|
|
37859
38790
|
if (!session) {
|
|
37860
38791
|
throw new Error("Unable to start session for vehicle service.");
|
|
37861
38792
|
}
|
|
37862
|
-
const
|
|
38793
|
+
const journal = cameraJournal();
|
|
37863
38794
|
try {
|
|
37864
38795
|
session.startTransaction();
|
|
37865
|
-
const siteCameras = [];
|
|
37866
|
-
let page = 1;
|
|
37867
|
-
let pages = 1;
|
|
37868
|
-
const limit = 20;
|
|
37869
|
-
do {
|
|
37870
|
-
const siteCameraReq = await _getAllSiteCameras({
|
|
37871
|
-
site,
|
|
37872
|
-
type: "anpr",
|
|
37873
|
-
page,
|
|
37874
|
-
limit
|
|
37875
|
-
});
|
|
37876
|
-
pages = siteCameraReq.pages || 1;
|
|
37877
|
-
siteCameras.push(...siteCameraReq.items);
|
|
37878
|
-
page++;
|
|
37879
|
-
} while (page <= pages);
|
|
37880
|
-
logger66.info(
|
|
37881
|
-
`deleteVehicle resolved ${siteCameras.length} ANPR camera(s) for site ${site}`
|
|
37882
|
-
);
|
|
37883
38796
|
const vehicle = await findOne({ _id: new ObjectId72(_id) });
|
|
37884
38797
|
const plateNumber = String(vehicle?.plateNumber ?? "");
|
|
37885
|
-
const
|
|
37886
|
-
|
|
37887
|
-
|
|
37888
|
-
|
|
37889
|
-
|
|
37890
|
-
|
|
37891
|
-
|
|
37892
|
-
|
|
37893
|
-
|
|
37894
|
-
|
|
37895
|
-
|
|
37896
|
-
|
|
37897
|
-
|
|
37898
|
-
|
|
37899
|
-
|
|
37900
|
-
|
|
37901
|
-
|
|
37902
|
-
}) ?? ""
|
|
37903
|
-
);
|
|
37904
|
-
if (!found.exists)
|
|
37905
|
-
continue;
|
|
37906
|
-
if (found.recNo)
|
|
37907
|
-
recnoForCamera = found.recNo;
|
|
37908
|
-
} catch (error) {
|
|
37909
|
-
const outcome2 = anprRevokeThrew(error);
|
|
37910
|
-
logCameraFailure("deleteVehicle lookup", camera, outcome2.detail);
|
|
37911
|
-
failures.push({ camera: label, reason: outcome2.reason });
|
|
37912
|
-
continue;
|
|
37913
|
-
}
|
|
37914
|
-
}
|
|
37915
|
-
if (!recnoForCamera)
|
|
37916
|
-
continue;
|
|
37917
|
-
const outcome = await _removePlateNumber({
|
|
37918
|
-
host,
|
|
37919
|
-
username,
|
|
37920
|
-
password,
|
|
37921
|
-
recno: recnoForCamera,
|
|
37922
|
-
mode: _mode
|
|
37923
|
-
});
|
|
37924
|
-
if (!outcome?.ok) {
|
|
37925
|
-
logCameraFailure(
|
|
37926
|
-
"deleteVehicle revoke",
|
|
37927
|
-
camera,
|
|
37928
|
-
outcome?.detail ?? "no outcome returned"
|
|
37929
|
-
);
|
|
37930
|
-
failures.push({
|
|
37931
|
-
camera: label,
|
|
37932
|
-
reason: outcome?.reason || "the camera could not be reached"
|
|
37933
|
-
});
|
|
37934
|
-
}
|
|
37935
|
-
}
|
|
38798
|
+
const cameraSite = vehicle?.site ?? site;
|
|
38799
|
+
const siteCameras = await camerasAt(cameraSite, { activeOnly: true });
|
|
38800
|
+
logger66.info(
|
|
38801
|
+
`deleteVehicle resolved ${siteCameras.length} ANPR camera(s) for site ${cameraSite}`
|
|
38802
|
+
);
|
|
38803
|
+
const modes = plateNumber ? await revokableModes(cameraSite, plateNumber, [_id]) : [];
|
|
38804
|
+
const failures = await removeFromCameras(
|
|
38805
|
+
siteCameras,
|
|
38806
|
+
modes,
|
|
38807
|
+
plateNumber,
|
|
38808
|
+
{
|
|
38809
|
+
owner: String(vehicle?.name ?? ""),
|
|
38810
|
+
...cameraWindow(vehicle?.start, vehicle?.end)
|
|
38811
|
+
},
|
|
38812
|
+
journal,
|
|
38813
|
+
vehicle?.type === "blocklist" /* BLOCKLIST */
|
|
38814
|
+
);
|
|
37936
38815
|
if (failures.length) {
|
|
37937
38816
|
const message = anprRevokeFailureMessage(failures);
|
|
37938
38817
|
if (!bypass)
|
|
@@ -37942,13 +38821,14 @@ function useVehicleService() {
|
|
|
37942
38821
|
await _deleteVehicle(_id, session);
|
|
37943
38822
|
await _pullVehicleByRecNo(
|
|
37944
38823
|
recno,
|
|
37945
|
-
{ peopleId: vehicle?.peopleId, site, plateNumber },
|
|
38824
|
+
{ peopleId: vehicle?.peopleId, site: cameraSite, plateNumber },
|
|
37946
38825
|
session
|
|
37947
38826
|
);
|
|
37948
38827
|
await session.commitTransaction();
|
|
37949
38828
|
return `Vehicle plate number deleted from ${type} record successfully.`;
|
|
37950
38829
|
} catch (error) {
|
|
37951
38830
|
logger66.error("Error in vehicle service delete:", error);
|
|
38831
|
+
await rollbackCameras(journal, error);
|
|
37952
38832
|
await session.abortTransaction();
|
|
37953
38833
|
throw error;
|
|
37954
38834
|
} finally {
|
|
@@ -37963,13 +38843,14 @@ function useVehicleService() {
|
|
|
37963
38843
|
const org = await _getById(orgId);
|
|
37964
38844
|
if (!org)
|
|
37965
38845
|
throw new BadRequestError90("Org not found");
|
|
37966
|
-
const vehicle = await
|
|
37967
|
-
const plate = vehicle.plates.find((p) => p._id.toString() === id);
|
|
37968
|
-
const _plateNumber = plate?.plateNumber;
|
|
38846
|
+
const vehicle = await findOne({ _id: new ObjectId72(id) });
|
|
37969
38847
|
if (!vehicle) {
|
|
37970
|
-
throw new
|
|
38848
|
+
throw new NotFoundError25("Vehicle not found.");
|
|
37971
38849
|
}
|
|
38850
|
+
const _plateNumber = String(vehicle.plateNumber ?? "");
|
|
37972
38851
|
const owner = vehicle.name;
|
|
38852
|
+
const isBlocklist = vehicle.type === "blocklist" /* BLOCKLIST */;
|
|
38853
|
+
const mode = listMode(vehicle.type);
|
|
37973
38854
|
const hasStart = typeof vehicle.start === "string" ? vehicle.start.trim() !== "" : !!vehicle.start;
|
|
37974
38855
|
const hasEnd = typeof vehicle.end === "string" ? vehicle.end.trim() !== "" : !!vehicle.end;
|
|
37975
38856
|
let startDate = null;
|
|
@@ -37987,70 +38868,48 @@ function useVehicleService() {
|
|
|
37987
38868
|
endDate.setFullYear(endDate.getFullYear() + 10);
|
|
37988
38869
|
}
|
|
37989
38870
|
}
|
|
37990
|
-
const startDahua = startDate ?
|
|
37991
|
-
const endDahua = endDate ?
|
|
38871
|
+
const startDahua = startDate ? startDate.toISOString() : "";
|
|
38872
|
+
const endDahua = endDate ? endDate.toISOString() : "";
|
|
38873
|
+
const journal = cameraJournal();
|
|
37992
38874
|
try {
|
|
37993
38875
|
session.startTransaction();
|
|
37994
|
-
const
|
|
37995
|
-
|
|
37996
|
-
let pages = 1;
|
|
37997
|
-
const limit = 20;
|
|
37998
|
-
let value = {};
|
|
37999
|
-
do {
|
|
38000
|
-
const siteCameraReq = await _getAllSiteCameras({
|
|
38001
|
-
site: siteId,
|
|
38002
|
-
type: "anpr",
|
|
38003
|
-
direction: ["both", "entry", "residents"],
|
|
38004
|
-
page,
|
|
38005
|
-
limit
|
|
38006
|
-
});
|
|
38007
|
-
pages = siteCameraReq.pages || 1;
|
|
38008
|
-
siteCameras.push(...siteCameraReq.items);
|
|
38009
|
-
page++;
|
|
38010
|
-
} while (page <= pages);
|
|
38876
|
+
const cameraSite = vehicle.site ?? siteId;
|
|
38877
|
+
const siteCameras = isBlocklist ? await blockCamerasAt(cameraSite) : await allowCamerasAt(cameraSite);
|
|
38011
38878
|
if (!siteCameras.length) {
|
|
38012
38879
|
throw new BadRequestError90(anprNoCameraRefusal("approved"));
|
|
38013
38880
|
}
|
|
38014
|
-
|
|
38015
|
-
|
|
38016
|
-
|
|
38017
|
-
host,
|
|
38018
|
-
username,
|
|
38019
|
-
password,
|
|
38881
|
+
const { failures, recNo } = await writeToCameras(
|
|
38882
|
+
siteCameras,
|
|
38883
|
+
{
|
|
38020
38884
|
plateNumber: _plateNumber,
|
|
38021
|
-
mode
|
|
38885
|
+
mode,
|
|
38886
|
+
owner,
|
|
38022
38887
|
start: startDahua,
|
|
38023
|
-
end: endDahua
|
|
38024
|
-
|
|
38025
|
-
|
|
38026
|
-
|
|
38027
|
-
|
|
38028
|
-
|
|
38029
|
-
|
|
38030
|
-
|
|
38031
|
-
|
|
38032
|
-
|
|
38033
|
-
|
|
38034
|
-
|
|
38035
|
-
|
|
38036
|
-
camera: cameraLabel(camera),
|
|
38037
|
-
reason: anprWriteReason(
|
|
38038
|
-
`HTTP ${dahuaResponse?.statusCode ?? "none"}`
|
|
38039
|
-
)
|
|
38040
|
-
}
|
|
38041
|
-
])
|
|
38042
|
-
);
|
|
38043
|
-
}
|
|
38044
|
-
const responseData = dahuaResponse?.data.toString("utf-8");
|
|
38045
|
-
value.recNo = responseData.split("=")[1]?.trim();
|
|
38888
|
+
end: endDahua
|
|
38889
|
+
},
|
|
38890
|
+
journal
|
|
38891
|
+
);
|
|
38892
|
+
if (failures.length) {
|
|
38893
|
+
throw new BadRequestError90(
|
|
38894
|
+
cameraFailureSentence({
|
|
38895
|
+
failures,
|
|
38896
|
+
operation: "added to",
|
|
38897
|
+
outcome: "approved",
|
|
38898
|
+
consequence: isBlocklist ? "it would still open the barrier" : "it would not open the barrier"
|
|
38899
|
+
})
|
|
38900
|
+
);
|
|
38046
38901
|
}
|
|
38047
|
-
value
|
|
38048
|
-
|
|
38902
|
+
const value = {
|
|
38903
|
+
status: "active" /* ACTIVE */,
|
|
38904
|
+
// Kept for older readers only - one camera's number, never replayed.
|
|
38905
|
+
...recNo ? { recNo } : {}
|
|
38906
|
+
};
|
|
38907
|
+
if (vehicle.peopleId && _plateNumber) {
|
|
38049
38908
|
await _pushVehicleById(
|
|
38050
38909
|
vehicle.peopleId,
|
|
38051
38910
|
{
|
|
38052
38911
|
plateNumber: _plateNumber,
|
|
38053
|
-
recNo
|
|
38912
|
+
recNo
|
|
38054
38913
|
},
|
|
38055
38914
|
session
|
|
38056
38915
|
);
|
|
@@ -38065,6 +38924,7 @@ function useVehicleService() {
|
|
|
38065
38924
|
return "Vehicle plate number approved and added successfully.";
|
|
38066
38925
|
} catch (error) {
|
|
38067
38926
|
logger66.error("Error in vehicle service approving and adding:", error);
|
|
38927
|
+
await rollbackCameras(journal, error);
|
|
38068
38928
|
await session.abortTransaction();
|
|
38069
38929
|
throw error;
|
|
38070
38930
|
} finally {
|
|
@@ -38073,71 +38933,38 @@ function useVehicleService() {
|
|
|
38073
38933
|
}
|
|
38074
38934
|
async function processDeletingExpiredVehicles() {
|
|
38075
38935
|
const vehicles = await _getAllExpiredVehicles();
|
|
38936
|
+
const sweptIds = vehicles.map((vehicle) => vehicle?._id);
|
|
38937
|
+
const modesByVehicle = /* @__PURE__ */ new Map();
|
|
38076
38938
|
const { deletableIds, skipped } = await sweepExpiredVehicles(vehicles, {
|
|
38077
|
-
|
|
38078
|
-
|
|
38079
|
-
|
|
38080
|
-
|
|
38081
|
-
const limit = 20;
|
|
38082
|
-
do {
|
|
38083
|
-
const siteCameraReq = await _getAllSiteCameras({
|
|
38084
|
-
site,
|
|
38085
|
-
type: "anpr",
|
|
38086
|
-
page,
|
|
38087
|
-
limit
|
|
38088
|
-
});
|
|
38089
|
-
pages = siteCameraReq.pages || 1;
|
|
38090
|
-
cameras.push(...siteCameraReq.items);
|
|
38091
|
-
page++;
|
|
38092
|
-
} while (page <= pages);
|
|
38093
|
-
return cameras;
|
|
38094
|
-
},
|
|
38939
|
+
// Every ACTIVE ANPR camera at the site (an inactive one is skipped, as
|
|
38940
|
+
// by every add and update); which of them each list is taken off is
|
|
38941
|
+
// `mustRevokeFrom`.
|
|
38942
|
+
camerasForSite: (site) => camerasAt(site, { activeOnly: true }),
|
|
38095
38943
|
revoke: async (camera, vehicle) => {
|
|
38096
|
-
const host = camera.host;
|
|
38097
|
-
const username = camera.username;
|
|
38098
|
-
const password = camera.password;
|
|
38099
|
-
const mode = vehicle?.type !== "whitelist" /* WHITELIST */ ? "TrafficBlackList" /* TRAFFIC_BLACKLIST */ : "TrafficRedList" /* TRAFFIC_REDLIST */;
|
|
38100
38944
|
const plateNumber = String(vehicle?.plateNumber ?? "");
|
|
38101
|
-
|
|
38102
|
-
if (plateNumber) {
|
|
38103
|
-
try {
|
|
38104
|
-
const found = parseDahuaFind(
|
|
38105
|
-
await _getPlateNumber({
|
|
38106
|
-
host,
|
|
38107
|
-
username,
|
|
38108
|
-
password,
|
|
38109
|
-
mode,
|
|
38110
|
-
plateNumber,
|
|
38111
|
-
requireOk: true
|
|
38112
|
-
}) ?? ""
|
|
38113
|
-
);
|
|
38114
|
-
if (!found.exists)
|
|
38115
|
-
return ANPR_NOTHING_TO_REVOKE;
|
|
38116
|
-
if (found.recNo)
|
|
38117
|
-
recnoForCamera = found.recNo;
|
|
38118
|
-
} catch (error) {
|
|
38119
|
-
const outcome2 = anprRevokeThrew(error);
|
|
38120
|
-
logCameraFailure("expiredVehicle lookup", camera, outcome2.detail);
|
|
38121
|
-
return outcome2;
|
|
38122
|
-
}
|
|
38123
|
-
}
|
|
38124
|
-
if (!recnoForCamera)
|
|
38945
|
+
if (!plateNumber)
|
|
38125
38946
|
return ANPR_NOTHING_TO_REVOKE;
|
|
38126
|
-
const
|
|
38127
|
-
|
|
38128
|
-
|
|
38129
|
-
|
|
38130
|
-
|
|
38131
|
-
|
|
38132
|
-
|
|
38133
|
-
|
|
38134
|
-
|
|
38135
|
-
|
|
38136
|
-
|
|
38137
|
-
|
|
38138
|
-
|
|
38947
|
+
const key = String(vehicle?._id ?? plateNumber);
|
|
38948
|
+
let modes = modesByVehicle.get(key);
|
|
38949
|
+
if (!modes) {
|
|
38950
|
+
modes = revokableModes(vehicle?.site, plateNumber, sweptIds);
|
|
38951
|
+
modesByVehicle.set(key, modes);
|
|
38952
|
+
}
|
|
38953
|
+
let resolved;
|
|
38954
|
+
try {
|
|
38955
|
+
resolved = await modes;
|
|
38956
|
+
} catch (error) {
|
|
38957
|
+
return anprRevokeThrew(error);
|
|
38958
|
+
}
|
|
38959
|
+
for (const mode of resolved) {
|
|
38960
|
+
const blocked = vehicle?.type === "blocklist" /* BLOCKLIST */;
|
|
38961
|
+
if (!mustRevokeFrom(camera.direction, listOf(mode), blocked))
|
|
38962
|
+
continue;
|
|
38963
|
+
const outcome = await removePlateFromCamera(camera, mode, plateNumber);
|
|
38964
|
+
if (!outcome.ok)
|
|
38965
|
+
return outcome;
|
|
38139
38966
|
}
|
|
38140
|
-
return
|
|
38967
|
+
return ANPR_NOTHING_TO_REVOKE;
|
|
38141
38968
|
},
|
|
38142
38969
|
cameraLabel
|
|
38143
38970
|
});
|
|
@@ -38164,90 +38991,83 @@ function useVehicleService() {
|
|
|
38164
38991
|
"Only property management can reactivate vehicles."
|
|
38165
38992
|
);
|
|
38166
38993
|
}
|
|
38167
|
-
const vehicle = await
|
|
38168
|
-
const plate = vehicle.plates.find((p) => p._id.toString() === id);
|
|
38169
|
-
const _plateNumber = plate?.plateNumber;
|
|
38994
|
+
const vehicle = await findOne({ _id: new ObjectId72(id) });
|
|
38170
38995
|
if (!vehicle) {
|
|
38171
|
-
throw new
|
|
38996
|
+
throw new NotFoundError25("Vehicle not found.");
|
|
38172
38997
|
}
|
|
38998
|
+
const _plateNumber = String(vehicle.plateNumber ?? "");
|
|
38173
38999
|
const owner = vehicle.name;
|
|
38174
|
-
|
|
38175
|
-
|
|
38176
|
-
|
|
38177
|
-
const
|
|
38178
|
-
|
|
38179
|
-
|
|
38180
|
-
|
|
39000
|
+
const isBlocklist = vehicle.type === "blocklist" /* BLOCKLIST */;
|
|
39001
|
+
const mode = listMode(vehicle.type);
|
|
39002
|
+
const ownStart = validDate(vehicle.start);
|
|
39003
|
+
const ownEnd = validDate(vehicle.end);
|
|
39004
|
+
let startDate;
|
|
39005
|
+
let endDate;
|
|
39006
|
+
if (ownEnd) {
|
|
39007
|
+
if (ownEnd.getTime() <= Date.now()) {
|
|
39008
|
+
throw new BadRequestError90(
|
|
39009
|
+
`This vehicle's expiry date (${dahuaWallClock(
|
|
39010
|
+
ownEnd,
|
|
39011
|
+
await siteTimezone(vehicle.site ?? siteId)
|
|
39012
|
+
)}) has already passed, so it cannot be restored as it is. Add it again with new dates.`
|
|
39013
|
+
);
|
|
39014
|
+
}
|
|
39015
|
+
startDate = ownStart ?? /* @__PURE__ */ new Date();
|
|
39016
|
+
endDate = ownEnd;
|
|
39017
|
+
} else {
|
|
39018
|
+
startDate = ownStart ?? /* @__PURE__ */ new Date();
|
|
39019
|
+
endDate = new Date(startDate);
|
|
39020
|
+
endDate.setFullYear(endDate.getFullYear() + 10);
|
|
39021
|
+
}
|
|
39022
|
+
const startDahua = startDate.toISOString();
|
|
39023
|
+
const endDahua = endDate.toISOString();
|
|
39024
|
+
const journal = cameraJournal();
|
|
38181
39025
|
try {
|
|
38182
39026
|
session.startTransaction();
|
|
38183
|
-
|
|
38184
|
-
const siteCameras =
|
|
38185
|
-
let page = 1;
|
|
38186
|
-
let pages = 1;
|
|
38187
|
-
const limit = 20;
|
|
38188
|
-
do {
|
|
38189
|
-
const siteCameraReq = await _getAllSiteCameras({
|
|
38190
|
-
site: siteId,
|
|
38191
|
-
type: "anpr",
|
|
38192
|
-
direction: ["both", "entry"],
|
|
38193
|
-
page,
|
|
38194
|
-
limit
|
|
38195
|
-
});
|
|
38196
|
-
pages = siteCameraReq.pages || 1;
|
|
38197
|
-
siteCameras.push(...siteCameraReq.items);
|
|
38198
|
-
page++;
|
|
38199
|
-
} while (page <= pages);
|
|
39027
|
+
const cameraSite = vehicle.site ?? siteId;
|
|
39028
|
+
const siteCameras = isBlocklist ? await blockCamerasAt(cameraSite) : await allowCamerasAt(cameraSite);
|
|
38200
39029
|
if (!siteCameras.length) {
|
|
38201
39030
|
throw new BadRequestError90(anprNoCameraRefusal("restored"));
|
|
38202
39031
|
}
|
|
38203
|
-
|
|
38204
|
-
|
|
38205
|
-
|
|
38206
|
-
host,
|
|
38207
|
-
username,
|
|
38208
|
-
password,
|
|
39032
|
+
const { failures, recNo } = await writeToCameras(
|
|
39033
|
+
siteCameras,
|
|
39034
|
+
{
|
|
38209
39035
|
plateNumber: _plateNumber,
|
|
38210
|
-
mode
|
|
39036
|
+
mode,
|
|
39037
|
+
owner,
|
|
38211
39038
|
start: startDahua,
|
|
38212
|
-
end: endDahua
|
|
38213
|
-
|
|
38214
|
-
|
|
38215
|
-
|
|
38216
|
-
|
|
38217
|
-
|
|
38218
|
-
|
|
38219
|
-
|
|
38220
|
-
|
|
38221
|
-
|
|
38222
|
-
|
|
38223
|
-
|
|
38224
|
-
|
|
38225
|
-
camera: cameraLabel(camera),
|
|
38226
|
-
reason: anprWriteReason(
|
|
38227
|
-
`HTTP ${dahuaResponse?.statusCode ?? "none"}`
|
|
38228
|
-
)
|
|
38229
|
-
}
|
|
38230
|
-
])
|
|
38231
|
-
);
|
|
38232
|
-
}
|
|
38233
|
-
const responseData = dahuaResponse?.data.toString("utf-8");
|
|
38234
|
-
value.recNo = responseData.split("=")[1]?.trim();
|
|
39039
|
+
end: endDahua
|
|
39040
|
+
},
|
|
39041
|
+
journal
|
|
39042
|
+
);
|
|
39043
|
+
if (failures.length) {
|
|
39044
|
+
throw new BadRequestError90(
|
|
39045
|
+
isBlocklist ? cameraFailureSentence({
|
|
39046
|
+
failures,
|
|
39047
|
+
operation: "added to",
|
|
39048
|
+
outcome: "restored",
|
|
39049
|
+
consequence: "it would still open the barrier"
|
|
39050
|
+
}) : anprRestoreFailureMessage(failures)
|
|
39051
|
+
);
|
|
38235
39052
|
}
|
|
38236
|
-
|
|
38237
|
-
if (vehicle.peopleId && value.recNo) {
|
|
39053
|
+
if (vehicle.peopleId && _plateNumber) {
|
|
38238
39054
|
await _pushVehicleById(
|
|
38239
39055
|
vehicle.peopleId,
|
|
38240
39056
|
{
|
|
38241
39057
|
plateNumber: _plateNumber,
|
|
38242
|
-
recNo
|
|
39058
|
+
recNo
|
|
38243
39059
|
},
|
|
38244
39060
|
session
|
|
38245
39061
|
);
|
|
38246
39062
|
}
|
|
38247
39063
|
const formattedValue = {
|
|
38248
|
-
|
|
38249
|
-
|
|
38250
|
-
|
|
39064
|
+
status: "active" /* ACTIVE */,
|
|
39065
|
+
// Kept for older readers only - one camera's number, never replayed.
|
|
39066
|
+
...recNo ? { recNo } : {},
|
|
39067
|
+
// Dates, not ISO strings: the expiry sweep only matches a BSON Date
|
|
39068
|
+
// (see `expiredVehicleSweepFilter`), so a string here never expired.
|
|
39069
|
+
start: startDate,
|
|
39070
|
+
end: endDate,
|
|
38251
39071
|
deletedAt: ""
|
|
38252
39072
|
};
|
|
38253
39073
|
await _updateVehicleById(id, formattedValue, session);
|
|
@@ -38255,21 +39075,62 @@ function useVehicleService() {
|
|
|
38255
39075
|
return "Vehicle reactivated successfully.";
|
|
38256
39076
|
} catch (error) {
|
|
38257
39077
|
logger66.error("Error in vehicle service reactivation:", error);
|
|
39078
|
+
await rollbackCameras(journal, error);
|
|
38258
39079
|
await session.abortTransaction();
|
|
38259
39080
|
throw error;
|
|
38260
39081
|
} finally {
|
|
38261
39082
|
session.endSession();
|
|
38262
39083
|
}
|
|
38263
39084
|
}
|
|
38264
|
-
async function
|
|
38265
|
-
|
|
38266
|
-
|
|
38267
|
-
|
|
38268
|
-
|
|
38269
|
-
|
|
38270
|
-
|
|
39085
|
+
async function rewriteOnCameras(vehicle, site, next, journal) {
|
|
39086
|
+
const oldPlate = String(vehicle.plateNumber ?? "");
|
|
39087
|
+
const held = listsHeldBy(vehicle);
|
|
39088
|
+
const targets = [];
|
|
39089
|
+
if (held.allow) {
|
|
39090
|
+
targets.push({
|
|
39091
|
+
mode: "TrafficRedList" /* TRAFFIC_REDLIST */,
|
|
39092
|
+
cameras: await allowCamerasAt(site)
|
|
39093
|
+
});
|
|
39094
|
+
}
|
|
39095
|
+
if (held.block) {
|
|
39096
|
+
targets.push({
|
|
39097
|
+
mode: "TrafficBlackList" /* TRAFFIC_BLACKLIST */,
|
|
39098
|
+
cameras: await blockCamerasAt(site)
|
|
39099
|
+
});
|
|
39100
|
+
}
|
|
39101
|
+
if (!targets.some((target) => target.cameras.length)) {
|
|
39102
|
+
throw new BadRequestError90(anprNoCameraRefusal("updated"));
|
|
39103
|
+
}
|
|
39104
|
+
const removeModes = next.plateNumber !== oldPlate ? await revokableModes(site, oldPlate, [vehicle._id]) : targets.map((target) => target.mode);
|
|
39105
|
+
const removeFailures = await removeFromCameras(
|
|
39106
|
+
await camerasAt(site, { activeOnly: true }),
|
|
39107
|
+
removeModes,
|
|
39108
|
+
oldPlate,
|
|
39109
|
+
{
|
|
39110
|
+
owner: String(vehicle.name ?? ""),
|
|
39111
|
+
...cameraWindow(vehicle.start, vehicle.end)
|
|
39112
|
+
},
|
|
39113
|
+
journal,
|
|
39114
|
+
held.block
|
|
39115
|
+
);
|
|
39116
|
+
if (removeFailures.length) {
|
|
39117
|
+
throw new BadRequestError90(
|
|
39118
|
+
anprRevokeFailureMessage(removeFailures, "updated")
|
|
39119
|
+
);
|
|
39120
|
+
}
|
|
39121
|
+
const writeFailures = [];
|
|
39122
|
+
for (const target of targets) {
|
|
39123
|
+
const { failures } = await writeToCameras(
|
|
39124
|
+
target.cameras,
|
|
39125
|
+
{ ...next, mode: target.mode },
|
|
39126
|
+
journal
|
|
39127
|
+
);
|
|
39128
|
+
writeFailures.push(...failures);
|
|
39129
|
+
}
|
|
39130
|
+
if (writeFailures.length) {
|
|
39131
|
+
throw new BadRequestError90(
|
|
39132
|
+
anprWriteFailureMessage(writeFailures, "updated")
|
|
38271
39133
|
);
|
|
38272
|
-
return false;
|
|
38273
39134
|
}
|
|
38274
39135
|
}
|
|
38275
39136
|
async function updateVehicleById(_id, value) {
|
|
@@ -38277,241 +39138,75 @@ function useVehicleService() {
|
|
|
38277
39138
|
if (!session) {
|
|
38278
39139
|
throw new Error("Unable to start session for vehicle service.");
|
|
38279
39140
|
}
|
|
39141
|
+
const journal = cameraJournal();
|
|
38280
39142
|
try {
|
|
38281
39143
|
session.startTransaction();
|
|
38282
|
-
const vehicle = await
|
|
38283
|
-
|
|
38284
|
-
|
|
38285
|
-
|
|
38286
|
-
const _start = vehicle.start;
|
|
38287
|
-
const _end = vehicle.end;
|
|
38288
|
-
const _recNo = plate.recNo;
|
|
38289
|
-
const _type = value.type ? value.type : plate.type;
|
|
39144
|
+
const vehicle = await findOne({ _id: new ObjectId72(_id) });
|
|
39145
|
+
if (!vehicle) {
|
|
39146
|
+
throw new NotFoundError25("Vehicle not found.");
|
|
39147
|
+
}
|
|
38290
39148
|
if (value.peopleId) {
|
|
38291
39149
|
value.peopleId = new ObjectId72(value.peopleId);
|
|
38292
39150
|
}
|
|
38293
39151
|
const { name, plateNumber, start, end, recNo, type, unit, site, block, level, ...rest } = value;
|
|
38294
|
-
|
|
38295
|
-
|
|
38296
|
-
|
|
38297
|
-
|
|
38298
|
-
|
|
38299
|
-
|
|
38300
|
-
|
|
38301
|
-
|
|
38302
|
-
|
|
38303
|
-
|
|
38304
|
-
|
|
38305
|
-
|
|
38306
|
-
|
|
38307
|
-
|
|
38308
|
-
|
|
38309
|
-
|
|
38310
|
-
|
|
38311
|
-
|
|
38312
|
-
|
|
38313
|
-
|
|
38314
|
-
|
|
38315
|
-
throw new BadRequestError90(anprNoCameraRefusal("updated"));
|
|
39152
|
+
const oldPlate = String(vehicle.plateNumber ?? "");
|
|
39153
|
+
const oldName = String(vehicle.name ?? "");
|
|
39154
|
+
const oldType = String(vehicle.type || "whitelist" /* WHITELIST */);
|
|
39155
|
+
const newPlate = plateNumber ? String(plateNumber).trim() : oldPlate;
|
|
39156
|
+
const newName = name ? String(name) : oldName;
|
|
39157
|
+
const newType = type ? String(type) : oldType;
|
|
39158
|
+
const cameraSite = vehicle.site ?? site;
|
|
39159
|
+
const startDate = validDate(start);
|
|
39160
|
+
const endDate = validDate(end);
|
|
39161
|
+
const datesChanged = !!startDate && startDate.getTime() !== validDate(vehicle.start)?.getTime() || !!endDate && endDate.getTime() !== validDate(vehicle.end)?.getTime();
|
|
39162
|
+
const isLive = vehicle.status === "active" /* ACTIVE */;
|
|
39163
|
+
let typeFields = {};
|
|
39164
|
+
if (newType !== oldType) {
|
|
39165
|
+
if (newPlate !== oldPlate) {
|
|
39166
|
+
throw new BadRequestError90(
|
|
39167
|
+
"Change the plate number and the vehicle type in two separate steps."
|
|
39168
|
+
);
|
|
39169
|
+
}
|
|
39170
|
+
if (newType === "blocklist" /* BLOCKLIST */) {
|
|
39171
|
+
if (isLive) {
|
|
39172
|
+
await blockOnCameras(vehicle, cameraSite, newName, journal);
|
|
38316
39173
|
}
|
|
38317
|
-
|
|
38318
|
-
|
|
38319
|
-
|
|
38320
|
-
|
|
38321
|
-
|
|
38322
|
-
|
|
38323
|
-
|
|
38324
|
-
|
|
38325
|
-
|
|
38326
|
-
|
|
38327
|
-
|
|
38328
|
-
|
|
38329
|
-
removePlateNumber
|
|
38330
|
-
);
|
|
38331
|
-
if (!responseForDeletion?.ok) {
|
|
38332
|
-
logCameraFailure(
|
|
38333
|
-
"updateVehicleById revoke",
|
|
38334
|
-
camera,
|
|
38335
|
-
responseForDeletion?.detail ?? "no outcome returned"
|
|
38336
|
-
);
|
|
38337
|
-
throw new BadRequestError90(
|
|
38338
|
-
anprRevokeFailureMessage(
|
|
38339
|
-
[
|
|
38340
|
-
{
|
|
38341
|
-
camera: cameraLabel(camera),
|
|
38342
|
-
reason: responseForDeletion?.reason || "the camera could not be reached"
|
|
38343
|
-
}
|
|
38344
|
-
],
|
|
38345
|
-
"updated"
|
|
38346
|
-
)
|
|
38347
|
-
);
|
|
38348
|
-
}
|
|
38349
|
-
const dahuaPayload = {
|
|
38350
|
-
host,
|
|
38351
|
-
username,
|
|
38352
|
-
password,
|
|
38353
|
-
plateNumber,
|
|
38354
|
-
mode: value.type === "blocklist" /* BLOCKLIST */ ? "TrafficBlackList" /* TRAFFIC_BLACKLIST */ : "TrafficRedList" /* TRAFFIC_REDLIST */,
|
|
38355
|
-
//whitelist or blocklist
|
|
38356
|
-
owner: name,
|
|
38357
|
-
...startDahua ? { start: startDahua } : {},
|
|
38358
|
-
...endDahua ? { end: endDahua } : {}
|
|
38359
|
-
};
|
|
38360
|
-
let dahuaResponse;
|
|
38361
|
-
try {
|
|
38362
|
-
dahuaResponse = await _addPlateNumber(dahuaPayload);
|
|
38363
|
-
} catch (error) {
|
|
38364
|
-
logCameraFailure("updateVehicleById write", camera, error);
|
|
38365
|
-
const restored = await restorePlateOnCamera({
|
|
38366
|
-
...removePlateNumber,
|
|
38367
|
-
plateNumber: _plateNumber,
|
|
38368
|
-
owner: _name,
|
|
38369
|
-
...startDahua ? { start: startDahua } : {},
|
|
38370
|
-
...endDahua ? { end: endDahua } : {}
|
|
38371
|
-
});
|
|
38372
|
-
throw new BadRequestError90(
|
|
38373
|
-
anprWriteFailureMessage(
|
|
38374
|
-
[{ camera: cameraLabel(camera), reason: anprWriteReason(error) }],
|
|
38375
|
-
"updated"
|
|
38376
|
-
) + (restored ? ` The previous plate number ${_plateNumber} is still registered, so nothing has changed.` : ` WARNING: ${_plateNumber} was taken off this camera and could not be put back - it will NOT open the barrier until someone restores it.`)
|
|
38377
|
-
);
|
|
38378
|
-
}
|
|
38379
|
-
if (dahuaResponse?.statusCode !== 200) {
|
|
38380
|
-
logCameraFailure(
|
|
38381
|
-
"updateVehicleById write",
|
|
38382
|
-
camera,
|
|
38383
|
-
`HTTP ${dahuaResponse?.statusCode ?? "none"}`
|
|
38384
|
-
);
|
|
38385
|
-
throw new BadRequestError90(
|
|
38386
|
-
anprWriteFailureMessage(
|
|
38387
|
-
[
|
|
38388
|
-
{
|
|
38389
|
-
camera: cameraLabel(camera),
|
|
38390
|
-
reason: anprWriteReason(
|
|
38391
|
-
`HTTP ${dahuaResponse?.statusCode ?? "none"}`
|
|
38392
|
-
)
|
|
38393
|
-
}
|
|
38394
|
-
],
|
|
38395
|
-
"updated"
|
|
38396
|
-
)
|
|
38397
|
-
);
|
|
38398
|
-
}
|
|
38399
|
-
const responseData = dahuaResponse?.data?.toString("utf-8") ?? "";
|
|
38400
|
-
value.recNo = responseData.split("=")[1]?.trim();
|
|
38401
|
-
if (value.peopleId && value.recNo) {
|
|
38402
|
-
await _pushVehicleById(
|
|
38403
|
-
value.peopleId,
|
|
38404
|
-
{
|
|
38405
|
-
plateNumber,
|
|
38406
|
-
recNo: value.recNo
|
|
38407
|
-
},
|
|
38408
|
-
session
|
|
38409
|
-
);
|
|
38410
|
-
}
|
|
38411
|
-
} else {
|
|
38412
|
-
const existing = parseDahuaFind(
|
|
38413
|
-
await _getPlateNumber({
|
|
38414
|
-
host,
|
|
38415
|
-
username,
|
|
38416
|
-
password,
|
|
38417
|
-
mode: _type === "blocklist" /* BLOCKLIST */ ? "TrafficBlackList" /* TRAFFIC_BLACKLIST */ : "TrafficRedList" /* TRAFFIC_REDLIST */,
|
|
38418
|
-
plateNumber,
|
|
38419
|
-
requireOk: true
|
|
38420
|
-
}) ?? ""
|
|
38421
|
-
);
|
|
38422
|
-
if (existing.exists) {
|
|
38423
|
-
const outcome = await _removePlateNumber({
|
|
38424
|
-
host,
|
|
38425
|
-
username,
|
|
38426
|
-
password,
|
|
38427
|
-
mode: _type === "blocklist" /* BLOCKLIST */ ? "TrafficBlackList" /* TRAFFIC_BLACKLIST */ : "TrafficRedList" /* TRAFFIC_REDLIST */,
|
|
38428
|
-
recno: existing.recNo || _recNo
|
|
38429
|
-
});
|
|
38430
|
-
if (!outcome?.ok) {
|
|
38431
|
-
logCameraFailure(
|
|
38432
|
-
"updateVehicleById revoke",
|
|
38433
|
-
camera,
|
|
38434
|
-
outcome?.detail ?? "no outcome returned"
|
|
38435
|
-
);
|
|
38436
|
-
throw new BadRequestError90(
|
|
38437
|
-
anprRevokeFailureMessage([
|
|
38438
|
-
{
|
|
38439
|
-
camera: cameraLabel(camera),
|
|
38440
|
-
reason: outcome?.reason || "the camera could not be reached"
|
|
38441
|
-
}
|
|
38442
|
-
], "updated")
|
|
38443
|
-
);
|
|
38444
|
-
}
|
|
38445
|
-
}
|
|
38446
|
-
const dahuaPayload = {
|
|
38447
|
-
host,
|
|
38448
|
-
username,
|
|
38449
|
-
password,
|
|
38450
|
-
plateNumber,
|
|
38451
|
-
// recno: _recNo,
|
|
38452
|
-
mode: _type === "blocklist" /* BLOCKLIST */ ? "TrafficBlackList" /* TRAFFIC_BLACKLIST */ : "TrafficRedList" /* TRAFFIC_REDLIST */,
|
|
38453
|
-
start: startDahua,
|
|
38454
|
-
end: endDahua,
|
|
38455
|
-
owner: name ? name : _name
|
|
38456
|
-
};
|
|
38457
|
-
let dahuaResponse;
|
|
38458
|
-
try {
|
|
38459
|
-
dahuaResponse = await _addPlateNumber(dahuaPayload);
|
|
38460
|
-
} catch (error) {
|
|
38461
|
-
logCameraFailure("updateVehicleById write", camera, error);
|
|
38462
|
-
const restored = existing.exists ? await restorePlateOnCamera({
|
|
38463
|
-
...dahuaPayload,
|
|
38464
|
-
owner: _name
|
|
38465
|
-
}) : true;
|
|
38466
|
-
throw new BadRequestError90(
|
|
38467
|
-
anprWriteFailureMessage(
|
|
38468
|
-
[{ camera: cameraLabel(camera), reason: anprWriteReason(error) }],
|
|
38469
|
-
"updated"
|
|
38470
|
-
) + (restored ? " The vehicle is unchanged and still registered." : ` WARNING: ${plateNumber} was taken off this camera and could not be put back - it will NOT open the barrier until someone restores it.`)
|
|
38471
|
-
);
|
|
38472
|
-
}
|
|
38473
|
-
if (dahuaResponse?.statusCode !== 200) {
|
|
38474
|
-
logCameraFailure(
|
|
38475
|
-
"updateVehicleById write",
|
|
38476
|
-
camera,
|
|
38477
|
-
`HTTP ${dahuaResponse?.statusCode ?? "none"}`
|
|
38478
|
-
);
|
|
38479
|
-
throw new BadRequestError90(
|
|
38480
|
-
anprWriteFailureMessage(
|
|
38481
|
-
[
|
|
38482
|
-
{
|
|
38483
|
-
camera: cameraLabel(camera),
|
|
38484
|
-
reason: anprWriteReason(
|
|
38485
|
-
`HTTP ${dahuaResponse?.statusCode ?? "none"}`
|
|
38486
|
-
)
|
|
38487
|
-
}
|
|
38488
|
-
],
|
|
38489
|
-
"updated"
|
|
38490
|
-
)
|
|
38491
|
-
);
|
|
38492
|
-
}
|
|
38493
|
-
const responseData = dahuaResponse?.data?.toString("utf-8") ?? "";
|
|
38494
|
-
value.recNo = responseData.split("=")[1]?.trim();
|
|
38495
|
-
if (value.peopleId && value.recNo) {
|
|
38496
|
-
await _pushVehicleById(
|
|
38497
|
-
value.peopleId,
|
|
38498
|
-
{
|
|
38499
|
-
plateNumber,
|
|
38500
|
-
recNo: value.recNo
|
|
38501
|
-
},
|
|
38502
|
-
session
|
|
38503
|
-
);
|
|
38504
|
-
}
|
|
38505
|
-
}
|
|
39174
|
+
typeFields = {
|
|
39175
|
+
type: "blocklist" /* BLOCKLIST */,
|
|
39176
|
+
blockedFrom: oldType === "whitelist" /* WHITELIST */ ? "whitelist" /* WHITELIST */ : String(vehicle.blockedFrom ?? "")
|
|
39177
|
+
};
|
|
39178
|
+
} else {
|
|
39179
|
+
if (isLive) {
|
|
39180
|
+
await unblockOnCameras(
|
|
39181
|
+
vehicle,
|
|
39182
|
+
cameraSite,
|
|
39183
|
+
{ restore: true, owner: newName },
|
|
39184
|
+
journal
|
|
39185
|
+
);
|
|
38506
39186
|
}
|
|
39187
|
+
typeFields = { type: "whitelist" /* WHITELIST */, blockedFrom: "" };
|
|
38507
39188
|
}
|
|
39189
|
+
} else if (isLive && (newPlate !== oldPlate || newName !== oldName || datesChanged)) {
|
|
39190
|
+
await rewriteOnCameras(
|
|
39191
|
+
vehicle,
|
|
39192
|
+
cameraSite,
|
|
39193
|
+
{
|
|
39194
|
+
plateNumber: newPlate,
|
|
39195
|
+
owner: newName,
|
|
39196
|
+
...cameraWindow(startDate ?? vehicle.start, endDate ?? vehicle.end)
|
|
39197
|
+
},
|
|
39198
|
+
journal
|
|
39199
|
+
);
|
|
38508
39200
|
}
|
|
38509
39201
|
const formattedValue = {
|
|
38510
39202
|
...rest,
|
|
38511
39203
|
...name && { name },
|
|
38512
|
-
...plateNumber && { plateNumber },
|
|
38513
|
-
|
|
38514
|
-
|
|
39204
|
+
...plateNumber && { plateNumber: newPlate },
|
|
39205
|
+
// Dates, not ISO strings: the expiry sweep only matches a BSON Date
|
|
39206
|
+
// (see `expiredVehicleSweepFilter`), so an end date edited to a string
|
|
39207
|
+
// never expired.
|
|
39208
|
+
...startDate && { start: startDate },
|
|
39209
|
+
...endDate && { end: endDate },
|
|
38515
39210
|
...block && {
|
|
38516
39211
|
block: typeof block === "string" ? new ObjectId72(block) : block
|
|
38517
39212
|
},
|
|
@@ -38521,13 +39216,27 @@ function useVehicleService() {
|
|
|
38521
39216
|
...unit && {
|
|
38522
39217
|
unit: typeof unit === "string" ? new ObjectId72(unit) : unit
|
|
38523
39218
|
},
|
|
38524
|
-
...
|
|
38525
|
-
...
|
|
39219
|
+
...type && { type },
|
|
39220
|
+
...typeFields
|
|
38526
39221
|
};
|
|
38527
39222
|
await _updateVehicleById(_id, formattedValue, session);
|
|
39223
|
+
const peopleId = value.peopleId || vehicle.peopleId;
|
|
39224
|
+
if (isLive && peopleId && newPlate !== oldPlate) {
|
|
39225
|
+
await _pushVehicleById(
|
|
39226
|
+
peopleId,
|
|
39227
|
+
{ plateNumber: newPlate, recNo: "" },
|
|
39228
|
+
session
|
|
39229
|
+
);
|
|
39230
|
+
await _pullVehicleByRecNo(
|
|
39231
|
+
"",
|
|
39232
|
+
{ peopleId, site: cameraSite, plateNumber: oldPlate },
|
|
39233
|
+
session
|
|
39234
|
+
);
|
|
39235
|
+
}
|
|
38528
39236
|
await session.commitTransaction();
|
|
38529
39237
|
} catch (error) {
|
|
38530
39238
|
logger66.error("Error in vehicle service update:", error);
|
|
39239
|
+
await rollbackCameras(journal, error);
|
|
38531
39240
|
await session.abortTransaction();
|
|
38532
39241
|
throw error;
|
|
38533
39242
|
} finally {
|
|
@@ -38563,25 +39272,12 @@ function useVehicleService() {
|
|
|
38563
39272
|
if (sanitizedValues.length === 0) {
|
|
38564
39273
|
throw new Error("No valid vehicle plate numbers found.");
|
|
38565
39274
|
}
|
|
38566
|
-
|
|
38567
|
-
|
|
38568
|
-
|
|
38569
|
-
|
|
38570
|
-
|
|
38571
|
-
|
|
38572
|
-
const siteCameraReq = await _getAllSiteCameras({
|
|
38573
|
-
site,
|
|
38574
|
-
type: "anpr",
|
|
38575
|
-
direction: ["both", "entry"],
|
|
38576
|
-
page,
|
|
38577
|
-
limit
|
|
38578
|
-
});
|
|
38579
|
-
pages = siteCameraReq.pages || 1;
|
|
38580
|
-
siteCameras.push(...siteCameraReq.items);
|
|
38581
|
-
page++;
|
|
38582
|
-
} while (page <= pages);
|
|
38583
|
-
}
|
|
38584
|
-
if (!siteCameras.length) {
|
|
39275
|
+
const isBlockRow = (row) => row.type !== "whitelist" /* WHITELIST */;
|
|
39276
|
+
const allowCameras = site ? await allowCamerasAt(site) : [];
|
|
39277
|
+
const blockCameras = site ? await blockCamerasAt(site) : [];
|
|
39278
|
+
const hasAllowRows = sanitizedValues.some((row) => !isBlockRow(row));
|
|
39279
|
+
const hasBlockRows = sanitizedValues.some((row) => isBlockRow(row));
|
|
39280
|
+
if (hasAllowRows && !allowCameras.length || hasBlockRows && !blockCameras.length) {
|
|
38585
39281
|
throw new Error("No site cameras found.");
|
|
38586
39282
|
}
|
|
38587
39283
|
const written = /* @__PURE__ */ new Map();
|
|
@@ -38590,58 +39286,63 @@ function useVehicleService() {
|
|
|
38590
39286
|
site,
|
|
38591
39287
|
plateNumber: value.plateNumber
|
|
38592
39288
|
}));
|
|
38593
|
-
const
|
|
38594
|
-
|
|
38595
|
-
|
|
38596
|
-
|
|
38597
|
-
|
|
38598
|
-
|
|
38599
|
-
|
|
38600
|
-
|
|
38601
|
-
|
|
38602
|
-
|
|
38603
|
-
|
|
38604
|
-
|
|
38605
|
-
|
|
38606
|
-
|
|
38607
|
-
|
|
38608
|
-
|
|
38609
|
-
|
|
38610
|
-
|
|
38611
|
-
|
|
38612
|
-
|
|
38613
|
-
|
|
39289
|
+
const writeRow = async (camera, row) => {
|
|
39290
|
+
const rowId = String(row._id);
|
|
39291
|
+
const vehicleValue = sanitizedValues[Number(rowId)];
|
|
39292
|
+
const plateNumber = vehicleValue.plateNumber;
|
|
39293
|
+
const { host, username, password } = camera;
|
|
39294
|
+
const dahuaPayload = {
|
|
39295
|
+
host,
|
|
39296
|
+
username,
|
|
39297
|
+
password,
|
|
39298
|
+
plateNumber,
|
|
39299
|
+
mode: vehicleValue.type === "whitelist" /* WHITELIST */ ? "TrafficRedList" /* TRAFFIC_REDLIST */ : "TrafficBlackList" /* TRAFFIC_BLACKLIST */,
|
|
39300
|
+
...vehicleValue.start ? { start: String(vehicleValue.start) } : {},
|
|
39301
|
+
...vehicleValue.end ? { end: String(vehicleValue.end) } : {},
|
|
39302
|
+
...vehicleValue.name ? { owner: vehicleValue.name } : {},
|
|
39303
|
+
...vehicleValue.vehicleModel ? { vehicleType: vehicleValue.vehicleModel } : {},
|
|
39304
|
+
...vehicleValue.vehicleColor ? { vehicleColor: vehicleValue.vehicleColor } : {},
|
|
39305
|
+
// Dates on the camera's clock, in the site's zone.
|
|
39306
|
+
timezone: await siteTimezone(camera.site ?? site)
|
|
39307
|
+
};
|
|
39308
|
+
try {
|
|
39309
|
+
const dahuaResponse = await _bulkInsertPlateNumber(dahuaPayload);
|
|
39310
|
+
const responseStatus = dahuaResponse?.statusCode || dahuaResponse?.status || dahuaResponse?.res?.status || "unknown";
|
|
39311
|
+
const responseData = dahuaResponse?.data?.toString("utf-8") ?? "";
|
|
39312
|
+
if (responseStatus !== 200) {
|
|
39313
|
+
logCameraFailure(
|
|
39314
|
+
"bulk add",
|
|
39315
|
+
camera,
|
|
39316
|
+
`HTTP ${responseStatus}: ${responseData.slice(0, 500)}`
|
|
39317
|
+
);
|
|
39318
|
+
return {
|
|
39319
|
+
ok: false,
|
|
39320
|
+
reason: anprWriteReason(`HTTP ${responseStatus}`)
|
|
38614
39321
|
};
|
|
38615
|
-
try {
|
|
38616
|
-
const dahuaResponse = await _bulkInsertPlateNumber(dahuaPayload);
|
|
38617
|
-
const responseStatus = dahuaResponse?.statusCode || dahuaResponse?.status || dahuaResponse?.res?.status || "unknown";
|
|
38618
|
-
const responseData = dahuaResponse?.data?.toString("utf-8") ?? "";
|
|
38619
|
-
if (responseStatus !== 200) {
|
|
38620
|
-
logCameraFailure(
|
|
38621
|
-
"bulk add",
|
|
38622
|
-
camera,
|
|
38623
|
-
`HTTP ${responseStatus}: ${responseData.slice(0, 500)}`
|
|
38624
|
-
);
|
|
38625
|
-
return {
|
|
38626
|
-
ok: false,
|
|
38627
|
-
reason: anprWriteReason(`HTTP ${responseStatus}`)
|
|
38628
|
-
};
|
|
38629
|
-
}
|
|
38630
|
-
const recNo = String(responseData.split("=")[1]?.trim() ?? "");
|
|
38631
|
-
if (recNo) {
|
|
38632
|
-
vehicleValue.recNo = vehicleValue.recNo || recNo;
|
|
38633
|
-
const entries = written.get(rowId) ?? [];
|
|
38634
|
-
entries.push({ camera, recno: recNo });
|
|
38635
|
-
written.set(rowId, entries);
|
|
38636
|
-
}
|
|
38637
|
-
return { ok: true, reason: "" };
|
|
38638
|
-
} catch (error) {
|
|
38639
|
-
logCameraFailure("bulk add", camera, error);
|
|
38640
|
-
return { ok: false, reason: anprWriteReason(error) };
|
|
38641
|
-
}
|
|
38642
39322
|
}
|
|
39323
|
+
const recNo = String(responseData.split("=")[1]?.trim() ?? "");
|
|
39324
|
+
if (recNo) {
|
|
39325
|
+
vehicleValue.recNo = vehicleValue.recNo || recNo;
|
|
39326
|
+
const entries = written.get(rowId) ?? [];
|
|
39327
|
+
entries.push({ camera, recno: recNo });
|
|
39328
|
+
written.set(rowId, entries);
|
|
39329
|
+
}
|
|
39330
|
+
return { ok: true, reason: "" };
|
|
39331
|
+
} catch (error) {
|
|
39332
|
+
logCameraFailure("bulk add", camera, error);
|
|
39333
|
+
return { ok: false, reason: anprWriteReason(error) };
|
|
38643
39334
|
}
|
|
39335
|
+
};
|
|
39336
|
+
const allowPass = await sweepExpiredVehicles(
|
|
39337
|
+
sweepRows.filter((row) => !isBlockRow(sanitizedValues[Number(row._id)])),
|
|
39338
|
+
{ camerasForSite: async () => allowCameras, cameraLabel, revoke: writeRow }
|
|
39339
|
+
);
|
|
39340
|
+
const blockPass = await sweepExpiredVehicles(
|
|
39341
|
+
sweepRows.filter((row) => isBlockRow(sanitizedValues[Number(row._id)])),
|
|
39342
|
+
{ camerasForSite: async () => blockCameras, cameraLabel, revoke: writeRow }
|
|
38644
39343
|
);
|
|
39344
|
+
const savableIds = [...allowPass.deletableIds, ...blockPass.deletableIds];
|
|
39345
|
+
const skipped = [...allowPass.skipped, ...blockPass.skipped];
|
|
38645
39346
|
const failedRows = [];
|
|
38646
39347
|
for (const entry of skipped) {
|
|
38647
39348
|
const stranded = [];
|
|
@@ -38686,123 +39387,26 @@ function useVehicleService() {
|
|
|
38686
39387
|
if (!session) {
|
|
38687
39388
|
throw new Error("Unable to start session for vehicle service.");
|
|
38688
39389
|
}
|
|
39390
|
+
const journal = cameraJournal();
|
|
38689
39391
|
try {
|
|
38690
39392
|
session.startTransaction();
|
|
38691
|
-
const vehicle = await
|
|
38692
|
-
|
|
38693
|
-
|
|
38694
|
-
const _plateNumber = plate?.plateNumber;
|
|
38695
|
-
const _start = vehicle.start;
|
|
38696
|
-
const _end = vehicle.end;
|
|
38697
|
-
const _recNo = plate.recNo;
|
|
38698
|
-
const _type = plate.type;
|
|
38699
|
-
if (value.peopleId) {
|
|
38700
|
-
value.peopleId = new ObjectId72(value.peopleId);
|
|
39393
|
+
const vehicle = await findOne({ _id: new ObjectId72(_id) });
|
|
39394
|
+
if (!vehicle) {
|
|
39395
|
+
throw new NotFoundError25("Vehicle not found.");
|
|
38701
39396
|
}
|
|
38702
|
-
const
|
|
38703
|
-
const
|
|
38704
|
-
|
|
38705
|
-
|
|
38706
|
-
if (_plateNumber) {
|
|
38707
|
-
const siteCameras = [];
|
|
38708
|
-
let page = 1;
|
|
38709
|
-
let pages = 1;
|
|
38710
|
-
const limit = 20;
|
|
38711
|
-
do {
|
|
38712
|
-
const siteCameraReq = await _getAllSiteCameras({
|
|
38713
|
-
site,
|
|
38714
|
-
type: "anpr",
|
|
38715
|
-
direction: ["both", "entry"],
|
|
38716
|
-
page,
|
|
38717
|
-
limit
|
|
38718
|
-
});
|
|
38719
|
-
pages = siteCameraReq.pages || 1;
|
|
38720
|
-
siteCameras.push(...siteCameraReq.items);
|
|
38721
|
-
page++;
|
|
38722
|
-
} while (page <= pages);
|
|
38723
|
-
if (!siteCameras.length) {
|
|
38724
|
-
throw new BadRequestError90(anprNoCameraRefusal("blocked"));
|
|
38725
|
-
}
|
|
38726
|
-
for (const camera of siteCameras) {
|
|
38727
|
-
const { host, username, password } = camera;
|
|
38728
|
-
const removePlateNumber = {
|
|
38729
|
-
host,
|
|
38730
|
-
username,
|
|
38731
|
-
password,
|
|
38732
|
-
mode: _type === "blocklist" /* BLOCKLIST */ ? "TrafficBlackList" /* TRAFFIC_BLACKLIST */ : "TrafficRedList" /* TRAFFIC_REDLIST */,
|
|
38733
|
-
recno: _recNo
|
|
38734
|
-
};
|
|
38735
|
-
const responseForDeletion = await _removePlateNumber(
|
|
38736
|
-
removePlateNumber
|
|
38737
|
-
);
|
|
38738
|
-
if (!responseForDeletion?.ok) {
|
|
38739
|
-
logCameraFailure(
|
|
38740
|
-
"blocklistVehicles revoke",
|
|
38741
|
-
camera,
|
|
38742
|
-
responseForDeletion?.detail ?? "no outcome returned"
|
|
38743
|
-
);
|
|
38744
|
-
throw new BadRequestError90(
|
|
38745
|
-
anprRevokeFailureMessage(
|
|
38746
|
-
[
|
|
38747
|
-
{
|
|
38748
|
-
camera: cameraLabel(camera),
|
|
38749
|
-
reason: responseForDeletion?.reason || "the camera could not be reached"
|
|
38750
|
-
}
|
|
38751
|
-
],
|
|
38752
|
-
"updated"
|
|
38753
|
-
)
|
|
38754
|
-
);
|
|
38755
|
-
}
|
|
38756
|
-
const dahuaPayload = {
|
|
38757
|
-
host,
|
|
38758
|
-
username,
|
|
38759
|
-
password,
|
|
38760
|
-
plateNumber: _plateNumber,
|
|
38761
|
-
mode: "TrafficBlackList" /* TRAFFIC_BLACKLIST */,
|
|
38762
|
-
owner: _name,
|
|
38763
|
-
...startDahua ? { start: startDahua } : {},
|
|
38764
|
-
...endDahua ? { end: endDahua } : {}
|
|
38765
|
-
};
|
|
38766
|
-
const dahuaResponse = await _addPlateNumber(dahuaPayload);
|
|
38767
|
-
if (dahuaResponse?.statusCode !== 200) {
|
|
38768
|
-
logCameraFailure(
|
|
38769
|
-
"blocklistVehicles write",
|
|
38770
|
-
camera,
|
|
38771
|
-
`HTTP ${dahuaResponse?.statusCode ?? "none"}`
|
|
38772
|
-
);
|
|
38773
|
-
throw new BadRequestError90(
|
|
38774
|
-
anprBlockFailureMessage([
|
|
38775
|
-
{
|
|
38776
|
-
camera: cameraLabel(camera),
|
|
38777
|
-
reason: anprWriteReason(
|
|
38778
|
-
`HTTP ${dahuaResponse?.statusCode ?? "none"}`
|
|
38779
|
-
)
|
|
38780
|
-
}
|
|
38781
|
-
])
|
|
38782
|
-
);
|
|
38783
|
-
}
|
|
38784
|
-
const responseData = dahuaResponse?.data?.toString("utf-8") ?? "";
|
|
38785
|
-
value.recNo = responseData.split("=")[1]?.trim();
|
|
38786
|
-
const normalizedPlateNumber = _plateNumber;
|
|
38787
|
-
if (value.peopleId && value.recNo) {
|
|
38788
|
-
await _pushVehicleById(
|
|
38789
|
-
value.peopleId,
|
|
38790
|
-
{
|
|
38791
|
-
plateNumber: normalizedPlateNumber,
|
|
38792
|
-
recNo: value.recNo
|
|
38793
|
-
},
|
|
38794
|
-
session
|
|
38795
|
-
);
|
|
38796
|
-
}
|
|
38797
|
-
}
|
|
39397
|
+
const owner = String(value.name || vehicle.name || "");
|
|
39398
|
+
const cameraSite = vehicle.site ?? value.site;
|
|
39399
|
+
if (vehicle.status === "active" /* ACTIVE */) {
|
|
39400
|
+
await blockOnCameras(vehicle, cameraSite, owner, journal);
|
|
38798
39401
|
}
|
|
38799
39402
|
const formattedValue = {
|
|
38800
39403
|
type: "blocklist" /* BLOCKLIST */,
|
|
38801
|
-
|
|
39404
|
+
blockedFrom: vehicle.type === "whitelist" /* WHITELIST */ ? "whitelist" /* WHITELIST */ : String(vehicle.blockedFrom ?? "")
|
|
38802
39405
|
};
|
|
38803
39406
|
await _updateVehicleById(_id, formattedValue, session);
|
|
38804
39407
|
await session.commitTransaction();
|
|
38805
39408
|
} catch (error) {
|
|
39409
|
+
await rollbackCameras(journal, error);
|
|
38806
39410
|
await session.abortTransaction();
|
|
38807
39411
|
throw error;
|
|
38808
39412
|
} finally {
|
|
@@ -38814,85 +39418,222 @@ function useVehicleService() {
|
|
|
38814
39418
|
if (!session) {
|
|
38815
39419
|
throw new Error("Unable to start session for vehicle service.");
|
|
38816
39420
|
}
|
|
39421
|
+
const journal = cameraJournal();
|
|
38817
39422
|
try {
|
|
38818
39423
|
session.startTransaction();
|
|
38819
|
-
const vehicle = await
|
|
38820
|
-
|
|
38821
|
-
|
|
38822
|
-
|
|
39424
|
+
const vehicle = await findOne({ _id: new ObjectId72(_id) });
|
|
39425
|
+
if (!vehicle) {
|
|
39426
|
+
throw new NotFoundError25("Vehicle not found.");
|
|
39427
|
+
}
|
|
39428
|
+
if (vehicle.type !== "blocklist" /* BLOCKLIST */) {
|
|
39429
|
+
throw new BadRequestError90("This vehicle is not on the blocklist.");
|
|
39430
|
+
}
|
|
39431
|
+
const _plateNumber = String(vehicle.plateNumber ?? "");
|
|
39432
|
+
const cameraSite = vehicle.site ?? value.site;
|
|
39433
|
+
const restore = vehicle.blockedFrom === "whitelist" /* WHITELIST */;
|
|
38823
39434
|
if (value.peopleId) {
|
|
38824
39435
|
value.peopleId = new ObjectId72(value.peopleId);
|
|
38825
39436
|
}
|
|
38826
|
-
|
|
38827
|
-
|
|
38828
|
-
|
|
38829
|
-
|
|
38830
|
-
|
|
38831
|
-
|
|
38832
|
-
do {
|
|
38833
|
-
const siteCameraReq = await _getAllSiteCameras({
|
|
38834
|
-
site,
|
|
38835
|
-
type: "anpr",
|
|
38836
|
-
direction: ["both", "entry"],
|
|
38837
|
-
page,
|
|
38838
|
-
limit
|
|
38839
|
-
});
|
|
38840
|
-
pages = siteCameraReq.pages || 1;
|
|
38841
|
-
siteCameras.push(...siteCameraReq.items);
|
|
38842
|
-
page++;
|
|
38843
|
-
} while (page <= pages);
|
|
38844
|
-
logger66.info(
|
|
38845
|
-
`removeFromBlocklist resolved ${siteCameras.length} ANPR camera(s)`
|
|
39437
|
+
if (vehicle.status === "active" /* ACTIVE */) {
|
|
39438
|
+
await unblockOnCameras(
|
|
39439
|
+
vehicle,
|
|
39440
|
+
cameraSite,
|
|
39441
|
+
{ restore, owner: String(vehicle.name ?? "") },
|
|
39442
|
+
journal
|
|
38846
39443
|
);
|
|
38847
|
-
|
|
38848
|
-
|
|
38849
|
-
|
|
38850
|
-
|
|
38851
|
-
|
|
38852
|
-
|
|
38853
|
-
|
|
38854
|
-
|
|
38855
|
-
|
|
38856
|
-
const responseForDeletion = await _removePlateNumber(
|
|
38857
|
-
removePlateNumber
|
|
38858
|
-
);
|
|
38859
|
-
if (!responseForDeletion?.ok) {
|
|
38860
|
-
logCameraFailure(
|
|
38861
|
-
"removeFromBlocklist revoke",
|
|
38862
|
-
camera,
|
|
38863
|
-
responseForDeletion?.detail ?? "no outcome returned"
|
|
38864
|
-
);
|
|
38865
|
-
throw new BadRequestError90(
|
|
38866
|
-
anprUnblockFailureMessage([
|
|
38867
|
-
{
|
|
38868
|
-
camera: cameraLabel(camera),
|
|
38869
|
-
reason: responseForDeletion?.reason || "the camera could not be reached"
|
|
38870
|
-
}
|
|
38871
|
-
])
|
|
38872
|
-
);
|
|
38873
|
-
}
|
|
38874
|
-
}
|
|
39444
|
+
}
|
|
39445
|
+
if (restore) {
|
|
39446
|
+
await _updateVehicleById(
|
|
39447
|
+
_id,
|
|
39448
|
+
{ type: "whitelist" /* WHITELIST */, blockedFrom: "" },
|
|
39449
|
+
session
|
|
39450
|
+
);
|
|
39451
|
+
await session.commitTransaction();
|
|
39452
|
+
return "Vehicle unblocked and restored to the whitelist.";
|
|
38875
39453
|
}
|
|
38876
39454
|
await _deleteVehicle(_id, session);
|
|
38877
|
-
|
|
39455
|
+
const peopleId = value.peopleId || vehicle.peopleId;
|
|
39456
|
+
if ((peopleId || cameraSite) && _plateNumber) {
|
|
38878
39457
|
await _pullVehicleByRecNo(
|
|
38879
|
-
|
|
39458
|
+
"",
|
|
38880
39459
|
{
|
|
38881
|
-
peopleId
|
|
38882
|
-
site,
|
|
39460
|
+
peopleId,
|
|
39461
|
+
site: cameraSite,
|
|
38883
39462
|
plateNumber: _plateNumber
|
|
38884
39463
|
},
|
|
38885
39464
|
session
|
|
38886
39465
|
);
|
|
38887
39466
|
}
|
|
38888
39467
|
await session.commitTransaction();
|
|
39468
|
+
return "Vehicle removed from the blocklist.";
|
|
38889
39469
|
} catch (error) {
|
|
39470
|
+
await rollbackCameras(journal, error);
|
|
38890
39471
|
await session.abortTransaction();
|
|
38891
39472
|
throw error;
|
|
38892
39473
|
} finally {
|
|
38893
39474
|
session.endSession();
|
|
38894
39475
|
}
|
|
38895
39476
|
}
|
|
39477
|
+
async function fillCameraLists(site, camera, lists) {
|
|
39478
|
+
let target;
|
|
39479
|
+
if (camera.id) {
|
|
39480
|
+
const stored = (await camerasAt(site)).find(
|
|
39481
|
+
(candidate) => String(candidate._id) === String(camera.id)
|
|
39482
|
+
);
|
|
39483
|
+
if (!stored)
|
|
39484
|
+
throw new NotFoundError25("Camera not found.");
|
|
39485
|
+
target = {
|
|
39486
|
+
...stored,
|
|
39487
|
+
...camera.host ? { host: camera.host } : {},
|
|
39488
|
+
...camera.username ? { username: camera.username } : {},
|
|
39489
|
+
...camera.password ? { password: camera.password } : {},
|
|
39490
|
+
...camera.direction ? { direction: camera.direction } : {}
|
|
39491
|
+
};
|
|
39492
|
+
} else {
|
|
39493
|
+
target = {
|
|
39494
|
+
host: String(camera.host ?? ""),
|
|
39495
|
+
username: String(camera.username ?? ""),
|
|
39496
|
+
password: String(camera.password ?? ""),
|
|
39497
|
+
name: camera.name,
|
|
39498
|
+
direction: camera.direction,
|
|
39499
|
+
// Whose clock the season pass windows are written on.
|
|
39500
|
+
site
|
|
39501
|
+
};
|
|
39502
|
+
}
|
|
39503
|
+
const vehicles = await _getLivePlatesBySite(String(site));
|
|
39504
|
+
const unique = (rows) => {
|
|
39505
|
+
const seen = /* @__PURE__ */ new Set();
|
|
39506
|
+
return rows.filter((row) => {
|
|
39507
|
+
const key = normalisePlate(row.plateNumber);
|
|
39508
|
+
if (!key || seen.has(key))
|
|
39509
|
+
return false;
|
|
39510
|
+
seen.add(key);
|
|
39511
|
+
return true;
|
|
39512
|
+
});
|
|
39513
|
+
};
|
|
39514
|
+
const blocked = lists.block ? unique(vehicles.filter((vehicle) => listsHeldBy(vehicle).block)) : [];
|
|
39515
|
+
const allowed = lists.allow ? unique(vehicles.filter((vehicle) => listsHeldBy(vehicle).allow)) : [];
|
|
39516
|
+
const writes = [
|
|
39517
|
+
...blocked.map((vehicle) => ({
|
|
39518
|
+
plateNumber: String(vehicle.plateNumber),
|
|
39519
|
+
mode: "TrafficBlackList" /* TRAFFIC_BLACKLIST */,
|
|
39520
|
+
owner: String(vehicle.name ?? ""),
|
|
39521
|
+
...blockWindow(vehicle.end)
|
|
39522
|
+
})),
|
|
39523
|
+
...allowed.map((vehicle) => ({
|
|
39524
|
+
plateNumber: String(vehicle.plateNumber),
|
|
39525
|
+
mode: "TrafficRedList" /* TRAFFIC_REDLIST */,
|
|
39526
|
+
owner: String(vehicle.name ?? ""),
|
|
39527
|
+
...cameraWindow(vehicle.start, vehicle.end)
|
|
39528
|
+
}))
|
|
39529
|
+
];
|
|
39530
|
+
const written = { allowed: 0, blocked: 0 };
|
|
39531
|
+
for (const write of writes) {
|
|
39532
|
+
try {
|
|
39533
|
+
await insertPlateOnCamera(target, write);
|
|
39534
|
+
} catch (error) {
|
|
39535
|
+
logCameraFailure("fill camera", target, error);
|
|
39536
|
+
throw new BadRequestError90(
|
|
39537
|
+
`Only ${written.allowed + written.blocked} of ${writes.length} plates could be added to ${cameraLabel(target)} before it stopped: ${anprWriteReason(error)}.`
|
|
39538
|
+
);
|
|
39539
|
+
}
|
|
39540
|
+
if (write.mode === "TrafficBlackList" /* TRAFFIC_BLACKLIST */)
|
|
39541
|
+
written.blocked++;
|
|
39542
|
+
else
|
|
39543
|
+
written.allowed++;
|
|
39544
|
+
}
|
|
39545
|
+
logger66.info(
|
|
39546
|
+
`fillCameraLists wrote ${written.allowed} allowed and ${written.blocked} blocked plate(s) for site ${String(site)}`
|
|
39547
|
+
);
|
|
39548
|
+
return written;
|
|
39549
|
+
}
|
|
39550
|
+
async function newCameraPlateRows(site, direction) {
|
|
39551
|
+
const lists = listsFilledByDirectionChange("", direction);
|
|
39552
|
+
if (!lists.allow && !lists.block)
|
|
39553
|
+
return [];
|
|
39554
|
+
const vehicles = await _getLivePlatesBySite(String(site));
|
|
39555
|
+
const rowsFor = (list) => {
|
|
39556
|
+
const seen = /* @__PURE__ */ new Set();
|
|
39557
|
+
const rows = [];
|
|
39558
|
+
for (const vehicle of vehicles) {
|
|
39559
|
+
const held = listsHeldBy(vehicle);
|
|
39560
|
+
if (list === "blocked" ? !held.block : !held.allow)
|
|
39561
|
+
continue;
|
|
39562
|
+
const key = normalisePlate(vehicle.plateNumber);
|
|
39563
|
+
if (!key || seen.has(key))
|
|
39564
|
+
continue;
|
|
39565
|
+
seen.add(key);
|
|
39566
|
+
rows.push({
|
|
39567
|
+
id: `${list}:${String(vehicle._id)}`,
|
|
39568
|
+
vehicle: String(vehicle._id),
|
|
39569
|
+
list,
|
|
39570
|
+
plateNumber: String(vehicle.plateNumber ?? ""),
|
|
39571
|
+
name: String(vehicle.name ?? ""),
|
|
39572
|
+
block: String(vehicle.blockName ?? ""),
|
|
39573
|
+
level: String(vehicle.levelName ?? ""),
|
|
39574
|
+
unit: String(vehicle.unitName ?? ""),
|
|
39575
|
+
kind: list === "blocked" ? "Blocked" : vehicle.seasonPassType ? "Season pass" : "Whitelist"
|
|
39576
|
+
});
|
|
39577
|
+
}
|
|
39578
|
+
return rows;
|
|
39579
|
+
};
|
|
39580
|
+
return [
|
|
39581
|
+
...lists.block ? rowsFor("blocked") : [],
|
|
39582
|
+
...lists.allow ? rowsFor("allowed") : []
|
|
39583
|
+
];
|
|
39584
|
+
}
|
|
39585
|
+
async function writeVehicleToNewCamera(site, device, vehicleId, list, options = {}) {
|
|
39586
|
+
let siteId;
|
|
39587
|
+
let id;
|
|
39588
|
+
try {
|
|
39589
|
+
siteId = new ObjectId72(String(site));
|
|
39590
|
+
id = new ObjectId72(String(vehicleId));
|
|
39591
|
+
} catch {
|
|
39592
|
+
throw new BadRequestError90("Invalid vehicle or site ID.");
|
|
39593
|
+
}
|
|
39594
|
+
const vehicle = await findOne({
|
|
39595
|
+
_id: id,
|
|
39596
|
+
site: siteId,
|
|
39597
|
+
status: "active" /* ACTIVE */
|
|
39598
|
+
});
|
|
39599
|
+
if (!vehicle)
|
|
39600
|
+
throw new NotFoundError25("Vehicle not found.");
|
|
39601
|
+
const held = listsHeldBy(vehicle);
|
|
39602
|
+
if (list === "blocked" ? !held.block : !held.allow) {
|
|
39603
|
+
throw new BadRequestError90(
|
|
39604
|
+
"This vehicle no longer belongs on that list. Refresh and try again."
|
|
39605
|
+
);
|
|
39606
|
+
}
|
|
39607
|
+
const write = {
|
|
39608
|
+
plateNumber: String(vehicle.plateNumber ?? ""),
|
|
39609
|
+
mode: list === "blocked" ? "TrafficBlackList" /* TRAFFIC_BLACKLIST */ : "TrafficRedList" /* TRAFFIC_REDLIST */,
|
|
39610
|
+
owner: String(vehicle.name ?? ""),
|
|
39611
|
+
...list === "blocked" ? blockWindow(vehicle.end) : cameraWindow(vehicle.start, vehicle.end)
|
|
39612
|
+
};
|
|
39613
|
+
const target = {
|
|
39614
|
+
host: device.host,
|
|
39615
|
+
username: device.username,
|
|
39616
|
+
password: device.password,
|
|
39617
|
+
name: device.name,
|
|
39618
|
+
direction: device.direction,
|
|
39619
|
+
// Whose clock the season pass window is written on.
|
|
39620
|
+
site: siteId
|
|
39621
|
+
};
|
|
39622
|
+
try {
|
|
39623
|
+
if (options.onlyIfMissing)
|
|
39624
|
+
await ensurePlateOnCamera(target, write);
|
|
39625
|
+
else
|
|
39626
|
+
await insertPlateOnCamera(target, write);
|
|
39627
|
+
return { ok: true };
|
|
39628
|
+
} catch (error) {
|
|
39629
|
+
logCameraFailure("new camera plate", target, error);
|
|
39630
|
+
return {
|
|
39631
|
+
ok: false,
|
|
39632
|
+
reason: anprWriteReason(error),
|
|
39633
|
+
kind: classifyDahuaFailure(error?.message ?? error)
|
|
39634
|
+
};
|
|
39635
|
+
}
|
|
39636
|
+
}
|
|
38896
39637
|
return {
|
|
38897
39638
|
add,
|
|
38898
39639
|
deleteVehicle,
|
|
@@ -38902,7 +39643,10 @@ function useVehicleService() {
|
|
|
38902
39643
|
updateVehicleById,
|
|
38903
39644
|
bulkUpsertVehicles,
|
|
38904
39645
|
blocklistVehicles,
|
|
38905
|
-
removeFromBlocklist
|
|
39646
|
+
removeFromBlocklist,
|
|
39647
|
+
fillCameraLists,
|
|
39648
|
+
newCameraPlateRows,
|
|
39649
|
+
writeVehicleToNewCamera
|
|
38906
39650
|
};
|
|
38907
39651
|
}
|
|
38908
39652
|
|
|
@@ -39084,7 +39828,7 @@ import {
|
|
|
39084
39828
|
AppError as AppError17,
|
|
39085
39829
|
BadRequestError as BadRequestError92,
|
|
39086
39830
|
logger as logger68,
|
|
39087
|
-
NotFoundError as
|
|
39831
|
+
NotFoundError as NotFoundError26,
|
|
39088
39832
|
UnauthorizedError as UnauthorizedError12
|
|
39089
39833
|
} from "@7365admin1/node-server-utils";
|
|
39090
39834
|
|
|
@@ -39231,7 +39975,7 @@ function siteModulesHandlers({
|
|
|
39231
39975
|
const actor = await requireSiteWrite2(req, siteId);
|
|
39232
39976
|
const site = await _getSiteById(siteId);
|
|
39233
39977
|
if (!site)
|
|
39234
|
-
throw new
|
|
39978
|
+
throw new NotFoundError26("Site not found.");
|
|
39235
39979
|
if (!ownsSite(actor, site))
|
|
39236
39980
|
throw new UnauthorizedError12("Not authorized.");
|
|
39237
39981
|
const org = site?.orgId ? await _getOrgById(site.orgId.toString()) : null;
|
|
@@ -40426,7 +41170,7 @@ function useBuildingRepo() {
|
|
|
40426
41170
|
import {
|
|
40427
41171
|
BadRequestError as BadRequestError98,
|
|
40428
41172
|
logger as logger75,
|
|
40429
|
-
NotFoundError as
|
|
41173
|
+
NotFoundError as NotFoundError27,
|
|
40430
41174
|
useAtlas as useAtlas59
|
|
40431
41175
|
} from "@7365admin1/node-server-utils";
|
|
40432
41176
|
import { ObjectId as ObjectId77 } from "mongodb";
|
|
@@ -41039,7 +41783,7 @@ function useBuildingService() {
|
|
|
41039
41783
|
session
|
|
41040
41784
|
);
|
|
41041
41785
|
if (!file) {
|
|
41042
|
-
throw new
|
|
41786
|
+
throw new NotFoundError27("File not found.");
|
|
41043
41787
|
}
|
|
41044
41788
|
}
|
|
41045
41789
|
}
|
|
@@ -41082,7 +41826,7 @@ function useBuildingService() {
|
|
|
41082
41826
|
session.startTransaction();
|
|
41083
41827
|
const building = await _getById(id);
|
|
41084
41828
|
if (!building) {
|
|
41085
|
-
throw new
|
|
41829
|
+
throw new NotFoundError27("Building not found.");
|
|
41086
41830
|
}
|
|
41087
41831
|
const dataFiles = data?.buildingFiles || [];
|
|
41088
41832
|
const buildingFiles = building?.buildingFiles || [];
|
|
@@ -42041,7 +42785,7 @@ import {
|
|
|
42041
42785
|
BadRequestError as BadRequestError100,
|
|
42042
42786
|
logger as logger77,
|
|
42043
42787
|
useAtlas as useAtlas60,
|
|
42044
|
-
NotFoundError as
|
|
42788
|
+
NotFoundError as NotFoundError28
|
|
42045
42789
|
} from "@7365admin1/node-server-utils";
|
|
42046
42790
|
function useBuildingUnitService() {
|
|
42047
42791
|
const { add: _add, updateById: _updateById, getById } = useBuildingUnitRepo();
|
|
@@ -42063,7 +42807,7 @@ function useBuildingUnitService() {
|
|
|
42063
42807
|
session
|
|
42064
42808
|
);
|
|
42065
42809
|
if (!file) {
|
|
42066
|
-
throw new
|
|
42810
|
+
throw new NotFoundError28("File not found.");
|
|
42067
42811
|
}
|
|
42068
42812
|
}
|
|
42069
42813
|
}
|
|
@@ -43124,40 +43868,42 @@ function useVehicleController() {
|
|
|
43124
43868
|
}
|
|
43125
43869
|
const { _id, ...rest } = value;
|
|
43126
43870
|
await requireVehicleReach(req, _id);
|
|
43127
|
-
await _blocklistVehicles(_id, rest);
|
|
43128
|
-
res.json({ message: "Successfully blocked vehicle." });
|
|
43129
|
-
return;
|
|
43130
|
-
} catch (error) {
|
|
43131
|
-
logger79.log({ level: "error", message: error.message });
|
|
43132
|
-
next(error);
|
|
43133
|
-
return;
|
|
43134
|
-
}
|
|
43135
|
-
}
|
|
43136
|
-
async function removeFromBlocklist(req, res, next) {
|
|
43137
|
-
try {
|
|
43138
|
-
const schema2 = Joi57.object({
|
|
43139
|
-
_id: Joi57.string().hex().length(24).required(),
|
|
43140
|
-
site: Joi57.string().hex().length(24).required(),
|
|
43141
|
-
peopleId: Joi57.string().hex().length(24).optional().allow(null, ""),
|
|
43142
|
-
remarks: Joi57.string().optional().allow("", null)
|
|
43871
|
+
await _blocklistVehicles(_id, rest);
|
|
43872
|
+
res.json({ message: "Successfully blocked vehicle." });
|
|
43873
|
+
return;
|
|
43874
|
+
} catch (error) {
|
|
43875
|
+
logger79.log({ level: "error", message: error.message });
|
|
43876
|
+
next(error);
|
|
43877
|
+
return;
|
|
43878
|
+
}
|
|
43879
|
+
}
|
|
43880
|
+
async function removeFromBlocklist(req, res, next) {
|
|
43881
|
+
try {
|
|
43882
|
+
const schema2 = Joi57.object({
|
|
43883
|
+
_id: Joi57.string().hex().length(24).required(),
|
|
43884
|
+
site: Joi57.string().hex().length(24).required(),
|
|
43885
|
+
peopleId: Joi57.string().hex().length(24).optional().allow(null, ""),
|
|
43886
|
+
remarks: Joi57.string().optional().allow("", null)
|
|
43887
|
+
});
|
|
43888
|
+
const { error, value } = schema2.validate(
|
|
43889
|
+
{
|
|
43890
|
+
_id: req.params.id,
|
|
43891
|
+
...req.body
|
|
43892
|
+
},
|
|
43893
|
+
{ abortEarly: false }
|
|
43894
|
+
);
|
|
43895
|
+
if (error) {
|
|
43896
|
+
const messages = error.details.map((d) => d.message).join(", ");
|
|
43897
|
+
logger79.log({ level: "error", message: messages });
|
|
43898
|
+
next(new BadRequestError102(messages));
|
|
43899
|
+
return;
|
|
43900
|
+
}
|
|
43901
|
+
const { _id, ...rest } = value;
|
|
43902
|
+
await requireVehicleReach(req, _id);
|
|
43903
|
+
const message = await _removeFromBlocklist(_id, rest);
|
|
43904
|
+
res.json({
|
|
43905
|
+
message: message || "Successfully removed vehicle from blocklist."
|
|
43143
43906
|
});
|
|
43144
|
-
const { error, value } = schema2.validate(
|
|
43145
|
-
{
|
|
43146
|
-
_id: req.params.id,
|
|
43147
|
-
...req.body
|
|
43148
|
-
},
|
|
43149
|
-
{ abortEarly: false }
|
|
43150
|
-
);
|
|
43151
|
-
if (error) {
|
|
43152
|
-
const messages = error.details.map((d) => d.message).join(", ");
|
|
43153
|
-
logger79.log({ level: "error", message: messages });
|
|
43154
|
-
next(new BadRequestError102(messages));
|
|
43155
|
-
return;
|
|
43156
|
-
}
|
|
43157
|
-
const { _id, ...rest } = value;
|
|
43158
|
-
await requireVehicleReach(req, _id);
|
|
43159
|
-
await _removeFromBlocklist(_id, rest);
|
|
43160
|
-
res.json({ message: "Successfully removed vehicle from blocklist." });
|
|
43161
43907
|
return;
|
|
43162
43908
|
} catch (error) {
|
|
43163
43909
|
logger79.log({ level: "error", message: error.message });
|
|
@@ -43187,7 +43933,9 @@ function useVehicleController() {
|
|
|
43187
43933
|
// src/services/site-camera.service.ts
|
|
43188
43934
|
import {
|
|
43189
43935
|
BadRequestError as BadRequestError103,
|
|
43190
|
-
|
|
43936
|
+
NotFoundError as NotFoundError29,
|
|
43937
|
+
useAtlas as useAtlas61,
|
|
43938
|
+
logger as logger80
|
|
43191
43939
|
} from "@7365admin1/node-server-utils";
|
|
43192
43940
|
function useSiteCameraService() {
|
|
43193
43941
|
const { add: _add, getAllCameraWithPassword: _getAllCameraWithPassword } = useSiteCameraRepo();
|
|
@@ -43260,15 +44008,74 @@ function useSiteCameraService() {
|
|
|
43260
44008
|
session.endSession();
|
|
43261
44009
|
}
|
|
43262
44010
|
}
|
|
44011
|
+
async function clearPlateLists(site, camera, lists, action = "type-change") {
|
|
44012
|
+
let target;
|
|
44013
|
+
const id = typeof camera === "string" ? camera : "";
|
|
44014
|
+
if (typeof camera === "string") {
|
|
44015
|
+
const cameras = [];
|
|
44016
|
+
let page = 1;
|
|
44017
|
+
let pages = 1;
|
|
44018
|
+
const limit = 20;
|
|
44019
|
+
do {
|
|
44020
|
+
const siteCameraReq = await _getAllCameraWithPassword({
|
|
44021
|
+
site: String(site ?? ""),
|
|
44022
|
+
type: "anpr",
|
|
44023
|
+
page,
|
|
44024
|
+
limit
|
|
44025
|
+
});
|
|
44026
|
+
pages = siteCameraReq.pages || 1;
|
|
44027
|
+
cameras.push(...siteCameraReq.items);
|
|
44028
|
+
page++;
|
|
44029
|
+
} while (page <= pages);
|
|
44030
|
+
target = cameras.find((candidate) => String(candidate._id) === camera);
|
|
44031
|
+
if (!target)
|
|
44032
|
+
throw new NotFoundError29("Camera not found.");
|
|
44033
|
+
} else {
|
|
44034
|
+
target = camera;
|
|
44035
|
+
}
|
|
44036
|
+
const label = cameraLabel(target);
|
|
44037
|
+
const notDone = action === "add" ? "The camera has NOT been added." : action === "sync" ? "No vehicles have been added to it." : "The camera type has NOT been changed.";
|
|
44038
|
+
const cleared = [];
|
|
44039
|
+
const steps = [
|
|
44040
|
+
...lists.allow ? [{ list: "allowed", mode: "TrafficRedList" /* TRAFFIC_REDLIST */ }] : [],
|
|
44041
|
+
...lists.block ? [{ list: "blocked", mode: "TrafficBlackList" /* TRAFFIC_BLACKLIST */ }] : []
|
|
44042
|
+
];
|
|
44043
|
+
for (const [index, step] of steps.entries()) {
|
|
44044
|
+
if (index > 0)
|
|
44045
|
+
await new Promise((resolve) => setTimeout(resolve, 3e3));
|
|
44046
|
+
const outcome = await dahuaService.clearPlateList({
|
|
44047
|
+
host: target.host,
|
|
44048
|
+
username: target.username,
|
|
44049
|
+
password: target.password,
|
|
44050
|
+
mode: step.mode
|
|
44051
|
+
});
|
|
44052
|
+
if (!outcome?.ok) {
|
|
44053
|
+
logger80.error(
|
|
44054
|
+
`clearPlateLists could not clear ${step.mode} on camera ${id || target.host}: ${outcome?.detail ?? "no outcome"}`
|
|
44055
|
+
);
|
|
44056
|
+
const listName = step.list === "allowed" ? "allowed list" : "blocklist";
|
|
44057
|
+
const reason = outcome?.reason || "the camera could not be reached";
|
|
44058
|
+
throw new BadRequestError103(
|
|
44059
|
+
cleared.length ? `The allowed list on ${label} was cleared, but its blocklist could not be: ${reason}. ${notDone} Until it is sorted out, whitelist and season pass vehicles will not be let in through this camera. Please check the camera and try again.` : `The ${listName} on ${label} could not be cleared: ${reason}. Nothing on the camera has changed. ${notDone} Please check the camera and try again.`
|
|
44060
|
+
);
|
|
44061
|
+
}
|
|
44062
|
+
cleared.push(step.list);
|
|
44063
|
+
}
|
|
44064
|
+
logger80.info(
|
|
44065
|
+
`clearPlateLists cleared ${cleared.join(" and ") || "nothing"} on camera ${id || target.host}`
|
|
44066
|
+
);
|
|
44067
|
+
return cleared;
|
|
44068
|
+
}
|
|
43263
44069
|
return {
|
|
43264
44070
|
add,
|
|
43265
44071
|
listenToCapturedPlateNumber,
|
|
43266
|
-
updateById
|
|
44072
|
+
updateById,
|
|
44073
|
+
clearPlateLists
|
|
43267
44074
|
};
|
|
43268
44075
|
}
|
|
43269
44076
|
|
|
43270
44077
|
// src/controllers/site-camera.controller.ts
|
|
43271
|
-
import { BadRequestError as BadRequestError104, NotFoundError as
|
|
44078
|
+
import { BadRequestError as BadRequestError104, NotFoundError as NotFoundError30 } from "@7365admin1/node-server-utils";
|
|
43272
44079
|
import Joi58 from "joi";
|
|
43273
44080
|
import { ObjectId as ObjectId78 } from "mongodb";
|
|
43274
44081
|
|
|
@@ -43399,8 +44206,29 @@ function summariseBulkCameraPlan(results) {
|
|
|
43399
44206
|
}
|
|
43400
44207
|
|
|
43401
44208
|
// src/controllers/site-camera.controller.ts
|
|
44209
|
+
var CLEARED_ONLY_NOTE = " The camera's plate lists were cleared.";
|
|
44210
|
+
function filledNote(filled, cleared = true) {
|
|
44211
|
+
const parts = [
|
|
44212
|
+
...filled.allowed ? [`${filled.allowed} allowed`] : [],
|
|
44213
|
+
...filled.blocked ? [`${filled.blocked} blocked`] : []
|
|
44214
|
+
];
|
|
44215
|
+
const total = filled.allowed + filled.blocked;
|
|
44216
|
+
if (!total) {
|
|
44217
|
+
return cleared ? " The camera's plate lists were cleared; there were no vehicle plates to add." : " There were no vehicle plates to add to the camera.";
|
|
44218
|
+
}
|
|
44219
|
+
const added = `${parts.join(" and ")} ${total === 1 ? "plate was" : "plates were"} added`;
|
|
44220
|
+
return cleared ? ` The camera's plate lists were cleared and ${added} to it.` : ` ${added} to the camera.`;
|
|
44221
|
+
}
|
|
44222
|
+
function cameraListClearConfirmation(lists) {
|
|
44223
|
+
const what = lists.allow && lists.block ? "its allowed list and its blocklist" : lists.block ? "its blocklist" : "its allowed list";
|
|
44224
|
+
return `This change clears every plate on ${what}, on the camera itself - including plates added directly on the camera. Confirm the change to continue.`;
|
|
44225
|
+
}
|
|
43402
44226
|
function useSiteCameraController() {
|
|
43403
|
-
const {
|
|
44227
|
+
const {
|
|
44228
|
+
add: _add,
|
|
44229
|
+
updateById: _updateById,
|
|
44230
|
+
clearPlateLists: _clearPlateLists
|
|
44231
|
+
} = useSiteCameraService();
|
|
43404
44232
|
const {
|
|
43405
44233
|
add: _addOne,
|
|
43406
44234
|
activeHostsBySite: _activeHostsBySite,
|
|
@@ -43422,8 +44250,8 @@ function useSiteCameraController() {
|
|
|
43422
44250
|
permissions: cameraManagePermissions(params.type)
|
|
43423
44251
|
});
|
|
43424
44252
|
} catch (error) {
|
|
43425
|
-
if (error instanceof
|
|
43426
|
-
throw new
|
|
44253
|
+
if (error instanceof NotFoundError30) {
|
|
44254
|
+
throw new NotFoundError30(params.notFound);
|
|
43427
44255
|
}
|
|
43428
44256
|
throw error;
|
|
43429
44257
|
}
|
|
@@ -43484,6 +44312,12 @@ function useSiteCameraController() {
|
|
|
43484
44312
|
return null;
|
|
43485
44313
|
}
|
|
43486
44314
|
async function add(req, res, next) {
|
|
44315
|
+
const clearCameraLists = req.body?.clearCameraLists === true;
|
|
44316
|
+
const skipPlateSync = req.body?.skipPlateSync === true;
|
|
44317
|
+
if (req.body && typeof req.body === "object") {
|
|
44318
|
+
delete req.body.clearCameraLists;
|
|
44319
|
+
delete req.body.skipPlateSync;
|
|
44320
|
+
}
|
|
43487
44321
|
const data = req.body;
|
|
43488
44322
|
const refusal = applyDerivedHost(data);
|
|
43489
44323
|
if (refusal) {
|
|
@@ -43502,6 +44336,52 @@ function useSiteCameraController() {
|
|
|
43502
44336
|
type: value.type,
|
|
43503
44337
|
notFound: "Site not found."
|
|
43504
44338
|
});
|
|
44339
|
+
const isActiveAnpr = !skipPlateSync && String(value.type ?? "").toLowerCase() === "anpr" && String(value.status ?? "active") === "active";
|
|
44340
|
+
const lists = isActiveAnpr ? listsFilledByDirectionChange("", value.direction) : { allow: false, block: false };
|
|
44341
|
+
const willClear = isActiveAnpr && clearCameraLists;
|
|
44342
|
+
let cleared = false;
|
|
44343
|
+
let filled = null;
|
|
44344
|
+
if (willClear || lists.allow || lists.block) {
|
|
44345
|
+
const taken = await _activeHostsBySite(value.site);
|
|
44346
|
+
if (taken.includes(value.host)) {
|
|
44347
|
+
throw new BadRequestError104("ANPR already exist.");
|
|
44348
|
+
}
|
|
44349
|
+
const device = {
|
|
44350
|
+
host: value.host,
|
|
44351
|
+
username: value.username,
|
|
44352
|
+
password: value.password,
|
|
44353
|
+
name: value.name,
|
|
44354
|
+
direction: value.direction
|
|
44355
|
+
};
|
|
44356
|
+
const everything = { allow: true, block: true };
|
|
44357
|
+
if (willClear) {
|
|
44358
|
+
await _clearPlateLists(value.site, device, everything, "add");
|
|
44359
|
+
cleared = true;
|
|
44360
|
+
}
|
|
44361
|
+
if (lists.allow || lists.block) {
|
|
44362
|
+
try {
|
|
44363
|
+
filled = await useVehicleService().fillCameraLists(
|
|
44364
|
+
value.site,
|
|
44365
|
+
device,
|
|
44366
|
+
lists
|
|
44367
|
+
);
|
|
44368
|
+
} catch (error2) {
|
|
44369
|
+
let undone = null;
|
|
44370
|
+
if (willClear) {
|
|
44371
|
+
try {
|
|
44372
|
+
await _clearPlateLists(value.site, device, everything, "add");
|
|
44373
|
+
undone = true;
|
|
44374
|
+
} catch {
|
|
44375
|
+
undone = false;
|
|
44376
|
+
}
|
|
44377
|
+
}
|
|
44378
|
+
if (error2 && typeof error2 === "object") {
|
|
44379
|
+
error2.message = `${error2.message ?? ""} The camera has NOT been added` + (undone === true ? ` and the plates written to it were cleared again.` : undone === false ? `, and the plates written to it could NOT be cleared again - clear them on the camera by hand.` : `. The plates already written to it stay on the camera.`) + ` Please check the camera and try again.`;
|
|
44380
|
+
}
|
|
44381
|
+
throw error2;
|
|
44382
|
+
}
|
|
44383
|
+
}
|
|
44384
|
+
}
|
|
43505
44385
|
const vehicle = await _add(value);
|
|
43506
44386
|
await recordConsoleAction({
|
|
43507
44387
|
action: "camera.added" /* CAMERA_ADDED */,
|
|
@@ -43511,8 +44391,10 @@ function useSiteCameraController() {
|
|
|
43511
44391
|
after: pickAuditFields("camera.added" /* CAMERA_ADDED */, value)
|
|
43512
44392
|
});
|
|
43513
44393
|
res.status(201).json({
|
|
43514
|
-
message:
|
|
43515
|
-
data: { vehicle }
|
|
44394
|
+
message: `ANPR(s) added successfully.` + (filled ? filledNote(filled, cleared) : cleared ? CLEARED_ONLY_NOTE : ""),
|
|
44395
|
+
data: { vehicle },
|
|
44396
|
+
...filled ? { addedPlates: filled } : {},
|
|
44397
|
+
...cleared ? { clearedLists: ["allowed", "blocked"] } : {}
|
|
43516
44398
|
});
|
|
43517
44399
|
} catch (error2) {
|
|
43518
44400
|
next(error2);
|
|
@@ -43608,8 +44490,8 @@ function useSiteCameraController() {
|
|
|
43608
44490
|
try {
|
|
43609
44491
|
await entitleSite({ siteId: site, userId: callerId5(req) });
|
|
43610
44492
|
} catch (error2) {
|
|
43611
|
-
if (error2 instanceof
|
|
43612
|
-
throw new
|
|
44493
|
+
if (error2 instanceof NotFoundError30) {
|
|
44494
|
+
throw new NotFoundError30("Site not found.");
|
|
43613
44495
|
}
|
|
43614
44496
|
throw error2;
|
|
43615
44497
|
}
|
|
@@ -43621,6 +44503,12 @@ function useSiteCameraController() {
|
|
|
43621
44503
|
}
|
|
43622
44504
|
}
|
|
43623
44505
|
async function updateById(req, res, next) {
|
|
44506
|
+
const confirmClearCameraLists = req.body?.confirmClearCameraLists === true;
|
|
44507
|
+
const skipPlateSync = req.body?.skipPlateSync === true;
|
|
44508
|
+
if (req.body && typeof req.body === "object") {
|
|
44509
|
+
delete req.body.confirmClearCameraLists;
|
|
44510
|
+
delete req.body.skipPlateSync;
|
|
44511
|
+
}
|
|
43624
44512
|
const value = req.body;
|
|
43625
44513
|
const id = req.params.id ?? "";
|
|
43626
44514
|
const refusal = applyDerivedHost(value);
|
|
@@ -43640,7 +44528,7 @@ function useSiteCameraController() {
|
|
|
43640
44528
|
try {
|
|
43641
44529
|
const camera = await loadForWrite(id);
|
|
43642
44530
|
if (!camera)
|
|
43643
|
-
throw new
|
|
44531
|
+
throw new NotFoundError30("Camera not found.");
|
|
43644
44532
|
let staffId = null;
|
|
43645
44533
|
try {
|
|
43646
44534
|
await authorizeWrite({
|
|
@@ -43655,6 +44543,43 @@ function useSiteCameraController() {
|
|
|
43655
44543
|
throw error2;
|
|
43656
44544
|
}
|
|
43657
44545
|
const update = staffId ? staffUpdate(camera, value.status) : value;
|
|
44546
|
+
const lists = String(camera.type ?? "").toLowerCase() === "anpr" ? listsClearedByDirectionChange(camera.direction, update?.direction) : { allow: false, block: false };
|
|
44547
|
+
let clearedLists = null;
|
|
44548
|
+
if (lists.allow || lists.block) {
|
|
44549
|
+
if (!confirmClearCameraLists) {
|
|
44550
|
+
throw new BadRequestError104(cameraListClearConfirmation(lists));
|
|
44551
|
+
}
|
|
44552
|
+
clearedLists = await _clearPlateLists(camera.site, id, lists);
|
|
44553
|
+
}
|
|
44554
|
+
const fill = !skipPlateSync && String(camera.type ?? "").toLowerCase() === "anpr" && String(update?.status ?? camera.status ?? "active") === "active" ? listsFilledByDirectionChange(camera.direction, update?.direction) : { allow: false, block: false };
|
|
44555
|
+
let filled = null;
|
|
44556
|
+
if (fill.allow || fill.block) {
|
|
44557
|
+
await _clearPlateLists(camera.site, id, fill);
|
|
44558
|
+
try {
|
|
44559
|
+
filled = await useVehicleService().fillCameraLists(
|
|
44560
|
+
camera.site,
|
|
44561
|
+
{
|
|
44562
|
+
id,
|
|
44563
|
+
host: update?.host,
|
|
44564
|
+
username: update?.username,
|
|
44565
|
+
password: update?.password
|
|
44566
|
+
},
|
|
44567
|
+
fill
|
|
44568
|
+
);
|
|
44569
|
+
} catch (error2) {
|
|
44570
|
+
let undone = false;
|
|
44571
|
+
try {
|
|
44572
|
+
await _clearPlateLists(camera.site, id, fill);
|
|
44573
|
+
undone = true;
|
|
44574
|
+
} catch {
|
|
44575
|
+
undone = false;
|
|
44576
|
+
}
|
|
44577
|
+
if (error2 && typeof error2 === "object") {
|
|
44578
|
+
error2.message = `${error2.message ?? ""} The camera type has NOT been changed` + (undone ? ` and the plates written to it were cleared again.` : `, and the plates written to it could NOT be cleared again - clear them on the camera by hand.`) + ` Please check the camera and try again.`;
|
|
44579
|
+
}
|
|
44580
|
+
throw error2;
|
|
44581
|
+
}
|
|
44582
|
+
}
|
|
43658
44583
|
const message = await _updateById(id, update);
|
|
43659
44584
|
if (staffId) {
|
|
43660
44585
|
await recordConsoleAction({
|
|
@@ -43676,8 +44601,11 @@ function useSiteCameraController() {
|
|
|
43676
44601
|
after: pickAuditFields("camera.updated" /* CAMERA_UPDATED */, update)
|
|
43677
44602
|
});
|
|
43678
44603
|
}
|
|
44604
|
+
const clearedNote = clearedLists === null ? "" : clearedLists.length === 2 ? " The camera's allowed list and blocklist were cleared." : clearedLists[0] === "blocked" ? " The camera's blocklist was cleared." : " The camera's allowed list was cleared.";
|
|
43679
44605
|
res.json({
|
|
43680
|
-
message
|
|
44606
|
+
message: `${message}${clearedNote}${filled ? filledNote(filled) : ""}`,
|
|
44607
|
+
...clearedLists === null ? {} : { clearedLists },
|
|
44608
|
+
...filled ? { addedPlates: filled } : {}
|
|
43681
44609
|
});
|
|
43682
44610
|
} catch (error2) {
|
|
43683
44611
|
next(error2);
|
|
@@ -43696,7 +44624,7 @@ function useSiteCameraController() {
|
|
|
43696
44624
|
try {
|
|
43697
44625
|
const camera = await loadForWrite(id);
|
|
43698
44626
|
if (!camera)
|
|
43699
|
-
throw new
|
|
44627
|
+
throw new NotFoundError30("Camera not found.");
|
|
43700
44628
|
await authorizeWrite({
|
|
43701
44629
|
req,
|
|
43702
44630
|
site: camera.site,
|
|
@@ -43718,12 +44646,216 @@ function useSiteCameraController() {
|
|
|
43718
44646
|
next(error2);
|
|
43719
44647
|
}
|
|
43720
44648
|
}
|
|
44649
|
+
const schemaNewCameraDevice = {
|
|
44650
|
+
host: Joi58.string().trim().required(),
|
|
44651
|
+
username: Joi58.string().required(),
|
|
44652
|
+
password: Joi58.string().required(),
|
|
44653
|
+
name: Joi58.string().optional().allow("", null),
|
|
44654
|
+
direction: Joi58.string().required().valid("entry", "exit", "both", "visitors", "residents")
|
|
44655
|
+
};
|
|
44656
|
+
async function authorizeNewCamera(req, siteId, host) {
|
|
44657
|
+
await authorizeWrite({
|
|
44658
|
+
req,
|
|
44659
|
+
site: siteId,
|
|
44660
|
+
type: "anpr",
|
|
44661
|
+
notFound: "Site not found."
|
|
44662
|
+
});
|
|
44663
|
+
const taken = await _activeHostsBySite(siteId);
|
|
44664
|
+
if (taken.includes(host)) {
|
|
44665
|
+
throw new BadRequestError104("ANPR already exist.");
|
|
44666
|
+
}
|
|
44667
|
+
}
|
|
44668
|
+
async function newCameraPlates(req, res, next) {
|
|
44669
|
+
const { error, value } = Joi58.object({
|
|
44670
|
+
siteId: Joi58.string().hex().length(24).required(),
|
|
44671
|
+
host: Joi58.string().trim().required(),
|
|
44672
|
+
direction: schemaNewCameraDevice.direction
|
|
44673
|
+
}).validate({
|
|
44674
|
+
siteId: req.params.siteId,
|
|
44675
|
+
host: req.query?.host,
|
|
44676
|
+
direction: req.query?.direction
|
|
44677
|
+
});
|
|
44678
|
+
if (error) {
|
|
44679
|
+
next(new BadRequestError104(error.message));
|
|
44680
|
+
return;
|
|
44681
|
+
}
|
|
44682
|
+
try {
|
|
44683
|
+
await authorizeNewCamera(req, value.siteId, value.host);
|
|
44684
|
+
const items = await useVehicleService().newCameraPlateRows(
|
|
44685
|
+
value.siteId,
|
|
44686
|
+
value.direction
|
|
44687
|
+
);
|
|
44688
|
+
res.json({ items });
|
|
44689
|
+
} catch (error2) {
|
|
44690
|
+
next(error2);
|
|
44691
|
+
}
|
|
44692
|
+
}
|
|
44693
|
+
async function clearNewCamera(req, res, next) {
|
|
44694
|
+
const { error, value } = Joi58.object({
|
|
44695
|
+
siteId: Joi58.string().hex().length(24).required(),
|
|
44696
|
+
...schemaNewCameraDevice
|
|
44697
|
+
}).validate({ siteId: req.params.siteId, ...req.body ?? {} });
|
|
44698
|
+
if (error) {
|
|
44699
|
+
next(new BadRequestError104(error.message));
|
|
44700
|
+
return;
|
|
44701
|
+
}
|
|
44702
|
+
try {
|
|
44703
|
+
await authorizeNewCamera(req, value.siteId, value.host);
|
|
44704
|
+
const { siteId, ...device } = value;
|
|
44705
|
+
const cleared = await _clearPlateLists(
|
|
44706
|
+
siteId,
|
|
44707
|
+
device,
|
|
44708
|
+
{ allow: true, block: true },
|
|
44709
|
+
"add"
|
|
44710
|
+
);
|
|
44711
|
+
res.json({
|
|
44712
|
+
message: "The camera's allowed list and blocklist were cleared.",
|
|
44713
|
+
clearedLists: cleared
|
|
44714
|
+
});
|
|
44715
|
+
} catch (error2) {
|
|
44716
|
+
next(error2);
|
|
44717
|
+
}
|
|
44718
|
+
}
|
|
44719
|
+
async function addNewCameraPlate(req, res, next) {
|
|
44720
|
+
const { error, value } = Joi58.object({
|
|
44721
|
+
siteId: Joi58.string().hex().length(24).required(),
|
|
44722
|
+
...schemaNewCameraDevice,
|
|
44723
|
+
vehicle: Joi58.string().hex().length(24).required(),
|
|
44724
|
+
list: Joi58.string().valid("allowed", "blocked").required(),
|
|
44725
|
+
onlyIfMissing: Joi58.boolean().optional().default(false)
|
|
44726
|
+
}).validate({ siteId: req.params.siteId, ...req.body ?? {} });
|
|
44727
|
+
if (error) {
|
|
44728
|
+
next(new BadRequestError104(error.message));
|
|
44729
|
+
return;
|
|
44730
|
+
}
|
|
44731
|
+
try {
|
|
44732
|
+
await authorizeNewCamera(req, value.siteId, value.host);
|
|
44733
|
+
const { siteId, vehicle, list, onlyIfMissing, ...device } = value;
|
|
44734
|
+
const outcome = await useVehicleService().writeVehicleToNewCamera(
|
|
44735
|
+
siteId,
|
|
44736
|
+
device,
|
|
44737
|
+
vehicle,
|
|
44738
|
+
list,
|
|
44739
|
+
{ onlyIfMissing }
|
|
44740
|
+
);
|
|
44741
|
+
res.json(outcome);
|
|
44742
|
+
} catch (error2) {
|
|
44743
|
+
next(error2);
|
|
44744
|
+
}
|
|
44745
|
+
}
|
|
44746
|
+
async function loadForSync(req, id) {
|
|
44747
|
+
if (!ObjectId78.isValid(id))
|
|
44748
|
+
throw new NotFoundError30("Camera not found.");
|
|
44749
|
+
const camera = await _findOne(
|
|
44750
|
+
{ _id: new ObjectId78(id) },
|
|
44751
|
+
{
|
|
44752
|
+
site: 1,
|
|
44753
|
+
type: 1,
|
|
44754
|
+
host: 1,
|
|
44755
|
+
username: 1,
|
|
44756
|
+
password: 1,
|
|
44757
|
+
name: 1,
|
|
44758
|
+
direction: 1,
|
|
44759
|
+
status: 1
|
|
44760
|
+
}
|
|
44761
|
+
);
|
|
44762
|
+
if (!camera)
|
|
44763
|
+
throw new NotFoundError30("Camera not found.");
|
|
44764
|
+
await authorizeWrite({
|
|
44765
|
+
req,
|
|
44766
|
+
site: camera.site,
|
|
44767
|
+
type: camera.type,
|
|
44768
|
+
notFound: "Camera not found."
|
|
44769
|
+
});
|
|
44770
|
+
if (String(camera.type ?? "").toLowerCase() !== "anpr") {
|
|
44771
|
+
throw new BadRequestError104("Only ANPR cameras hold vehicle plates.");
|
|
44772
|
+
}
|
|
44773
|
+
if (String(camera.status ?? "active") !== "active") {
|
|
44774
|
+
throw new BadRequestError104(
|
|
44775
|
+
"This camera is inactive. Switch it on before adding vehicles to it."
|
|
44776
|
+
);
|
|
44777
|
+
}
|
|
44778
|
+
const lists = listsFilledByDirectionChange("", camera.direction);
|
|
44779
|
+
if (!lists.allow && !lists.block) {
|
|
44780
|
+
throw new BadRequestError104("Exit cameras receive no vehicle plates.");
|
|
44781
|
+
}
|
|
44782
|
+
return {
|
|
44783
|
+
site: String(camera.site ?? ""),
|
|
44784
|
+
device: {
|
|
44785
|
+
host: String(camera.host ?? ""),
|
|
44786
|
+
username: String(camera.username ?? ""),
|
|
44787
|
+
password: String(camera.password ?? ""),
|
|
44788
|
+
name: camera.name ?? "",
|
|
44789
|
+
direction: String(camera.direction ?? "")
|
|
44790
|
+
}
|
|
44791
|
+
};
|
|
44792
|
+
}
|
|
44793
|
+
async function cameraPlates(req, res, next) {
|
|
44794
|
+
try {
|
|
44795
|
+
const { site, device } = await loadForSync(req, String(req.params.id));
|
|
44796
|
+
const items = await useVehicleService().newCameraPlateRows(
|
|
44797
|
+
site,
|
|
44798
|
+
device.direction
|
|
44799
|
+
);
|
|
44800
|
+
res.json({ items });
|
|
44801
|
+
} catch (error) {
|
|
44802
|
+
next(error);
|
|
44803
|
+
}
|
|
44804
|
+
}
|
|
44805
|
+
async function clearCamera(req, res, next) {
|
|
44806
|
+
try {
|
|
44807
|
+
const { site, device } = await loadForSync(req, String(req.params.id));
|
|
44808
|
+
const cleared = await _clearPlateLists(
|
|
44809
|
+
site,
|
|
44810
|
+
device,
|
|
44811
|
+
{ allow: true, block: true },
|
|
44812
|
+
"sync"
|
|
44813
|
+
);
|
|
44814
|
+
res.json({
|
|
44815
|
+
message: "The camera's allowed list and blocklist were cleared.",
|
|
44816
|
+
clearedLists: cleared
|
|
44817
|
+
});
|
|
44818
|
+
} catch (error) {
|
|
44819
|
+
next(error);
|
|
44820
|
+
}
|
|
44821
|
+
}
|
|
44822
|
+
async function addCameraPlate(req, res, next) {
|
|
44823
|
+
const { error, value } = Joi58.object({
|
|
44824
|
+
vehicle: Joi58.string().hex().length(24).required(),
|
|
44825
|
+
list: Joi58.string().valid("allowed", "blocked").required(),
|
|
44826
|
+
onlyIfMissing: Joi58.boolean().optional().default(true)
|
|
44827
|
+
}).validate(req.body ?? {});
|
|
44828
|
+
if (error) {
|
|
44829
|
+
next(new BadRequestError104(error.message));
|
|
44830
|
+
return;
|
|
44831
|
+
}
|
|
44832
|
+
try {
|
|
44833
|
+
const { site, device } = await loadForSync(req, String(req.params.id));
|
|
44834
|
+
const outcome = await useVehicleService().writeVehicleToNewCamera(
|
|
44835
|
+
site,
|
|
44836
|
+
device,
|
|
44837
|
+
value.vehicle,
|
|
44838
|
+
value.list,
|
|
44839
|
+
// Absent means look first: a saved camera may already hold the plate.
|
|
44840
|
+
{ onlyIfMissing: value.onlyIfMissing !== false }
|
|
44841
|
+
);
|
|
44842
|
+
res.json(outcome);
|
|
44843
|
+
} catch (error2) {
|
|
44844
|
+
next(error2);
|
|
44845
|
+
}
|
|
44846
|
+
}
|
|
43721
44847
|
return {
|
|
43722
44848
|
add,
|
|
43723
44849
|
addBulk,
|
|
43724
44850
|
getAll,
|
|
43725
44851
|
updateById,
|
|
43726
|
-
deleteById
|
|
44852
|
+
deleteById,
|
|
44853
|
+
newCameraPlates,
|
|
44854
|
+
clearNewCamera,
|
|
44855
|
+
addNewCameraPlate,
|
|
44856
|
+
cameraPlates,
|
|
44857
|
+
clearCamera,
|
|
44858
|
+
addCameraPlate
|
|
43727
44859
|
};
|
|
43728
44860
|
}
|
|
43729
44861
|
|
|
@@ -43888,7 +45020,7 @@ function useCameraViewController() {
|
|
|
43888
45020
|
import {
|
|
43889
45021
|
BadRequestError as BadRequestError106,
|
|
43890
45022
|
useAtlas as useAtlas62,
|
|
43891
|
-
NotFoundError as
|
|
45023
|
+
NotFoundError as NotFoundError31
|
|
43892
45024
|
} from "@7365admin1/node-server-utils";
|
|
43893
45025
|
|
|
43894
45026
|
// src/utils/site-name.util.ts
|
|
@@ -43995,7 +45127,7 @@ function useCustomerSiteService() {
|
|
|
43995
45127
|
const siteIdStr = siteId.toString();
|
|
43996
45128
|
const orgData = await _getByOrgId(value.siteOrg);
|
|
43997
45129
|
if (!orgData) {
|
|
43998
|
-
throw new
|
|
45130
|
+
throw new NotFoundError31("Organization not found.");
|
|
43999
45131
|
}
|
|
44000
45132
|
if (!orgData.defaultSite) {
|
|
44001
45133
|
const updateOrgField = await _updateFieldById(
|
|
@@ -44029,7 +45161,7 @@ function useCustomerSiteService() {
|
|
|
44029
45161
|
try {
|
|
44030
45162
|
const verification = await getVerificationById(invite);
|
|
44031
45163
|
if (!verification) {
|
|
44032
|
-
throw new
|
|
45164
|
+
throw new NotFoundError31("Invite not found.");
|
|
44033
45165
|
}
|
|
44034
45166
|
const siteId = verification.metadata?.siteId?.toString() ?? "";
|
|
44035
45167
|
if (!siteId) {
|
|
@@ -44041,17 +45173,17 @@ function useCustomerSiteService() {
|
|
|
44041
45173
|
}
|
|
44042
45174
|
const org = await _getByOrgId(orgId);
|
|
44043
45175
|
if (!org) {
|
|
44044
|
-
throw new
|
|
45176
|
+
throw new NotFoundError31("Organization not found.");
|
|
44045
45177
|
}
|
|
44046
45178
|
const site = await getSiteById(siteId);
|
|
44047
45179
|
if (!site) {
|
|
44048
|
-
throw new
|
|
45180
|
+
throw new NotFoundError31("Site not found.");
|
|
44049
45181
|
}
|
|
44050
45182
|
const serviceProvider = await getOrgByEmail(
|
|
44051
45183
|
String(verification.email ?? "").trim().toLowerCase()
|
|
44052
45184
|
);
|
|
44053
45185
|
if (!serviceProvider) {
|
|
44054
|
-
throw new
|
|
45186
|
+
throw new NotFoundError31("Organization not found.");
|
|
44055
45187
|
}
|
|
44056
45188
|
const providerOrgId = serviceProvider._id?.toString() ?? "";
|
|
44057
45189
|
const alreadyEngaged = await _countActiveByOrgAndSite(
|
|
@@ -44066,7 +45198,7 @@ function useCustomerSiteService() {
|
|
|
44066
45198
|
return "This site is already on your list.";
|
|
44067
45199
|
}
|
|
44068
45200
|
if (verification.status === "awaiting-approval") {
|
|
44069
|
-
throw new
|
|
45201
|
+
throw new NotFoundError31("Invite not found.");
|
|
44070
45202
|
}
|
|
44071
45203
|
if (verification.status && verification.status !== "pending") {
|
|
44072
45204
|
throw new BadRequestError106(
|
|
@@ -44112,7 +45244,7 @@ function useCustomerSiteService() {
|
|
|
44112
45244
|
try {
|
|
44113
45245
|
const customerSite = await _getById(customerSiteId);
|
|
44114
45246
|
if (!customerSite) {
|
|
44115
|
-
throw new
|
|
45247
|
+
throw new NotFoundError31("Customer site not found.");
|
|
44116
45248
|
}
|
|
44117
45249
|
const siteId = customerSite.site;
|
|
44118
45250
|
await updateSiteById(
|
|
@@ -44442,7 +45574,7 @@ import {
|
|
|
44442
45574
|
useCache as useCache34,
|
|
44443
45575
|
logger as logger83,
|
|
44444
45576
|
makeCacheKey as makeCacheKey29,
|
|
44445
|
-
NotFoundError as
|
|
45577
|
+
NotFoundError as NotFoundError32
|
|
44446
45578
|
} from "@7365admin1/node-server-utils";
|
|
44447
45579
|
function useAttendanceSettingsRepository() {
|
|
44448
45580
|
const db2 = useAtlas63.getDb();
|
|
@@ -44544,7 +45676,7 @@ function useAttendanceSettingsRepository() {
|
|
|
44544
45676
|
{ session }
|
|
44545
45677
|
);
|
|
44546
45678
|
if (res.matchedCount === 0) {
|
|
44547
|
-
throw new
|
|
45679
|
+
throw new NotFoundError32("Attendance settings not found for this site.");
|
|
44548
45680
|
}
|
|
44549
45681
|
delNamespace().then(() => {
|
|
44550
45682
|
logger83.info(`Cache cleared for namespace: ${namespace_collection3}`);
|
|
@@ -44729,7 +45861,7 @@ import {
|
|
|
44729
45861
|
useCache as useCache35,
|
|
44730
45862
|
logger as logger87,
|
|
44731
45863
|
makeCacheKey as makeCacheKey30,
|
|
44732
|
-
NotFoundError as
|
|
45864
|
+
NotFoundError as NotFoundError33
|
|
44733
45865
|
} from "@7365admin1/node-server-utils";
|
|
44734
45866
|
function useAttendanceRepository() {
|
|
44735
45867
|
const db2 = useAtlas65.getDb();
|
|
@@ -45074,7 +46206,7 @@ function useAttendanceRepository() {
|
|
|
45074
46206
|
}
|
|
45075
46207
|
]).toArray();
|
|
45076
46208
|
if (!data || data.length === 0) {
|
|
45077
|
-
throw new
|
|
46209
|
+
throw new NotFoundError33("Attendance not found.");
|
|
45078
46210
|
}
|
|
45079
46211
|
setCache(cacheKey, data[0], 15 * 60).then(() => {
|
|
45080
46212
|
logger87.info(`Cache set for key: ${cacheKey}`);
|
|
@@ -45108,7 +46240,7 @@ function useAttendanceRepository() {
|
|
|
45108
46240
|
try {
|
|
45109
46241
|
const data = await collection.aggregate([{ $match: query2 }]).toArray();
|
|
45110
46242
|
if (!data || data.length === 0) {
|
|
45111
|
-
throw new
|
|
46243
|
+
throw new NotFoundError33("Attendance not found.");
|
|
45112
46244
|
}
|
|
45113
46245
|
setCache(cacheKey, data[0], 15 * 60).then(() => {
|
|
45114
46246
|
logger87.info(`Cache set for key: ${cacheKey}`);
|
|
@@ -45539,7 +46671,7 @@ import { ObjectId as ObjectId90 } from "mongodb";
|
|
|
45539
46671
|
import {
|
|
45540
46672
|
BadRequestError as BadRequestError118,
|
|
45541
46673
|
logger as logger90,
|
|
45542
|
-
NotFoundError as
|
|
46674
|
+
NotFoundError as NotFoundError36,
|
|
45543
46675
|
useAtlas as useAtlas68,
|
|
45544
46676
|
hashPassword as hashPassword3,
|
|
45545
46677
|
useMailer as useMailer3,
|
|
@@ -45552,14 +46684,14 @@ import { ObjectId as ObjectId85 } from "mongodb";
|
|
|
45552
46684
|
import {
|
|
45553
46685
|
BadRequestError as BadRequestError117,
|
|
45554
46686
|
InternalServerError as InternalServerError42,
|
|
45555
|
-
NotFoundError as
|
|
46687
|
+
NotFoundError as NotFoundError35
|
|
45556
46688
|
} from "@7365admin1/node-server-utils";
|
|
45557
46689
|
|
|
45558
46690
|
// src/repositories/platform-terms.repo.ts
|
|
45559
46691
|
import {
|
|
45560
46692
|
BadRequestError as BadRequestError116,
|
|
45561
46693
|
InternalServerError as InternalServerError41,
|
|
45562
|
-
NotFoundError as
|
|
46694
|
+
NotFoundError as NotFoundError34,
|
|
45563
46695
|
useAtlas as useAtlas67
|
|
45564
46696
|
} from "@7365admin1/node-server-utils";
|
|
45565
46697
|
import { ObjectId as ObjectId84 } from "mongodb";
|
|
@@ -45658,7 +46790,7 @@ function usePlatformTermsRepo() {
|
|
|
45658
46790
|
try {
|
|
45659
46791
|
_id = new ObjectId84(id);
|
|
45660
46792
|
} catch {
|
|
45661
|
-
throw new
|
|
46793
|
+
throw new NotFoundError34("Terms version not found.");
|
|
45662
46794
|
}
|
|
45663
46795
|
let data;
|
|
45664
46796
|
try {
|
|
@@ -45667,7 +46799,7 @@ function usePlatformTermsRepo() {
|
|
|
45667
46799
|
throw new InternalServerError41("Failed to retrieve terms version.");
|
|
45668
46800
|
}
|
|
45669
46801
|
if (!data) {
|
|
45670
|
-
throw new
|
|
46802
|
+
throw new NotFoundError34("Terms version not found.");
|
|
45671
46803
|
}
|
|
45672
46804
|
return data;
|
|
45673
46805
|
}
|
|
@@ -45680,7 +46812,7 @@ function usePlatformTermsRepo() {
|
|
|
45680
46812
|
throw new InternalServerError41("Failed to retrieve latest terms.");
|
|
45681
46813
|
}
|
|
45682
46814
|
if (!data) {
|
|
45683
|
-
throw new
|
|
46815
|
+
throw new NotFoundError34("No terms version found.");
|
|
45684
46816
|
}
|
|
45685
46817
|
return data;
|
|
45686
46818
|
}
|
|
@@ -45777,14 +46909,14 @@ function usePlatformTermsService() {
|
|
|
45777
46909
|
const latest = await _getLatest();
|
|
45778
46910
|
const record = await _getAcceptedTerms(userId);
|
|
45779
46911
|
if (!record) {
|
|
45780
|
-
throw new
|
|
46912
|
+
throw new NotFoundError35("User not found.");
|
|
45781
46913
|
}
|
|
45782
46914
|
return buildStatus(latest, record);
|
|
45783
46915
|
}
|
|
45784
46916
|
async function acceptByUserId(userId, termsId, session) {
|
|
45785
46917
|
const latest = await assertLatestTerms(termsId);
|
|
45786
46918
|
if (!await _getAcceptedTerms(userId, session)) {
|
|
45787
|
-
throw new
|
|
46919
|
+
throw new NotFoundError35("User not found.");
|
|
45788
46920
|
}
|
|
45789
46921
|
await _setAcceptedTerms(userId, latest._id, session);
|
|
45790
46922
|
return { acceptedTerms: latest._id, version: latest.version };
|
|
@@ -45792,7 +46924,7 @@ function usePlatformTermsService() {
|
|
|
45792
46924
|
async function resolveTermsPrincipal(personId) {
|
|
45793
46925
|
const person = await _getPersonById(personId);
|
|
45794
46926
|
if (!person || person.status === "deleted") {
|
|
45795
|
-
throw new
|
|
46927
|
+
throw new NotFoundError35("Person not found.");
|
|
45796
46928
|
}
|
|
45797
46929
|
return person.user ? { userId: person.user } : { userId: null };
|
|
45798
46930
|
}
|
|
@@ -45804,7 +46936,7 @@ function usePlatformTermsService() {
|
|
|
45804
46936
|
const latest = await _getLatest();
|
|
45805
46937
|
const record = await _getPersonAcceptedTerms(personId);
|
|
45806
46938
|
if (!record) {
|
|
45807
|
-
throw new
|
|
46939
|
+
throw new NotFoundError35("Person not found.");
|
|
45808
46940
|
}
|
|
45809
46941
|
return buildStatus(latest, record);
|
|
45810
46942
|
}
|
|
@@ -45827,7 +46959,7 @@ function usePlatformTermsService() {
|
|
|
45827
46959
|
if (version) {
|
|
45828
46960
|
const found = await _getByVersion(version);
|
|
45829
46961
|
if (!found) {
|
|
45830
|
-
throw new
|
|
46962
|
+
throw new NotFoundError35("Terms version not found.");
|
|
45831
46963
|
}
|
|
45832
46964
|
terms = found._id;
|
|
45833
46965
|
}
|
|
@@ -45920,7 +47052,7 @@ function usePersonService() {
|
|
|
45920
47052
|
session
|
|
45921
47053
|
);
|
|
45922
47054
|
if (!file) {
|
|
45923
|
-
throw new
|
|
47055
|
+
throw new NotFoundError36("File not found.");
|
|
45924
47056
|
}
|
|
45925
47057
|
}
|
|
45926
47058
|
}
|
|
@@ -46274,7 +47406,7 @@ function usePersonService() {
|
|
|
46274
47406
|
session.startTransaction();
|
|
46275
47407
|
const person = await _getByUserId(id.toString());
|
|
46276
47408
|
if (!person)
|
|
46277
|
-
throw new
|
|
47409
|
+
throw new NotFoundError36("Person not found.");
|
|
46278
47410
|
await _updateById(person._id, { status: "deleted" }, session);
|
|
46279
47411
|
if (person.user) {
|
|
46280
47412
|
await _updateUserFieldById(
|
|
@@ -47377,7 +48509,7 @@ import axios from "axios";
|
|
|
47377
48509
|
import {
|
|
47378
48510
|
BadRequestError as BadRequestError121,
|
|
47379
48511
|
InternalServerError as InternalServerError44,
|
|
47380
|
-
NotFoundError as
|
|
48512
|
+
NotFoundError as NotFoundError37,
|
|
47381
48513
|
UnauthorizedError as UnauthorizedError14,
|
|
47382
48514
|
logger as logger93
|
|
47383
48515
|
} from "@7365admin1/node-server-utils";
|
|
@@ -50932,14 +52064,14 @@ function useHidAmicoService() {
|
|
|
50932
52064
|
try {
|
|
50933
52065
|
reader = await repo.getById(readerId, options);
|
|
50934
52066
|
} catch {
|
|
50935
|
-
throw new
|
|
52067
|
+
throw new NotFoundError37(HID_READER_NOT_FOUND);
|
|
50936
52068
|
}
|
|
50937
52069
|
try {
|
|
50938
52070
|
await authorizeSiteForCaller(String(reader.site || ""), userId);
|
|
50939
52071
|
} catch (error) {
|
|
50940
52072
|
if (error instanceof UnauthorizedError14)
|
|
50941
52073
|
throw error;
|
|
50942
|
-
throw new
|
|
52074
|
+
throw new NotFoundError37(HID_READER_NOT_FOUND);
|
|
50943
52075
|
}
|
|
50944
52076
|
return reader;
|
|
50945
52077
|
}
|
|
@@ -50948,7 +52080,7 @@ function useHidAmicoService() {
|
|
|
50948
52080
|
try {
|
|
50949
52081
|
reader = await repo.getById(readerId);
|
|
50950
52082
|
} catch {
|
|
50951
|
-
throw new
|
|
52083
|
+
throw new NotFoundError37(HID_READER_NOT_FOUND);
|
|
50952
52084
|
}
|
|
50953
52085
|
try {
|
|
50954
52086
|
await useCameraViewService().entitleSite({
|
|
@@ -50956,7 +52088,7 @@ function useHidAmicoService() {
|
|
|
50956
52088
|
userId
|
|
50957
52089
|
});
|
|
50958
52090
|
} catch {
|
|
50959
|
-
throw new
|
|
52091
|
+
throw new NotFoundError37(HID_READER_NOT_FOUND);
|
|
50960
52092
|
}
|
|
50961
52093
|
return reader;
|
|
50962
52094
|
}
|
|
@@ -54926,7 +56058,7 @@ function useVisitorTransactionService() {
|
|
|
54926
56058
|
import Joi69 from "joi";
|
|
54927
56059
|
import moment5 from "moment-timezone";
|
|
54928
56060
|
import { ObjectId as ObjectId93 } from "mongodb";
|
|
54929
|
-
import { BadRequestError as BadRequestError124, NotFoundError as
|
|
56061
|
+
import { BadRequestError as BadRequestError124, NotFoundError as NotFoundError39, InternalServerError as InternalServerError46, UnauthorizedError as UnauthorizedError15, logger as logger97 } from "@7365admin1/node-server-utils";
|
|
54930
56062
|
|
|
54931
56063
|
// src/models/visitor-invite.model.ts
|
|
54932
56064
|
import Joi67 from "joi";
|
|
@@ -59181,7 +60313,7 @@ function useVisitorTransactionController() {
|
|
|
59181
60313
|
const status = onboardingSettings?.enabled ? "approved" /* APPROVED */ : "pending" /* PENDING */;
|
|
59182
60314
|
const site = await _getSiteById(siteId);
|
|
59183
60315
|
if (!site) {
|
|
59184
|
-
next(new
|
|
60316
|
+
next(new NotFoundError39("Site not found."));
|
|
59185
60317
|
return;
|
|
59186
60318
|
}
|
|
59187
60319
|
const { error, value } = schemaSelfServiceVisitor.validate(req.body, {
|
|
@@ -59280,7 +60412,7 @@ function useVisitorTransactionController() {
|
|
|
59280
60412
|
}
|
|
59281
60413
|
const data = await _getSelfServiceVisitorPreview(id);
|
|
59282
60414
|
if (!data) {
|
|
59283
|
-
next(new
|
|
60415
|
+
next(new NotFoundError39("Registration not found."));
|
|
59284
60416
|
return;
|
|
59285
60417
|
}
|
|
59286
60418
|
res.status(200).json({
|
|
@@ -59337,7 +60469,7 @@ function useVisitorTransactionController() {
|
|
|
59337
60469
|
});
|
|
59338
60470
|
if (!decision.ok) {
|
|
59339
60471
|
if (decision.httpStatus === 404) {
|
|
59340
|
-
next(new
|
|
60472
|
+
next(new NotFoundError39(decision.reason));
|
|
59341
60473
|
return;
|
|
59342
60474
|
}
|
|
59343
60475
|
if (decision.httpStatus === 429) {
|
|
@@ -60277,7 +61409,22 @@ function useGuestManagementController() {
|
|
|
60277
61409
|
// src/controllers/person.controller.ts
|
|
60278
61410
|
import Joi72 from "joi";
|
|
60279
61411
|
import { BadRequestError as BadRequestError127, logger as logger100 } from "@7365admin1/node-server-utils";
|
|
60280
|
-
import { NotFoundError as
|
|
61412
|
+
import { NotFoundError as NotFoundError40, UnauthorizedError as UnauthorizedError16 } from "@7365admin1/node-server-utils";
|
|
61413
|
+
|
|
61414
|
+
// src/utils/people-create-gate.util.ts
|
|
61415
|
+
var PEOPLE_CREATE_REACH_MODE_DEFAULT = "log";
|
|
61416
|
+
function peopleCreateReachMode(env2 = process.env) {
|
|
61417
|
+
return gateMode(
|
|
61418
|
+
env2.PEOPLE_CREATE_REACH_MODE,
|
|
61419
|
+
["log", "enforce", "off"],
|
|
61420
|
+
PEOPLE_CREATE_REACH_MODE_DEFAULT
|
|
61421
|
+
);
|
|
61422
|
+
}
|
|
61423
|
+
function peopleCreateVerdict(params) {
|
|
61424
|
+
if (params.mode === "off" || params.reachable)
|
|
61425
|
+
return { refuse: false, record: false };
|
|
61426
|
+
return { refuse: params.mode === "enforce", record: true };
|
|
61427
|
+
}
|
|
60281
61428
|
|
|
60282
61429
|
// src/utils/self-signup-files.util.ts
|
|
60283
61430
|
function dropUnsentFiles(body) {
|
|
@@ -60321,7 +61468,7 @@ function usePersonController() {
|
|
|
60321
61468
|
return actor;
|
|
60322
61469
|
const person = await _getById(_id);
|
|
60323
61470
|
if (!person)
|
|
60324
|
-
throw new
|
|
61471
|
+
throw new NotFoundError40("Person not found.");
|
|
60325
61472
|
const site = person.site?.toString() ?? "";
|
|
60326
61473
|
if (site) {
|
|
60327
61474
|
await useCameraViewService().entitleSite({ siteId: site, userId: actor.id });
|
|
@@ -60387,6 +61534,41 @@ function usePersonController() {
|
|
|
60387
61534
|
next(new BadRequestError127(messages));
|
|
60388
61535
|
return;
|
|
60389
61536
|
}
|
|
61537
|
+
try {
|
|
61538
|
+
const mode = peopleCreateReachMode();
|
|
61539
|
+
if (mode !== "off") {
|
|
61540
|
+
const actor = await resolveInviteActor(callerId(req));
|
|
61541
|
+
let reachable = true;
|
|
61542
|
+
if (!staffMayBypass(actor)) {
|
|
61543
|
+
const site = payload?.site ? String(payload.site) : "";
|
|
61544
|
+
const org = payload?.org ? String(payload.org) : "";
|
|
61545
|
+
if (site) {
|
|
61546
|
+
reachable = await useCameraViewService().entitleSite({ siteId: site, userId: actor.id }).then(
|
|
61547
|
+
() => true,
|
|
61548
|
+
() => false
|
|
61549
|
+
);
|
|
61550
|
+
} else if (org) {
|
|
61551
|
+
reachable = actor.orgIds.includes(org);
|
|
61552
|
+
}
|
|
61553
|
+
}
|
|
61554
|
+
const verdict = peopleCreateVerdict({ mode, reachable });
|
|
61555
|
+
if (verdict.record) {
|
|
61556
|
+
logger100.log({
|
|
61557
|
+
level: "info",
|
|
61558
|
+
message: `[people-create] ${verdict.refuse ? "refused" : "would-refuse"} caller=${actor.id} site=${payload?.site ?? ""} org=${payload?.org ?? ""} mode=${mode}`
|
|
61559
|
+
});
|
|
61560
|
+
}
|
|
61561
|
+
if (verdict.refuse) {
|
|
61562
|
+
next(new UnauthorizedError16("Not authorized."));
|
|
61563
|
+
return;
|
|
61564
|
+
}
|
|
61565
|
+
}
|
|
61566
|
+
} catch (gateError) {
|
|
61567
|
+
logger100.log({
|
|
61568
|
+
level: "warn",
|
|
61569
|
+
message: `[people-create] reach check failed open: ${gateError?.message}`
|
|
61570
|
+
});
|
|
61571
|
+
}
|
|
60390
61572
|
try {
|
|
60391
61573
|
const data = await _add(payload);
|
|
60392
61574
|
res.status(201).json({ message: data });
|
|
@@ -61714,10 +62896,10 @@ import {
|
|
|
61714
62896
|
import { ObjectId as ObjectId101 } from "mongodb";
|
|
61715
62897
|
|
|
61716
62898
|
// src/utils/update-result.util.ts
|
|
61717
|
-
import { NotFoundError as
|
|
62899
|
+
import { NotFoundError as NotFoundError41 } from "@7365admin1/node-server-utils";
|
|
61718
62900
|
function assertUpdateMatched(result, message) {
|
|
61719
62901
|
if (result?.matchedCount === 0) {
|
|
61720
|
-
throw new
|
|
62902
|
+
throw new NotFoundError41(message);
|
|
61721
62903
|
}
|
|
61722
62904
|
return result;
|
|
61723
62905
|
}
|
|
@@ -62879,7 +64061,7 @@ function usePatrolLogRepo() {
|
|
|
62879
64061
|
import {
|
|
62880
64062
|
BadRequestError as BadRequestError140,
|
|
62881
64063
|
logger as logger112,
|
|
62882
|
-
NotFoundError as
|
|
64064
|
+
NotFoundError as NotFoundError42,
|
|
62883
64065
|
useAtlas as useAtlas78
|
|
62884
64066
|
} from "@7365admin1/node-server-utils";
|
|
62885
64067
|
function patrolProblem(status) {
|
|
@@ -62919,7 +64101,7 @@ function usePatrolLogService() {
|
|
|
62919
64101
|
session
|
|
62920
64102
|
);
|
|
62921
64103
|
if (file.matchedCount === 0) {
|
|
62922
|
-
throw new
|
|
64104
|
+
throw new NotFoundError42(`File not found: ${fileId}`);
|
|
62923
64105
|
}
|
|
62924
64106
|
}
|
|
62925
64107
|
}
|
|
@@ -63284,7 +64466,7 @@ import {
|
|
|
63284
64466
|
BadRequestError as BadRequestError143,
|
|
63285
64467
|
InternalServerError as InternalServerError52,
|
|
63286
64468
|
logger as logger114,
|
|
63287
|
-
NotFoundError as
|
|
64469
|
+
NotFoundError as NotFoundError43,
|
|
63288
64470
|
paginate as paginate36,
|
|
63289
64471
|
useAtlas as useAtlas79
|
|
63290
64472
|
} from "@7365admin1/node-server-utils";
|
|
@@ -63474,7 +64656,7 @@ function usePatrolEmailRepo() {
|
|
|
63474
64656
|
throw new InternalServerError52("Failed to retrieve the patrol email.");
|
|
63475
64657
|
}
|
|
63476
64658
|
if (!data) {
|
|
63477
|
-
throw new
|
|
64659
|
+
throw new NotFoundError43("Patrol email not found.");
|
|
63478
64660
|
}
|
|
63479
64661
|
const [decorated] = await withResendState([data], session);
|
|
63480
64662
|
return decorated;
|
|
@@ -63489,7 +64671,7 @@ function usePatrolEmailRepo() {
|
|
|
63489
64671
|
{ session }
|
|
63490
64672
|
);
|
|
63491
64673
|
if (result.matchedCount === 0) {
|
|
63492
|
-
throw new
|
|
64674
|
+
throw new NotFoundError43("Patrol email not found.");
|
|
63493
64675
|
}
|
|
63494
64676
|
return result;
|
|
63495
64677
|
} catch (error) {
|
|
@@ -63518,7 +64700,7 @@ import {
|
|
|
63518
64700
|
compileHandlebar as compileHandlebar5,
|
|
63519
64701
|
getDirectory as getDirectory5,
|
|
63520
64702
|
logger as logger116,
|
|
63521
|
-
NotFoundError as
|
|
64703
|
+
NotFoundError as NotFoundError44,
|
|
63522
64704
|
useMailer as useMailer5
|
|
63523
64705
|
} from "@7365admin1/node-server-utils";
|
|
63524
64706
|
|
|
@@ -63713,7 +64895,7 @@ function usePatrolEmailService() {
|
|
|
63713
64895
|
for (const id of logIds) {
|
|
63714
64896
|
const log = await _getPatrolLogById(id);
|
|
63715
64897
|
if (!log) {
|
|
63716
|
-
throw new
|
|
64898
|
+
throw new NotFoundError44("Patrol log not found.");
|
|
63717
64899
|
}
|
|
63718
64900
|
if (String(log.site) !== String(site)) {
|
|
63719
64901
|
throw new BadRequestError146(
|
|
@@ -63763,7 +64945,7 @@ function usePatrolEmailService() {
|
|
|
63763
64945
|
async function claimUploadedPdf(fileId) {
|
|
63764
64946
|
const file = await _getFileById(fileId);
|
|
63765
64947
|
if (!file) {
|
|
63766
|
-
throw new
|
|
64948
|
+
throw new NotFoundError44("Uploaded PDF not found.");
|
|
63767
64949
|
}
|
|
63768
64950
|
if (!isClaimableUpload(file)) {
|
|
63769
64951
|
throw new BadRequestError146(
|
|
@@ -63772,7 +64954,7 @@ function usePatrolEmailService() {
|
|
|
63772
64954
|
}
|
|
63773
64955
|
const promoted = await _updateFileStatusById(fileId, { status: "active" });
|
|
63774
64956
|
if (promoted.matchedCount === 0) {
|
|
63775
|
-
throw new
|
|
64957
|
+
throw new NotFoundError44("Uploaded PDF not found.");
|
|
63776
64958
|
}
|
|
63777
64959
|
return {
|
|
63778
64960
|
file: fileId,
|
|
@@ -63781,7 +64963,7 @@ function usePatrolEmailService() {
|
|
|
63781
64963
|
};
|
|
63782
64964
|
}
|
|
63783
64965
|
function sanitizeError(error) {
|
|
63784
|
-
if (error instanceof BadRequestError146 || error instanceof
|
|
64966
|
+
if (error instanceof BadRequestError146 || error instanceof NotFoundError44) {
|
|
63785
64967
|
return error.message;
|
|
63786
64968
|
}
|
|
63787
64969
|
return "Failed to send the patrol log email.";
|
|
@@ -64382,7 +65564,7 @@ import {
|
|
|
64382
65564
|
InternalServerError as InternalServerError53,
|
|
64383
65565
|
logger as logger118,
|
|
64384
65566
|
makeCacheKey as makeCacheKey38,
|
|
64385
|
-
NotFoundError as
|
|
65567
|
+
NotFoundError as NotFoundError45,
|
|
64386
65568
|
paginate as paginate37,
|
|
64387
65569
|
useAtlas as useAtlas80,
|
|
64388
65570
|
useCache as useCache43
|
|
@@ -64513,7 +65695,7 @@ function useSiteFacilityRepo() {
|
|
|
64513
65695
|
try {
|
|
64514
65696
|
const data = await collection.findOne({ _id }, { session });
|
|
64515
65697
|
if (!data) {
|
|
64516
|
-
throw new
|
|
65698
|
+
throw new NotFoundError45("Site facility not found.");
|
|
64517
65699
|
}
|
|
64518
65700
|
setCache(cacheKey, data, 15 * 60).then(() => {
|
|
64519
65701
|
logger118.info(`Cache set for key: ${cacheKey}`);
|
|
@@ -64638,7 +65820,7 @@ function useSiteFacilityRepo() {
|
|
|
64638
65820
|
try {
|
|
64639
65821
|
const data = await collection.findOne(query2, { session });
|
|
64640
65822
|
if (!data) {
|
|
64641
|
-
throw new
|
|
65823
|
+
throw new NotFoundError45("Site facility not found.");
|
|
64642
65824
|
}
|
|
64643
65825
|
setCache(cacheKey, data, 15 * 60).then(() => {
|
|
64644
65826
|
logger118.info(`Cache set for key: ${cacheKey}`);
|
|
@@ -64779,7 +65961,7 @@ function useSiteFacilityService() {
|
|
|
64779
65961
|
// src/controllers/site-facility.controller.ts
|
|
64780
65962
|
import {
|
|
64781
65963
|
BadRequestError as BadRequestError149,
|
|
64782
|
-
NotFoundError as
|
|
65964
|
+
NotFoundError as NotFoundError46,
|
|
64783
65965
|
UnauthorizedError as UnauthorizedError17,
|
|
64784
65966
|
logger as logger119
|
|
64785
65967
|
} from "@7365admin1/node-server-utils";
|
|
@@ -64799,7 +65981,7 @@ function useSiteFacilityController() {
|
|
|
64799
65981
|
const facility = await _getSiteFacilityById(id);
|
|
64800
65982
|
const { canReach } = await siteReachOf(req);
|
|
64801
65983
|
if (!await canReach(facility)) {
|
|
64802
|
-
throw new
|
|
65984
|
+
throw new NotFoundError46("Site facility not found.");
|
|
64803
65985
|
}
|
|
64804
65986
|
return facility;
|
|
64805
65987
|
}
|
|
@@ -65064,7 +66246,7 @@ import {
|
|
|
65064
66246
|
InternalServerError as InternalServerError54,
|
|
65065
66247
|
logger as logger120,
|
|
65066
66248
|
makeCacheKey as makeCacheKey39,
|
|
65067
|
-
NotFoundError as
|
|
66249
|
+
NotFoundError as NotFoundError47,
|
|
65068
66250
|
paginate as paginate38,
|
|
65069
66251
|
useAtlas as useAtlas82,
|
|
65070
66252
|
useCache as useCache44
|
|
@@ -65179,7 +66361,7 @@ function useServiceProviderBillingRepo() {
|
|
|
65179
66361
|
try {
|
|
65180
66362
|
const data = await collection.findOne({ _id }, { session });
|
|
65181
66363
|
if (!data) {
|
|
65182
|
-
throw new
|
|
66364
|
+
throw new NotFoundError47("Service provider billing not found.");
|
|
65183
66365
|
}
|
|
65184
66366
|
setCache(cacheKey, data, 15 * 60).then(() => {
|
|
65185
66367
|
logger120.info(`Cache set for key: ${cacheKey}`);
|
|
@@ -65734,7 +66916,7 @@ import {
|
|
|
65734
66916
|
InternalServerError as InternalServerError55,
|
|
65735
66917
|
logger as logger122,
|
|
65736
66918
|
makeCacheKey as makeCacheKey40,
|
|
65737
|
-
NotFoundError as
|
|
66919
|
+
NotFoundError as NotFoundError48,
|
|
65738
66920
|
paginate as paginate39,
|
|
65739
66921
|
toObjectId as toObjectId16,
|
|
65740
66922
|
useAtlas as useAtlas84,
|
|
@@ -65850,7 +67032,7 @@ function useSiteFacilityBookingRepo() {
|
|
|
65850
67032
|
try {
|
|
65851
67033
|
const data = await collection.findOne({ _id }, { session });
|
|
65852
67034
|
if (!data) {
|
|
65853
|
-
throw new
|
|
67035
|
+
throw new NotFoundError48("Site facility booking not found.");
|
|
65854
67036
|
}
|
|
65855
67037
|
setCache(cacheKey, data, 15 * 60).then(() => {
|
|
65856
67038
|
logger122.info(`Cache set for key: ${cacheKey}`);
|
|
@@ -65993,7 +67175,7 @@ function useSiteFacilityBookingService() {
|
|
|
65993
67175
|
// src/controllers/site-facility-booking.controller.ts
|
|
65994
67176
|
import {
|
|
65995
67177
|
BadRequestError as BadRequestError153,
|
|
65996
|
-
NotFoundError as
|
|
67178
|
+
NotFoundError as NotFoundError49,
|
|
65997
67179
|
logger as logger123
|
|
65998
67180
|
} from "@7365admin1/node-server-utils";
|
|
65999
67181
|
import Joi88 from "joi";
|
|
@@ -66011,7 +67193,7 @@ function useSiteFacilityBookingController() {
|
|
|
66011
67193
|
const booking = await _getSiteFacilityBookingById(id);
|
|
66012
67194
|
const { canReach } = await siteReachOf(req);
|
|
66013
67195
|
if (!await canReach(booking)) {
|
|
66014
|
-
throw new
|
|
67196
|
+
throw new NotFoundError49("Site facility booking not found.");
|
|
66015
67197
|
}
|
|
66016
67198
|
return booking;
|
|
66017
67199
|
}
|
|
@@ -66303,7 +67485,7 @@ import {
|
|
|
66303
67485
|
InternalServerError as InternalServerError56,
|
|
66304
67486
|
logger as logger124,
|
|
66305
67487
|
makeCacheKey as makeCacheKey41,
|
|
66306
|
-
NotFoundError as
|
|
67488
|
+
NotFoundError as NotFoundError50,
|
|
66307
67489
|
paginate as paginate40,
|
|
66308
67490
|
useAtlas as useAtlas86,
|
|
66309
67491
|
useCache as useCache46
|
|
@@ -66477,7 +67659,7 @@ function useDocumentManagementRepo() {
|
|
|
66477
67659
|
}
|
|
66478
67660
|
]).toArray();
|
|
66479
67661
|
if (!data || !data.length) {
|
|
66480
|
-
throw new
|
|
67662
|
+
throw new NotFoundError50("Document not found.");
|
|
66481
67663
|
}
|
|
66482
67664
|
setCache(cacheKey, data[0], 15 * 60).then(() => {
|
|
66483
67665
|
logger124.info(`Cache set for key: ${cacheKey}`);
|
|
@@ -66550,7 +67732,7 @@ function useDocumentManagementRepo() {
|
|
|
66550
67732
|
{ $set: updateValue }
|
|
66551
67733
|
);
|
|
66552
67734
|
if (res.matchedCount === 0) {
|
|
66553
|
-
throw new
|
|
67735
|
+
throw new NotFoundError50("Folder not found.");
|
|
66554
67736
|
}
|
|
66555
67737
|
delNamespace().then(() => {
|
|
66556
67738
|
logger124.info(`Cache cleared for namespace: ${namespace_collection3}`);
|
|
@@ -66640,7 +67822,7 @@ function useDocumentManagementRepo() {
|
|
|
66640
67822
|
session ? { session } : void 0
|
|
66641
67823
|
).toArray();
|
|
66642
67824
|
if (!folders || folders.length === 0) {
|
|
66643
|
-
throw new
|
|
67825
|
+
throw new NotFoundError50("Folder not found.");
|
|
66644
67826
|
}
|
|
66645
67827
|
const ids2 = folders[0].ids;
|
|
66646
67828
|
const res = await collection.updateMany(
|
|
@@ -66969,7 +68151,7 @@ function useDocumentManagementService() {
|
|
|
66969
68151
|
import {
|
|
66970
68152
|
BadRequestError as BadRequestError156,
|
|
66971
68153
|
logger as logger126,
|
|
66972
|
-
NotFoundError as
|
|
68154
|
+
NotFoundError as NotFoundError51
|
|
66973
68155
|
} from "@7365admin1/node-server-utils";
|
|
66974
68156
|
import Joi90 from "joi";
|
|
66975
68157
|
function useDocumentManagementController() {
|
|
@@ -66995,7 +68177,7 @@ function useDocumentManagementController() {
|
|
|
66995
68177
|
return;
|
|
66996
68178
|
const org = doc?.org?.toString() ?? "";
|
|
66997
68179
|
if (!org || !actor.orgIds.includes(org)) {
|
|
66998
|
-
throw new
|
|
68180
|
+
throw new NotFoundError51("Document not found.");
|
|
66999
68181
|
}
|
|
67000
68182
|
}
|
|
67001
68183
|
async function add(req, res, next) {
|
|
@@ -67403,7 +68585,7 @@ import {
|
|
|
67403
68585
|
InternalServerError as InternalServerError57,
|
|
67404
68586
|
logger as logger127,
|
|
67405
68587
|
makeCacheKey as makeCacheKey42,
|
|
67406
|
-
NotFoundError as
|
|
68588
|
+
NotFoundError as NotFoundError52,
|
|
67407
68589
|
paginate as paginate41,
|
|
67408
68590
|
useAtlas as useAtlas88,
|
|
67409
68591
|
useCache as useCache47
|
|
@@ -67539,7 +68721,7 @@ function useBulletinBoardRepo() {
|
|
|
67539
68721
|
try {
|
|
67540
68722
|
const data = await collection.findOne({ _id }, { session });
|
|
67541
68723
|
if (!data) {
|
|
67542
|
-
throw new
|
|
68724
|
+
throw new NotFoundError52("Bulletin board not found.");
|
|
67543
68725
|
}
|
|
67544
68726
|
setCache(cacheKey, data, 15 * 60).then(() => {
|
|
67545
68727
|
logger127.info(`Cache set for key: ${cacheKey}`);
|
|
@@ -67638,7 +68820,7 @@ function useBulletinBoardRepo() {
|
|
|
67638
68820
|
{ session }
|
|
67639
68821
|
);
|
|
67640
68822
|
if (res.matchedCount === 0) {
|
|
67641
|
-
throw new
|
|
68823
|
+
throw new NotFoundError52("No bulletin boards found to update.");
|
|
67642
68824
|
}
|
|
67643
68825
|
delNamespace().then(() => {
|
|
67644
68826
|
logger127.info(
|
|
@@ -67673,7 +68855,7 @@ function useBulletinBoardRepo() {
|
|
|
67673
68855
|
{ session }
|
|
67674
68856
|
);
|
|
67675
68857
|
if (res.matchedCount === 0) {
|
|
67676
|
-
throw new
|
|
68858
|
+
throw new NotFoundError52(
|
|
67677
68859
|
"No upcoming bulletin boards found to activate."
|
|
67678
68860
|
);
|
|
67679
68861
|
}
|
|
@@ -67705,7 +68887,7 @@ function useBulletinBoardRepo() {
|
|
|
67705
68887
|
}
|
|
67706
68888
|
|
|
67707
68889
|
// src/services/bulletin-board.service.ts
|
|
67708
|
-
import { useAtlas as useAtlas89, NotFoundError as
|
|
68890
|
+
import { useAtlas as useAtlas89, NotFoundError as NotFoundError53 } from "@7365admin1/node-server-utils";
|
|
67709
68891
|
import { ObjectId as ObjectId117 } from "mongodb";
|
|
67710
68892
|
|
|
67711
68893
|
// src/utils/bulletin-service-apps.util.ts
|
|
@@ -67752,7 +68934,7 @@ function useBulletinBoardService() {
|
|
|
67752
68934
|
session
|
|
67753
68935
|
);
|
|
67754
68936
|
if (!file) {
|
|
67755
|
-
throw new
|
|
68937
|
+
throw new NotFoundError53("File not found.");
|
|
67756
68938
|
}
|
|
67757
68939
|
}
|
|
67758
68940
|
}
|
|
@@ -67901,7 +69083,7 @@ function useBulletinBoardService() {
|
|
|
67901
69083
|
// src/controllers/bulletin-board.controller.ts
|
|
67902
69084
|
import {
|
|
67903
69085
|
BadRequestError as BadRequestError158,
|
|
67904
|
-
NotFoundError as
|
|
69086
|
+
NotFoundError as NotFoundError54,
|
|
67905
69087
|
UnauthorizedError as UnauthorizedError18,
|
|
67906
69088
|
logger as logger128
|
|
67907
69089
|
} from "@7365admin1/node-server-utils";
|
|
@@ -67918,7 +69100,7 @@ function useBulletinBoardController() {
|
|
|
67918
69100
|
const bulletin = await _getBulletinBoardById(id);
|
|
67919
69101
|
const { canReach } = await siteReachOf(req);
|
|
67920
69102
|
if (!await canReach(scopeOf(bulletin))) {
|
|
67921
|
-
throw new
|
|
69103
|
+
throw new NotFoundError54("Bulletin board not found.");
|
|
67922
69104
|
}
|
|
67923
69105
|
return bulletin;
|
|
67924
69106
|
}
|
|
@@ -69498,7 +70680,7 @@ import {
|
|
|
69498
70680
|
InternalServerError as InternalServerError60,
|
|
69499
70681
|
logger as logger137,
|
|
69500
70682
|
makeCacheKey as makeCacheKey45,
|
|
69501
|
-
NotFoundError as
|
|
70683
|
+
NotFoundError as NotFoundError57,
|
|
69502
70684
|
paginate as paginate44,
|
|
69503
70685
|
useAtlas as useAtlas94,
|
|
69504
70686
|
useCache as useCache50
|
|
@@ -69672,7 +70854,7 @@ function useEventManagementRepo() {
|
|
|
69672
70854
|
try {
|
|
69673
70855
|
const data = await collection.findOne({ _id }, { session });
|
|
69674
70856
|
if (!data) {
|
|
69675
|
-
throw new
|
|
70857
|
+
throw new NotFoundError57("Event not found.");
|
|
69676
70858
|
}
|
|
69677
70859
|
setCache(cacheKey, data, 15 * 60).then(() => {
|
|
69678
70860
|
logger137.info(`Cache set for key: ${cacheKey}`);
|
|
@@ -69769,7 +70951,7 @@ function useEventManagementRepo() {
|
|
|
69769
70951
|
{ session }
|
|
69770
70952
|
);
|
|
69771
70953
|
if (res.matchedCount === 0) {
|
|
69772
|
-
throw new
|
|
70954
|
+
throw new NotFoundError57("No bulletin boards found to update.");
|
|
69773
70955
|
}
|
|
69774
70956
|
delNamespace().then(() => {
|
|
69775
70957
|
logger137.info(
|
|
@@ -69881,7 +71063,7 @@ function useEventManagementService() {
|
|
|
69881
71063
|
// src/controllers/event-management.controller.ts
|
|
69882
71064
|
import {
|
|
69883
71065
|
BadRequestError as BadRequestError168,
|
|
69884
|
-
NotFoundError as
|
|
71066
|
+
NotFoundError as NotFoundError58,
|
|
69885
71067
|
logger as logger138
|
|
69886
71068
|
} from "@7365admin1/node-server-utils";
|
|
69887
71069
|
import Joi98 from "joi";
|
|
@@ -69896,7 +71078,7 @@ function useEventManagementController() {
|
|
|
69896
71078
|
const event = await _getEventManagementById(id);
|
|
69897
71079
|
const { canReach } = await siteReachOf(req);
|
|
69898
71080
|
if (!await canReach(event)) {
|
|
69899
|
-
throw new
|
|
71081
|
+
throw new NotFoundError58("Event not found.");
|
|
69900
71082
|
}
|
|
69901
71083
|
return event;
|
|
69902
71084
|
}
|
|
@@ -72799,7 +73981,7 @@ import {
|
|
|
72799
73981
|
InternalServerError as InternalServerError62,
|
|
72800
73982
|
logger as logger143,
|
|
72801
73983
|
makeCacheKey as makeCacheKey47,
|
|
72802
|
-
NotFoundError as
|
|
73984
|
+
NotFoundError as NotFoundError60,
|
|
72803
73985
|
paginate as paginate46,
|
|
72804
73986
|
useAtlas as useAtlas99,
|
|
72805
73987
|
useCache as useCache52
|
|
@@ -73002,7 +74184,7 @@ function useNfcPatrolTagRepo() {
|
|
|
73002
74184
|
}
|
|
73003
74185
|
const tag = await collection.findOne({ _id }, { session });
|
|
73004
74186
|
if (!tag) {
|
|
73005
|
-
throw new
|
|
74187
|
+
throw new NotFoundError60("NFC Patrol Tag not found.");
|
|
73006
74188
|
}
|
|
73007
74189
|
return tag;
|
|
73008
74190
|
}
|
|
@@ -73419,7 +74601,7 @@ import {
|
|
|
73419
74601
|
InternalServerError as InternalServerError63,
|
|
73420
74602
|
logger as logger146,
|
|
73421
74603
|
makeCacheKey as makeCacheKey48,
|
|
73422
|
-
NotFoundError as
|
|
74604
|
+
NotFoundError as NotFoundError61,
|
|
73423
74605
|
paginate as paginate47,
|
|
73424
74606
|
toObjectId as toObjectId17,
|
|
73425
74607
|
useAtlas as useAtlas101,
|
|
@@ -73563,7 +74745,7 @@ function useOccurrenceBookRepo() {
|
|
|
73563
74745
|
try {
|
|
73564
74746
|
const data = await collection.findOne({ _id }, { session });
|
|
73565
74747
|
if (!data) {
|
|
73566
|
-
throw new
|
|
74748
|
+
throw new NotFoundError61("Occurrence book not found.");
|
|
73567
74749
|
}
|
|
73568
74750
|
setCache(cacheKey, data, 15 * 60).then(() => {
|
|
73569
74751
|
logger146.info(`Cache set for key: ${cacheKey}`);
|
|
@@ -74079,7 +75261,7 @@ import {
|
|
|
74079
75261
|
InternalServerError as InternalServerError64,
|
|
74080
75262
|
logger as logger148,
|
|
74081
75263
|
makeCacheKey as makeCacheKey49,
|
|
74082
|
-
NotFoundError as
|
|
75264
|
+
NotFoundError as NotFoundError62,
|
|
74083
75265
|
paginate as paginate48,
|
|
74084
75266
|
useAtlas as useAtlas103,
|
|
74085
75267
|
useCache as useCache54
|
|
@@ -74220,7 +75402,7 @@ function useBulletinVideoRepo() {
|
|
|
74220
75402
|
{ session }
|
|
74221
75403
|
);
|
|
74222
75404
|
if (!data) {
|
|
74223
|
-
throw new
|
|
75405
|
+
throw new NotFoundError62("Bulletin video not found.");
|
|
74224
75406
|
}
|
|
74225
75407
|
setCache(cacheKey, data, 15 * 60).then(() => {
|
|
74226
75408
|
logger148.info(`Cache set for key: ${cacheKey}`);
|
|
@@ -74312,7 +75494,7 @@ function useBulletinVideoRepo() {
|
|
|
74312
75494
|
}
|
|
74313
75495
|
|
|
74314
75496
|
// src/services/bulletin-video.service.ts
|
|
74315
|
-
import { useAtlas as useAtlas104, NotFoundError as
|
|
75497
|
+
import { useAtlas as useAtlas104, NotFoundError as NotFoundError63 } from "@7365admin1/node-server-utils";
|
|
74316
75498
|
function useBulletinVideoService() {
|
|
74317
75499
|
const {
|
|
74318
75500
|
add: _add,
|
|
@@ -74333,7 +75515,7 @@ function useBulletinVideoService() {
|
|
|
74333
75515
|
session
|
|
74334
75516
|
);
|
|
74335
75517
|
if (!file) {
|
|
74336
|
-
throw new
|
|
75518
|
+
throw new NotFoundError63("File not found.");
|
|
74337
75519
|
}
|
|
74338
75520
|
}
|
|
74339
75521
|
await _add(value, session);
|
|
@@ -74389,7 +75571,7 @@ function useBulletinVideoService() {
|
|
|
74389
75571
|
// src/controllers/bulletin-video.controller.ts
|
|
74390
75572
|
import {
|
|
74391
75573
|
BadRequestError as BadRequestError181,
|
|
74392
|
-
NotFoundError as
|
|
75574
|
+
NotFoundError as NotFoundError64,
|
|
74393
75575
|
logger as logger149
|
|
74394
75576
|
} from "@7365admin1/node-server-utils";
|
|
74395
75577
|
import Joi108 from "joi";
|
|
@@ -74404,7 +75586,7 @@ function useBulletinVideoController() {
|
|
|
74404
75586
|
const video = await _getBulletinVideoById(id);
|
|
74405
75587
|
const { canReach } = await siteReachOf(req);
|
|
74406
75588
|
if (!await canReach(video)) {
|
|
74407
|
-
throw new
|
|
75589
|
+
throw new NotFoundError64("Bulletin video not found.");
|
|
74408
75590
|
}
|
|
74409
75591
|
return video;
|
|
74410
75592
|
}
|
|
@@ -75698,7 +76880,7 @@ import {
|
|
|
75698
76880
|
InternalServerError as InternalServerError66,
|
|
75699
76881
|
logger as logger155,
|
|
75700
76882
|
makeCacheKey as makeCacheKey51,
|
|
75701
|
-
NotFoundError as
|
|
76883
|
+
NotFoundError as NotFoundError66,
|
|
75702
76884
|
paginate as paginate50,
|
|
75703
76885
|
useAtlas as useAtlas107,
|
|
75704
76886
|
useCache as useCache56
|
|
@@ -75866,7 +77048,7 @@ function useEntryPassSettingsRepo() {
|
|
|
75866
77048
|
}
|
|
75867
77049
|
]).toArray();
|
|
75868
77050
|
if (!data || !data.length) {
|
|
75869
|
-
throw new
|
|
77051
|
+
throw new NotFoundError66("Entry Pass Settings not found.");
|
|
75870
77052
|
}
|
|
75871
77053
|
setCache(cacheKey, data[0], 15 * 60).then(() => {
|
|
75872
77054
|
logger155.info(`Cache set for key: ${cacheKey}`);
|
|
@@ -76002,7 +77184,7 @@ function useEntryPassSettingsRepo() {
|
|
|
76002
77184
|
}
|
|
76003
77185
|
]).toArray();
|
|
76004
77186
|
if (!data || !data.length) {
|
|
76005
|
-
throw new
|
|
77187
|
+
throw new NotFoundError66("Entry Pass Settings not found.");
|
|
76006
77188
|
}
|
|
76007
77189
|
setCache(cacheKey, data[0], 15 * 60).then(() => {
|
|
76008
77190
|
logger155.info(`Cache set for key: ${cacheKey}`);
|
|
@@ -76811,7 +77993,7 @@ function useNfcPatrolRouteService() {
|
|
|
76811
77993
|
import {
|
|
76812
77994
|
BadRequestError as BadRequestError193,
|
|
76813
77995
|
logger as logger162,
|
|
76814
|
-
NotFoundError as
|
|
77996
|
+
NotFoundError as NotFoundError68
|
|
76815
77997
|
} from "@7365admin1/node-server-utils";
|
|
76816
77998
|
import Joi115 from "joi";
|
|
76817
77999
|
function useNfcPatrolRouteController() {
|
|
@@ -76903,7 +78085,7 @@ function useNfcPatrolRouteController() {
|
|
|
76903
78085
|
const nfcPatrolRoute = await _getById(value?.id, value?.isStart);
|
|
76904
78086
|
await requireSiteReachIfSignedIn(req, nfcPatrolRoute?.site);
|
|
76905
78087
|
if (!nfcPatrolRoute) {
|
|
76906
|
-
throw new
|
|
78088
|
+
throw new NotFoundError68("NFC Patrol Route not found.");
|
|
76907
78089
|
}
|
|
76908
78090
|
res.json({
|
|
76909
78091
|
message: "Successfully retrieved nfc patrol route.",
|
|
@@ -77301,7 +78483,7 @@ function MIncidentReport(value) {
|
|
|
77301
78483
|
import {
|
|
77302
78484
|
useAtlas as useAtlas112,
|
|
77303
78485
|
BadRequestError as BadRequestError195,
|
|
77304
|
-
NotFoundError as
|
|
78486
|
+
NotFoundError as NotFoundError70
|
|
77305
78487
|
} from "@7365admin1/node-server-utils";
|
|
77306
78488
|
|
|
77307
78489
|
// src/repositories/incident-report.repo.ts
|
|
@@ -77310,7 +78492,7 @@ import {
|
|
|
77310
78492
|
InternalServerError as InternalServerError69,
|
|
77311
78493
|
logger as logger163,
|
|
77312
78494
|
makeCacheKey as makeCacheKey54,
|
|
77313
|
-
NotFoundError as
|
|
78495
|
+
NotFoundError as NotFoundError69,
|
|
77314
78496
|
paginate as paginate52,
|
|
77315
78497
|
useAtlas as useAtlas111,
|
|
77316
78498
|
useCache as useCache59
|
|
@@ -77568,7 +78750,7 @@ function useIncidentReportRepo() {
|
|
|
77568
78750
|
{ session }
|
|
77569
78751
|
);
|
|
77570
78752
|
if (!data) {
|
|
77571
|
-
throw new
|
|
78753
|
+
throw new NotFoundError69("Incident report not found.");
|
|
77572
78754
|
}
|
|
77573
78755
|
setCache(cacheKey, data, 15 * 60).then(() => {
|
|
77574
78756
|
logger163.info(`Cache set for key: ${cacheKey}`);
|
|
@@ -77851,7 +79033,7 @@ function useIncidentReportService() {
|
|
|
77851
79033
|
session
|
|
77852
79034
|
);
|
|
77853
79035
|
if (!file) {
|
|
77854
|
-
throw new
|
|
79036
|
+
throw new NotFoundError70("File not found.");
|
|
77855
79037
|
}
|
|
77856
79038
|
}
|
|
77857
79039
|
}
|
|
@@ -78334,7 +79516,7 @@ import {
|
|
|
78334
79516
|
InternalServerError as InternalServerError70,
|
|
78335
79517
|
logger as logger166,
|
|
78336
79518
|
makeCacheKey as makeCacheKey55,
|
|
78337
|
-
NotFoundError as
|
|
79519
|
+
NotFoundError as NotFoundError71,
|
|
78338
79520
|
useAtlas as useAtlas113,
|
|
78339
79521
|
useCache as useCache60
|
|
78340
79522
|
} from "@7365admin1/node-server-utils";
|
|
@@ -78424,7 +79606,7 @@ function useNfcPatrolSettingsRepository() {
|
|
|
78424
79606
|
{ session }
|
|
78425
79607
|
);
|
|
78426
79608
|
if (res.matchedCount === 0) {
|
|
78427
|
-
throw new
|
|
79609
|
+
throw new NotFoundError71("NFC patrol settings not found for this site.");
|
|
78428
79610
|
}
|
|
78429
79611
|
delNamespace().then(() => {
|
|
78430
79612
|
logger166.info(`Cache cleared for namespace: ${namespace_collection3}`);
|
|
@@ -78591,7 +79773,7 @@ import {
|
|
|
78591
79773
|
InternalServerError as InternalServerError71,
|
|
78592
79774
|
logger as logger169,
|
|
78593
79775
|
makeCacheKey as makeCacheKey56,
|
|
78594
|
-
NotFoundError as
|
|
79776
|
+
NotFoundError as NotFoundError72,
|
|
78595
79777
|
paginate as paginate53,
|
|
78596
79778
|
useAtlas as useAtlas115,
|
|
78597
79779
|
useCache as useCache61
|
|
@@ -78819,7 +80001,7 @@ function useOccurrenceSubjectRepo() {
|
|
|
78819
80001
|
try {
|
|
78820
80002
|
const data = await collection.findOne({ _id }, { session });
|
|
78821
80003
|
if (!data) {
|
|
78822
|
-
throw new
|
|
80004
|
+
throw new NotFoundError72("Occurrence subject not found.");
|
|
78823
80005
|
}
|
|
78824
80006
|
setCache(cacheKey, data, 15 * 60).then(() => {
|
|
78825
80007
|
logger169.info(`Cache set for key: ${cacheKey}`);
|
|
@@ -79300,7 +80482,7 @@ import {
|
|
|
79300
80482
|
InternalServerError as InternalServerError72,
|
|
79301
80483
|
logger as logger171,
|
|
79302
80484
|
makeCacheKey as makeCacheKey57,
|
|
79303
|
-
NotFoundError as
|
|
80485
|
+
NotFoundError as NotFoundError73,
|
|
79304
80486
|
paginate as paginate54,
|
|
79305
80487
|
useAtlas as useAtlas117,
|
|
79306
80488
|
useCache as useCache62
|
|
@@ -79446,7 +80628,7 @@ function useOnlineFormRepo() {
|
|
|
79446
80628
|
}
|
|
79447
80629
|
]).toArray();
|
|
79448
80630
|
if (!data || !data.length) {
|
|
79449
|
-
throw new
|
|
80631
|
+
throw new NotFoundError73("Document not found.");
|
|
79450
80632
|
}
|
|
79451
80633
|
setCache(cacheKey, data[0], 15 * 60).then(() => {
|
|
79452
80634
|
logger171.info(`Cache set for key: ${cacheKey}`);
|
|
@@ -79590,7 +80772,7 @@ function useOnlineFormRepo() {
|
|
|
79590
80772
|
// src/controllers/online-form.controller.ts
|
|
79591
80773
|
import {
|
|
79592
80774
|
BadRequestError as BadRequestError204,
|
|
79593
|
-
NotFoundError as
|
|
80775
|
+
NotFoundError as NotFoundError74,
|
|
79594
80776
|
UnauthorizedError as UnauthorizedError21,
|
|
79595
80777
|
logger as logger172
|
|
79596
80778
|
} from "@7365admin1/node-server-utils";
|
|
@@ -79608,7 +80790,7 @@ function useOnlineFormController() {
|
|
|
79608
80790
|
const form = await _getOnlineFormById(id);
|
|
79609
80791
|
const { canReach } = await siteReachOf(req);
|
|
79610
80792
|
if (!await canReach(form)) {
|
|
79611
|
-
throw new
|
|
80793
|
+
throw new NotFoundError74("Document not found.");
|
|
79612
80794
|
}
|
|
79613
80795
|
return form;
|
|
79614
80796
|
}
|
|
@@ -80187,7 +81369,7 @@ function useNfcPatrolLogRepo() {
|
|
|
80187
81369
|
// src/services/nfc-patrol-log.service.ts
|
|
80188
81370
|
import {
|
|
80189
81371
|
BadRequestError as BadRequestError207,
|
|
80190
|
-
NotFoundError as
|
|
81372
|
+
NotFoundError as NotFoundError76,
|
|
80191
81373
|
useAtlas as useAtlas120
|
|
80192
81374
|
} from "@7365admin1/node-server-utils";
|
|
80193
81375
|
function useNfcPatrolLogService() {
|
|
@@ -80344,7 +81526,7 @@ function useNfcPatrolLogService() {
|
|
|
80344
81526
|
async function sign(id, value) {
|
|
80345
81527
|
const log = await getById(id);
|
|
80346
81528
|
if (!log) {
|
|
80347
|
-
throw new
|
|
81529
|
+
throw new NotFoundError76("Patrol log not found.");
|
|
80348
81530
|
}
|
|
80349
81531
|
const canSign = log.checkPoints.every(
|
|
80350
81532
|
(c) => c.status === "Completed" || c.status === "Skipped"
|
|
@@ -83553,7 +84735,7 @@ function useManpowerDesignationRepo() {
|
|
|
83553
84735
|
// src/controllers/manpower-designations.controller.ts
|
|
83554
84736
|
import {
|
|
83555
84737
|
BadRequestError as BadRequestError216,
|
|
83556
|
-
NotFoundError as
|
|
84738
|
+
NotFoundError as NotFoundError79,
|
|
83557
84739
|
logger as logger183
|
|
83558
84740
|
} from "@7365admin1/node-server-utils";
|
|
83559
84741
|
import Joi131 from "joi";
|
|
@@ -83568,7 +84750,7 @@ function useManpowerDesignationCtrl() {
|
|
|
83568
84750
|
const row = await _getManpowerDesignationsById(_id);
|
|
83569
84751
|
const { canReach } = await siteReachOf(req);
|
|
83570
84752
|
if (!row || !await canReach({ site: row.siteId })) {
|
|
83571
|
-
throw new
|
|
84753
|
+
throw new NotFoundError79("Manpower designations not found.");
|
|
83572
84754
|
}
|
|
83573
84755
|
return row;
|
|
83574
84756
|
}
|
|
@@ -83582,7 +84764,7 @@ function useManpowerDesignationCtrl() {
|
|
|
83582
84764
|
}
|
|
83583
84765
|
const { actor, canReach } = await siteReachOf(req);
|
|
83584
84766
|
if (!await canReach({ site: payload.siteId })) {
|
|
83585
|
-
throw new
|
|
84767
|
+
throw new NotFoundError79("Site not found.");
|
|
83586
84768
|
}
|
|
83587
84769
|
payload.createdBy = actor.id;
|
|
83588
84770
|
payload.createdByName = actor.name;
|
|
@@ -86320,7 +87502,7 @@ import {
|
|
|
86320
87502
|
useAtlas as useAtlas133,
|
|
86321
87503
|
logger as logger198,
|
|
86322
87504
|
BadRequestError as BadRequestError227,
|
|
86323
|
-
NotFoundError as
|
|
87505
|
+
NotFoundError as NotFoundError80,
|
|
86324
87506
|
AppError as AppError34,
|
|
86325
87507
|
paginate as paginate65
|
|
86326
87508
|
} from "@7365admin1/node-server-utils";
|
|
@@ -86378,7 +87560,7 @@ var useRedDotPaymentRepo = () => {
|
|
|
86378
87560
|
{ session }
|
|
86379
87561
|
);
|
|
86380
87562
|
if (!payment) {
|
|
86381
|
-
throw new
|
|
87563
|
+
throw new NotFoundError80("Payment not found.");
|
|
86382
87564
|
}
|
|
86383
87565
|
return payment;
|
|
86384
87566
|
} catch (error) {
|
|
@@ -86710,7 +87892,7 @@ var useRedDotPaymentRepo = () => {
|
|
|
86710
87892
|
{ session }
|
|
86711
87893
|
);
|
|
86712
87894
|
if (res.matchedCount === 0) {
|
|
86713
|
-
throw new
|
|
87895
|
+
throw new NotFoundError80(
|
|
86714
87896
|
"Payment not found or is no longer pending."
|
|
86715
87897
|
);
|
|
86716
87898
|
}
|
|
@@ -87041,7 +88223,7 @@ import {
|
|
|
87041
88223
|
logger as logger201,
|
|
87042
88224
|
getDirectory as getDirectory7,
|
|
87043
88225
|
BadRequestError as BadRequestError231,
|
|
87044
|
-
NotFoundError as
|
|
88226
|
+
NotFoundError as NotFoundError83,
|
|
87045
88227
|
InternalServerError as InternalServerError85,
|
|
87046
88228
|
UnauthorizedError as UnauthorizedError22,
|
|
87047
88229
|
useAtlas as useAtlas137,
|
|
@@ -87055,7 +88237,7 @@ import {
|
|
|
87055
88237
|
BadRequestError as BadRequestError230,
|
|
87056
88238
|
comparePassword as comparePassword3,
|
|
87057
88239
|
InternalServerError as InternalServerError84,
|
|
87058
|
-
NotFoundError as
|
|
88240
|
+
NotFoundError as NotFoundError82,
|
|
87059
88241
|
useAtlas as useAtlas136,
|
|
87060
88242
|
useCache as useCache73
|
|
87061
88243
|
} from "@7365admin1/node-server-utils";
|
|
@@ -87069,7 +88251,7 @@ import {
|
|
|
87069
88251
|
logger as logger200,
|
|
87070
88252
|
BadRequestError as BadRequestError229,
|
|
87071
88253
|
paginate as paginate66,
|
|
87072
|
-
NotFoundError as
|
|
88254
|
+
NotFoundError as NotFoundError81,
|
|
87073
88255
|
AppError as AppError35,
|
|
87074
88256
|
useCache as useCache72,
|
|
87075
88257
|
makeCacheKey as makeCacheKey65,
|
|
@@ -87202,7 +88384,7 @@ function useUserRepoV2() {
|
|
|
87202
88384
|
]).toArray();
|
|
87203
88385
|
const data = results.length > 0 ? results[0] : null;
|
|
87204
88386
|
if (!data)
|
|
87205
|
-
throw new
|
|
88387
|
+
throw new NotFoundError81("User not found.");
|
|
87206
88388
|
setCache(cacheKey, data, 15 * 60).then(() => logger200.info(`Cache set for key: ${cacheKey}`)).catch(
|
|
87207
88389
|
(err) => logger200.error(`Failed to set cache for key: ${cacheKey}`, err)
|
|
87208
88390
|
);
|
|
@@ -87568,7 +88750,7 @@ function useAuthServiceV2() {
|
|
|
87568
88750
|
try {
|
|
87569
88751
|
const user = await getUserByEmail(email);
|
|
87570
88752
|
if (!user) {
|
|
87571
|
-
throw new
|
|
88753
|
+
throw new NotFoundError82(
|
|
87572
88754
|
"Invalid user email. Please check your email and try again."
|
|
87573
88755
|
);
|
|
87574
88756
|
}
|
|
@@ -87831,7 +89013,7 @@ function useVerificationServiceV2() {
|
|
|
87831
89013
|
session?.startTransaction();
|
|
87832
89014
|
const item = await _getByVerificationCode(verificationCode);
|
|
87833
89015
|
if (!item) {
|
|
87834
|
-
throw new
|
|
89016
|
+
throw new NotFoundError83("Verification not found.");
|
|
87835
89017
|
}
|
|
87836
89018
|
switch (item.status) {
|
|
87837
89019
|
case "expired" /* EXPIRED */:
|
|
@@ -88093,7 +89275,7 @@ function useVerificationServiceV2() {
|
|
|
88093
89275
|
async function resendSignUpVerification(email) {
|
|
88094
89276
|
const item = await _getPendingVerificationByEmail(email);
|
|
88095
89277
|
if (!item) {
|
|
88096
|
-
throw new
|
|
89278
|
+
throw new NotFoundError83(
|
|
88097
89279
|
"Pending verification not found."
|
|
88098
89280
|
);
|
|
88099
89281
|
}
|
|
@@ -88149,7 +89331,7 @@ function useVerificationServiceV2() {
|
|
|
88149
89331
|
}
|
|
88150
89332
|
async function assertInviteOpen(invite) {
|
|
88151
89333
|
if (!invite) {
|
|
88152
|
-
throw new
|
|
89334
|
+
throw new NotFoundError83("Invitation not found.");
|
|
88153
89335
|
}
|
|
88154
89336
|
if (invite.type !== "user-invite" /* USER_INVITE */ && invite.type !== "member-invite" /* MEMBER_INVITE */) {
|
|
88155
89337
|
throw new BadRequestError231("This link is not a user invitation.");
|
|
@@ -88317,7 +89499,7 @@ function useVerificationServiceV2() {
|
|
|
88317
89499
|
const email = await callerEmail(userId);
|
|
88318
89500
|
const invite = await _getVerificationByIdFresh(inviteId);
|
|
88319
89501
|
if (!invite) {
|
|
88320
|
-
throw new
|
|
89502
|
+
throw new NotFoundError83("Invitation not found.");
|
|
88321
89503
|
}
|
|
88322
89504
|
if (invite.email.toLowerCase() !== email.toLowerCase()) {
|
|
88323
89505
|
throw new UnauthorizedError22("Not authorized.");
|
|
@@ -89081,7 +90263,7 @@ import {
|
|
|
89081
90263
|
comparePassword as comparePassword4,
|
|
89082
90264
|
hashPassword as hashPassword5,
|
|
89083
90265
|
InternalServerError as InternalServerError86,
|
|
89084
|
-
NotFoundError as
|
|
90266
|
+
NotFoundError as NotFoundError84,
|
|
89085
90267
|
useAtlas as useAtlas138,
|
|
89086
90268
|
useS3 as useS33
|
|
89087
90269
|
} from "@7365admin1/node-server-utils";
|
|
@@ -89185,14 +90367,14 @@ function useUserServiceV2() {
|
|
|
89185
90367
|
try {
|
|
89186
90368
|
const otpDoc = await _getVerificationById(id);
|
|
89187
90369
|
if (!otpDoc) {
|
|
89188
|
-
throw new
|
|
90370
|
+
throw new NotFoundError84("You are using an invalid reset link.");
|
|
89189
90371
|
}
|
|
89190
90372
|
if (otpDoc.status === "complete" /* COMPLETE */) {
|
|
89191
90373
|
throw new BadRequestError234("This link has already been invalidated.");
|
|
89192
90374
|
}
|
|
89193
90375
|
const user = await _getUserByEmail(otpDoc.email);
|
|
89194
90376
|
if (!user) {
|
|
89195
|
-
throw new
|
|
90377
|
+
throw new NotFoundError84("User not found.");
|
|
89196
90378
|
}
|
|
89197
90379
|
if (!user._id) {
|
|
89198
90380
|
throw new InternalServerError86("Invalid user ID.");
|
|
@@ -90062,7 +91244,7 @@ function MPost(value) {
|
|
|
90062
91244
|
import {
|
|
90063
91245
|
BadRequestError as BadRequestError240,
|
|
90064
91246
|
InternalServerError as InternalServerError88,
|
|
90065
|
-
NotFoundError as
|
|
91247
|
+
NotFoundError as NotFoundError85,
|
|
90066
91248
|
paginate as paginate67,
|
|
90067
91249
|
useAtlas as useAtlas140
|
|
90068
91250
|
} from "@7365admin1/node-server-utils";
|
|
@@ -90285,7 +91467,7 @@ function usePostPrelovedRepo() {
|
|
|
90285
91467
|
]).toArray();
|
|
90286
91468
|
const data = result[0] ?? null;
|
|
90287
91469
|
if (!data)
|
|
90288
|
-
throw new
|
|
91470
|
+
throw new NotFoundError85("Post not found.");
|
|
90289
91471
|
return data;
|
|
90290
91472
|
} catch (error) {
|
|
90291
91473
|
throw error;
|
|
@@ -90595,7 +91777,7 @@ import { useAtlas as useAtlas142 } from "@7365admin1/node-server-utils";
|
|
|
90595
91777
|
import {
|
|
90596
91778
|
BadRequestError as BadRequestError241,
|
|
90597
91779
|
InternalServerError as InternalServerError89,
|
|
90598
|
-
NotFoundError as
|
|
91780
|
+
NotFoundError as NotFoundError86,
|
|
90599
91781
|
useAtlas as useAtlas141
|
|
90600
91782
|
} from "@7365admin1/node-server-utils";
|
|
90601
91783
|
import { ObjectId as ObjectId172 } from "mongodb";
|
|
@@ -90691,7 +91873,7 @@ function usePostFavoriteRepo() {
|
|
|
90691
91873
|
}
|
|
90692
91874
|
const result = await collection.findOne({ _id });
|
|
90693
91875
|
if (!result)
|
|
90694
|
-
throw new
|
|
91876
|
+
throw new NotFoundError86("Favorite not found.");
|
|
90695
91877
|
return result;
|
|
90696
91878
|
}
|
|
90697
91879
|
async function getByPostId(postId) {
|
|
@@ -90702,7 +91884,7 @@ function usePostFavoriteRepo() {
|
|
|
90702
91884
|
}
|
|
90703
91885
|
const result = await collection.findOne({ postId });
|
|
90704
91886
|
if (!result)
|
|
90705
|
-
throw new
|
|
91887
|
+
throw new NotFoundError86("Favorite not found.");
|
|
90706
91888
|
return result;
|
|
90707
91889
|
}
|
|
90708
91890
|
async function updateById(_id, userId, session) {
|
|
@@ -91214,7 +92396,7 @@ function MCategoryPreloved(value) {
|
|
|
91214
92396
|
import {
|
|
91215
92397
|
BadRequestError as BadRequestError244,
|
|
91216
92398
|
InternalServerError as InternalServerError90,
|
|
91217
|
-
NotFoundError as
|
|
92399
|
+
NotFoundError as NotFoundError87,
|
|
91218
92400
|
useAtlas as useAtlas144
|
|
91219
92401
|
} from "@7365admin1/node-server-utils";
|
|
91220
92402
|
import { ObjectId as ObjectId175 } from "mongodb";
|
|
@@ -91257,7 +92439,7 @@ function useCategoryPrelovedRepo() {
|
|
|
91257
92439
|
try {
|
|
91258
92440
|
const data = await collection.findOne({ _id: objectId2 });
|
|
91259
92441
|
if (!data) {
|
|
91260
|
-
throw new
|
|
92442
|
+
throw new NotFoundError87("Category not found.");
|
|
91261
92443
|
}
|
|
91262
92444
|
return data;
|
|
91263
92445
|
} catch (error) {
|
|
@@ -91286,7 +92468,7 @@ function useCategoryPrelovedRepo() {
|
|
|
91286
92468
|
}
|
|
91287
92469
|
const existing = await collection.findOne({ _id: objectId2 });
|
|
91288
92470
|
if (!existing)
|
|
91289
|
-
throw new
|
|
92471
|
+
throw new NotFoundError87("Category not found.");
|
|
91290
92472
|
const res = await collection.updateOne(
|
|
91291
92473
|
{ _id: objectId2 },
|
|
91292
92474
|
{ $set: { ...value, updatedAt: (/* @__PURE__ */ new Date()).toISOString() } }
|
|
@@ -91304,7 +92486,7 @@ function useCategoryPrelovedRepo() {
|
|
|
91304
92486
|
}
|
|
91305
92487
|
const existing = await collection.findOne({ _id: objectId2 });
|
|
91306
92488
|
if (!existing)
|
|
91307
|
-
throw new
|
|
92489
|
+
throw new NotFoundError87("Category not found.");
|
|
91308
92490
|
const res = await collection.deleteOne({ _id: objectId2 });
|
|
91309
92491
|
if (res.deletedCount === 0)
|
|
91310
92492
|
throw new InternalServerError90("Unable to delete category.");
|
|
@@ -91471,7 +92653,7 @@ function MSubcategoryPreloved(value) {
|
|
|
91471
92653
|
import {
|
|
91472
92654
|
BadRequestError as BadRequestError246,
|
|
91473
92655
|
InternalServerError as InternalServerError91,
|
|
91474
|
-
NotFoundError as
|
|
92656
|
+
NotFoundError as NotFoundError88,
|
|
91475
92657
|
useAtlas as useAtlas145
|
|
91476
92658
|
} from "@7365admin1/node-server-utils";
|
|
91477
92659
|
import { ObjectId as ObjectId177 } from "mongodb";
|
|
@@ -91522,7 +92704,7 @@ function useSubcategoryPrelovedRepo() {
|
|
|
91522
92704
|
try {
|
|
91523
92705
|
const data = await collection.findOne({ _id: objectId2 });
|
|
91524
92706
|
if (!data) {
|
|
91525
|
-
throw new
|
|
92707
|
+
throw new NotFoundError88("Subcategory not found.");
|
|
91526
92708
|
}
|
|
91527
92709
|
return data;
|
|
91528
92710
|
} catch (error) {
|
|
@@ -91797,7 +92979,7 @@ function MChatPreloved(value) {
|
|
|
91797
92979
|
import {
|
|
91798
92980
|
BadRequestError as BadRequestError248,
|
|
91799
92981
|
InternalServerError as InternalServerError92,
|
|
91800
|
-
NotFoundError as
|
|
92982
|
+
NotFoundError as NotFoundError89,
|
|
91801
92983
|
useAtlas as useAtlas146
|
|
91802
92984
|
} from "@7365admin1/node-server-utils";
|
|
91803
92985
|
import { ObjectId as ObjectId179 } from "mongodb";
|
|
@@ -91837,7 +93019,7 @@ function useChatPrelovedRepo() {
|
|
|
91837
93019
|
}
|
|
91838
93020
|
const existing = await collection.findOne({ _id: objectId2 });
|
|
91839
93021
|
if (!existing)
|
|
91840
|
-
throw new
|
|
93022
|
+
throw new NotFoundError89("Chat not found.");
|
|
91841
93023
|
value.edited = true;
|
|
91842
93024
|
value.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
91843
93025
|
const res = await collection.updateOne({ _id: objectId2 }, { $set: value });
|
|
@@ -91854,7 +93036,7 @@ function useChatPrelovedRepo() {
|
|
|
91854
93036
|
}
|
|
91855
93037
|
const existing = await collection.findOne({ _id: objectId2 });
|
|
91856
93038
|
if (!existing)
|
|
91857
|
-
throw new
|
|
93039
|
+
throw new NotFoundError89("Chat not found.");
|
|
91858
93040
|
const res = await collection.updateOne(
|
|
91859
93041
|
{ _id: objectId2 },
|
|
91860
93042
|
{ $set: { deletedAt: (/* @__PURE__ */ new Date()).toISOString(), updatedAt: (/* @__PURE__ */ new Date()).toISOString() } }
|
|
@@ -92587,7 +93769,7 @@ function useChannelPrelovedController() {
|
|
|
92587
93769
|
// src/repositories/bid-preloved.repo.ts
|
|
92588
93770
|
import {
|
|
92589
93771
|
InternalServerError as InternalServerError96,
|
|
92590
|
-
NotFoundError as
|
|
93772
|
+
NotFoundError as NotFoundError90,
|
|
92591
93773
|
useAtlas as useAtlas150
|
|
92592
93774
|
} from "@7365admin1/node-server-utils";
|
|
92593
93775
|
import { ObjectId as ObjectId183 } from "mongodb";
|
|
@@ -92609,7 +93791,7 @@ function useBidPrelovedRepo() {
|
|
|
92609
93791
|
const objectId2 = typeof _id === "string" ? new ObjectId183(_id) : _id;
|
|
92610
93792
|
const existing = await collection.findOne({ _id: objectId2 });
|
|
92611
93793
|
if (!existing)
|
|
92612
|
-
throw new
|
|
93794
|
+
throw new NotFoundError90("Bid not found.");
|
|
92613
93795
|
const res = await collection.updateOne(
|
|
92614
93796
|
{ _id: objectId2 },
|
|
92615
93797
|
{ $set: { status, updatedAt: (/* @__PURE__ */ new Date()).toISOString() } }
|
|
@@ -92623,7 +93805,7 @@ function useBidPrelovedRepo() {
|
|
|
92623
93805
|
_id = new ObjectId183(_id);
|
|
92624
93806
|
const result = await collection.findOne({ _id });
|
|
92625
93807
|
if (!result)
|
|
92626
|
-
throw new
|
|
93808
|
+
throw new NotFoundError90("Bid not found.");
|
|
92627
93809
|
return result;
|
|
92628
93810
|
}
|
|
92629
93811
|
return { add, getById, updateStatus };
|
|
@@ -92930,7 +94112,7 @@ var residentFormEntry = Joi163.object({
|
|
|
92930
94112
|
import {
|
|
92931
94113
|
BadRequestError as BadRequestError253,
|
|
92932
94114
|
InternalServerError as InternalServerError98,
|
|
92933
|
-
NotFoundError as
|
|
94115
|
+
NotFoundError as NotFoundError91,
|
|
92934
94116
|
paginate as paginate70,
|
|
92935
94117
|
useAtlas as useAtlas152,
|
|
92936
94118
|
useCache as useCache76
|
|
@@ -93035,7 +94217,7 @@ function useFormEntryRepo() {
|
|
|
93035
94217
|
try {
|
|
93036
94218
|
const [data] = await collection.aggregate([{ $match: query2 }]).toArray();
|
|
93037
94219
|
if (!data) {
|
|
93038
|
-
throw new
|
|
94220
|
+
throw new NotFoundError91("Document not found.");
|
|
93039
94221
|
}
|
|
93040
94222
|
return data;
|
|
93041
94223
|
} catch (error) {
|
|
@@ -93059,7 +94241,7 @@ function useFormEntryRepo() {
|
|
|
93059
94241
|
}
|
|
93060
94242
|
const onlineFormRequest = await collection.findOne({ _id });
|
|
93061
94243
|
if (!onlineFormRequest) {
|
|
93062
|
-
throw new
|
|
94244
|
+
throw new NotFoundError91("Online form not found.");
|
|
93063
94245
|
}
|
|
93064
94246
|
if (onlineFormRequest.userId) {
|
|
93065
94247
|
const user = await getUserById(onlineFormRequest.userId.toString());
|
|
@@ -93172,7 +94354,7 @@ function useFormEntryRepo() {
|
|
|
93172
94354
|
// src/controllers/online-forms-v2.controller.ts
|
|
93173
94355
|
import {
|
|
93174
94356
|
BadRequestError as BadRequestError254,
|
|
93175
|
-
NotFoundError as
|
|
94357
|
+
NotFoundError as NotFoundError92,
|
|
93176
94358
|
UnauthorizedError as UnauthorizedError25,
|
|
93177
94359
|
logger as logger218
|
|
93178
94360
|
} from "@7365admin1/node-server-utils";
|
|
@@ -93195,7 +94377,7 @@ function useFormEntryController() {
|
|
|
93195
94377
|
const owner = entry?.userId?.toString?.() ?? "";
|
|
93196
94378
|
const isOwner = Boolean(actor.id) && owner === actor.id;
|
|
93197
94379
|
if (!isOwner && !await canReach(entry)) {
|
|
93198
|
-
throw new
|
|
94380
|
+
throw new NotFoundError92("Document not found.");
|
|
93199
94381
|
}
|
|
93200
94382
|
return { entry, isOwner, actor };
|
|
93201
94383
|
}
|
|
@@ -95677,6 +96859,7 @@ export {
|
|
|
95677
96859
|
cameraGrant,
|
|
95678
96860
|
cameraHealthClaim,
|
|
95679
96861
|
cameraHealthSummary,
|
|
96862
|
+
cameraListClearConfirmation,
|
|
95680
96863
|
cameraManagePermissions,
|
|
95681
96864
|
cameraProbeCacheKey,
|
|
95682
96865
|
cameraTransports,
|
|
@@ -95740,6 +96923,7 @@ export {
|
|
|
95740
96923
|
incidents_namespace_collection,
|
|
95741
96924
|
invitationOffersRole,
|
|
95742
96925
|
isCameraEntitled,
|
|
96926
|
+
isDuplicateMemberError,
|
|
95743
96927
|
isDuplicateVersionError,
|
|
95744
96928
|
isLikelySelfServiceEmail,
|
|
95745
96929
|
isPatrolCctvCamera,
|