@7365admin1/core 3.32.2-staging.74 → 3.32.2-staging.76
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.changeset/dahua-credential-logging.md +19 -0
- package/dist/index.js +81 -13
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +81 -13
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -2
- package/test/camera-alert.util.test.mjs +109 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
---
|
|
2
|
+
"@7365admin1/core": patch
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Stop logging camera and mailer credentials
|
|
6
|
+
|
|
7
|
+
Six places wrote a secret or a resident's details to stdout on an ordinary code
|
|
8
|
+
path. `getTrafficJunction` dumped the whole `TSiteCamera` object — including the
|
|
9
|
+
camera's `username` and plaintext `password` — on every reconnect, and
|
|
10
|
+
`addPlateNumber` logged its argument, which carries the same password plus a
|
|
11
|
+
resident's plate number and owner name. `checkOutBySiteAndPlate` logged the
|
|
12
|
+
matched visitor transaction. `siteCameraRepo.updateById` logged the re-read
|
|
13
|
+
camera document, and `vehicleService.add` logged the whole array of a site's
|
|
14
|
+
cameras, both with credentials in them. `sendEmail` printed `MAILER_PASSWORD`
|
|
15
|
+
itself on every message sent.
|
|
16
|
+
|
|
17
|
+
All now log identifiers, counts and outcomes only, through the module's logger
|
|
18
|
+
rather than `console.log`; the mailer line is removed outright because it
|
|
19
|
+
carried nothing but the secret.
|
package/dist/index.js
CHANGED
|
@@ -22700,7 +22700,6 @@ async function sendEmail({ to, subject, text, html }) {
|
|
|
22700
22700
|
pass: MAILER_PASSWORD
|
|
22701
22701
|
}
|
|
22702
22702
|
});
|
|
22703
|
-
console.log(MAILER_EMAIL, MAILER_PASSWORD, MAILER_TRANSPORT_HOST, MAILER_TRANSPORT_PORT);
|
|
22704
22703
|
const mailOptions = {
|
|
22705
22704
|
from: `Seven 365 <${MAILER_EMAIL}>`,
|
|
22706
22705
|
to,
|
|
@@ -24737,6 +24736,47 @@ var loggerDahua = winston.createLogger({
|
|
|
24737
24736
|
]
|
|
24738
24737
|
});
|
|
24739
24738
|
|
|
24739
|
+
// src/utils/camera-alert.util.ts
|
|
24740
|
+
var DIRECTION_WORDS = {
|
|
24741
|
+
entry: "entry",
|
|
24742
|
+
exit: "exit",
|
|
24743
|
+
both: "entry and exit",
|
|
24744
|
+
visitors: "visitor",
|
|
24745
|
+
residents: "resident"
|
|
24746
|
+
};
|
|
24747
|
+
function describeCamera(camera) {
|
|
24748
|
+
const site = String(camera?.siteName ?? "").trim();
|
|
24749
|
+
const name = String(camera?.name ?? "").trim();
|
|
24750
|
+
const at = site ? ` at ${site}` : "";
|
|
24751
|
+
if (name)
|
|
24752
|
+
return `Camera "${name}"${at}`;
|
|
24753
|
+
const words = DIRECTION_WORDS[String(camera?.direction ?? "").trim().toLowerCase()];
|
|
24754
|
+
return words ? `The ${words} ANPR camera${at}` : `The ANPR camera${at}`;
|
|
24755
|
+
}
|
|
24756
|
+
function cameraUnreachableMessage(camera) {
|
|
24757
|
+
return `${describeCamera(camera)} is not responding. Plate reads and automatic barrier opening are stopped while it is down. The server keeps trying to reconnect on its own. If it does not come back, ask your site technician or the security-system installer to check the camera's power and network.`;
|
|
24758
|
+
}
|
|
24759
|
+
function cameraRecoveredMessage(camera) {
|
|
24760
|
+
return `${describeCamera(camera)} is responding again. Plate reads have resumed.`;
|
|
24761
|
+
}
|
|
24762
|
+
function cameraCredentialsMessage(camera) {
|
|
24763
|
+
return `${describeCamera(camera)} refused the login, so it cannot read plates. The username or password saved for it is wrong. An administrator can correct them in Site Settings > Cameras; if they are already correct, the camera's own account needs checking by your site technician.`;
|
|
24764
|
+
}
|
|
24765
|
+
function cameraNotAuthorisedMessage(camera) {
|
|
24766
|
+
return `${describeCamera(camera)} rejected our connection, so it cannot read plates. This usually means its account is not allowed to use the plate-reading feed. Please ask your site technician or the security-system installer to check the camera's account permissions.`;
|
|
24767
|
+
}
|
|
24768
|
+
var CAMERA_ALERT_AT_ATTEMPT = [1, 6, 30, 90];
|
|
24769
|
+
var CAMERA_ALERT_EVERY_AFTER = 90;
|
|
24770
|
+
function shouldAlertOnFailure(consecutiveFailures) {
|
|
24771
|
+
if (!Number.isInteger(consecutiveFailures) || consecutiveFailures < 1)
|
|
24772
|
+
return false;
|
|
24773
|
+
if (CAMERA_ALERT_AT_ATTEMPT.includes(consecutiveFailures))
|
|
24774
|
+
return true;
|
|
24775
|
+
if (consecutiveFailures < CAMERA_ALERT_EVERY_AFTER)
|
|
24776
|
+
return false;
|
|
24777
|
+
return (consecutiveFailures - CAMERA_ALERT_EVERY_AFTER) % CAMERA_ALERT_EVERY_AFTER === 0;
|
|
24778
|
+
}
|
|
24779
|
+
|
|
24740
24780
|
// src/services/dahua.service.ts
|
|
24741
24781
|
var cameraRegistry = /* @__PURE__ */ new Map();
|
|
24742
24782
|
var _savedOnDetected;
|
|
@@ -24929,7 +24969,9 @@ function useDahuaService() {
|
|
|
24929
24969
|
return;
|
|
24930
24970
|
try {
|
|
24931
24971
|
const result = await _checkOutBySiteAndPlate(site, plateNumber2);
|
|
24932
|
-
|
|
24972
|
+
loggerDahua.info(
|
|
24973
|
+
`checkOutBySiteAndPlate matched=${Boolean(result)} type=${result?.type ?? "none"}`
|
|
24974
|
+
);
|
|
24933
24975
|
if (onDetected2 && result?.type != "resident" && result?.site) {
|
|
24934
24976
|
onDetected2({ reload: true, site: result?.site?.toString(), siteName: camera?.siteName, cameraDirection: camera?.direction, direction });
|
|
24935
24977
|
}
|
|
@@ -25228,8 +25270,12 @@ function useDahuaService() {
|
|
|
25228
25270
|
);
|
|
25229
25271
|
}
|
|
25230
25272
|
async function getTrafficJunction(camera, signal, onDetected) {
|
|
25231
|
-
|
|
25273
|
+
loggerDahua.info(
|
|
25274
|
+
`getTrafficJunction starting for camera ${camera?._id} [${camera?.siteName}-${camera?.direction}] host ${camera?.host}`
|
|
25275
|
+
);
|
|
25232
25276
|
let authFailureCount = 0;
|
|
25277
|
+
let connectionFailureCount = 0;
|
|
25278
|
+
let alertedFailureCount = 0;
|
|
25233
25279
|
const MAX_AUTH_RETRIES = 10;
|
|
25234
25280
|
while (!signal.aborted) {
|
|
25235
25281
|
let bufferQueue = null;
|
|
@@ -25257,19 +25303,33 @@ function useDahuaService() {
|
|
|
25257
25303
|
onDetected({
|
|
25258
25304
|
site: camera?.site,
|
|
25259
25305
|
direction: camera?.direction,
|
|
25260
|
-
messagePermanent:
|
|
25306
|
+
messagePermanent: cameraCredentialsMessage(camera)
|
|
25261
25307
|
});
|
|
25262
25308
|
}
|
|
25263
25309
|
return;
|
|
25264
25310
|
} else if ([400, 500].includes(statusCode)) {
|
|
25265
25311
|
loggerDahua.error(`[${camera?.siteName}-${camera?.direction}] Connection error: ${statusCode}`);
|
|
25266
25312
|
if (onDetected) {
|
|
25267
|
-
onDetected({ site: camera?.site, messagePermanent:
|
|
25313
|
+
onDetected({ site: camera?.site, messagePermanent: cameraNotAuthorisedMessage(camera) });
|
|
25268
25314
|
}
|
|
25269
25315
|
return;
|
|
25270
25316
|
}
|
|
25271
25317
|
authFailureCount = 0;
|
|
25272
25318
|
isAuthFailure = false;
|
|
25319
|
+
if (alertedFailureCount > 0 && onDetected) {
|
|
25320
|
+
onDetected({
|
|
25321
|
+
site: camera?.site,
|
|
25322
|
+
direction: camera?.direction,
|
|
25323
|
+
// `event` and `camera` let a client group or clear an alert per
|
|
25324
|
+
// camera instead of matching on the message text. Older clients
|
|
25325
|
+
// ignore them and just render `message`, as they always have.
|
|
25326
|
+
event: "camera-recovered",
|
|
25327
|
+
camera: camera?._id?.toString(),
|
|
25328
|
+
message: cameraRecoveredMessage(camera)
|
|
25329
|
+
});
|
|
25330
|
+
}
|
|
25331
|
+
connectionFailureCount = 0;
|
|
25332
|
+
alertedFailureCount = 0;
|
|
25273
25333
|
loggerDahua.info(`[${camera?.siteName}-${camera?.direction}] Successfully connected to ANPR.`);
|
|
25274
25334
|
const contentType = response.res.headers["content-type"];
|
|
25275
25335
|
const boundaryMatch = contentType?.match(/boundary=(.*)$/i);
|
|
@@ -25323,7 +25383,7 @@ function useDahuaService() {
|
|
|
25323
25383
|
onDetected({
|
|
25324
25384
|
site: camera?.site,
|
|
25325
25385
|
direction: camera?.direction,
|
|
25326
|
-
messagePermanent:
|
|
25386
|
+
messagePermanent: cameraCredentialsMessage(camera)
|
|
25327
25387
|
});
|
|
25328
25388
|
}
|
|
25329
25389
|
return;
|
|
@@ -25343,7 +25403,7 @@ function useDahuaService() {
|
|
|
25343
25403
|
onDetected({
|
|
25344
25404
|
site: camera?.site,
|
|
25345
25405
|
direction: camera?.direction,
|
|
25346
|
-
messagePermanent:
|
|
25406
|
+
messagePermanent: cameraCredentialsMessage(camera)
|
|
25347
25407
|
});
|
|
25348
25408
|
}
|
|
25349
25409
|
return;
|
|
@@ -25351,14 +25411,18 @@ function useDahuaService() {
|
|
|
25351
25411
|
} else {
|
|
25352
25412
|
authFailureCount = 0;
|
|
25353
25413
|
isAuthFailure = false;
|
|
25414
|
+
connectionFailureCount++;
|
|
25354
25415
|
loggerDahua.error(
|
|
25355
|
-
`[${camera?.siteName}-${camera?.direction}] Connection lost or error: ${error.message || error}. Retrying in 10 seconds
|
|
25416
|
+
`[${camera?.siteName}-${camera?.direction}] Connection lost or error: ${error.message || error}. Retrying in 10 seconds... (failure ${connectionFailureCount})`
|
|
25356
25417
|
);
|
|
25357
|
-
if (onDetected) {
|
|
25418
|
+
if (onDetected && shouldAlertOnFailure(connectionFailureCount)) {
|
|
25419
|
+
alertedFailureCount = connectionFailureCount;
|
|
25358
25420
|
onDetected({
|
|
25359
25421
|
site: camera?.site,
|
|
25360
25422
|
direction: camera?.direction,
|
|
25361
|
-
|
|
25423
|
+
event: "camera-fault",
|
|
25424
|
+
camera: camera?._id?.toString(),
|
|
25425
|
+
message: cameraUnreachableMessage(camera)
|
|
25362
25426
|
});
|
|
25363
25427
|
}
|
|
25364
25428
|
}
|
|
@@ -25395,7 +25459,9 @@ function useDahuaService() {
|
|
|
25395
25459
|
return match ? match[1] : null;
|
|
25396
25460
|
}
|
|
25397
25461
|
async function addPlateNumber(value) {
|
|
25398
|
-
|
|
25462
|
+
loggerDahua.info(
|
|
25463
|
+
`addPlateNumber called for host ${value?.host} mode ${value?.mode}`
|
|
25464
|
+
);
|
|
25399
25465
|
let recno = null;
|
|
25400
25466
|
const validation = import_joi40.default.object({
|
|
25401
25467
|
host: import_joi40.default.string().required(),
|
|
@@ -26055,7 +26121,7 @@ function useSiteCameraRepo() {
|
|
|
26055
26121
|
}
|
|
26056
26122
|
if (result?._id) {
|
|
26057
26123
|
const result2 = await findOne({ _id: new import_mongodb46.ObjectId(result._id) }, void 0, { session });
|
|
26058
|
-
|
|
26124
|
+
import_node_server_utils74.logger.info(`Site camera ${result2?._id} re-read after update; reconnecting listener`);
|
|
26059
26125
|
if (result2?._id) {
|
|
26060
26126
|
const { listenToCamera } = useDahuaService();
|
|
26061
26127
|
await listenToCamera(result2);
|
|
@@ -27581,7 +27647,9 @@ function useVehicleService() {
|
|
|
27581
27647
|
siteCameras.push(...siteCameraReq.items);
|
|
27582
27648
|
page++;
|
|
27583
27649
|
} while (page <= pages);
|
|
27584
|
-
|
|
27650
|
+
import_node_server_utils78.logger.info(
|
|
27651
|
+
`addVehicle resolved ${siteCameras.length} site camera(s) for site ${value?.site}`
|
|
27652
|
+
);
|
|
27585
27653
|
}
|
|
27586
27654
|
for (const plateNumber of plateNumbers) {
|
|
27587
27655
|
const vehicleValue = {
|