@7365admin1/core 2.49.0 → 2.50.0

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/dist/index.mjs CHANGED
@@ -13831,14 +13831,14 @@ function useVisitorTransactionRepo() {
13831
13831
  );
13832
13832
  }
13833
13833
  }
13834
- async function updateById(_id, value, session) {
13834
+ async function updateById(_id, value, session, isNotManualCheckOut = true) {
13835
13835
  try {
13836
13836
  _id = new ObjectId40(_id);
13837
13837
  } catch (error) {
13838
13838
  throw new BadRequestError66("Invalid visitor transaction ID format.");
13839
13839
  }
13840
13840
  value.updatedAt = /* @__PURE__ */ new Date();
13841
- if (value.checkOut) {
13841
+ if (value.checkOut && isNotManualCheckOut) {
13842
13842
  value.manualCheckout = true;
13843
13843
  }
13844
13844
  try {
@@ -14598,6 +14598,22 @@ function useVehicleRepo() {
14598
14598
  throw error;
14599
14599
  }
14600
14600
  }
14601
+ async function getSpecificVehicleById(_id) {
14602
+ try {
14603
+ _id = new ObjectId42(_id);
14604
+ } catch (error) {
14605
+ throw new BadRequestError68("Invalid vehicle ID format.");
14606
+ }
14607
+ try {
14608
+ const result = await collection.findOne({ _id });
14609
+ if (!result) {
14610
+ throw new NotFoundError17("Vehicle not found.");
14611
+ }
14612
+ return result;
14613
+ } catch (error) {
14614
+ throw error;
14615
+ }
14616
+ }
14601
14617
  async function getByPlaceNumber(value) {
14602
14618
  const { error } = Joi38.string().required().validate(value);
14603
14619
  if (error) {
@@ -14856,7 +14872,8 @@ function useVehicleRepo() {
14856
14872
  deleteExpiredVehicles,
14857
14873
  getAllVehiclesByUnitId,
14858
14874
  getAllExpiredVehicles,
14859
- bulkUpsertVehicles
14875
+ bulkUpsertVehicles,
14876
+ getSpecificVehicleById
14860
14877
  };
14861
14878
  }
14862
14879
 
@@ -17136,6 +17153,7 @@ var loggerDahua = winston.createLogger({
17136
17153
 
17137
17154
  // src/services/dahua.service.ts
17138
17155
  var cameraRegistry = /* @__PURE__ */ new Map();
17156
+ var _savedOnDetected;
17139
17157
  function useDahuaDigest({
17140
17158
  host = "",
17141
17159
  username = "",
@@ -17200,7 +17218,7 @@ function useDahuaService() {
17200
17218
  queue.push(buffer);
17201
17219
  if (queue.length >= BACKPRESSURE_THRESHOLD && streamRef && !streamRef.isPaused()) {
17202
17220
  loggerDahua.warn(
17203
- `[${site}][${gate}] Queue at ${queue.length}/${MAX_QUEUE_SIZE}, pausing stream`
17221
+ `[${site}][${host}] Queue at ${queue.length}/${MAX_QUEUE_SIZE}, pausing stream`
17204
17222
  );
17205
17223
  streamRef.pause();
17206
17224
  }
@@ -17217,7 +17235,7 @@ function useDahuaService() {
17217
17235
  streamRef.resume();
17218
17236
  }
17219
17237
  loggerDahua.info(
17220
- `[${site}][${gate}] BufferQueue destroyed. Processed=${processedChunks}, Dropped=${droppedChunks}`
17238
+ `[${site}][${host}] BufferQueue destroyed. Processed=${processedChunks}, Dropped=${droppedChunks}`
17221
17239
  );
17222
17240
  }
17223
17241
  async function processNext(onDetected2) {
@@ -17265,9 +17283,9 @@ function useDahuaService() {
17265
17283
  const transactionId = existingOpenTransaction?._id?.toString() || "";
17266
17284
  if (existingOpenTransaction && !existingOpenTransaction.checkOut && transactionId) {
17267
17285
  await updateById(transactionId, {
17268
- checkOut: (/* @__PURE__ */ new Date()).toISOString(),
17269
- updatedAt: (/* @__PURE__ */ new Date()).toISOString()
17270
- });
17286
+ checkOut: /* @__PURE__ */ new Date(),
17287
+ updatedAt: /* @__PURE__ */ new Date()
17288
+ }, void 0, false);
17271
17289
  }
17272
17290
  } catch (error) {
17273
17291
  loggerDahua.error(
@@ -17326,7 +17344,7 @@ function useDahuaService() {
17326
17344
  currentTransactionId = transactionId?.toString();
17327
17345
  currentSnapshotField = "snapshotEntryImage";
17328
17346
  if (onDetected2) {
17329
- onDetected2({ _id: transactionId, site: result?.site?.toString(), plateNumber: result.plateNumber });
17347
+ onDetected2({ _id: transactionId, site: result?.site?.toString(), plateNumber: result.plateNumber, host, direction });
17330
17348
  }
17331
17349
  } catch (error) {
17332
17350
  console.log("failed to create visitor transaction", error);
@@ -17335,8 +17353,7 @@ function useDahuaService() {
17335
17353
  error
17336
17354
  );
17337
17355
  }
17338
- }
17339
- if (["exit", "both"].includes(designation) && direction.toLowerCase() === "leave" && plateNumber) {
17356
+ } else if (["exit", "both"].includes(designation) && direction.toLowerCase() === "leave" && plateNumber) {
17340
17357
  const vehicle = await getVehicleByPlateNumber(plateNumber);
17341
17358
  const existingOpenTransaction = await getOpenByPlateNumber(
17342
17359
  plateNumber,
@@ -17358,6 +17375,9 @@ function useDahuaService() {
17358
17375
  await removePlateNumber(dahuaPayload);
17359
17376
  }
17360
17377
  await closeOpenTransaction(plateNumber);
17378
+ if (onDetected2) {
17379
+ onDetected2({ reload: true, site: existingOpenTransaction?.site?.toString(), host, direction });
17380
+ }
17361
17381
  }
17362
17382
  }
17363
17383
  }
@@ -17414,7 +17434,7 @@ function useDahuaService() {
17414
17434
  try {
17415
17435
  await fsAsync.mkdir(dir, { recursive: true });
17416
17436
  await fsAsync.writeFile(snapFolder, accumulatedImageBuffer);
17417
- loggerDahua.debug(`[${host}] Saved image locally: ${filename}`);
17437
+ loggerDahua.info(`[${host}] Saved image locally: ${filename}`);
17418
17438
  const fileId = await _createFile(
17419
17439
  {
17420
17440
  originalname: filename,
@@ -17489,9 +17509,12 @@ function useDahuaService() {
17489
17509
  }
17490
17510
  }
17491
17511
  async function listenToCamera(camera, onDetected) {
17512
+ if (onDetected) {
17513
+ _savedOnDetected = onDetected;
17514
+ }
17492
17515
  if (!camera?._id) {
17493
17516
  loggerDahua.error(`Camera _id is required to listen to camera.`);
17494
- throw new BadRequestError73("Camera _id is required to listen to camera.");
17517
+ return;
17495
17518
  }
17496
17519
  const cameraId = camera._id.toString();
17497
17520
  if (cameraRegistry.has(cameraId)) {
@@ -17501,6 +17524,11 @@ function useDahuaService() {
17501
17524
  }
17502
17525
  const controller = new AbortController();
17503
17526
  cameraRegistry.set(cameraId, controller);
17527
+ const onDetectedHandler = onDetected || _savedOnDetected;
17528
+ if (!onDetectedHandler) {
17529
+ loggerDahua.error(`No plate detection handler registered for camera ${cameraId}`);
17530
+ return;
17531
+ }
17504
17532
  getTrafficJunction(
17505
17533
  camera.host,
17506
17534
  camera.username,
@@ -17510,7 +17538,7 @@ function useDahuaService() {
17510
17538
  camera.direction,
17511
17539
  cameraId,
17512
17540
  controller.signal,
17513
- onDetected
17541
+ onDetectedHandler
17514
17542
  );
17515
17543
  }
17516
17544
  async function getTrafficJunctionOld(host = "", username = "", password = "", site = "", gate = "", designation = "", cameraId, signal, onDetected) {
@@ -17631,9 +17659,12 @@ function useDahuaService() {
17631
17659
  timeout: 2e4,
17632
17660
  streaming: true
17633
17661
  });
17634
- if ([400, 401, 403, 500].includes(response.statusCode)) {
17662
+ if (response.statusCode === 401) {
17663
+ loggerDahua.error(`[${host}] 401 Unauthorized. Potential handshake or wrong credentials.`);
17664
+ continue;
17665
+ } else if ([400, 401, 403, 500].includes(response.statusCode)) {
17635
17666
  loggerDahua.error(`[${host}] Connection error: ${response.statusCode}`);
17636
- throw new Error(`Failed to connect to ANPR: ${response.statusCode}`);
17667
+ return;
17637
17668
  }
17638
17669
  loggerDahua.info(`[${host}] Successfully connected to ANPR.`);
17639
17670
  const contentType = response.res.headers["content-type"];
@@ -17680,16 +17711,31 @@ function useDahuaService() {
17680
17711
  });
17681
17712
  });
17682
17713
  } catch (error) {
17683
- if (signal.aborted)
17714
+ if (signal.aborted || error.name === "AbortError" || error.code === "UND_ERR_ABORTED") {
17715
+ loggerDahua.info(`[${host}] Connection closed by system (Abort).`);
17684
17716
  break;
17717
+ }
17685
17718
  loggerDahua.error(
17686
17719
  `[${host}] Connection lost or error: ${error.message || error}. Retrying in 5s...`
17687
17720
  );
17688
17721
  } finally {
17689
- if (bufferQueue?.destroy)
17690
- bufferQueue.destroy();
17691
- if (response?.res && !response.res.destroyed)
17692
- response.res.destroy();
17722
+ if (bufferQueue?.destroy) {
17723
+ try {
17724
+ bufferQueue.destroy();
17725
+ } catch (e) {
17726
+ loggerDahua.error(`[${host}] Error destroying buffer queue:`, e);
17727
+ }
17728
+ }
17729
+ if (response?.res && typeof response.res.destroy === "function" && !response.res.destroyed) {
17730
+ try {
17731
+ response.res.on("error", () => {
17732
+ loggerDahua.error(`[${host}] Stream error during cleanup, likely already closed.`);
17733
+ });
17734
+ response.res.destroy();
17735
+ } catch (err) {
17736
+ loggerDahua.debug(`Cleanup: stream already closing: ${err?.message}`);
17737
+ }
17738
+ }
17693
17739
  }
17694
17740
  if (!signal.aborted) {
17695
17741
  await new Promise((res) => setTimeout(res, 5e3));
@@ -17905,6 +17951,8 @@ function useSiteCameraRepo() {
17905
17951
  try {
17906
17952
  value = MSiteCamera(value);
17907
17953
  const res = await collection.insertOne(value, { session });
17954
+ const { listenToCamera } = useDahuaService();
17955
+ await listenToCamera({ ...value, _id: res.insertedId });
17908
17956
  delCachedData();
17909
17957
  return res.insertedId;
17910
17958
  } catch (error) {
@@ -19760,7 +19808,8 @@ function useVehicleController() {
19760
19808
  getVehicles: _getVehicles,
19761
19809
  getVehicleById: _getVehicleById,
19762
19810
  getVehiclesByNRIC: _getVehiclesByNRIC,
19763
- getAllVehiclesByUnitId: _getAllVehiclesByUnitId
19811
+ getAllVehiclesByUnitId: _getAllVehiclesByUnitId,
19812
+ getSpecificVehicleById: _getSpecificVehicleById
19764
19813
  } = useVehicleRepo();
19765
19814
  function normalizeRow(row) {
19766
19815
  return Object.fromEntries(
@@ -19874,7 +19923,9 @@ function useVehicleController() {
19874
19923
  return;
19875
19924
  }
19876
19925
  const headerRow = worksheet.getRow(1);
19877
- const headers = (headerRow.values || []).slice(1).map((header) => String(header ?? "").trim());
19926
+ const headers = (headerRow.values || []).slice(1).map(
19927
+ (header) => String(header ?? "").trim().replace(/\(.*\)/, "").trim()
19928
+ );
19878
19929
  worksheet.eachRow((row, rowNumber) => {
19879
19930
  if (rowNumber === 1)
19880
19931
  return;
@@ -19891,7 +19942,11 @@ function useVehicleController() {
19891
19942
  } else if (lowerName.endsWith(".csv")) {
19892
19943
  rows = await new Promise((resolve, reject) => {
19893
19944
  const parsed = [];
19894
- fs.createReadStream(path4).pipe(csv()).on("data", (row) => parsed.push(row)).on("end", () => resolve(parsed)).on("error", reject);
19945
+ fs.createReadStream(path4).pipe(csv()).on("headers", (headers) => {
19946
+ headers = headers.map(
19947
+ (h) => h.trim().replace(/\(.*\)/, "").trim()
19948
+ );
19949
+ }).on("data", (row) => parsed.push(row)).on("end", () => resolve(parsed)).on("error", reject);
19895
19950
  });
19896
19951
  } else {
19897
19952
  next(
@@ -20056,6 +20111,31 @@ function useVehicleController() {
20056
20111
  return;
20057
20112
  }
20058
20113
  }
20114
+ async function getSpecificVehicleById(req, res, next) {
20115
+ const schema2 = Joi46.object({
20116
+ _id: Joi46.string().hex().length(24).required()
20117
+ });
20118
+ const { error, value } = schema2.validate(
20119
+ { _id: req.params.id },
20120
+ { abortEarly: false }
20121
+ );
20122
+ if (error) {
20123
+ const messages = error.details.map((d) => d.message).join(", ");
20124
+ logger65.log({ level: "error", message: messages });
20125
+ next(new BadRequestError84(messages));
20126
+ return;
20127
+ }
20128
+ const { _id } = value;
20129
+ try {
20130
+ const site = await _getSpecificVehicleById(_id);
20131
+ res.json(site);
20132
+ return;
20133
+ } catch (error2) {
20134
+ logger65.log({ level: "error", message: error2.message });
20135
+ next(error2);
20136
+ return;
20137
+ }
20138
+ }
20059
20139
  async function updateVehicleById(req, res, next) {
20060
20140
  try {
20061
20141
  const schema2 = Joi46.object({
@@ -20275,7 +20355,8 @@ function useVehicleController() {
20275
20355
  getVehiclesByNRIC,
20276
20356
  reactivateVehicleById,
20277
20357
  getAllVehiclesByUnitId,
20278
- uploadSpreadsheetVehicles
20358
+ uploadSpreadsheetVehicles,
20359
+ getSpecificVehicleById
20279
20360
  };
20280
20361
  }
20281
20362
 
@@ -23421,6 +23502,12 @@ function useVisitorTransactionService() {
23421
23502
  const parsed = new Date(value.checkOut);
23422
23503
  value.checkOut = isNaN(parsed.getTime()) ? null : parsed;
23423
23504
  }
23505
+ if (value.site) {
23506
+ value.site = typeof value.site === "string" ? new ObjectId59(value.site) : value.site;
23507
+ }
23508
+ if (value.unit) {
23509
+ value.unit = typeof value.unit === "string" ? new ObjectId59(value.unit) : value.unit;
23510
+ }
23424
23511
  await _updateVisitorTansactionById(id, value, session);
23425
23512
  await session?.commitTransaction();
23426
23513
  return "Successfully updated visitor transaction.";
@@ -26260,7 +26347,7 @@ function usePatrolRouteRepo() {
26260
26347
  id: "$_id",
26261
26348
  start: startOfDay,
26262
26349
  end: endOfDay,
26263
- site: new ObjectId67(site)
26350
+ site: ObjectId67.isValid(site) ? new ObjectId67(site) : null
26264
26351
  },
26265
26352
  pipeline: [
26266
26353
  {
@@ -26291,10 +26378,8 @@ function usePatrolRouteRepo() {
26291
26378
  { $skip: page * limit },
26292
26379
  { $limit: limit }
26293
26380
  ];
26294
- const [items, countResult] = await Promise.all([
26295
- collection.aggregate(basePipeline, { session }).toArray(),
26296
- collection.aggregate([{ $match: query }, { $count: "total" }], { session }).toArray()
26297
- ]);
26381
+ const items = await collection.aggregate(basePipeline, { session }).toArray();
26382
+ const countResult = await collection.aggregate([{ $match: query }, { $count: "total" }], { session }).toArray();
26298
26383
  const totalCount = countResult[0]?.total || 0;
26299
26384
  const data = paginate28(items, page, limit, totalCount);
26300
26385
  setCache(cacheKey, data, 15 * 60).then(() => logger90.info(`Cache set for key: ${cacheKey}`)).catch(
@@ -26302,6 +26387,7 @@ function usePatrolRouteRepo() {
26302
26387
  );
26303
26388
  return data;
26304
26389
  } catch (error) {
26390
+ console.error("[getScheduledRoute] ERROR:", error?.message, error?.stack);
26305
26391
  throw error;
26306
26392
  }
26307
26393
  }
@@ -26588,6 +26674,7 @@ var schemaPatrolLog = Joi65.object({
26588
26674
  cameras: Joi65.array().items(schemeLogCamera).required(),
26589
26675
  status: Joi65.array().items(Joi65.string().valid("complete", "late", "incomplete")).required(),
26590
26676
  incidentReport: incidentReportLog,
26677
+ platform: Joi65.string().valid("web", "mobile").optional().allow(null, ""),
26591
26678
  createdAt: Joi65.date().optional(),
26592
26679
  updatedAt: Joi65.date().optional(),
26593
26680
  deletedAt: Joi65.date().optional()
@@ -26601,7 +26688,8 @@ var schemaUpdatePatrolLog = Joi65.object({
26601
26688
  end: Joi65.string().optional().allow(null, ""),
26602
26689
  cameras: Joi65.array().items(schemeLogCamera).optional().allow(null, ""),
26603
26690
  status: Joi65.array().items(Joi65.string().valid("complete", "late", "incomplete")).optional().allow(null, ""),
26604
- incidentReport: incidentReportLog
26691
+ incidentReport: incidentReportLog,
26692
+ platform: Joi65.string().valid("web", "mobile").optional().allow(null, "")
26605
26693
  });
26606
26694
  function MPatrolLog(value) {
26607
26695
  const { error } = schemaPatrolLog.validate(value);
@@ -26655,6 +26743,7 @@ function MPatrolLog(value) {
26655
26743
  status: value.status ?? [],
26656
26744
  route: value.route,
26657
26745
  incidentReport: value.incidentReport,
26746
+ platForm: value.platform ?? "",
26658
26747
  createdAt: value.createdAt ?? /* @__PURE__ */ new Date(),
26659
26748
  updatedAt: value.updatedAt ?? "",
26660
26749
  deletedAt: value.deletedAt ?? ""
@@ -27021,6 +27110,7 @@ function usePatrolLogService() {
27021
27110
  }
27022
27111
  }
27023
27112
  }
27113
+ value.platform = value.platform === "web" ? "web" : "mobile";
27024
27114
  const result = await _add(value, session);
27025
27115
  await session.commitTransaction();
27026
27116
  return result;
@@ -41257,6 +41347,7 @@ var schemaIncidentReport = Joi99.object({
41257
41347
  approvedBy: Joi99.string().hex().allow(null, "").optional(),
41258
41348
  approvedByName: Joi99.string().optional().allow("", null),
41259
41349
  remarks: Joi99.string().optional().allow("", null),
41350
+ platform: Joi99.string().valid("web", "mobile").optional().allow(null, ""),
41260
41351
  status: Joi99.string().valid("pending", "approved", "rejected").default("pending")
41261
41352
  });
41262
41353
  var schemaUpdateIncidentReport = Joi99.object({
@@ -41337,6 +41428,7 @@ var schemaUpdateIncidentReport = Joi99.object({
41337
41428
  approvedBy: Joi99.string().hex().allow(null, "").optional(),
41338
41429
  approvedByName: Joi99.string().optional().allow("", null),
41339
41430
  remarks: Joi99.string().optional().allow("", null),
41431
+ platform: Joi99.string().valid("web", "mobile").optional().allow(null, ""),
41340
41432
  status: Joi99.string().valid("pending", "approved", "rejected").default("pending")
41341
41433
  });
41342
41434
  function MIncidentReport(value) {
@@ -41398,6 +41490,7 @@ function MIncidentReport(value) {
41398
41490
  approvedByName: value.approvedByName ?? "",
41399
41491
  remarks: value.remarks ?? null,
41400
41492
  status: value.status ?? "pending",
41493
+ platForm: value.platform ?? "",
41401
41494
  createdAt: value.createdAt ?? /* @__PURE__ */ new Date(),
41402
41495
  updatedAt: value.updatedAt,
41403
41496
  deletedAt: value.deletedAt
@@ -41876,6 +41969,7 @@ function useIncidentReportService() {
41876
41969
  }
41877
41970
  }
41878
41971
  }
41972
+ value.platform = value.platform === "web" ? "web" : "mobile";
41879
41973
  const result = await _add(value, session);
41880
41974
  await session?.commitTransaction();
41881
41975
  return result;
@@ -42969,6 +43063,11 @@ function useOccurrenceEntryService() {
42969
43063
  if (!occurrenceEntry) {
42970
43064
  throw new Error("Occurrence entry not found.");
42971
43065
  }
43066
+ if (value.incidentReportId) {
43067
+ await _updateOccurrenceEntryById(id, value, session);
43068
+ await session?.commitTransaction();
43069
+ return "Successfully updated occurrence entry (incident report).";
43070
+ }
42972
43071
  const entryCount = await _getLatestSerialNumberInGroup(
42973
43072
  occurrenceEntry.dailyOccurrenceBookId,
42974
43073
  Number(occurrenceEntry.serialNumber)
@@ -43001,6 +43100,8 @@ function useOccurrenceEntryService() {
43001
43100
  value.date = value.date ? value.date : occurrenceEntry.date;
43002
43101
  value.userName = value.userName ? value.userName : occurrenceEntry.signature.name;
43003
43102
  value.createdByName = value.createdByName ? value.createdByName : occurrenceEntry.createdByName;
43103
+ if (occurrenceEntry.incidentReportId)
43104
+ value.incidentReportId = occurrenceEntry.incidentReportId;
43004
43105
  await _updateOccurrenceBookById(dobId, {
43005
43106
  totalInput: book.totalInput + 1
43006
43107
  });