@7365admin1/core 3.29.0 → 3.31.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
@@ -34503,6 +34503,10 @@ var PushTokenRepo = class {
34503
34503
  const docs = await this.collection().find({ userId: { $in: userIds }, appSlug: { $eq: appSlug } }).toArray();
34504
34504
  return docs.map((d) => d.token);
34505
34505
  }
34506
+ static async findUserIdsBySiteIdAndAppSlug(siteId, appSlug) {
34507
+ const docs = await this.collection().find({ siteId, appSlug }).toArray();
34508
+ return docs.map((d) => d.userId);
34509
+ }
34506
34510
  };
34507
34511
 
34508
34512
  // src/services/notification.service.ts
@@ -34648,6 +34652,24 @@ var NotificationService = class {
34648
34652
  "iservice365-ma-mobile-app"
34649
34653
  );
34650
34654
  }
34655
+ static async bulletinBoardCreatedForServiceApps(payload, appSlug) {
34656
+ await send(
34657
+ toStringArray(payload.to),
34658
+ "Bulletin Board",
34659
+ `There is new ${payload.status} bulletin board.`,
34660
+ {
34661
+ bulletinId: payload.bulletinId.toString(),
34662
+ status: payload.status,
34663
+ module: "bulletinBoard",
34664
+ screen: "/(user)/bulletinInfo",
34665
+ params: {
34666
+ id: payload.bulletinId.toString()
34667
+ }
34668
+ },
34669
+ false,
34670
+ appSlug
34671
+ );
34672
+ }
34651
34673
  };
34652
34674
 
34653
34675
  // src/utils/valid-values.ts
@@ -36297,7 +36319,6 @@ function usePersonService() {
36297
36319
  }
36298
36320
  return "People added successfully.";
36299
36321
  } catch (error) {
36300
- console.log("error message service", error);
36301
36322
  logger84.error("Error in people service add:", error);
36302
36323
  await session.abortTransaction();
36303
36324
  throw error;
@@ -36454,8 +36475,8 @@ function usePersonService() {
36454
36475
  phoneNumber: person.contact,
36455
36476
  org: person.org.toString(),
36456
36477
  site: person.site.toString(),
36457
- block: person.block ?? void 0,
36458
- level: person.level ?? void 0,
36478
+ block: person.block?.toString() ?? void 0,
36479
+ level: person.level?.toString() ?? void 0,
36459
36480
  unit: person.unit?.toString() ?? void 0,
36460
36481
  nric: person.nric,
36461
36482
  status: "active",
@@ -42658,6 +42679,101 @@ function useBulletinBoardService() {
42658
42679
  status: bulletinBoardStatus
42659
42680
  });
42660
42681
  }
42682
+ if (recipients?.find((item) => item === "mechanical_electrical")) {
42683
+ if (!siteId)
42684
+ return;
42685
+ const users2 = await PushTokenRepo.findUserIdsBySiteIdAndAppSlug(
42686
+ siteId.toString(),
42687
+ "iservice365-m-and-e-mobile-app"
42688
+ );
42689
+ if (users2.length <= 0)
42690
+ return;
42691
+ NotificationService.bulletinBoardCreatedForServiceApps(
42692
+ {
42693
+ to: users2,
42694
+ bulletinId: bulletinBoardId,
42695
+ subject: "Bulletin Board",
42696
+ status: bulletinBoardStatus
42697
+ },
42698
+ "iservice365-m-and-e-mobile-app"
42699
+ );
42700
+ }
42701
+ if (recipients?.find((item) => item === "pest_control_services")) {
42702
+ if (!siteId)
42703
+ return;
42704
+ const users2 = await PushTokenRepo.findUserIdsBySiteIdAndAppSlug(
42705
+ siteId.toString(),
42706
+ "iservice365-pest-control-mobile-app"
42707
+ );
42708
+ if (users2.length <= 0)
42709
+ return;
42710
+ NotificationService.bulletinBoardCreatedForServiceApps(
42711
+ {
42712
+ to: users2,
42713
+ bulletinId: bulletinBoardId,
42714
+ subject: "Bulletin Board",
42715
+ status: bulletinBoardStatus
42716
+ },
42717
+ "iservice365-pest-control-mobile-app"
42718
+ );
42719
+ }
42720
+ if (recipients?.find((item) => item === "pool_maintenance_services")) {
42721
+ if (!siteId)
42722
+ return;
42723
+ const users2 = await PushTokenRepo.findUserIdsBySiteIdAndAppSlug(
42724
+ siteId.toString(),
42725
+ "iservice365-pool-maintenance-mobile-app"
42726
+ );
42727
+ if (users2.length <= 0)
42728
+ return;
42729
+ NotificationService.bulletinBoardCreatedForServiceApps(
42730
+ {
42731
+ to: users2,
42732
+ bulletinId: bulletinBoardId,
42733
+ subject: "Bulletin Board",
42734
+ status: bulletinBoardStatus
42735
+ },
42736
+ "iservice365-pool-maintenance-mobile-app"
42737
+ );
42738
+ }
42739
+ if (recipients?.find((item) => item === "landscaping_services")) {
42740
+ if (!siteId)
42741
+ return;
42742
+ const users2 = await PushTokenRepo.findUserIdsBySiteIdAndAppSlug(
42743
+ siteId.toString(),
42744
+ "iservice365-landscaping-mobile-app"
42745
+ );
42746
+ if (users2.length <= 0)
42747
+ return;
42748
+ NotificationService.bulletinBoardCreatedForServiceApps(
42749
+ {
42750
+ to: users2,
42751
+ bulletinId: bulletinBoardId,
42752
+ subject: "Bulletin Board",
42753
+ status: bulletinBoardStatus
42754
+ },
42755
+ "iservice365-landscaping-mobile-app"
42756
+ );
42757
+ }
42758
+ if (recipients?.find((item) => item === "cleaning_services")) {
42759
+ if (!siteId)
42760
+ return;
42761
+ const users2 = await PushTokenRepo.findUserIdsBySiteIdAndAppSlug(
42762
+ siteId.toString(),
42763
+ "iservice365-hygiene-mobile-app"
42764
+ );
42765
+ if (users2.length <= 0)
42766
+ return;
42767
+ NotificationService.bulletinBoardCreatedForServiceApps(
42768
+ {
42769
+ to: users2,
42770
+ bulletinId: bulletinBoardId,
42771
+ subject: "Bulletin Board",
42772
+ status: bulletinBoardStatus
42773
+ },
42774
+ "iservice365-hygiene-mobile-app"
42775
+ );
42776
+ }
42661
42777
  await session?.commitTransaction();
