@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.js CHANGED
@@ -23318,6 +23318,7 @@ function useVehicleRepo() {
23318
23318
  if (error) {
23319
23319
  throw new import_node_server_utils71.BadRequestError(error.details[0].message);
23320
23320
  }
23321
+ console.log("getVehicleByPlateNumber plateNumber", plateNumber);
23321
23322
  try {
23322
23323
  const data = await collection.findOne(
23323
23324
  { plateNumber, status: "active" },
@@ -23330,6 +23331,14 @@ function useVehicleRepo() {
23330
23331
  );
23331
23332
  }
23332
23333
  }
23334
+ async function findOne(query) {
23335
+ try {
23336
+ const data = await collection.findOne(query);
23337
+ return data;
23338
+ } catch (error) {
23339
+ throw new import_node_server_utils71.InternalServerError("Failed to fetch findOne vehicle." + error);
23340
+ }
23341
+ }
23333
23342
  async function getVehicleBySiteAndPlateNumber(plateNumber, site, category, status) {
23334
23343
  const { error } = import_joi39.default.string().required().validate(plateNumber);
23335
23344
  if (error) {
@@ -23638,7 +23647,8 @@ function useVehicleRepo() {
23638
23647
  getSpecificVehicleById,
23639
23648
  getBlocklistedVehicles,
23640
23649
  getVehicleBySiteAndPlateNumber,
23641
- getBlocklistedVehicleByPlateNumber
23650
+ getBlocklistedVehicleByPlateNumber,
23651
+ findOne
23642
23652
  };
23643
23653
  }
23644
23654
 
@@ -23717,12 +23727,13 @@ async function useDahuaDigestWithRetry({
23717
23727
  headers = {},
23718
23728
  timeout = 1e4,
23719
23729
  streaming = false,
23720
- retries = 3,
23730
+ retries = 10,
23721
23731
  retryDelayMs = 0
23722
23732
  }) {
23723
23733
  let lastError;
23724
23734
  for (let attempt = 1; attempt <= retries; attempt++) {
23725
23735
  try {
23736
+ const effectiveTimeout = streaming ? 0 : timeout;
23726
23737
  const response = await useDahuaDigest({
23727
23738
  host,
23728
23739
  username,
@@ -23730,16 +23741,23 @@ async function useDahuaDigestWithRetry({
23730
23741
  endpoint,
23731
23742
  method,
23732
23743
  headers,
23733
- timeout,
23744
+ timeout: effectiveTimeout,
23734
23745
  streaming
23735
23746
  });
23736
- const text = getDahuaResponseText(response);
23737
23747
  const statusCode = getDahuaStatusCode(response);
23738
- const isOk = statusCode === 200 && text.includes("OK");
23739
- if (isOk) {
23740
- return response;
23748
+ if (streaming) {
23749
+ if (statusCode === 200) {
23750
+ return response;
23751
+ }
23752
+ } else {
23753
+ const text = getDahuaResponseText(response);
23754
+ const isOk = statusCode === 200 && text.includes("OK");
23755
+ if (isOk) {
23756
+ return response;
23757
+ }
23741
23758
  }
23742
- const shouldRetry = isDahuaInvalidAuthority(text);
23759
+ const errorText = streaming ? `HTTP ${statusCode}` : getDahuaResponseText(response);
23760
+ const shouldRetry = isDahuaInvalidAuthority(errorText);
23743
23761
  if (shouldRetry && attempt < retries) {
23744
23762
  loggerDahua.warn(
23745
23763
  `[${host}] Dahua Invalid Authority. Retrying ${attempt}/${retries}`
@@ -23749,7 +23767,7 @@ async function useDahuaDigestWithRetry({
23749
23767
  }
23750
23768
  continue;
23751
23769
  }
23752
- throw new Error(`Dahua response: ${text || `HTTP ${statusCode}`}`);
23770
+ throw new Error(`Dahua response: ${errorText || `HTTP ${statusCode}`}`);
23753
23771
  } catch (error) {
23754
23772
  lastError = error;
23755
23773
  const message = error?.message || String(error);
@@ -24132,6 +24150,7 @@ function useDahuaService() {
24132
24150
  if (camera.status !== "active") {
24133
24151
  if (cameraRegistry.has(cameraId)) {
24134
24152
  loggerDahua.info(`[${camera?.siteName}-${camera?.direction}] Camera is ${camera.status}. Stopping connection.`);
24153
+ console.log(`[${camera?.siteName}-${camera?.direction}] Camera is ${camera.status}. Stopping connection.`);
24135
24154
  cameraRegistry.get(cameraId)?.abort();
24136
24155
  cameraRegistry.delete(cameraId);
24137
24156
  }
@@ -24139,6 +24158,7 @@ function useDahuaService() {
24139
24158
  }
24140
24159
  if (cameraRegistry.has(cameraId)) {
24141
24160
  loggerDahua.info(`[${camera?.siteName}-${camera?.direction}] Stopping existing connection for update.`);
24161
+ console.log(`[${camera?.siteName}-${camera?.direction}] Stopping existing connection for update.`);
24142
24162
  cameraRegistry.get(cameraId)?.abort();
24143
24163
  cameraRegistry.delete(cameraId);
24144
24164
  }
@@ -24158,11 +24178,14 @@ function useDahuaService() {
24158
24178
  }
24159
24179
  async function getTrafficJunction(camera, signal, onDetected) {
24160
24180
  console.log(`getTrafficJunction camera object`, camera);
24181
+ let authFailureCount = 0;
24182
+ const MAX_AUTH_RETRIES = 10;
24161
24183
  while (!signal.aborted) {
24162
24184
  let bufferQueue = null;
24163
24185
  let response = null;
24186
+ let isAuthFailure = false;
24164
24187
  try {
24165
- response = await useDahuaDigest({
24188
+ response = await useDahuaDigestWithRetry({
24166
24189
  host: camera?.host,
24167
24190
  username: camera?.username,
24168
24191
  password: camera?.password,
@@ -24170,16 +24193,32 @@ function useDahuaService() {
24170
24193
  timeout: 2e4,
24171
24194
  streaming: true
24172
24195
  });
24173
- if (response.statusCode === 401) {
24174
- loggerDahua.error(`[${camera?.siteName}-${camera?.direction}] 401 Unauthorized. Potential handshake or wrong credentials.`);
24175
- continue;
24176
- } else if ([400, 401, 403, 500].includes(response.statusCode)) {
24177
- loggerDahua.error(`[${camera?.siteName}-${camera?.direction}] Connection error: ${response.statusCode}`);
24196
+ const statusCode = getDahuaStatusCode(response);
24197
+ if (statusCode === 401) {
24198
+ loggerDahua.error(`[${camera?.siteName}-${camera?.direction}] 401 Unauthorized - Handshake or Wrong Credentials`);
24199
+ console.log(`[${camera?.siteName}-${camera?.direction}] 401 Unauthorized - Handshake or Wrong Credentials`);
24200
+ throw new Error("401 Unauthorized - Handshake or Wrong Credentials");
24201
+ } else if (statusCode === 403) {
24202
+ loggerDahua.error(
24203
+ `[${camera?.siteName}-${camera?.direction}] 403 Forbidden. Account locked or invalid permissions.`
24204
+ );
24178
24205
  if (onDetected) {
24179
- onDetected({ site: camera?.site, messagePermanent: `Camera [${camera?.siteName}-${camera?.direction}]. Connection Error. Check username and password.` });
24206
+ onDetected({
24207
+ site: camera?.site,
24208
+ direction: camera?.direction,
24209
+ messagePermanent: `Camera [${camera?.siteName}-${camera?.direction}]. Connection Error. Check username and password and try after 10 minutes.`
24210
+ });
24211
+ }
24212
+ return;
24213
+ } else if ([400, 500].includes(statusCode)) {
24214
+ loggerDahua.error(`[${camera?.siteName}-${camera?.direction}] Connection error: ${statusCode}`);
24215
+ if (onDetected) {
24216
+ onDetected({ site: camera?.site, messagePermanent: `Camera [${camera?.siteName}-${camera?.direction}]. Connection Error. Possible Not Authorized.` });
24180
24217
  }
24181
24218
  return;
24182
24219
  }
24220
+ authFailureCount = 0;
24221
+ isAuthFailure = false;
24183
24222
  loggerDahua.info(`[${camera?.siteName}-${camera?.direction}] Successfully connected to ANPR.`);
24184
24223
  const contentType = response.res.headers["content-type"];
24185
24224
  const boundaryMatch = contentType?.match(/boundary=(.*)$/i);
@@ -24224,15 +24263,53 @@ function useDahuaService() {
24224
24263
  loggerDahua.info(`[${camera?.siteName}-${camera?.direction}] Connection closed by system (Abort).`);
24225
24264
  break;
24226
24265
  }
24227
- loggerDahua.error(
24228
- `[${camera?.siteName}-${camera?.direction}] Connection lost or error: ${error.message || error}. Retrying in 10 seconds...`
24229
- );
24230
- if (onDetected) {
24231
- onDetected({
24232
- site: camera?.site,
24233
- direction: camera?.direction,
24234
- 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.`
24235
- });
24266
+ const errMsg = String(error?.message || error);
24267
+ if (errMsg.includes("403") || errMsg.includes("Forbidden")) {
24268
+ loggerDahua.error(
24269
+ `[${camera?.siteName}-${camera?.direction}] 403 Forbidden thrown by Dahua client. Account locked or invalid credentials.`
24270
+ );
24271
+ if (onDetected) {
24272
+ onDetected({
24273
+ site: camera?.site,
24274
+ direction: camera?.direction,
24275
+ messagePermanent: `Camera [${camera?.siteName}-${camera?.direction}]. Connection Error. Check username and password and try after 10 minutes.`
24276
+ });
24277
+ }
24278
+ return;
24279
+ }
24280
+ const isAuthError = errMsg.includes("401") || errMsg.includes("Unauthorized") || errMsg.includes("Invalid Authority");
24281
+ if (isAuthError) {
24282
+ authFailureCount++;
24283
+ isAuthFailure = true;
24284
+ loggerDahua.warn(
24285
+ `[${camera?.siteName}-${camera?.direction}] Auth attempt ${authFailureCount}/${MAX_AUTH_RETRIES}`
24286
+ );
24287
+ if (authFailureCount >= MAX_AUTH_RETRIES) {
24288
+ loggerDahua.error(
24289
+ `[${camera?.siteName}-${camera?.direction}] Max auth failures reached (${MAX_AUTH_RETRIES}). Wrong credentials.`
24290
+ );
24291
+ if (onDetected) {
24292
+ onDetected({
24293
+ site: camera?.site,
24294
+ direction: camera?.direction,
24295
+ messagePermanent: `Camera [${camera?.siteName}-${camera?.direction}]. Connection Error. Check username and password.`
24296
+ });
24297
+ }
24298
+ return;
24299
+ }
24300
+ } else {
24301
+ authFailureCount = 0;
24302
+ isAuthFailure = false;
24303
+ loggerDahua.error(
24304
+ `[${camera?.siteName}-${camera?.direction}] Connection lost or error: ${error.message || error}. Retrying in 10 seconds...`
24305
+ );
24306
+ if (onDetected) {
24307
+ onDetected({
24308
+ site: camera?.site,
24309
+ direction: camera?.direction,
24310
+ 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.`
24311
+ });
24312
+ }
24236
24313
  }
24237
24314
  } finally {
24238
24315
  if (bufferQueue?.destroy) {
@@ -24254,7 +24331,8 @@ function useDahuaService() {
24254
24331
  }
24255
24332
  }
24256
24333
  if (!signal.aborted) {
24257
- await new Promise((res) => setTimeout(res, 1e4));
24334
+ const waitMs = isAuthFailure ? 1e3 : 1e4;
24335
+ await new Promise((res) => setTimeout(res, waitMs));
24258
24336
  }
24259
24337
  }
24260
24338
  loggerDahua.info(`[${camera?.siteName}-${camera?.direction}] ANPR Listener stopped.`);
@@ -24925,7 +25003,8 @@ function useSiteCameraRepo() {
24925
25003
  throw new import_node_server_utils73.BadRequestError("Site camera not found.");
24926
25004
  }
24927
25005
  if (result?._id) {
24928
- const result2 = await findOne({ _id: new import_mongodb44.ObjectId(result._id) });
25006
+ const result2 = await findOne({ _id: new import_mongodb44.ObjectId(result._id) }, void 0, { session });
25007
+ console.log("updateById result2", result2);
24929
25008
  if (result2?._id) {
24930
25009
  const { listenToCamera } = useDahuaService();
24931
25010
  await listenToCamera(result2);
@@ -24990,6 +25069,9 @@ function useSiteCameraRepo() {
24990
25069
  throw new import_node_server_utils73.BadRequestError("Invalid site ID format");
24991
25070
  }
24992
25071
  }
25072
+ if (value.status) {
25073
+ query.status = value.status;
25074
+ }
24993
25075
  try {
24994
25076
  const items = await collection.aggregate([
24995
25077
  {
@@ -25060,7 +25142,7 @@ function useSiteCameraRepo() {
25060
25142
  throw new import_node_server_utils73.BadRequestError("Failed to retrieve site cameras.");
25061
25143
  }
25062
25144
  }
25063
- async function findOne(query, project = {}) {
25145
+ async function findOne(query, project = {}, options = {}) {
25064
25146
  try {
25065
25147
  const pipeline = [
25066
25148
  { $match: query },
@@ -25109,14 +25191,14 @@ function useSiteCameraRepo() {
25109
25191
  if (Object.keys(project).length > 0) {
25110
25192
  pipeline.push({ $project: project });
25111
25193
  }
25112
- const result = await collection.aggregate(pipeline).toArray();
25194
+ const result = await collection.aggregate(pipeline, options).toArray();
25113
25195
  return result.length > 0 ? result[0] : null;
25114
25196
  } catch (error) {
25115
25197
  console.error("Error in findOne aggregation:", error);
25116
25198
  throw error;
25117
25199
  }
25118
25200
  }
25119
- async function findMany(query, project = {}) {
25201
+ async function findMany(query, project = {}, options = {}) {
25120
25202
  try {
25121
25203
  const pipeline = [
25122
25204
  { $match: query },
@@ -25165,10 +25247,10 @@ function useSiteCameraRepo() {
25165
25247
  if (Object.keys(project).length > 0) {
25166
25248
  pipeline.push({ $project: project });
25167
25249
  }
25168
- const result = await collection.aggregate(pipeline).toArray();
25250
+ const result = await collection.aggregate(pipeline, options).toArray();
25169
25251
  return result.length > 0 ? result : [];
25170
25252
  } catch (error) {
25171
- console.error("Error in findOne aggregation:", error);
25253
+ console.error("Error in findMany aggregation:", error);
25172
25254
  throw error;
25173
25255
  }
25174
25256
  }
@@ -25185,7 +25267,9 @@ function useSiteCameraRepo() {
25185
25267
  getBySiteGuardPost,
25186
25268
  updateById,
25187
25269
  deleteById,
25188
- getAllCameraWithPassword
25270
+ getAllCameraWithPassword,
25271
+ findMany,
25272
+ findOne
25189
25273
  };
25190
25274
  }
25191
25275
 
@@ -25303,9 +25387,9 @@ function usePersonRepo() {
25303
25387
  { email: { $regex: search, $options: "i" } },
25304
25388
  { nric: { $regex: search, $options: "i" } },
25305
25389
  { "plates.plateNumber": { $regex: search, $options: "i" } },
25306
- { contact: { $regex: makeContainsRegex(contact) } },
25307
- { contacts: { $regex: makeContainsRegex(contact) } },
25308
- { plateNumbers: { $regex: makeContainsRegex(plateNumber) } }
25390
+ { contact: { $regex: makeContainsRegex(search) } },
25391
+ { contacts: { $regex: makeContainsRegex(search) } },
25392
+ { plateNumbers: { $regex: makeContainsRegex(search) } }
25309
25393
  ]
25310
25394
  },
25311
25395
  ...import_mongodb45.ObjectId.isValid(org) && { org: new import_mongodb45.ObjectId(org) },
@@ -27098,7 +27182,8 @@ function useVehicleService() {
27098
27182
  deleteExpiredVehicles: _deleteExpiredVehicles,
27099
27183
  getVehicleByPlateNumber: _getVehicleByPlateNumber,
27100
27184
  getAllExpiredVehicles: _getAllExpiredVehicles,
27101
- bulkUpsertVehicles: _bulkUpsertVehicles
27185
+ bulkUpsertVehicles: _bulkUpsertVehicles,
27186
+ findOne
27102
27187
  } = useVehicleRepo();
27103
27188
  const {
27104
27189
  addPlateNumber: _addPlateNumber,
@@ -27122,15 +27207,18 @@ function useVehicleService() {
27122
27207
  throw new Error("Unable to start session for vehicle service.");
27123
27208
  }
27124
27209
  }
27210
+ console.log("add vehicle service value.plateNumber", value.plateNumber);
27125
27211
  const [_vehiclePlateNumber] = value.plateNumber;
27126
- const [existingPlateNumber, org] = await Promise.all([
27127
- _getVehicleByPlateNumber(_vehiclePlateNumber),
27212
+ console.log("add vehicle service plateNumber", _vehiclePlateNumber);
27213
+ const [
27214
+ // existingPlateNumber,
27215
+ org
27216
+ ] = await Promise.all([
27217
+ // _getVehicleByPlateNumber(_vehiclePlateNumber),
27128
27218
  _getById(value.org)
27129
27219
  ]);
27130
27220
  if (!org)
27131
27221
  throw new import_node_server_utils78.BadRequestError("Org not found");
27132
- if (existingPlateNumber)
27133
- throw new import_node_server_utils78.BadRequestError("Vehicle plate number already exists");
27134
27222
  if (!Object.values(OrgNature).includes(org.nature)) {
27135
27223
  throw new import_node_server_utils78.BadRequestError(
27136
27224
  "This organization is not allowed to add vehicles."
@@ -27143,7 +27231,6 @@ function useVehicleService() {
27143
27231
  user,
27144
27232
  org: value.org
27145
27233
  });
27146
- console.log("userPermissions vehicle.service.ts", userPermissions);
27147
27234
  const hasSecurityBypass = addedBySecurity && Array.isArray(userPermissions) && (userPermissions.includes("*") || userPermissions.includes("vehicleManagement:approve-vehicle"));
27148
27235
  if (hasSecurityBypass) {
27149
27236
  addedByPM = true;
@@ -27202,17 +27289,16 @@ function useVehicleService() {
27202
27289
  const siteCameraReq = await _getAllSiteCameras({
27203
27290
  site: value.site,
27204
27291
  type: "anpr",
27205
- direction: ["both", "entry"],
27292
+ direction: ["both", "entry", "residents"],
27206
27293
  page,
27207
- limit
27294
+ limit,
27295
+ status: "active"
27208
27296
  });
27209
27297
  pages = siteCameraReq.pages || 1;
27210
27298
  siteCameras.push(...siteCameraReq.items);
27211
27299
  page++;
27212
27300
  } while (page <= pages);
27213
- if (!siteCameras.length) {
27214
- throw new import_node_server_utils78.BadRequestError("No site cameras found.");
27215
- }
27301
+ console.log("add vehicle service siteCameras", siteCameras);
27216
27302
  }
27217
27303
  for (const plateNumber of plateNumbers) {
27218
27304
  const vehicleValue = {
@@ -31829,6 +31915,7 @@ function useSiteCameraService() {
31829
31915
  const { add: _add, getAllCameraWithPassword: _getAllCameraWithPassword } = useSiteCameraRepo();
31830
31916
  const { updateById: _updateById } = useSiteRepo();
31831
31917
  const dahuaService = useDahuaService();
31918
+ const { updateById: _siteCameraUpdateById } = useSiteCameraRepo();
31832
31919
  let cleanupTask = null;
31833
31920
  async function add(value) {
31834
31921
  const { error } = schemaSiteCamera.validate(value);
@@ -31862,6 +31949,7 @@ function useSiteCameraService() {
31862
31949
  type: "anpr",
31863
31950
  page,
31864
31951
  limit
31952
+ // site: "6923d6664150ca6a69b9f2b2"
31865
31953
  });
31866
31954
  pages = siteCameraReq.pages || 1;
31867
31955
  siteCameras.push(...siteCameraReq.items);
@@ -31877,9 +31965,27 @@ function useSiteCameraService() {
31877
31965
  }
31878
31966
  }
31879
31967
  }
31968
+ async function updateById(id, value) {
31969
+ const session = import_node_server_utils93.useAtlas.getClient()?.startSession();
31970
+ if (!session) {
31971
+ throw new Error("Unable to start session for site camera service.");
31972
+ }
31973
+ try {
31974
+ session.startTransaction();
31975
+ const result = await _siteCameraUpdateById(id, value, session);
31976
+ await session.commitTransaction();
31977
+ return result;
31978
+ } catch (error) {
31979
+ await session.abortTransaction();
31980
+ throw error;
31981
+ } finally {
31982
+ session.endSession();
31983
+ }
31984
+ }
31880
31985
  return {
31881
31986
  add,
31882
- listenToCapturedPlateNumber
31987
+ listenToCapturedPlateNumber,
31988
+ updateById
31883
31989
  };
31884
31990
  }
31885
31991
 
@@ -31887,10 +31993,9 @@ function useSiteCameraService() {
31887
31993
  var import_node_server_utils94 = require("@7365admin1/node-server-utils");
31888
31994
  var import_joi49 = __toESM(require("joi"));
31889
31995
  function useSiteCameraController() {
31890
- const { add: _add } = useSiteCameraService();
31996
+ const { add: _add, updateById: _updateById } = useSiteCameraService();
31891
31997
  const {
31892
31998
  getAll: _getAll,
31893
- updateById: _updateById,
31894
31999
  deleteById: _deleteById
31895
32000
  } = useSiteCameraRepo();
31896
32001
  async function add(req, res, next) {
@@ -33150,7 +33255,7 @@ function useAttendanceRepository() {
33150
33255
  localField: "user",
33151
33256
  foreignField: "_id",
33152
33257
  as: "user",
33153
- pipeline: [{ $project: { name: 1 } }]
33258
+ pipeline: [{ $project: { name: 1, profile: 1 } }]
33154
33259
  }
33155
33260
  },
33156
33261
  { $unwind: { path: "$user", preserveNullAndEmptyArrays: true } }
@@ -33164,6 +33269,7 @@ function useAttendanceRepository() {
33164
33269
  {
33165
33270
  $project: {
33166
33271
  userName: "$user.name",
33272
+ userProfile: "$user.profile",
33167
33273
  checkInTimestamp: "$checkIn.timestamp",
33168
33274
  checkOutTimestamp: "$checkOut.timestamp",
33169
33275
  createdAt: 1
@@ -35636,7 +35742,7 @@ function useVisitorTransactionService() {
35636
35742
  value.unitName = unit?.name;
35637
35743
  }
35638
35744
  const { nric = "", name = "", contact = "", plateNumber = "", email = "", site = "", type = "guest", company, createdBy } = value;
35639
- if (/^(guest|walk-in|contractor|delivery)$/.test(type)) {
35745
+ if (/^(guest|walk-in|contractor|delivery)$/.test(type) && site) {
35640
35746
  await addUpdateFromVisitor({
35641
35747
  nric,
35642
35748
  name,
@@ -35867,7 +35973,7 @@ function useVisitorTransactionService() {
35867
35973
  value.checkIn = isNaN(parsed.getTime()) ? null : parsed;
35868
35974
  }
35869
35975
  const { nric = "", name = "", contact = "", plateNumber = "", email = "", site = "", type = "guest", company, createdBy } = value;
35870
- if (/^(guest|walk-in|contractor|delivery)$/.test(type) && !value.checkOut) {
35976
+ if (/^(guest|walk-in|contractor|delivery)$/.test(type) && site) {
35871
35977
  await addUpdateFromVisitor({
35872
35978
  nric,
35873
35979
  name,
@@ -36051,7 +36157,9 @@ function useVisitorTransactionService() {
36051
36157
  value.site,
36052
36158
  session,
36053
36159
  void 0,
36054
- id
36160
+ id,
36161
+ void 0,
36162
+ true
36055
36163
  );
36056
36164
  item.receivedDate = /* @__PURE__ */ new Date();
36057
36165
  item.status = "Not Returned" /* NOT_RETURNED */;
@@ -36100,7 +36208,9 @@ function useVisitorTransactionService() {
36100
36208
  value.site,
36101
36209
  session,
36102
36210
  void 0,
36103
- id
36211
+ id,
36212
+ void 0,
36213
+ true
36104
36214
  );
36105
36215
  item.receivedDate = /* @__PURE__ */ new Date();
36106
36216
  item.status = "Not Returned" /* NOT_RETURNED */;
@@ -55287,8 +55397,11 @@ function useNfcPatrolRouteRepo() {
55287
55397
  const query = {
55288
55398
  site
55289
55399
  };
55290
- if (search) {
55291
- query.$text = { $search: search };
55400
+ if (search?.trim()) {
55401
+ query.name = {
55402
+ $regex: search.trim(),
55403
+ $options: "i"
55404
+ };
55292
55405
  }
55293
55406
  if (filter === "today" || filter === "notToday") {
55294
55407
  const singaporeDateString = (/* @__PURE__ */ new Date()).toLocaleString("en-US", {
@@ -58651,13 +58764,9 @@ function useNfcPatrolLogRepo() {
58651
58764
  };
58652
58765
  if (date) {
58653
58766
  if (type === "month") {
58654
- const [year, month] = date.split("-").map(Number);
58655
- const endDay = new Date(year, month, 0).getDate();
58656
58767
  query.date = {
58657
- $gte: `${year}-${String(month).padStart(2, "0")}-01`,
58658
- $lte: `${year}-${String(month).padStart(2, "0")}-${String(
58659
- endDay
58660
- ).padStart(2, "0")}`
58768
+ $gte: `${date}-01-01`,
58769
+ $lte: `${date}-12-31`
58661
58770
  };
58662
58771
  } else {
58663
58772
  query.date = date;
@@ -58978,47 +59087,52 @@ function useNfcPatrolLogController() {
58978
59087
  page: import_joi111.default.number().integer().min(1).default(1),
58979
59088
  limit: import_joi111.default.number().integer().min(1).max(100).default(10),
58980
59089
  site: import_joi111.default.string().length(24).hex().required(),
58981
- date: import_joi111.default.string().pattern(/^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$/).required().messages({
58982
- "string.pattern.base": "Date must be in YYYY-MM-DD format (e.g. 2026-07-17)"
59090
+ date: import_joi111.default.string().pattern(/^\d{4}(-(0[1-9]|1[0-2])(-(0[1-9]|[12][0-9]|3[01]))?)?$/).optional().messages({
59091
+ "string.pattern.base": "Date must be YYYY or YYYY-MM or YYYY-MM-DD"
58983
59092
  }),
58984
59093
  type: import_joi111.default.string().valid("month").optional(),
58985
59094
  route: import_joi111.default.object({
58986
- _id: import_joi111.default.string().length(24).hex().required(),
58987
- startTime: import_joi111.default.string().pattern(/^([01]\d|2[0-3]):([0-5]\d)$/).required()
58988
- }).required()
59095
+ _id: import_joi111.default.string().length(24).hex().optional(),
59096
+ startTime: import_joi111.default.string().pattern(/^([01]\d|2[0-3]):([0-5]\d)$/).optional().messages({
59097
+ "string.pattern.base": "startTime must be in HH:mm 24-hour format"
59098
+ })
59099
+ }).min(1).optional()
58989
59100
  });
58990
59101
  const query = {
59102
+ page: req.query.page,
59103
+ limit: req.query.limit,
58991
59104
  site: req.query.site,
58992
59105
  date: req.query.date,
58993
- route: {
58994
- _id: req.query["route._id"],
58995
- startTime: req.query["route.startTime"]
58996
- }
59106
+ type: req.query.type,
59107
+ route: req.query["route._id"] || req.query["route.startTime"] ? {
59108
+ ...req.query["route._id"] ? { _id: req.query["route._id"] } : {},
59109
+ ...req.query["route.startTime"] ? { startTime: req.query["route.startTime"] } : {}
59110
+ } : void 0
58997
59111
  };
58998
59112
  const { error, value } = validation.validate(query, {
58999
- abortEarly: false
59113
+ abortEarly: false,
59114
+ allowUnknown: true,
59115
+ stripUnknown: true
59000
59116
  });
59001
59117
  if (error) {
59002
- const messages = error.details.map((d) => d.message).join(", ");
59003
- import_node_server_utils200.logger.log({ level: "error", message: messages });
59004
- next(new import_node_server_utils200.BadRequestError(messages));
59005
- return;
59118
+ return next(
59119
+ new import_node_server_utils200.BadRequestError(
59120
+ error.details.map((d) => d.message).join(", ")
59121
+ )
59122
+ );
59006
59123
  }
59007
- const { page, limit, site, date, route } = value;
59008
59124
  try {
59009
59125
  const data = await _getAllBySite({
59010
- page,
59011
- limit,
59012
- site,
59013
- date,
59014
- route
59126
+ page: value.page,
59127
+ limit: value.limit,
59128
+ site: value.site,
59129
+ date: value.date,
59130
+ type: value.type,
59131
+ route: value.route
59015
59132
  });
59016
- res.status(200).json(data);
59017
- return;
59133
+ return res.status(200).json(data);
59018
59134
  } catch (error2) {
59019
- import_node_server_utils200.logger.log({ level: "error", message: error2.message });
59020
- next(error2);
59021
- return;
59135
+ return next(error2);
59022
59136
  }
59023
59137
  }
59024
59138
  async function completeCheckpoint(req, res, next) {
@@ -64548,34 +64662,62 @@ var import_axios3 = __toESM(require("axios"));
64548
64662
 
64549
64663
  // src/utils/payment-signature.util.ts
64550
64664
  var crypto3 = __toESM(require("crypto"));
64665
+ function hasOwn(obj, key) {
64666
+ return Object.prototype.hasOwnProperty.call(obj, key);
64667
+ }
64668
+ function sha512(input) {
64669
+ return crypto3.createHash("sha512").update(input).digest("hex");
64670
+ }
64671
+ function assertSecret(secretKey) {
64672
+ if (!secretKey) {
64673
+ throw new Error("Payment signature: missing secret key.");
64674
+ }
64675
+ }
64551
64676
  function paymentSignature(params, secretKey) {
64552
- const fieldOrder = ["mid", "order_id", "payment_type", "amount", "ccy", "card_no", "exp_date", "cvv2"];
64677
+ assertSecret(secretKey);
64678
+ const fieldOrder = [
64679
+ "mid",
64680
+ "order_id",
64681
+ "payment_type",
64682
+ "amount",
64683
+ "ccy",
64684
+ "card_no",
64685
+ "exp_date",
64686
+ "cvv2"
64687
+ ];
64553
64688
  let concatenated = "";
64554
64689
  fieldOrder.forEach((v) => {
64555
- if (!params.hasOwnProperty(v)) {
64690
+ if (!hasOwn(params, v)) {
64556
64691
  return;
64557
- } else if (v === "cvv2") {
64558
- concatenated += (params[v] ?? "").toString().slice(-1);
64692
+ }
64693
+ const raw = String(params[v] ?? "");
64694
+ if (v === "cvv2") {
64695
+ concatenated += raw.slice(-1);
64559
64696
  } else if (v === "card_no") {
64560
- concatenated += (params[v] ?? "").substring(0, 6) + (params[v] ?? "").slice(-4);
64697
+ concatenated += raw.substring(0, 6) + raw.slice(-4);
64561
64698
  } else {
64562
- concatenated += params[v];
64699
+ concatenated += raw;
64563
64700
  }
64564
64701
  });
64565
64702
  concatenated += secretKey;
64566
- const signature = crypto3.createHash("sha512").update(concatenated).digest("hex");
64567
- return signature;
64703
+ return sha512(concatenated);
64568
64704
  }
64569
64705
  function genericSignature(params, secretKey) {
64570
- const paramsCopy = { ...params };
64706
+ assertSecret(secretKey);
64707
+ const paramsCopy = {
64708
+ ...params
64709
+ };
64571
64710
  delete paramsCopy["signature"];
64572
- const sortedParams = Object.keys(paramsCopy).sort().reduce((obj, key) => {
64573
- obj[key] = paramsCopy[key];
64574
- return obj;
64575
- }, {});
64576
- const concatenated = Object.values(sortedParams).join("") + secretKey;
64577
- const signature = crypto3.createHash("sha512").update(concatenated).digest("hex");
64578
- return signature;
64711
+ const concatenated = Object.keys(paramsCopy).sort().map((key) => {
64712
+ const value = paramsCopy[key];
64713
+ if (value !== null && typeof value === "object") {
64714
+ throw new Error(
64715
+ `Payment signature: non-primitive value for field "${key}".`
64716
+ );
64717
+ }
64718
+ return String(value ?? "");
64719
+ }).join("") + secretKey;
64720
+ return sha512(concatenated);
64579
64721
  }
64580
64722
 
64581
64723
  // src/repositories/reddot-payment.repository.ts
@@ -64998,7 +65140,7 @@ var useRedDotPaymentSvc = () => {
64998
65140
  };
64999
65141
  if (!process.env.MERCHANT_ENQUIRY) {
65000
65142
  throw new Error(
65001
- "MERCHANT_PAYMENT environment variable is not defined."
65143
+ "MERCHANT_ENQUIRY environment variable is not defined."
65002
65144
  );
65003
65145
  }
65004
65146
  const url = process.env.MERCHANT_ENQUIRY;