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