42662
42778
  return "Successfully added bulletin board.";
42663
42779
  } catch (error) {
@@ -43087,7 +43203,16 @@ function useSiteBillingItemRepo() {
43087
43203
  const { delNamespace, setCache, getCache } = useCache43(namespace_collection);
43088
43204
  async function createIndexes() {
43089
43205
  try {
43090
- await collection.createIndexes([{ key: { site: 1, status: 1 } }]);
43206
+ await collection.createIndexes([
43207
+ // Serves the API list path: getAll filters by { site, status }.
43208
+ { key: { site: 1, status: 1 } },
43209
+ // Serves the cron path: processBilling() queries { status: "active" }
43210
+ // with no site, so the site-leading index above cannot be used.
43211
+ // A status-leading index avoids a full collection scan each run;
43212
+ // billingType is included so the recurring/non-recurring split can
43213
+ // be pushed down to the query later without another index.
43214
+ { key: { status: 1, billingType: 1 } }
43215
+ ]);
43091
43216
  return `Successfully created indexes for ${namespace_collection}.`;
43092
43217
  } catch (error) {
43093
43218
  logger111.log({
@@ -43099,22 +43224,6 @@ function useSiteBillingItemRepo() {
43099
43224
  );
43100
43225
  }
43101
43226
  }
43102
- async function createTextIndex() {
43103
- try {
43104
- await collection.createIndex({
43105
- name: "text"
43106
- });
43107
- return "Successfully created text index on site billing item.";
43108
- } catch (error) {
43109
- logger111.log({
43110
- level: "error",
43111
- message: error.message
43112
- });
43113
- throw new InternalServerError44(
43114
- "Failed to create text index on site billing item."
43115
- );
43116
- }
43117
- }
43118
43227
  async function add(value, session) {
43119
43228
  try {
43120
43229
  value = MBillingItem(value);
@@ -43313,7 +43422,6 @@ function useSiteBillingItemRepo() {
43313
43422
  });
43314
43423
  }
43315
43424
  return {
43316
- createTextIndex,
43317
43425
  add,
43318
43426
  getAll,
43319
43427
  getById,
@@ -47020,7 +47128,8 @@ function UseAccessManagementRepo() {
47020
47128
  type: 1,
47021
47129
  cardNo: 1,
47022
47130
  isActivated: 1,
47023
- replacementStatus: 1
47131
+ replacementStatus: 1,
47132
+ damage: 1
47024
47133
  }
47025
47134
  }
47026
47135
  ],
@@ -48210,26 +48319,20 @@ function UseAccessManagementRepo() {
48210
48319
  requestDate: 1,
48211
48320
  createdAt: 1,
48212
48321
  updatedAt: 1,
48213
- userCred: 1
48322
+ userCred: 1,
48323
+ damage: 1
48214
48324
  }
48215
48325
  }
48216
48326
  );
48217
48327
  if (!card)
48218
48328
  return null;
48219
- const site = await collectionName("sites").findOne(
48220
- { _id: card.site },
48221
- { projection: { name: 1, status: 1 } }
48222
- );
48223
- const user = card.userId ? await collectionName("users").findOne(
48224
- { _id: card.userId },
48225
- { projection: { name: 1, email: 1 } }
48226
- ) : null;
48227
- const status = card.userId === null && card.isActivated === true ? "available" : card.userId !== null && card.isActivated === true ? "assigned" : card.isActivated === false && card.replacementStatus !== null ? "replaced" : "deleted";
48329
+ const history = await collectionName("access-cards-history").find({ cardId: convertedCardId }).toArray();
48228
48330
  return {
48229
48331
  ...card,
48230
- status,
48231
- site,
48232
- user
48332
+ // status,
48333
+ // site,
48334
+ // user,
48335
+ history
48233
48336
  };
48234
48337
  } catch (error) {
48235
48338
  throw new Error(error.message);
@@ -48549,6 +48652,16 @@ function UseAccessManagementRepo() {
48549
48652
  const updatedVisitor = result?._id;
48550
48653
  const assignCards = cards.map((card) => card._id);
48551
48654
  if (assignCards.length > 0) {
48655
+ const historyRecords = assignCards.map((cardId) => ({
48656
+ cardId: new ObjectId97(cardId),
48657
+ userId: updatedVisitor,
48658
+ ...params.user,
48659
+ createdAt: /* @__PURE__ */ new Date()
48660
+ }));
48661
+ await collectionName("access-cards-history").insertMany(
48662
+ historyRecords,
48663
+ { session }
48664
+ );
48552
48665
  const bulkOps = assignCards.map((cardId) => {
48553
48666
  const updateFields = {
48554
48667
  staffNo: `STAFF-${cardId.toString().slice(-10)}`,
@@ -48663,11 +48776,35 @@ function UseAccessManagementRepo() {
48663
48776
  const session = useAtlas78.getClient()?.startSession();
48664
48777
  try {
48665
48778
  await session?.startTransaction();
48666
- const userId = new ObjectId97(params.userId);
48667
- const result = await collection().updateMany(
48668
- { userId },
48669
- { $set: { userId: null, status: "Available", updatedAt: /* @__PURE__ */ new Date() } }
48670
- );
48779
+ const { cards } = params;
48780
+ const isDamage = (status) => status.toLocaleLowerCase() === "lost" || status.toLocaleLowerCase() === "damage";
48781
+ const bulkOps = cards.map((card) => ({
48782
+ updateOne: {
48783
+ filter: { _id: new ObjectId97(card.cardId) },
48784
+ update: {
48785
+ $set: {
48786
+ userId: null,
48787
+ status: card.status,
48788
+ remarks: card.remarks,
48789
+ damage: isDamage(card.status),
48790
+ updatedAt: /* @__PURE__ */ new Date(),
48791
+ userCred: null
48792
+ }
48793
+ }
48794
+ }
48795
+ }));
48796
+ const result = await collection().bulkWrite(bulkOps, { session });
48797
+ const historyRecords = cards.map((card) => ({
48798
+ cardId: new ObjectId97(card.cardId),
48799
+ userId: new ObjectId97(card.userId),
48800
+ status: card.status,
48801
+ remarks: card.remarks,
48802
+ damage: isDamage(card.status),
48803
+ createdAt: /* @__PURE__ */ new Date()
48804
+ }));
48805
+ await collectionName("access-cards-history").insertMany(historyRecords, {
48806
+ session
48807
+ });
48671
48808
  await session?.commitTransaction();
48672
48809
  return result;
48673
48810
  } catch (error) {
@@ -50630,15 +50767,22 @@ function useAccessManagementController() {
50630
50767
  };
50631
50768
  const checkoutVisitor = async (req, res) => {
50632
50769
  try {
50633
- const { userId } = req.body;
50770
+ const { cards } = req.body;
50634
50771
  const schema2 = Joi87.object({
50635
- userId: Joi87.string().hex().length(24).required()
50772
+ cards: Joi87.array().items(
50773
+ Joi87.object({
50774
+ cardId: Joi87.string().hex().length(24).required(),
50775
+ userId: Joi87.string().hex().length(24).required(),
50776
+ status: Joi87.string().required(),
50777
+ remarks: Joi87.string().allow("").required()
50778
+ })
50779
+ ).min(1).required()
50636
50780
  });
50637
- const { error } = schema2.validate({ userId });
50781
+ const { error } = schema2.validate({ cards });
50638
50782
  if (error) {
50639
50783
  return res.status(400).json({ message: error.message });
50640
50784
  }
50641
- const result = await checkoutVisitorSvc({ userId });
50785
+ const result = await checkoutVisitorSvc({ cards });
50642
50786
  return res.status(200).json({ message: "Success", data: result });
50643
50787
  } catch (error) {
50644
50788
  return res.status(500).json({
@@ -51329,6 +51473,11 @@ var schemaCreateNfcPatrolLog = Joi89.object({
51329
51473
  startTime: Joi89.string().required(),
51330
51474
  createdBy: Joi89.string().length(24).hex().optional().allow("", null)
51331
51475
  });
51476
+ var schemaSignNfcPatrolLog = Joi89.object({
51477
+ guardName: Joi89.string().required(),
51478
+ guardSignatureFileId: Joi89.string().length(24).hex().required(),
51479
+ signedAt: Joi89.date().optional()
51480
+ });
51332
51481
  var schemaNfcPatrolLog = Joi89.object({
51333
51482
  _id: Joi89.string().length(24).hex().optional().allow(null, ""),
51334
51483
  site: Joi89.string().length(24).hex().required(),
@@ -51367,6 +51516,7 @@ var schemaNfcPatrolLog = Joi89.object({
51367
51516
  skippedRemarks: Joi89.string().required()
51368
51517
  })
51369
51518
  ).min(0).required(),
51519
+ sign: schemaSignNfcPatrolLog.optional(),
51370
51520
  createdBy: Joi89.string().length(24).hex().optional().allow(null, ""),
51371
51521
  createdAt: Joi89.date().optional()
51372
51522
  });
@@ -51402,6 +51552,15 @@ function MNfcPatrolLog(valueArg) {
51402
51552
  throw new BadRequestError149("Invalid route _id format");
51403
51553
  }
51404
51554
  }
51555
+ if (value.sign?.guardSignatureFileId) {
51556
+ try {
51557
+ value.sign.guardSignatureFileId = new ObjectId100(
51558
+ value.sign.guardSignatureFileId
51559
+ );
51560
+ } catch {
51561
+ throw new BadRequestError149("Invalid guardSignatureFileId format");
51562
+ }
51563
+ }
51405
51564
  return {
51406
51565
  _id: value._id ?? new ObjectId100(),
51407
51566
  site: value.site,
@@ -51410,6 +51569,7 @@ function MNfcPatrolLog(valueArg) {
51410
51569
  startDateTime: value.startDateTime,
51411
51570
  endDateTime: value.endDateTime,
51412
51571
  checkPoints: value.checkPoints,
51572
+ sign: value.sign,
51413
51573
  createdAt: value.createdAt ?? /* @__PURE__ */ new Date(),
51414
51574
  createdBy: value.createdBy ?? void 0
51415
51575
  };
@@ -52888,8 +53048,8 @@ function useStatementOfAccountRepo() {
52888
53048
  async function createIndexes() {
52889
53049
  try {
52890
53050
  await collection.createIndexes([
52891
- { key: { site: 1, status: 1 } },
52892
- { key: { site: 1, unitId: 1, status: 1 } },
53051
+ { key: { site: 1, status: 1, createdAt: -1 } },
53052
+ { key: { site: 1, unitId: 1, status: 1, createdAt: -1 } },
52893
53053
  { key: { createdAt: -1 } }
52894
53054
  ]);
52895
53055
  return `Successfully created indexes for ${namespace_collection}.`;
@@ -52955,12 +53115,7 @@ function useStatementOfAccountRepo() {
52955
53115
  let endDate = new Date(dateTo);
52956
53116
  endDate.setHours(23, 59, 59, 999);
52957
53117
  dateExpr = {
52958
- $expr: {
52959
- $and: [
52960
- { $gte: ["$createdAt", startDate] },
52961
- { $lte: ["$createdAt", endDate] }
52962
- ]
52963
- }
53118
+ createdAt: { $gte: startDate, $lte: endDate }
52964
53119
  };
52965
53120
  }
52966
53121
  const unitSearchRegex = search ? search.trim().replace(/\s+/g, "").replace(/\//g, "\\s*/\\s*") : null;
@@ -53145,12 +53300,7 @@ function useStatementOfAccountRepo() {
53145
53300
  let endDate = new Date(dateTo);
53146
53301
  endDate.setHours(23, 59, 59, 999);
53147
53302
  dateExpr = {
53148
- $expr: {
53149
- $and: [
53150
- { $gte: ["$createdAt", startDate] },
53151
- { $lte: ["$createdAt", endDate] }
53152
- ]
53153
- }
53303
+ createdAt: { $gte: startDate, $lte: endDate }
53154
53304
  };
53155
53305
  }
53156
53306
  const unitSearchRegex = search ? search.trim().replace(/\s+/g, "").replace(/\//g, "\\s*/\\s*") : null;
@@ -58082,18 +58232,10 @@ function useNfcPatrolLogRepo() {
58082
58232
  }
58083
58233
  }
58084
58234
  async function add(value, session) {
58085
- try {
58086
- value = MNfcPatrolLog(value);
58087
- const res = await collection.insertOne(value, { session });
58088
- delCachedData();
58089
- return res.insertedId;
58090
- } catch (error) {
58091
- const isDuplicated = error.message.includes("duplicate");
58092
- if (isDuplicated) {
58093
- throw new BadRequestError179("NFC Patrol Log already exists.");
58094
- }
58095
- throw error;
58096
- }
58235
+ value = MNfcPatrolLog(value);
58236
+ const res = await collection.insertOne(value, { session });
58237
+ delCachedData();
58238
+ return res.insertedId;
58097
58239
  }
58098
58240
  async function getAllBySite({
58099
58241
  page = 1,
@@ -58209,25 +58351,44 @@ function useNfcPatrolLogRepo() {
58209
58351
  delCachedData();
58210
58352
  return res;
58211
58353
  }
58354
+ async function sign(id, value, session) {
58355
+ id = new ObjectId121(id);
58356
+ await collection.updateOne(
58357
+ { _id: id },
58358
+ {
58359
+ $set: {
58360
+ sign: {
58361
+ ...value,
58362
+ signedAt: /* @__PURE__ */ new Date()
58363
+ }
58364
+ }
58365
+ },
58366
+ { session }
58367
+ );
58368
+ }
58212
58369
  return {
58213
58370
  createIndexes,
58214
58371
  add,
58215
58372
  getAllBySite,
58216
58373
  getById,
58217
- updateCheckpoint
58374
+ updateCheckpoint,
58375
+ sign
58218
58376
  };
58219
58377
  }
58220
58378
 
58221
58379
  // src/services/nfc-patrol-log.service.ts
58222
58380
  import {
58223
58381
  BadRequestError as BadRequestError180,
58382
+ NotFoundError as NotFoundError50,
58224
58383
  useAtlas as useAtlas100
58225
58384
  } from "@7365admin1/node-server-utils";
58226
58385
  function useNfcPatrolLogService() {
58227
58386
  const {
58228
58387
  add: _add,
58229
58388
  getById,
58230
- updateCheckpoint
58389
+ updateCheckpoint,
58390
+ getAllBySite: _getAllBySite,
58391
+ sign: repoSign
58231
58392
  } = useNfcPatrolLogRepo();
58232
58393
  const routeRepo = useNfcPatrolRouteRepo();
58233
58394
  const tagRepo = useNfcPatrolTagRepo();
@@ -58242,9 +58403,30 @@ function useNfcPatrolLogService() {
58242
58403
  if (!route.startTimes?.length) {
58243
58404
  throw new BadRequestError180("Route does not have any start time.");
58244
58405
  }
58406
+ if (!route.startTimes.includes(value.startTime)) {
58407
+ throw new BadRequestError180("Invalid route start time.");
58408
+ }
58409
+ const existedLog = await _getAllBySite(
58410
+ {
58411
+ site: value.site,
58412
+ page: 1,
58413
+ limit: 1,
58414
+ date: value.date,
58415
+ route: {
58416
+ _id: route._id.toString(),
58417
+ startTime: value.startTime
58418
+ }
58419
+ },
58420
+ session
58421
+ );
58422
+ if (existedLog.items.length > 0) {
58423
+ throw new BadRequestError180(
58424
+ `Patrol log for ${value.startTime} already exists on ${value.date}.`
58425
+ );
58426
+ }
58245
58427
  const checkPoints = [];
58246
58428
  let currentTime = /* @__PURE__ */ new Date(
58247
- `${value.date}T${route.startTimes[0]}:00`
58429
+ `${value.date}T${value.startTime}:00`
58248
58430
  );
58249
58431
  for (const checkPoint of route.checkPoints) {
58250
58432
  const tag = await tagRepo.getById(checkPoint.nfcTag_id, session);
@@ -58278,7 +58460,7 @@ function useNfcPatrolLogService() {
58278
58460
  _id: route._id.toString(),
58279
58461
  name: route.name,
58280
58462
  tolerance: Number(route.tolerance),
58281
- startTime: route.startTimes[0],
58463
+ startTime: value.startTime,
58282
58464
  endTime: currentTime.toISOString().substring(11, 16)
58283
58465
  },
58284
58466
  date: value.date,
@@ -58291,7 +58473,6 @@ function useNfcPatrolLogService() {
58291
58473
  await session?.commitTransaction();
58292
58474
  return "Successfully added NFC Patrol Log.";
58293
58475
  } catch (error) {
58294
- console.error("Add NFC Patrol Log Error:", error);
58295
58476
  await session?.abortTransaction();
58296
58477
  throw error;
58297
58478
  } finally {
@@ -58343,9 +58524,26 @@ function useNfcPatrolLogService() {
58343
58524
  await session?.endSession();
58344
58525
  }
58345
58526
  }
58527
+ async function sign(id, value) {
58528
+ const log = await getById(id);
58529
+ if (!log) {
58530
+ throw new NotFoundError50("Patrol log not found.");
58531
+ }
58532
+ const canSign = log.checkPoints.every(
58533
+ (c) => c.status === "Completed" || c.status === "Skipped"
58534
+ );
58535
+ if (!canSign) {
58536
+ throw new BadRequestError180(
58537
+ "All checkpoints must be completed or skipped before signing."
58538
+ );
58539
+ }
58540
+ await repoSign(id, value);
58541
+ return "Successfully signed patrol.";
58542
+ }
58346
58543
  return {
58347
58544
  add,
58348
- completeCheckpoint
58545
+ completeCheckpoint,
58546
+ sign
58349
58547
  };
58350
58548
  }
58351
58549
 
@@ -58356,7 +58554,7 @@ import {
58356
58554
  } from "@7365admin1/node-server-utils";
58357
58555
  import Joi111 from "joi";
58358
58556
  function useNfcPatrolLogController() {
58359
- const { add: _add, completeCheckpoint: _completeCheckpoint } = useNfcPatrolLogService();
58557
+ const { add: _add, completeCheckpoint: _completeCheckpoint, sign: _sign } = useNfcPatrolLogService();
58360
58558
  const { getAllBySite: _getAllBySite, getById: _getById } = useNfcPatrolLogRepo();
58361
58559
  async function add(req, res, next) {
58362
58560
  try {
@@ -58448,11 +58646,26 @@ function useNfcPatrolLogController() {
58448
58646
  next(error);
58449
58647
  }
58450
58648
  }
58649
+ async function sign(req, res, next) {
58650
+ try {
58651
+ const value = await schemaSignNfcPatrolLog.validateAsync(req.body);
58652
+ const result = await _sign(
58653
+ req.params.id,
58654
+ value
58655
+ );
58656
+ res.json({
58657
+ message: result
58658
+ });
58659
+ } catch (err) {
58660
+ next(err);
58661
+ }
58662
+ }
58451
58663
  return {
58452
58664
  add,
58453
58665
  getAllBySite,
58454
58666
  completeCheckpoint,
58455
- getLog
58667
+ getLog,
58668
+ sign
58456
58669
  };
58457
58670
  }
58458
58671
 
@@ -64564,7 +64777,7 @@ import {
64564
64777
  logger as logger181,
64565
64778
  getDirectory as getDirectory5,
64566
64779
  BadRequestError as BadRequestError202,
64567
- NotFoundError as NotFoundError52,
64780
+ NotFoundError as NotFoundError53,
64568
64781
  InternalServerError as InternalServerError70,
64569
64782
  useAtlas as useAtlas115,
64570
64783
  hashPassword as hashPassword4
@@ -64695,7 +64908,7 @@ function useVerificationServiceV2() {
64695
64908
  session?.startTransaction();
64696
64909
  const item = await _getByVerificationCode(verificationCode);
64697
64910
  if (!item) {
64698
- throw new NotFoundError52("Verification not found.");
64911
+ throw new NotFoundError53("Verification not found.");
64699
64912
  }
64700
64913
  switch (item.status) {
64701
64914
  case "expired" /* EXPIRED */:
@@ -64933,7 +65146,7 @@ function useVerificationServiceV2() {
64933
65146
  async function resendSignUpVerification(email) {
64934
65147
  const item = await _getPendingVerificationByEmail(email);
64935
65148
  if (!item) {
64936
- throw new NotFoundError52(
65149
+ throw new NotFoundError53(
64937
65150
  "Pending verification not found."
64938
65151
  );
64939
65152
  }
@@ -65253,7 +65466,7 @@ import {
65253
65466
  BadRequestError as BadRequestError205,
65254
65467
  comparePassword as comparePassword3,
65255
65468
  InternalServerError as InternalServerError72,
65256
- NotFoundError as NotFoundError54,
65469
+ NotFoundError as NotFoundError55,
65257
65470
  useCache as useCache68
65258
65471
  } from "@7365admin1/node-server-utils";
65259
65472
  import { v4 as uuidv42 } from "uuid";
@@ -65266,7 +65479,7 @@ import {
65266
65479
  logger as logger183,
65267
65480
  BadRequestError as BadRequestError204,
65268
65481
  paginate as paginate60,
65269
- NotFoundError as NotFoundError53,
65482
+ NotFoundError as NotFoundError54,
65270
65483
  AppError as AppError29,
65271
65484
  useCache as useCache67,
65272
65485
  makeCacheKey as makeCacheKey65,
@@ -65399,7 +65612,7 @@ function useUserRepoV2() {
65399
65612
  ]).toArray();
65400
65613
  const data = results.length > 0 ? results[0] : null;
65401
65614
  if (!data)
65402
- throw new NotFoundError53("User not found.");
65615
+ throw new NotFoundError54("User not found.");
65403
65616
  setCache(cacheKey, data, 15 * 60).then(() => logger183.info(`Cache set for key: ${cacheKey}`)).catch(
65404
65617
  (err) => logger183.error(`Failed to set cache for key: ${cacheKey}`, err)
65405
65618
  );
@@ -65740,7 +65953,7 @@ function useAuthServiceV2() {
65740
65953
  try {
65741
65954
  const user = await getUserByEmail(email);
65742
65955
  if (!user) {
65743
- throw new NotFoundError54(
65956
+ throw new NotFoundError55(
65744
65957
  "Invalid user email. Please check your email and try again."
65745
65958
  );
65746
65959
  }
@@ -65815,7 +66028,7 @@ import {
65815
66028
  comparePassword as comparePassword4,
65816
66029
  hashPassword as hashPassword5,
65817
66030
  InternalServerError as InternalServerError73,
65818
- NotFoundError as NotFoundError55,
66031
+ NotFoundError as NotFoundError56,
65819
66032
  useAtlas as useAtlas117,
65820
66033
  useS3 as useS33
65821
66034
  } from "@7365admin1/node-server-utils";
@@ -65919,14 +66132,14 @@ function useUserServiceV2() {
65919
66132
  try {
65920
66133
  const otpDoc = await _getVerificationById(id);
65921
66134
  if (!otpDoc) {
65922
- throw new NotFoundError55("You are using an invalid reset link.");
66135
+ throw new NotFoundError56("You are using an invalid reset link.");
65923
66136
  }
65924
66137
  if (otpDoc.status === "complete" /* COMPLETE */) {
65925
66138
  throw new BadRequestError206("This link has already been invalidated.");
65926
66139
  }
65927
66140
  const user = await _getUserByEmail(otpDoc.email);
65928
66141
  if (!user) {
65929
- throw new NotFoundError55("User not found.");
66142
+ throw new NotFoundError56("User not found.");
65930
66143
  }
65931
66144
  if (!user._id) {
65932
66145
  throw new InternalServerError73("Invalid user ID.");
@@ -66753,7 +66966,7 @@ function MPost(value) {
66753
66966
  import {
66754
66967
  BadRequestError as BadRequestError212,
66755
66968
  InternalServerError as InternalServerError75,
66756
- NotFoundError as NotFoundError56,
66969
+ NotFoundError as NotFoundError57,
66757
66970
  paginate as paginate61,
66758
66971
  useAtlas as useAtlas119
66759
66972
  } from "@7365admin1/node-server-utils";
@@ -66878,7 +67091,7 @@ function usePostPrelovedRepo() {
66878
67091
  ]).toArray();
66879
67092
  const data = result[0] ?? null;
66880
67093
  if (!data)
66881
- throw new NotFoundError56("Post not found.");
67094
+ throw new NotFoundError57("Post not found.");
66882
67095
  return data;
66883
67096
  } catch (error) {
66884
67097
  throw error;
@@ -67142,7 +67355,7 @@ import { useAtlas as useAtlas121 } from "@7365admin1/node-server-utils";
67142
67355
  import {
67143
67356
  BadRequestError as BadRequestError213,
67144
67357
  InternalServerError as InternalServerError76,
67145
- NotFoundError as NotFoundError57,
67358
+ NotFoundError as NotFoundError58,
67146
67359
  useAtlas as useAtlas120
67147
67360
  } from "@7365admin1/node-server-utils";
67148
67361
  import { ObjectId as ObjectId140 } from "mongodb";
@@ -67228,7 +67441,7 @@ function usePostFavoriteRepo() {
67228
67441
  }
67229
67442
  const result = await collection.findOne({ _id });
67230
67443
  if (!result)
67231
- throw new NotFoundError57("Favorite not found.");
67444
+ throw new NotFoundError58("Favorite not found.");
67232
67445
  return result;
67233
67446
  }
67234
67447
  async function getByPostId(postId) {
@@ -67239,7 +67452,7 @@ function usePostFavoriteRepo() {
67239
67452
  }
67240
67453
  const result = await collection.findOne({ postId });
67241
67454
  if (!result)
67242
- throw new NotFoundError57("Favorite not found.");
67455
+ throw new NotFoundError58("Favorite not found.");
67243
67456
  return result;
67244
67457
  }
67245
67458
  async function updateById(_id, userId, session) {
@@ -67745,7 +67958,7 @@ function MCategoryPreloved(value) {
67745
67958
  import {
67746
67959
  BadRequestError as BadRequestError216,
67747
67960
  InternalServerError as InternalServerError77,
67748
- NotFoundError as NotFoundError58,
67961
+ NotFoundError as NotFoundError59,
67749
67962
  useAtlas as useAtlas123
67750
67963
  } from "@7365admin1/node-server-utils";
67751
67964
  import { ObjectId as ObjectId143 } from "mongodb";
@@ -67788,7 +68001,7 @@ function useCategoryPrelovedRepo() {
67788
68001
  try {
67789
68002
  const data = await collection.findOne({ _id: objectId2 });
67790
68003
  if (!data) {
67791
- throw new NotFoundError58("Category not found.");
68004
+ throw new NotFoundError59("Category not found.");
67792
68005
  }
67793
68006
  return data;
67794
68007
  } catch (error) {
@@ -67817,7 +68030,7 @@ function useCategoryPrelovedRepo() {
67817
68030
  }
67818
68031
  const existing = await collection.findOne({ _id: objectId2 });
67819
68032
  if (!existing)
67820
- throw new NotFoundError58("Category not found.");
68033
+ throw new NotFoundError59("Category not found.");
67821
68034
  const res = await collection.updateOne(
67822
68035
  { _id: objectId2 },
67823
68036
  { $set: { ...value, updatedAt: (/* @__PURE__ */ new Date()).toISOString() } }
@@ -67835,7 +68048,7 @@ function useCategoryPrelovedRepo() {
67835
68048
  }
67836
68049
  const existing = await collection.findOne({ _id: objectId2 });
67837
68050
  if (!existing)
67838
- throw new NotFoundError58("Category not found.");
68051
+ throw new NotFoundError59("Category not found.");
67839
68052
  const res = await collection.deleteOne({ _id: objectId2 });
67840
68053
  if (res.deletedCount === 0)
67841
68054
  throw new InternalServerError77("Unable to delete category.");
@@ -68002,7 +68215,7 @@ function MSubcategoryPreloved(value) {
68002
68215
  import {
68003
68216
  BadRequestError as BadRequestError218,
68004
68217
  InternalServerError as InternalServerError78,
68005
- NotFoundError as NotFoundError59,
68218
+ NotFoundError as NotFoundError60,
68006
68219
  useAtlas as useAtlas124
68007
68220
  } from "@7365admin1/node-server-utils";
68008
68221
  import { ObjectId as ObjectId145 } from "mongodb";
@@ -68053,7 +68266,7 @@ function useSubcategoryPrelovedRepo() {
68053
68266
  try {
68054
68267
  const data = await collection.findOne({ _id: objectId2 });
68055
68268
  if (!data) {
68056
- throw new NotFoundError59("Subcategory not found.");
68269
+ throw new NotFoundError60("Subcategory not found.");
68057
68270
  }
68058
68271
  return data;
68059
68272
  } catch (error) {
@@ -68328,7 +68541,7 @@ function MChatPreloved(value) {
68328
68541
  import {
68329
68542
  BadRequestError as BadRequestError220,
68330
68543
  InternalServerError as InternalServerError79,
68331
- NotFoundError as NotFoundError60,
68544
+ NotFoundError as NotFoundError61,
68332
68545
  useAtlas as useAtlas125
68333
68546
  } from "@7365admin1/node-server-utils";
68334
68547
  import { ObjectId as ObjectId147 } from "mongodb";
@@ -68356,7 +68569,7 @@ function useChatPrelovedRepo() {
68356
68569
  }
68357
68570
  const existing = await collection.findOne({ _id: objectId2 });
68358
68571
  if (!existing)
68359
- throw new NotFoundError60("Chat not found.");
68572
+ throw new NotFoundError61("Chat not found.");
68360
68573
  value.edited = true;
68361
68574
  value.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
68362
68575
  const res = await collection.updateOne({ _id: objectId2 }, { $set: value });
@@ -68373,7 +68586,7 @@ function useChatPrelovedRepo() {
68373
68586
  }
68374
68587
  const existing = await collection.findOne({ _id: objectId2 });
68375
68588
  if (!existing)
68376
- throw new NotFoundError60("Chat not found.");
68589
+ throw new NotFoundError61("Chat not found.");
68377
68590
  const res = await collection.updateOne(
68378
68591
  { _id: objectId2 },
68379
68592
  { $set: { deletedAt: (/* @__PURE__ */ new Date()).toISOString(), updatedAt: (/* @__PURE__ */ new Date()).toISOString() } }
@@ -69054,7 +69267,7 @@ function MBidPreloved(value) {
69054
69267
  // src/repositories/bid-preloved.repo.ts
69055
69268
  import {
69056
69269
  InternalServerError as InternalServerError82,
69057
- NotFoundError as NotFoundError61,
69270
+ NotFoundError as NotFoundError62,
69058
69271
  useAtlas as useAtlas128
69059
69272
  } from "@7365admin1/node-server-utils";
69060
69273
  import { ObjectId as ObjectId151 } from "mongodb";
@@ -69076,7 +69289,7 @@ function useBidPrelovedRepo() {
69076
69289
  const objectId2 = typeof _id === "string" ? new ObjectId151(_id) : _id;
69077
69290
  const existing = await collection.findOne({ _id: objectId2 });
69078
69291
  if (!existing)
69079
- throw new NotFoundError61("Bid not found.");
69292
+ throw new NotFoundError62("Bid not found.");
69080
69293
  const res = await collection.updateOne(
69081
69294
  { _id: objectId2 },
69082
69295
  { $set: { status, updatedAt: (/* @__PURE__ */ new Date()).toISOString() } }
@@ -69090,7 +69303,7 @@ function useBidPrelovedRepo() {
69090
69303
  _id = new ObjectId151(_id);
69091
69304
  const result = await collection.findOne({ _id });
69092
69305
  if (!result)
69093
- throw new NotFoundError61("Bid not found.");
69306
+ throw new NotFoundError62("Bid not found.");
69094
69307
  return result;
69095
69308
  }
69096
69309
  return { add, getById, updateStatus };
@@ -69371,7 +69584,7 @@ import {
69371
69584
  InternalServerError as InternalServerError84,
69372
69585
  logger as logger197,
69373
69586
  makeCacheKey as makeCacheKey66,
69374
- NotFoundError as NotFoundError62,
69587
+ NotFoundError as NotFoundError63,
69375
69588
  paginate as paginate64,
69376
69589
  useAtlas as useAtlas130,
69377
69590
  useCache as useCache71
@@ -69476,7 +69689,7 @@ function useFormEntryRepo() {
69476
69689
  try {
69477
69690
  const [data] = await collection.aggregate([{ $match: query }]).toArray();
69478
69691
  if (!data) {
69479
- throw new NotFoundError62("Document not found.");
69692
+ throw new NotFoundError63("Document not found.");
69480
69693
  }
69481
69694
  return data;
69482
69695
  } catch (error) {
@@ -69500,11 +69713,11 @@ function useFormEntryRepo() {
69500
69713
  }
69501
69714
  const onlineFormRequest = await collection.findOne({ _id });
69502
69715
  if (!onlineFormRequest) {
69503
- throw new NotFoundError62("Online form not found.");
69716
+ throw new NotFoundError63("Online form not found.");
69504
69717
  }
69505
69718
  const user = await getUserById(onlineFormRequest.userId.toString());
69506
69719
  if (!user || !user._id) {
69507
- throw new NotFoundError62("User not found.");
69720
+ throw new NotFoundError63("User not found.");
69508
69721
  }
69509
69722
  const userId = user._id.toString();
69510
69723
  await NotificationService.onlineFormRequestStatusUpdated({
@@ -70755,6 +70968,17 @@ function formatHidResponse(data) {
70755
70968
  function sanitizeHidPath(path5) {
70756
70969
  return path5.replace(/([?&]session=)[^&]+/i, "$1<redacted>");
70757
70970
  }
70971
+ function normalizeBaseUrl(value) {
70972
+ return String(value || "").replace(/\/+$/, "");
70973
+ }
70974
+ function getRelayAuth() {
70975
+ const username = process.env.HID_AMICO_RELAY_USERNAME;
70976
+ const password = process.env.HID_AMICO_RELAY_PASSWORD;
70977
+ if (!username || !password) {
70978
+ return void 0;
70979
+ }
70980
+ return { username, password };
70981
+ }
70758
70982
  function toHidRequestError(error, path5) {
70759
70983
  if (axios4.isAxiosError(error)) {
70760
70984
  const status = error.response?.status;
@@ -70794,10 +71018,12 @@ var HidAmicoClient = class {
70794
71018
  constructor(reader) {
70795
71019
  this.reader = reader;
70796
71020
  this.session = "";
71021
+ const relayAuth = getRelayAuth();
70797
71022
  this.http = axios4.create({
70798
- baseURL: reader.baseUrl.replace(/\/+$/, ""),
71023
+ baseURL: normalizeBaseUrl(reader.baseUrl),
70799
71024
  timeout: 15e3,
70800
- headers: { "Content-Type": "application/json" }
71025
+ headers: { "Content-Type": "application/json" },
71026
+ ...relayAuth ? { auth: relayAuth } : {}
70801
71027
  });
70802
71028
  }
70803
71029
  async post(path5, body) {
@@ -71030,7 +71256,12 @@ function useHidAmicoService() {
71030
71256
  try {
71031
71257
  await client.login();
71032
71258
  const valid = await client.sessionIsValid();
71033
- await repo.updateById(id, { lastSeenAt: /* @__PURE__ */ new Date(), lastSyncMessage: "Connection test succeeded." });
71259
+ await repo.updateById(id, {
71260
+ status: "active",
71261
+ lastSeenAt: /* @__PURE__ */ new Date(),
71262
+ lastSyncStatus: "completed",
71263
+ lastSyncMessage: "Connection test succeeded."
71264
+ });
71034
71265
  await repo.addEvent({ reader: id, site: reader.site, type: "test", payload: { valid } });
71035
71266
  return { valid, message: "HID Amico connection test succeeded." };
71036
71267
  } catch (error) {
@@ -71060,6 +71291,7 @@ function useHidAmicoService() {
71060
71291
  }
71061
71292
  const message = batches.length ? `Synced ${batches.length} HID object batch(es).` : "HID reader session verified. No sync payload was supplied.";
71062
71293
  await repo.updateById(id, {
71294
+ status: "active",
71063
71295
  lastSeenAt: /* @__PURE__ */ new Date(),
71064
71296
  lastSyncAt: /* @__PURE__ */ new Date(),
71065
71297
  lastSyncStatus: "completed",
@@ -71866,6 +72098,7 @@ export {
71866
72098
  schemaPostFavorite,
71867
72099
  schemaServiceProvider,
71868
72100
  schemaServiceProviderBilling,
72101
+ schemaSignNfcPatrolLog,
71869
72102
  schemaSiteCamera,
71870
72103
  schemaSiteFacility,
71871
72104
  schemaSiteFacilityBooking,