@7365admin1/core 3.38.0 → 3.40.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
@@ -23061,6 +23061,7 @@ function useVehicleRepo() {
23061
23061
  if (error) {
23062
23062
  throw new BadRequestError69(error.details[0].message);
23063
23063
  }
23064
+ console.log("getVehicleByPlateNumber plateNumber", plateNumber);
23064
23065
  try {
23065
23066
  const data = await collection.findOne(
23066
23067
  { plateNumber, status: "active" },
@@ -23073,6 +23074,14 @@ function useVehicleRepo() {
23073
23074
  );
23074
23075
  }
23075
23076
  }
23077
+ async function findOne(query) {
23078
+ try {
23079
+ const data = await collection.findOne(query);
23080
+ return data;
23081
+ } catch (error) {
23082
+ throw new InternalServerError25("Failed to fetch findOne vehicle." + error);
23083
+ }
23084
+ }
23076
23085
  async function getVehicleBySiteAndPlateNumber(plateNumber, site, category, status) {
23077
23086
  const { error } = Joi39.string().required().validate(plateNumber);
23078
23087
  if (error) {
@@ -23381,7 +23390,8 @@ function useVehicleRepo() {
23381
23390
  getSpecificVehicleById,
23382
23391
  getBlocklistedVehicles,
23383
23392
  getVehicleBySiteAndPlateNumber,
23384
- getBlocklistedVehicleByPlateNumber
23393
+ getBlocklistedVehicleByPlateNumber,
23394
+ findOne
23385
23395
  };
23386
23396
  }
23387
23397
 
@@ -23460,12 +23470,13 @@ async function useDahuaDigestWithRetry({
23460
23470
  headers = {},
23461
23471
  timeout = 1e4,
23462
23472
  streaming = false,
23463
- retries = 3,
23473
+ retries = 10,
23464
23474
  retryDelayMs = 0
23465
23475
  }) {
23466
23476
  let lastError;
23467
23477
  for (let attempt = 1; attempt <= retries; attempt++) {
23468
23478
  try {
23479
+ const effectiveTimeout = streaming ? 0 : timeout;
23469
23480
  const response = await useDahuaDigest({
23470
23481
  host,
23471
23482
  username,
@@ -23473,16 +23484,23 @@ async function useDahuaDigestWithRetry({
23473
23484
  endpoint,
23474
23485
  method,
23475
23486
  headers,
23476
- timeout,
23487
+ timeout: effectiveTimeout,
23477
23488
  streaming
23478
23489
  });
23479
- const text = getDahuaResponseText(response);
23480
23490
  const statusCode = getDahuaStatusCode(response);
23481
- const isOk = statusCode === 200 && text.includes("OK");
23482
- if (isOk) {
23483
- return response;
23491
+ if (streaming) {
23492
+ if (statusCode === 200) {
23493
+ return response;
23494
+ }
23495
+ } else {
23496
+ const text = getDahuaResponseText(response);
23497
+ const isOk = statusCode === 200 && text.includes("OK");
23498
+ if (isOk) {
23499
+ return response;
23500
+ }
23484
23501
  }
23485
- const shouldRetry = isDahuaInvalidAuthority(text);
23502
+ const errorText = streaming ? `HTTP ${statusCode}` : getDahuaResponseText(response);
23503
+ const shouldRetry = isDahuaInvalidAuthority(errorText);
23486
23504
  if (shouldRetry && attempt < retries) {
23487
23505
  loggerDahua.warn(
23488
23506
  `[${host}] Dahua Invalid Authority. Retrying ${attempt}/${retries}`
@@ -23492,7 +23510,7 @@ async function useDahuaDigestWithRetry({
23492
23510
  }
23493
23511
  continue;
23494
23512
  }
23495
- throw new Error(`Dahua response: ${text || `HTTP ${statusCode}`}`);
23513
+ throw new Error(`Dahua response: ${errorText || `HTTP ${statusCode}`}`);
23496
23514
  } catch (error) {
23497
23515
  lastError = error;
23498
23516
  const message = error?.message || String(error);
@@ -23875,6 +23893,7 @@ function useDahuaService() {
23875
23893
  if (camera.status !== "active") {
23876
23894
  if (cameraRegistry.has(cameraId)) {
23877
23895
  loggerDahua.info(`[${camera?.siteName}-${camera?.direction}] Camera is ${camera.status}. Stopping connection.`);
23896
+ console.log(`[${camera?.siteName}-${camera?.direction}] Camera is ${camera.status}. Stopping connection.`);
23878
23897
  cameraRegistry.get(cameraId)?.abort();
23879
23898
  cameraRegistry.delete(cameraId);
23880
23899
  }
@@ -23882,6 +23901,7 @@ function useDahuaService() {
23882
23901
  }
23883
23902
  if (cameraRegistry.has(cameraId)) {
23884
23903
  loggerDahua.info(`[${camera?.siteName}-${camera?.direction}] Stopping existing connection for update.`);
23904
+ console.log(`[${camera?.siteName}-${camera?.direction}] Stopping existing connection for update.`);
23885
23905
  cameraRegistry.get(cameraId)?.abort();
23886
23906
  cameraRegistry.delete(cameraId);
23887
23907
  }
@@ -23901,11 +23921,14 @@ function useDahuaService() {
23901
23921
  }
23902
23922
  async function getTrafficJunction(camera, signal, onDetected) {
23903
23923
  console.log(`getTrafficJunction camera object`, camera);
23924
+ let authFailureCount = 0;
23925
+ const MAX_AUTH_RETRIES = 10;
23904
23926
  while (!signal.aborted) {
23905
23927
  let bufferQueue = null;
23906
23928
  let response = null;
23929
+ let isAuthFailure = false;
23907
23930
  try {
23908
- response = await useDahuaDigest({
23931
+ response = await useDahuaDigestWithRetry({
23909
23932
  host: camera?.host,
23910
23933
  username: camera?.username,
23911
23934
  password: camera?.password,
@@ -23913,16 +23936,32 @@ function useDahuaService() {
23913
23936
  timeout: 2e4,
23914
23937
  streaming: true
23915
23938
  });
23916
- if (response.statusCode === 401) {
23917
- loggerDahua.error(`[${camera?.siteName}-${camera?.direction}] 401 Unauthorized. Potential handshake or wrong credentials.`);
23918
- continue;
23919
- } else if ([400, 401, 403, 500].includes(response.statusCode)) {
23920
- loggerDahua.error(`[${camera?.siteName}-${camera?.direction}] Connection error: ${response.statusCode}`);
23939
+ const statusCode = getDahuaStatusCode(response);
23940
+ if (statusCode === 401) {
23941
+ loggerDahua.error(`[${camera?.siteName}-${camera?.direction}] 401 Unauthorized - Handshake or Wrong Credentials`);
23942
+ console.log(`[${camera?.siteName}-${camera?.direction}] 401 Unauthorized - Handshake or Wrong Credentials`);
23943
+ throw new Error("401 Unauthorized - Handshake or Wrong Credentials");
23944
+ } else if (statusCode === 403) {
23945
+ loggerDahua.error(
23946
+ `[${camera?.siteName}-${camera?.direction}] 403 Forbidden. Account locked or invalid permissions.`
23947
+ );
23921
23948
  if (onDetected) {
23922
- onDetected({ site: camera?.site, messagePermanent: `Camera [${camera?.siteName}-${camera?.direction}]. Connection Error. Check username and password.` });
23949
+ onDetected({
23950
+ site: camera?.site,
23951
+ direction: camera?.direction,
23952
+ messagePermanent: `Camera [${camera?.siteName}-${camera?.direction}]. Connection Error. Check username and password and try after 10 minutes.`
23953
+ });
23954
+ }
23955
+ return;
23956
+ } else if ([400, 500].includes(statusCode)) {
23957
+ loggerDahua.error(`[${camera?.siteName}-${camera?.direction}] Connection error: ${statusCode}`);
23958
+ if (onDetected) {
23959
+ onDetected({ site: camera?.site, messagePermanent: `Camera [${camera?.siteName}-${camera?.direction}]. Connection Error. Possible Not Authorized.` });
23923
23960
  }
23924
23961
  return;
23925
23962
  }
23963
+ authFailureCount = 0;
23964
+ isAuthFailure = false;
23926
23965
  loggerDahua.info(`[${camera?.siteName}-${camera?.direction}] Successfully connected to ANPR.`);
23927
23966
  const contentType = response.res.headers["content-type"];
23928
23967
  const boundaryMatch = contentType?.match(/boundary=(.*)$/i);
@@ -23967,15 +24006,53 @@ function useDahuaService() {
23967
24006
  loggerDahua.info(`[${camera?.siteName}-${camera?.direction}] Connection closed by system (Abort).`);
23968
24007
  break;
23969
24008
  }
23970
- loggerDahua.error(
23971
- `[${camera?.siteName}-${camera?.direction}] Connection lost or error: ${error.message || error}. Retrying in 10 seconds...`
23972
- );
23973
- if (onDetected) {
23974
- onDetected({
23975
- site: camera?.site,
23976
- direction: camera?.direction,
23977
- message: `Camera in [${camera?.siteName}-${camera?.direction}] Connection Lost. Retrying in 10 seconds. Check the camera if this issue persists, Or set the camera to Inactive to stop this notification.`
23978
- });
24009
+ const errMsg = String(error?.message || error);
24010
+ if (errMsg.includes("403") || errMsg.includes("Forbidden")) {
24011
+ loggerDahua.error(
24012
+ `[${camera?.siteName}-${camera?.direction}] 403 Forbidden thrown by Dahua client. Account locked or invalid credentials.`
24013
+ );
24014
+ if (onDetected) {
24015
+ onDetected({
24016
+ site: camera?.site,
24017
+ direction: camera?.direction,
24018
+ messagePermanent: `Camera [${camera?.siteName}-${camera?.direction}]. Connection Error. Check username and password and try after 10 minutes.`
24019
+ });
24020
+ }
24021
+ return;
24022
+ }
24023
+ const isAuthError = errMsg.includes("401") || errMsg.includes("Unauthorized") || errMsg.includes("Invalid Authority");
24024
+ if (isAuthError) {
24025
+ authFailureCount++;
24026
+ isAuthFailure = true;
24027
+ loggerDahua.warn(
24028
+ `[${camera?.siteName}-${camera?.direction}] Auth attempt ${authFailureCount}/${MAX_AUTH_RETRIES}`
24029
+ );
24030
+ if (authFailureCount >= MAX_AUTH_RETRIES) {
24031
+ loggerDahua.error(
24032
+ `[${camera?.siteName}-${camera?.direction}] Max auth failures reached (${MAX_AUTH_RETRIES}). Wrong credentials.`
24033
+ );
24034
+ if (onDetected) {
24035
+ onDetected({
24036
+ site: camera?.site,
24037
+ direction: camera?.direction,
24038
+ messagePermanent: `Camera [${camera?.siteName}-${camera?.direction}]. Connection Error. Check username and password.`
24039
+ });
24040
+ }
24041
+ return;
24042
+ }
24043
+ } else {
24044
+ authFailureCount = 0;
24045
+ isAuthFailure = false;
24046
+ loggerDahua.error(
24047
+ `[${camera?.siteName}-${camera?.direction}] Connection lost or error: ${error.message || error}. Retrying in 10 seconds...`
24048
+ );
24049
+ if (onDetected) {
24050
+ onDetected({
24051
+ site: camera?.site,
24052
+ direction: camera?.direction,
24053
+ message: `Camera in [${camera?.siteName}-${camera?.direction}] Connection Lost. Retrying in 10 seconds. Check the camera if this issue persists, Or set the camera to Inactive to stop this notification.`
24054
+ });
24055
+ }
23979
24056
  }
23980
24057
  } finally {
23981
24058
  if (bufferQueue?.destroy) {
@@ -23997,7 +24074,8 @@ function useDahuaService() {
23997
24074
  }
23998
24075
  }
23999
24076
  if (!signal.aborted) {
24000
- await new Promise((res) => setTimeout(res, 1e4));
24077
+ const waitMs = isAuthFailure ? 1e3 : 1e4;
24078
+ await new Promise((res) => setTimeout(res, waitMs));
24001
24079
  }
24002
24080
  }
24003
24081
  loggerDahua.info(`[${camera?.siteName}-${camera?.direction}] ANPR Listener stopped.`);
@@ -24668,7 +24746,8 @@ function useSiteCameraRepo() {
24668
24746
  throw new BadRequestError71("Site camera not found.");
24669
24747
  }
24670
24748
  if (result?._id) {
24671
- const result2 = await findOne({ _id: new ObjectId44(result._id) });
24749
+ const result2 = await findOne({ _id: new ObjectId44(result._id) }, void 0, { session });
24750
+ console.log("updateById result2", result2);
24672
24751
  if (result2?._id) {
24673
24752
  const { listenToCamera } = useDahuaService();
24674
24753
  await listenToCamera(result2);
@@ -24733,6 +24812,9 @@ function useSiteCameraRepo() {
24733
24812
  throw new BadRequestError71("Invalid site ID format");
24734
24813
  }
24735
24814
  }
24815
+ if (value.status) {
24816
+ query.status = value.status;
24817
+ }
24736
24818
  try {
24737
24819
  const items = await collection.aggregate([
24738
24820
  {
@@ -24803,7 +24885,7 @@ function useSiteCameraRepo() {
24803
24885
  throw new BadRequestError71("Failed to retrieve site cameras.");
24804
24886
  }
24805
24887
  }
24806
- async function findOne(query, project = {}) {
24888
+ async function findOne(query, project = {}, options = {}) {
24807
24889
  try {
24808
24890
  const pipeline = [
24809
24891
  { $match: query },
@@ -24852,14 +24934,14 @@ function useSiteCameraRepo() {
24852
24934
  if (Object.keys(project).length > 0) {
24853
24935
  pipeline.push({ $project: project });
24854
24936
  }
24855
- const result = await collection.aggregate(pipeline).toArray();
24937
+ const result = await collection.aggregate(pipeline, options).toArray();
24856
24938
  return result.length > 0 ? result[0] : null;
24857
24939
  } catch (error) {
24858
24940
  console.error("Error in findOne aggregation:", error);
24859
24941
  throw error;
24860
24942
  }
24861
24943
  }
24862
- async function findMany(query, project = {}) {
24944
+ async function findMany(query, project = {}, options = {}) {
24863
24945
  try {
24864
24946
  const pipeline = [
24865
24947
  { $match: query },
@@ -24908,10 +24990,10 @@ function useSiteCameraRepo() {
24908
24990
  if (Object.keys(project).length > 0) {
24909
24991
  pipeline.push({ $project: project });
24910
24992
  }
24911
- const result = await collection.aggregate(pipeline).toArray();
24993
+ const result = await collection.aggregate(pipeline, options).toArray();
24912
24994
  return result.length > 0 ? result : [];
24913
24995
  } catch (error) {
24914
- console.error("Error in findOne aggregation:", error);
24996
+ console.error("Error in findMany aggregation:", error);
24915
24997
  throw error;
24916
24998
  }
24917
24999
  }
@@ -24928,7 +25010,9 @@ function useSiteCameraRepo() {
24928
25010
  getBySiteGuardPost,
24929
25011
  updateById,
24930
25012
  deleteById,
24931
- getAllCameraWithPassword
25013
+ getAllCameraWithPassword,
25014
+ findMany,
25015
+ findOne
24932
25016
  };
24933
25017
  }
24934
25018
 
@@ -25060,9 +25144,9 @@ function usePersonRepo() {
25060
25144
  { email: { $regex: search, $options: "i" } },
25061
25145
  { nric: { $regex: search, $options: "i" } },
25062
25146
  { "plates.plateNumber": { $regex: search, $options: "i" } },
25063
- { contact: { $regex: makeContainsRegex(contact) } },
25064
- { contacts: { $regex: makeContainsRegex(contact) } },
25065
- { plateNumbers: { $regex: makeContainsRegex(plateNumber) } }
25147
+ { contact: { $regex: makeContainsRegex(search) } },
25148
+ { contacts: { $regex: makeContainsRegex(search) } },
25149
+ { plateNumbers: { $regex: makeContainsRegex(search) } }
25066
25150
  ]
25067
25151
  },
25068
25152
  ...ObjectId45.isValid(org) && { org: new ObjectId45(org) },
@@ -26865,7 +26949,8 @@ function useVehicleService() {
26865
26949
  deleteExpiredVehicles: _deleteExpiredVehicles,
26866
26950
  getVehicleByPlateNumber: _getVehicleByPlateNumber,
26867
26951
  getAllExpiredVehicles: _getAllExpiredVehicles,
26868
- bulkUpsertVehicles: _bulkUpsertVehicles
26952
+ bulkUpsertVehicles: _bulkUpsertVehicles,
26953
+ findOne
26869
26954
  } = useVehicleRepo();
26870
26955
  const {
26871
26956
  addPlateNumber: _addPlateNumber,
@@ -26889,15 +26974,18 @@ function useVehicleService() {
26889
26974
  throw new Error("Unable to start session for vehicle service.");
26890
26975
  }
26891
26976
  }
26977
+ console.log("add vehicle service value.plateNumber", value.plateNumber);
26892
26978
  const [_vehiclePlateNumber] = value.plateNumber;
26893
- const [existingPlateNumber, org] = await Promise.all([
26894
- _getVehicleByPlateNumber(_vehiclePlateNumber),
26979
+ console.log("add vehicle service plateNumber", _vehiclePlateNumber);
26980
+ const [
26981
+ // existingPlateNumber,
26982
+ org
26983
+ ] = await Promise.all([
26984
+ // _getVehicleByPlateNumber(_vehiclePlateNumber),
26895
26985
  _getById(value.org)
26896
26986
  ]);
26897
26987
  if (!org)
26898
26988
  throw new BadRequestError75("Org not found");
26899
- if (existingPlateNumber)
26900
- throw new BadRequestError75("Vehicle plate number already exists");
26901
26989
  if (!Object.values(OrgNature).includes(org.nature)) {
26902
26990
  throw new BadRequestError75(
26903
26991
  "This organization is not allowed to add vehicles."
@@ -26910,7 +26998,6 @@ function useVehicleService() {
26910
26998
  user,
26911
26999
  org: value.org
26912
27000
  });
26913
- console.log("userPermissions vehicle.service.ts", userPermissions);
26914
27001
  const hasSecurityBypass = addedBySecurity && Array.isArray(userPermissions) && (userPermissions.includes("*") || userPermissions.includes("vehicleManagement:approve-vehicle"));
26915
27002
  if (hasSecurityBypass) {
26916
27003
  addedByPM = true;
@@ -26969,17 +27056,16 @@ function useVehicleService() {
26969
27056
  const siteCameraReq = await _getAllSiteCameras({
26970
27057
  site: value.site,
26971
27058
  type: "anpr",
26972
- direction: ["both", "entry"],
27059
+ direction: ["both", "entry", "residents"],
26973
27060
  page,
26974
- limit
27061
+ limit,
27062
+ status: "active"
26975
27063
  });
26976
27064
  pages = siteCameraReq.pages || 1;
26977
27065
  siteCameras.push(...siteCameraReq.items);
26978
27066
  page++;
26979
27067
  } while (page <= pages);
26980
- if (!siteCameras.length) {
26981
- throw new BadRequestError75("No site cameras found.");
26982
- }
27068
+ console.log("add vehicle service siteCameras", siteCameras);
26983
27069
  }
26984
27070
  for (const plateNumber of plateNumbers) {
26985
27071
  const vehicleValue = {
@@ -31633,6 +31719,7 @@ function useSiteCameraService() {
31633
31719
  const { add: _add, getAllCameraWithPassword: _getAllCameraWithPassword } = useSiteCameraRepo();
31634
31720
  const { updateById: _updateById } = useSiteRepo();
31635
31721
  const dahuaService = useDahuaService();
31722
+ const { updateById: _siteCameraUpdateById } = useSiteCameraRepo();
31636
31723
  let cleanupTask = null;
31637
31724
  async function add(value) {
31638
31725
  const { error } = schemaSiteCamera.validate(value);
@@ -31666,6 +31753,7 @@ function useSiteCameraService() {
31666
31753
  type: "anpr",
31667
31754
  page,
31668
31755
  limit
31756
+ // site: "6923d6664150ca6a69b9f2b2"
31669
31757
  });
31670
31758
  pages = siteCameraReq.pages || 1;
31671
31759
  siteCameras.push(...siteCameraReq.items);
@@ -31681,9 +31769,27 @@ function useSiteCameraService() {
31681
31769
  }
31682
31770
  }
31683
31771
  }
31772
+ async function updateById(id, value) {
31773
+ const session = useAtlas43.getClient()?.startSession();
31774
+ if (!session) {
31775
+ throw new Error("Unable to start session for site camera service.");
31776
+ }
31777
+ try {
31778
+ session.startTransaction();
31779
+ const result = await _siteCameraUpdateById(id, value, session);
31780
+ await session.commitTransaction();
31781
+ return result;
31782
+ } catch (error) {
31783
+ await session.abortTransaction();
31784
+ throw error;
31785
+ } finally {
31786
+ session.endSession();
31787
+ }
31788
+ }
31684
31789
  return {
31685
31790
  add,
31686
- listenToCapturedPlateNumber
31791
+ listenToCapturedPlateNumber,
31792
+ updateById
31687
31793
  };
31688
31794
  }
31689
31795
 
@@ -31691,10 +31797,9 @@ function useSiteCameraService() {
31691
31797
  import { BadRequestError as BadRequestError89 } from "@7365admin1/node-server-utils";
31692
31798
  import Joi49 from "joi";
31693
31799
  function useSiteCameraController() {
31694
- const { add: _add } = useSiteCameraService();
31800
+ const { add: _add, updateById: _updateById } = useSiteCameraService();
31695
31801
  const {
31696
31802
  getAll: _getAll,
31697
- updateById: _updateById,
31698
31803
  deleteById: _deleteById
31699
31804
  } = useSiteCameraRepo();
31700
31805
  async function add(req, res, next) {
@@ -32983,7 +33088,7 @@ function useAttendanceRepository() {
32983
33088
  localField: "user",
32984
33089
  foreignField: "_id",
32985
33090
  as: "user",
32986
- pipeline: [{ $project: { name: 1 } }]
33091
+ pipeline: [{ $project: { name: 1, profile: 1 } }]
32987
33092
  }
32988
33093
  },
32989
33094
  { $unwind: { path: "$user", preserveNullAndEmptyArrays: true } }
@@ -32997,6 +33102,7 @@ function useAttendanceRepository() {
32997
33102
  {
32998
33103
  $project: {
32999
33104
  userName: "$user.name",
33105
+ userProfile: "$user.profile",
33000
33106
  checkInTimestamp: "$checkIn.timestamp",
33001
33107
  checkOutTimestamp: "$checkOut.timestamp",
33002
33108
  createdAt: 1
@@ -35487,7 +35593,7 @@ function useVisitorTransactionService() {
35487
35593
  value.unitName = unit?.name;
35488
35594
  }
35489
35595
  const { nric = "", name = "", contact = "", plateNumber = "", email = "", site = "", type = "guest", company, createdBy } = value;
35490
- if (/^(guest|walk-in|contractor|delivery)$/.test(type)) {
35596
+ if (/^(guest|walk-in|contractor|delivery)$/.test(type) && site) {
35491
35597
  await addUpdateFromVisitor({
35492
35598
  nric,
35493
35599
  name,
@@ -35718,7 +35824,7 @@ function useVisitorTransactionService() {
35718
35824
  value.checkIn = isNaN(parsed.getTime()) ? null : parsed;
35719
35825
  }
35720
35826
  const { nric = "", name = "", contact = "", plateNumber = "", email = "", site = "", type = "guest", company, createdBy } = value;
35721
- if (/^(guest|walk-in|contractor|delivery)$/.test(type) && !value.checkOut) {
35827
+ if (/^(guest|walk-in|contractor|delivery)$/.test(type) && site) {
35722
35828
  await addUpdateFromVisitor({
35723
35829
  nric,
35724
35830
  name,
@@ -35902,7 +36008,9 @@ function useVisitorTransactionService() {
35902
36008
  value.site,
35903
36009
  session,
35904
36010
  void 0,
35905
- id
36011
+ id,
36012
+ void 0,
36013
+ true
35906
36014
  );
35907
36015
  item.receivedDate = /* @__PURE__ */ new Date();
35908
36016
  item.status = "Not Returned" /* NOT_RETURNED */;
@@ -35951,7 +36059,9 @@ function useVisitorTransactionService() {
35951
36059
  value.site,
35952
36060
  session,
35953
36061
  void 0,
35954
- id
36062
+ id,
36063
+ void 0,
36064
+ true
35955
36065
  );
35956
36066
  item.receivedDate = /* @__PURE__ */ new Date();
35957
36067
  item.status = "Not Returned" /* NOT_RETURNED */;
@@ -55352,8 +55462,11 @@ function useNfcPatrolRouteRepo() {
55352
55462
  const query = {
55353
55463
  site
55354
55464
  };
55355
- if (search) {
55356
- query.$text = { $search: search };
55465
+ if (search?.trim()) {
55466
+ query.name = {
55467
+ $regex: search.trim(),
55468
+ $options: "i"
55469
+ };
55357
55470
  }
55358
55471
  if (filter === "today" || filter === "notToday") {
55359
55472
  const singaporeDateString = (/* @__PURE__ */ new Date()).toLocaleString("en-US", {
@@ -58775,13 +58888,9 @@ function useNfcPatrolLogRepo() {
58775
58888
  };
58776
58889
  if (date) {
58777
58890
  if (type === "month") {
58778
- const [year, month] = date.split("-").map(Number);
58779
- const endDay = new Date(year, month, 0).getDate();
58780
58891
  query.date = {
58781
- $gte: `${year}-${String(month).padStart(2, "0")}-01`,
58782
- $lte: `${year}-${String(month).padStart(2, "0")}-${String(
58783
- endDay
58784
- ).padStart(2, "0")}`
58892
+ $gte: `${date}-01-01`,
58893
+ $lte: `${date}-12-31`
58785
58894
  };
58786
58895
  } else {
58787
58896
  query.date = date;
@@ -59109,47 +59218,52 @@ function useNfcPatrolLogController() {
59109
59218
  page: Joi111.number().integer().min(1).default(1),
59110
59219
  limit: Joi111.number().integer().min(1).max(100).default(10),
59111
59220
  site: Joi111.string().length(24).hex().required(),
59112
- date: Joi111.string().pattern(/^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$/).required().messages({
59113
- "string.pattern.base": "Date must be in YYYY-MM-DD format (e.g. 2026-07-17)"
59221
+ date: Joi111.string().pattern(/^\d{4}(-(0[1-9]|1[0-2])(-(0[1-9]|[12][0-9]|3[01]))?)?$/).optional().messages({
59222
+ "string.pattern.base": "Date must be YYYY or YYYY-MM or YYYY-MM-DD"
59114
59223
  }),
59115
59224
  type: Joi111.string().valid("month").optional(),
59116
59225
  route: Joi111.object({
59117
- _id: Joi111.string().length(24).hex().required(),
59118
- startTime: Joi111.string().pattern(/^([01]\d|2[0-3]):([0-5]\d)$/).required()
59119
- }).required()
59226
+ _id: Joi111.string().length(24).hex().optional(),
59227
+ startTime: Joi111.string().pattern(/^([01]\d|2[0-3]):([0-5]\d)$/).optional().messages({
59228
+ "string.pattern.base": "startTime must be in HH:mm 24-hour format"
59229
+ })
59230
+ }).min(1).optional()
59120
59231
  });
59121
59232
  const query = {
59233
+ page: req.query.page,
59234
+ limit: req.query.limit,
59122
59235
  site: req.query.site,
59123
59236
  date: req.query.date,
59124
- route: {
59125
- _id: req.query["route._id"],
59126
- startTime: req.query["route.startTime"]
59127
- }
59237
+ type: req.query.type,
59238
+ route: req.query["route._id"] || req.query["route.startTime"] ? {
59239
+ ...req.query["route._id"] ? { _id: req.query["route._id"] } : {},
59240
+ ...req.query["route.startTime"] ? { startTime: req.query["route.startTime"] } : {}
59241
+ } : void 0
59128
59242
  };
59129
59243
  const { error, value } = validation.validate(query, {
59130
- abortEarly: false
59244
+ abortEarly: false,
59245
+ allowUnknown: true,
59246
+ stripUnknown: true
59131
59247
  });
59132
59248
  if (error) {
59133
- const messages = error.details.map((d) => d.message).join(", ");
59134
- logger159.log({ level: "error", message: messages });
59135
- next(new BadRequestError181(messages));
59136
- return;
59249
+ return next(
59250
+ new BadRequestError181(
59251
+ error.details.map((d) => d.message).join(", ")
59252
+ )
59253
+ );
59137
59254
  }
59138
- const { page, limit, site, date, route } = value;
59139
59255
  try {
59140
59256
  const data = await _getAllBySite({
59141
- page,
59142
- limit,
59143
- site,
59144
- date,
59145
- route
59257
+ page: value.page,
59258
+ limit: value.limit,
59259
+ site: value.site,
59260
+ date: value.date,
59261
+ type: value.type,
59262
+ route: value.route
59146
59263
  });
59147
- res.status(200).json(data);
59148
- return;
59264
+ return res.status(200).json(data);
59149
59265
  } catch (error2) {
59150
- logger159.log({ level: "error", message: error2.message });
59151
- next(error2);
59152
- return;
59266
+ return next(error2);
59153
59267
  }
59154
59268
  }
59155
59269
  async function completeCheckpoint(req, res, next) {
@@ -64737,34 +64851,62 @@ import axios3 from "axios";
64737
64851
 
64738
64852
  // src/utils/payment-signature.util.ts
64739
64853
  import * as crypto3 from "crypto";
64854
+ function hasOwn(obj, key) {
64855
+ return Object.prototype.hasOwnProperty.call(obj, key);
64856
+ }
64857
+ function sha512(input) {
64858
+ return crypto3.createHash("sha512").update(input).digest("hex");
64859
+ }
64860
+ function assertSecret(secretKey) {
64861
+ if (!secretKey) {
64862
+ throw new Error("Payment signature: missing secret key.");
64863
+ }
64864
+ }
64740
64865
  function paymentSignature(params, secretKey) {
64741
- const fieldOrder = ["mid", "order_id", "payment_type", "amount", "ccy", "card_no", "exp_date", "cvv2"];
64866
+ assertSecret(secretKey);
64867
+ const fieldOrder = [
64868
+ "mid",
64869
+ "order_id",
64870
+ "payment_type",
64871
+ "amount",
64872
+ "ccy",
64873
+ "card_no",
64874
+ "exp_date",
64875
+ "cvv2"
64876
+ ];
64742
64877
  let concatenated = "";
64743
64878
  fieldOrder.forEach((v) => {
64744
- if (!params.hasOwnProperty(v)) {
64879
+ if (!hasOwn(params, v)) {
64745
64880
  return;
64746
- } else if (v === "cvv2") {
64747
- concatenated += (params[v] ?? "").toString().slice(-1);
64881
+ }
64882
+ const raw = String(params[v] ?? "");
64883
+ if (v === "cvv2") {
64884
+ concatenated += raw.slice(-1);
64748
64885
  } else if (v === "card_no") {
64749
- concatenated += (params[v] ?? "").substring(0, 6) + (params[v] ?? "").slice(-4);
64886
+ concatenated += raw.substring(0, 6) + raw.slice(-4);
64750
64887
  } else {
64751
- concatenated += params[v];
64888
+ concatenated += raw;
64752
64889
  }
64753
64890
  });
64754
64891
  concatenated += secretKey;
64755
- const signature = crypto3.createHash("sha512").update(concatenated).digest("hex");
64756
- return signature;
64892
+ return sha512(concatenated);
64757
64893
  }
64758
64894
  function genericSignature(params, secretKey) {
64759
- const paramsCopy = { ...params };
64895
+ assertSecret(secretKey);
64896
+ const paramsCopy = {
64897
+ ...params
64898
+ };
64760
64899
  delete paramsCopy["signature"];
64761
- const sortedParams = Object.keys(paramsCopy).sort().reduce((obj, key) => {
64762
- obj[key] = paramsCopy[key];
64763
- return obj;
64764
- }, {});
64765
- const concatenated = Object.values(sortedParams).join("") + secretKey;
64766
- const signature = crypto3.createHash("sha512").update(concatenated).digest("hex");
64767
- return signature;
64900
+ const concatenated = Object.keys(paramsCopy).sort().map((key) => {
64901
+ const value = paramsCopy[key];
64902
+ if (value !== null && typeof value === "object") {
64903
+ throw new Error(
64904
+ `Payment signature: non-primitive value for field "${key}".`
64905
+ );
64906
+ }
64907
+ return String(value ?? "");
64908
+ }).join("") + secretKey;
64909
+ return sha512(concatenated);
64768
64910
  }
64769
64911
 
64770
64912
  // src/repositories/reddot-payment.repository.ts
@@ -65193,7 +65335,7 @@ var useRedDotPaymentSvc = () => {
65193
65335
  };
65194
65336
  if (!process.env.MERCHANT_ENQUIRY) {
65195
65337
  throw new Error(
65196
- "MERCHANT_PAYMENT environment variable is not defined."
65338
+ "MERCHANT_ENQUIRY environment variable is not defined."
65197
65339
  );
65198
65340
  }
65199
65341
  const url = process.env.MERCHANT_ENQUIRY;