@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.js CHANGED
@@ -6128,6 +6128,7 @@ __export(src_exports, {
6128
6128
  schemaPostFavorite: () => schemaPostFavorite,
6129
6129
  schemaServiceProvider: () => schemaServiceProvider,
6130
6130
  schemaServiceProviderBilling: () => schemaServiceProviderBilling,
6131
+ schemaSignNfcPatrolLog: () => schemaSignNfcPatrolLog,
6131
6132
  schemaSiteCamera: () => schemaSiteCamera,
6132
6133
  schemaSiteFacility: () => schemaSiteFacility,
6133
6134
  schemaSiteFacilityBooking: () => schemaSiteFacilityBooking,
@@ -34660,6 +34661,10 @@ var PushTokenRepo = class {
34660
34661
  const docs = await this.collection().find({ userId: { $in: userIds }, appSlug: { $eq: appSlug } }).toArray();
34661
34662
  return docs.map((d) => d.token);
34662
34663
  }
34664
+ static async findUserIdsBySiteIdAndAppSlug(siteId, appSlug) {
34665
+ const docs = await this.collection().find({ siteId, appSlug }).toArray();
34666
+ return docs.map((d) => d.userId);
34667
+ }
34663
34668
  };
34664
34669
 
34665
34670
  // src/services/notification.service.ts
@@ -34805,6 +34810,24 @@ var NotificationService = class {
34805
34810
  "iservice365-ma-mobile-app"
34806
34811
  );
34807
34812
  }
34813
+ static async bulletinBoardCreatedForServiceApps(payload, appSlug) {
34814
+ await send(
34815
+ toStringArray(payload.to),
34816
+ "Bulletin Board",
34817
+ `There is new ${payload.status} bulletin board.`,
34818
+ {
34819
+ bulletinId: payload.bulletinId.toString(),
34820
+ status: payload.status,
34821
+ module: "bulletinBoard",
34822
+ screen: "/(user)/bulletinInfo",
34823
+ params: {
34824
+ id: payload.bulletinId.toString()
34825
+ }
34826
+ },
34827
+ false,
34828
+ appSlug
34829
+ );
34830
+ }
34808
34831
  };
34809
34832
 
34810
34833
  // src/utils/valid-values.ts
@@ -36437,7 +36460,6 @@ function usePersonService() {
36437
36460
  }
36438
36461
  return "People added successfully.";
36439
36462
  } catch (error) {
36440
- console.log("error message service", error);
36441
36463
  import_node_server_utils112.logger.error("Error in people service add:", error);
36442
36464
  await session.abortTransaction();
36443
36465
  throw error;
@@ -36594,8 +36616,8 @@ function usePersonService() {
36594
36616
  phoneNumber: person.contact,
36595
36617
  org: person.org.toString(),
36596
36618
  site: person.site.toString(),
36597
- block: person.block ?? void 0,
36598
- level: person.level ?? void 0,
36619
+ block: person.block?.toString() ?? void 0,
36620
+ level: person.level?.toString() ?? void 0,
36599
36621
  unit: person.unit?.toString() ?? void 0,
36600
36622
  nric: person.nric,
36601
36623
  status: "active",
@@ -42709,6 +42731,101 @@ function useBulletinBoardService() {
42709
42731
  status: bulletinBoardStatus
42710
42732
  });
42711
42733
  }
42734
+ if (recipients?.find((item) => item === "mechanical_electrical")) {
42735
+ if (!siteId)
42736
+ return;
42737
+ const users2 = await PushTokenRepo.findUserIdsBySiteIdAndAppSlug(
42738
+ siteId.toString(),
42739
+ "iservice365-m-and-e-mobile-app"
42740
+ );
42741
+ if (users2.length <= 0)
42742
+ return;
42743
+ NotificationService.bulletinBoardCreatedForServiceApps(
42744
+ {
42745
+ to: users2,
42746
+ bulletinId: bulletinBoardId,
42747
+ subject: "Bulletin Board",
42748
+ status: bulletinBoardStatus
42749
+ },
42750
+ "iservice365-m-and-e-mobile-app"
42751
+ );
42752
+ }
42753
+ if (recipients?.find((item) => item === "pest_control_services")) {
42754
+ if (!siteId)
42755
+ return;
42756
+ const users2 = await PushTokenRepo.findUserIdsBySiteIdAndAppSlug(
42757
+ siteId.toString(),
42758
+ "iservice365-pest-control-mobile-app"
42759
+ );
42760
+ if (users2.length <= 0)
42761
+ return;
42762
+ NotificationService.bulletinBoardCreatedForServiceApps(
42763
+ {
42764
+ to: users2,
42765
+ bulletinId: bulletinBoardId,
42766
+ subject: "Bulletin Board",
42767
+ status: bulletinBoardStatus
42768
+ },
42769
+ "iservice365-pest-control-mobile-app"
42770
+ );
42771
+ }
42772
+ if (recipients?.find((item) => item === "pool_maintenance_services")) {
42773
+ if (!siteId)
42774
+ return;
42775
+ const users2 = await PushTokenRepo.findUserIdsBySiteIdAndAppSlug(
42776
+ siteId.toString(),
42777
+ "iservice365-pool-maintenance-mobile-app"
42778
+ );
42779
+ if (users2.length <= 0)
42780
+ return;
42781
+ NotificationService.bulletinBoardCreatedForServiceApps(
42782
+ {
42783
+ to: users2,
42784
+ bulletinId: bulletinBoardId,
42785
+ subject: "Bulletin Board",
42786
+ status: bulletinBoardStatus
42787
+ },
42788
+ "iservice365-pool-maintenance-mobile-app"
42789
+ );
42790
+ }
42791
+ if (recipients?.find((item) => item === "landscaping_services")) {
42792
+ if (!siteId)
42793
+ return;
42794
+ const users2 = await PushTokenRepo.findUserIdsBySiteIdAndAppSlug(
42795
+ siteId.toString(),
42796
+ "iservice365-landscaping-mobile-app"
42797
+ );
42798
+ if (users2.length <= 0)
42799
+ return;
42800
+ NotificationService.bulletinBoardCreatedForServiceApps(
42801
+ {
42802
+ to: users2,
42803
+ bulletinId: bulletinBoardId,
42804
+ subject: "Bulletin Board",
42805
+ status: bulletinBoardStatus
42806
+ },
42807
+ "iservice365-landscaping-mobile-app"
42808
+ );
42809
+ }
42810
+ if (recipients?.find((item) => item === "cleaning_services")) {
42811
+ if (!siteId)
42812
+ return;
42813
+ const users2 = await PushTokenRepo.findUserIdsBySiteIdAndAppSlug(
42814
+ siteId.toString(),
42815
+ "iservice365-hygiene-mobile-app"
42816
+ );
42817
+ if (users2.length <= 0)
42818
+ return;
42819
+ NotificationService.bulletinBoardCreatedForServiceApps(
42820
+ {
42821
+ to: users2,
42822
+ bulletinId: bulletinBoardId,
42823
+ subject: "Bulletin Board",
42824
+ status: bulletinBoardStatus
42825
+ },
42826
+ "iservice365-hygiene-mobile-app"
42827
+ );
42828
+ }
42712
42829
  await session?.commitTransaction();
42713
42830
  return "Successfully added bulletin board.";
42714
42831
  } catch (error) {
@@ -43129,7 +43246,16 @@ function useSiteBillingItemRepo() {
43129
43246
  const { delNamespace, setCache, getCache } = (0, import_node_server_utils144.useCache)(namespace_collection);
43130
43247
  async function createIndexes() {
43131
43248
  try {
43132
- await collection.createIndexes([{ key: { site: 1, status: 1 } }]);
43249
+ await collection.createIndexes([
43250
+ // Serves the API list path: getAll filters by { site, status }.
43251
+ { key: { site: 1, status: 1 } },
43252
+ // Serves the cron path: processBilling() queries { status: "active" }
43253
+ // with no site, so the site-leading index above cannot be used.
43254
+ // A status-leading index avoids a full collection scan each run;
43255
+ // billingType is included so the recurring/non-recurring split can
43256
+ // be pushed down to the query later without another index.
43257
+ { key: { status: 1, billingType: 1 } }
43258
+ ]);
43133
43259
  return `Successfully created indexes for ${namespace_collection}.`;
43134
43260
  } catch (error) {
43135
43261
  import_node_server_utils144.logger.log({
@@ -43141,22 +43267,6 @@ function useSiteBillingItemRepo() {
43141
43267
  );
43142
43268
  }
43143
43269
  }
43144
- async function createTextIndex() {
43145
- try {
43146
- await collection.createIndex({
43147
- name: "text"
43148
- });
43149
- return "Successfully created text index on site billing item.";
43150
- } catch (error) {
43151
- import_node_server_utils144.logger.log({
43152
- level: "error",
43153
- message: error.message
43154
- });
43155
- throw new import_node_server_utils144.InternalServerError(
43156
- "Failed to create text index on site billing item."
43157
- );
43158
- }
43159
- }
43160
43270
  async function add(value, session) {
43161
43271
  try {
43162
43272
  value = MBillingItem(value);
@@ -43355,7 +43465,6 @@ function useSiteBillingItemRepo() {
43355
43465
  });
43356
43466
  }
43357
43467
  return {
43358
- createTextIndex,
43359
43468
  add,
43360
43469
  getAll,
43361
43470
  getById,
@@ -47019,7 +47128,8 @@ function UseAccessManagementRepo() {
47019
47128
  type: 1,
47020
47129
  cardNo: 1,
47021
47130
  isActivated: 1,
47022
- replacementStatus: 1
47131
+ replacementStatus: 1,
47132
+ damage: 1
47023
47133
  }
47024
47134
  }
47025
47135
  ],
@@ -48209,26 +48319,20 @@ function UseAccessManagementRepo() {
48209
48319
  requestDate: 1,
48210
48320
  createdAt: 1,
48211
48321
  updatedAt: 1,
48212
- userCred: 1
48322
+ userCred: 1,
48323
+ damage: 1
48213
48324
  }
48214
48325
  }
48215
48326
  );
48216
48327
  if (!card)
48217
48328
  return null;
48218
- const site = await collectionName("sites").findOne(
48219
- { _id: card.site },
48220
- { projection: { name: 1, status: 1 } }
48221
- );
48222
- const user = card.userId ? await collectionName("users").findOne(
48223
- { _id: card.userId },
48224
- { projection: { name: 1, email: 1 } }
48225
- ) : null;
48226
- 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();
48227
48330
  return {
48228
48331
  ...card,
48229
- status,
48230
- site,
48231
- user
48332
+ // status,
48333
+ // site,
48334
+ // user,
48335
+ history
48232
48336
  };
48233
48337
  } catch (error) {
48234
48338
  throw new Error(error.message);
@@ -48548,6 +48652,16 @@ function UseAccessManagementRepo() {
48548
48652
  const updatedVisitor = result?._id;
48549
48653
  const assignCards = cards.map((card) => card._id);
48550
48654
  if (assignCards.length > 0) {
48655
+ const historyRecords = assignCards.map((cardId) => ({
48656
+ cardId: new import_mongodb97.ObjectId(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
+ );
48551
48665
  const bulkOps = assignCards.map((cardId) => {
48552
48666
  const updateFields = {
48553
48667
  staffNo: `STAFF-${cardId.toString().slice(-10)}`,
@@ -48662,11 +48776,35 @@ function UseAccessManagementRepo() {
48662
48776
  const session = import_node_server_utils158.useAtlas.getClient()?.startSession();
48663
48777
  try {
48664
48778
  await session?.startTransaction();
48665
- const userId = new import_mongodb97.ObjectId(params.userId);
48666
- const result = await collection().updateMany(
48667
- { userId },
48668
- { $set: { userId: null, status: "Available", updatedAt: /* @__PURE__ */ new Date() } }
48669
- );
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 import_mongodb97.ObjectId(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 import_mongodb97.ObjectId(card.cardId),
48799
+ userId: new import_mongodb97.ObjectId(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
+ });
48670
48808
  await session?.commitTransaction();
48671
48809
  return result;
48672
48810
  } catch (error) {
@@ -50629,15 +50767,22 @@ function useAccessManagementController() {
50629
50767
  };
50630
50768
  const checkoutVisitor = async (req, res) => {
50631
50769
  try {
50632
- const { userId } = req.body;
50770
+ const { cards } = req.body;
50633
50771
  const schema2 = import_joi87.default.object({
50634
- userId: import_joi87.default.string().hex().length(24).required()
50772
+ cards: import_joi87.default.array().items(
50773
+ import_joi87.default.object({
50774
+ cardId: import_joi87.default.string().hex().length(24).required(),
50775
+ userId: import_joi87.default.string().hex().length(24).required(),
50776
+ status: import_joi87.default.string().required(),
50777
+ remarks: import_joi87.default.string().allow("").required()
50778
+ })
50779
+ ).min(1).required()
50635
50780
  });
50636
- const { error } = schema2.validate({ userId });
50781
+ const { error } = schema2.validate({ cards });
50637
50782
  if (error) {
50638
50783
  return res.status(400).json({ message: error.message });
50639
50784
  }
50640
- const result = await checkoutVisitorSvc({ userId });
50785
+ const result = await checkoutVisitorSvc({ cards });
50641
50786
  return res.status(200).json({ message: "Success", data: result });
50642
50787
  } catch (error) {
50643
50788
  return res.status(500).json({
@@ -51319,6 +51464,11 @@ var schemaCreateNfcPatrolLog = import_joi89.default.object({
51319
51464
  startTime: import_joi89.default.string().required(),
51320
51465
  createdBy: import_joi89.default.string().length(24).hex().optional().allow("", null)
51321
51466
  });
51467
+ var schemaSignNfcPatrolLog = import_joi89.default.object({
51468
+ guardName: import_joi89.default.string().required(),
51469
+ guardSignatureFileId: import_joi89.default.string().length(24).hex().required(),
51470
+ signedAt: import_joi89.default.date().optional()
51471
+ });
51322
51472
  var schemaNfcPatrolLog = import_joi89.default.object({
51323
51473
  _id: import_joi89.default.string().length(24).hex().optional().allow(null, ""),
51324
51474
  site: import_joi89.default.string().length(24).hex().required(),
@@ -51357,6 +51507,7 @@ var schemaNfcPatrolLog = import_joi89.default.object({
51357
51507
  skippedRemarks: import_joi89.default.string().required()
51358
51508
  })
51359
51509
  ).min(0).required(),
51510
+ sign: schemaSignNfcPatrolLog.optional(),
51360
51511
  createdBy: import_joi89.default.string().length(24).hex().optional().allow(null, ""),
51361
51512
  createdAt: import_joi89.default.date().optional()
51362
51513
  });
@@ -51392,6 +51543,15 @@ function MNfcPatrolLog(valueArg) {
51392
51543
  throw new import_node_server_utils163.BadRequestError("Invalid route _id format");
51393
51544
  }
51394
51545
  }
51546
+ if (value.sign?.guardSignatureFileId) {
51547
+ try {
51548
+ value.sign.guardSignatureFileId = new import_mongodb100.ObjectId(
51549
+ value.sign.guardSignatureFileId
51550
+ );
51551
+ } catch {
51552
+ throw new import_node_server_utils163.BadRequestError("Invalid guardSignatureFileId format");
51553
+ }
51554
+ }
51395
51555
  return {
51396
51556
  _id: value._id ?? new import_mongodb100.ObjectId(),
51397
51557
  site: value.site,
@@ -51400,6 +51560,7 @@ function MNfcPatrolLog(valueArg) {
51400
51560
  startDateTime: value.startDateTime,
51401
51561
  endDateTime: value.endDateTime,
51402
51562
  checkPoints: value.checkPoints,
51563
+ sign: value.sign,
51403
51564
  createdAt: value.createdAt ?? /* @__PURE__ */ new Date(),
51404
51565
  createdBy: value.createdBy ?? void 0
51405
51566
  };
@@ -52850,8 +53011,8 @@ function useStatementOfAccountRepo() {
52850
53011
  async function createIndexes() {
52851
53012
  try {
52852
53013
  await collection.createIndexes([
52853
- { key: { site: 1, status: 1 } },
52854
- { key: { site: 1, unitId: 1, status: 1 } },
53014
+ { key: { site: 1, status: 1, createdAt: -1 } },
53015
+ { key: { site: 1, unitId: 1, status: 1, createdAt: -1 } },
52855
53016
  { key: { createdAt: -1 } }
52856
53017
  ]);
52857
53018
  return `Successfully created indexes for ${namespace_collection}.`;
@@ -52917,12 +53078,7 @@ function useStatementOfAccountRepo() {
52917
53078
  let endDate = new Date(dateTo);
52918
53079
  endDate.setHours(23, 59, 59, 999);
52919
53080
  dateExpr = {
52920
- $expr: {
52921
- $and: [
52922
- { $gte: ["$createdAt", startDate] },
52923
- { $lte: ["$createdAt", endDate] }
52924
- ]
52925
- }
53081
+ createdAt: { $gte: startDate, $lte: endDate }
52926
53082
  };
52927
53083
  }
52928
53084
  const unitSearchRegex = search ? search.trim().replace(/\s+/g, "").replace(/\//g, "\\s*/\\s*") : null;
@@ -53107,12 +53263,7 @@ function useStatementOfAccountRepo() {
53107
53263
  let endDate = new Date(dateTo);
53108
53264
  endDate.setHours(23, 59, 59, 999);
53109
53265
  dateExpr = {
53110
- $expr: {
53111
- $and: [
53112
- { $gte: ["$createdAt", startDate] },
53113
- { $lte: ["$createdAt", endDate] }
53114
- ]
53115
- }
53266
+ createdAt: { $gte: startDate, $lte: endDate }
53116
53267
  };
53117
53268
  }
53118
53269
  const unitSearchRegex = search ? search.trim().replace(/\s+/g, "").replace(/\//g, "\\s*/\\s*") : null;
@@ -57957,18 +58108,10 @@ function useNfcPatrolLogRepo() {
57957
58108
  }
57958
58109
  }
57959
58110
  async function add(value, session) {
57960
- try {
57961
- value = MNfcPatrolLog(value);
57962
- const res = await collection.insertOne(value, { session });
57963
- delCachedData();
57964
- return res.insertedId;
57965
- } catch (error) {
57966
- const isDuplicated = error.message.includes("duplicate");
57967
- if (isDuplicated) {
57968
- throw new import_node_server_utils198.BadRequestError("NFC Patrol Log already exists.");
57969
- }
57970
- throw error;
57971
- }
58111
+ value = MNfcPatrolLog(value);
58112
+ const res = await collection.insertOne(value, { session });
58113
+ delCachedData();
58114
+ return res.insertedId;
57972
58115
  }
57973
58116
  async function getAllBySite({
57974
58117
  page = 1,
@@ -58084,12 +58227,28 @@ function useNfcPatrolLogRepo() {
58084
58227
  delCachedData();
58085
58228
  return res;
58086
58229
  }
58230
+ async function sign(id, value, session) {
58231
+ id = new import_mongodb121.ObjectId(id);
58232
+ await collection.updateOne(
58233
+ { _id: id },
58234
+ {
58235
+ $set: {
58236
+ sign: {
58237
+ ...value,
58238
+ signedAt: /* @__PURE__ */ new Date()
58239
+ }
58240
+ }
58241
+ },
58242
+ { session }
58243
+ );
58244
+ }
58087
58245
  return {
58088
58246
  createIndexes,
58089
58247
  add,
58090
58248
  getAllBySite,
58091
58249
  getById,
58092
- updateCheckpoint
58250
+ updateCheckpoint,
58251
+ sign
58093
58252
  };
58094
58253
  }
58095
58254
 
@@ -58099,7 +58258,9 @@ function useNfcPatrolLogService() {
58099
58258
  const {
58100
58259
  add: _add,
58101
58260
  getById,
58102
- updateCheckpoint
58261
+ updateCheckpoint,
58262
+ getAllBySite: _getAllBySite,
58263
+ sign: repoSign
58103
58264
  } = useNfcPatrolLogRepo();
58104
58265
  const routeRepo = useNfcPatrolRouteRepo();
58105
58266
  const tagRepo = useNfcPatrolTagRepo();
@@ -58114,9 +58275,30 @@ function useNfcPatrolLogService() {
58114
58275
  if (!route.startTimes?.length) {
58115
58276
  throw new import_node_server_utils199.BadRequestError("Route does not have any start time.");
58116
58277
  }
58278
+ if (!route.startTimes.includes(value.startTime)) {
58279
+ throw new import_node_server_utils199.BadRequestError("Invalid route start time.");
58280
+ }
58281
+ const existedLog = await _getAllBySite(
58282
+ {
58283
+ site: value.site,
58284
+ page: 1,
58285
+ limit: 1,
58286
+ date: value.date,
58287
+ route: {
58288
+ _id: route._id.toString(),
58289
+ startTime: value.startTime
58290
+ }
58291
+ },
58292
+ session
58293
+ );
58294
+ if (existedLog.items.length > 0) {
58295
+ throw new import_node_server_utils199.BadRequestError(
58296
+ `Patrol log for ${value.startTime} already exists on ${value.date}.`
58297
+ );
58298
+ }
58117
58299
  const checkPoints = [];
58118
58300
  let currentTime = /* @__PURE__ */ new Date(
58119
- `${value.date}T${route.startTimes[0]}:00`
58301
+ `${value.date}T${value.startTime}:00`
58120
58302
  );
58121
58303
  for (const checkPoint of route.checkPoints) {
58122
58304
  const tag = await tagRepo.getById(checkPoint.nfcTag_id, session);
@@ -58150,7 +58332,7 @@ function useNfcPatrolLogService() {
58150
58332
  _id: route._id.toString(),
58151
58333
  name: route.name,
58152
58334
  tolerance: Number(route.tolerance),
58153
- startTime: route.startTimes[0],
58335
+ startTime: value.startTime,
58154
58336
  endTime: currentTime.toISOString().substring(11, 16)
58155
58337
  },
58156
58338
  date: value.date,
@@ -58163,7 +58345,6 @@ function useNfcPatrolLogService() {
58163
58345
  await session?.commitTransaction();
58164
58346
  return "Successfully added NFC Patrol Log.";
58165
58347
  } catch (error) {
58166
- console.error("Add NFC Patrol Log Error:", error);
58167
58348
  await session?.abortTransaction();
58168
58349
  throw error;
58169
58350
  } finally {
@@ -58215,9 +58396,26 @@ function useNfcPatrolLogService() {
58215
58396
  await session?.endSession();
58216
58397
  }
58217
58398
  }
58399
+ async function sign(id, value) {
58400
+ const log = await getById(id);
58401
+ if (!log) {
58402
+ throw new import_node_server_utils199.NotFoundError("Patrol log not found.");
58403
+ }
58404
+ const canSign = log.checkPoints.every(
58405
+ (c) => c.status === "Completed" || c.status === "Skipped"
58406
+ );
58407
+ if (!canSign) {
58408
+ throw new import_node_server_utils199.BadRequestError(
58409
+ "All checkpoints must be completed or skipped before signing."
58410
+ );
58411
+ }
58412
+ await repoSign(id, value);
58413
+ return "Successfully signed patrol.";
58414
+ }
58218
58415
  return {
58219
58416
  add,
58220
- completeCheckpoint
58417
+ completeCheckpoint,
58418
+ sign
58221
58419
  };
58222
58420
  }
58223
58421
 
@@ -58225,7 +58423,7 @@ function useNfcPatrolLogService() {
58225
58423
  var import_node_server_utils200 = require("@7365admin1/node-server-utils");
58226
58424
  var import_joi111 = __toESM(require("joi"));
58227
58425
  function useNfcPatrolLogController() {
58228
- const { add: _add, completeCheckpoint: _completeCheckpoint } = useNfcPatrolLogService();
58426
+ const { add: _add, completeCheckpoint: _completeCheckpoint, sign: _sign } = useNfcPatrolLogService();
58229
58427
  const { getAllBySite: _getAllBySite, getById: _getById } = useNfcPatrolLogRepo();
58230
58428
  async function add(req, res, next) {
58231
58429
  try {
@@ -58317,11 +58515,26 @@ function useNfcPatrolLogController() {
58317
58515
  next(error);
58318
58516
  }
58319
58517
  }
58518
+ async function sign(req, res, next) {
58519
+ try {
58520
+ const value = await schemaSignNfcPatrolLog.validateAsync(req.body);
58521
+ const result = await _sign(
58522
+ req.params.id,
58523
+ value
58524
+ );
58525
+ res.json({
58526
+ message: result
58527
+ });
58528
+ } catch (err) {
58529
+ next(err);
58530
+ }
58531
+ }
58320
58532
  return {
58321
58533
  add,
58322
58534
  getAllBySite,
58323
58535
  completeCheckpoint,
58324
- getLog
58536
+ getLog,
58537
+ sign
58325
58538
  };
58326
58539
  }
58327
58540
 
@@ -70471,6 +70684,17 @@ function formatHidResponse(data) {
70471
70684
  function sanitizeHidPath(path5) {
70472
70685
  return path5.replace(/([?&]session=)[^&]+/i, "$1<redacted>");
70473
70686
  }
70687
+ function normalizeBaseUrl(value) {
70688
+ return String(value || "").replace(/\/+$/, "");
70689
+ }
70690
+ function getRelayAuth() {
70691
+ const username = process.env.HID_AMICO_RELAY_USERNAME;
70692
+ const password = process.env.HID_AMICO_RELAY_PASSWORD;
70693
+ if (!username || !password) {
70694
+ return void 0;
70695
+ }
70696
+ return { username, password };
70697
+ }
70474
70698
  function toHidRequestError(error, path5) {
70475
70699
  if (import_axios4.default.isAxiosError(error)) {
70476
70700
  const status = error.response?.status;
@@ -70510,10 +70734,12 @@ var HidAmicoClient = class {
70510
70734
  constructor(reader) {
70511
70735
  this.reader = reader;
70512
70736
  this.session = "";
70737
+ const relayAuth = getRelayAuth();
70513
70738
  this.http = import_axios4.default.create({
70514
- baseURL: reader.baseUrl.replace(/\/+$/, ""),
70739
+ baseURL: normalizeBaseUrl(reader.baseUrl),
70515
70740
  timeout: 15e3,
70516
- headers: { "Content-Type": "application/json" }
70741
+ headers: { "Content-Type": "application/json" },
70742
+ ...relayAuth ? { auth: relayAuth } : {}
70517
70743
  });
70518
70744
  }
70519
70745
  async post(path5, body) {
@@ -70746,7 +70972,12 @@ function useHidAmicoService() {
70746
70972
  try {
70747
70973
  await client.login();
70748
70974
  const valid = await client.sessionIsValid();
70749
- await repo.updateById(id, { lastSeenAt: /* @__PURE__ */ new Date(), lastSyncMessage: "Connection test succeeded." });
70975
+ await repo.updateById(id, {
70976
+ status: "active",
70977
+ lastSeenAt: /* @__PURE__ */ new Date(),
70978
+ lastSyncStatus: "completed",
70979
+ lastSyncMessage: "Connection test succeeded."
70980
+ });
70750
70981
  await repo.addEvent({ reader: id, site: reader.site, type: "test", payload: { valid } });
70751
70982
  return { valid, message: "HID Amico connection test succeeded." };
70752
70983
  } catch (error) {
@@ -70776,6 +71007,7 @@ function useHidAmicoService() {
70776
71007
  }
70777
71008
  const message = batches.length ? `Synced ${batches.length} HID object batch(es).` : "HID reader session verified. No sync payload was supplied.";
70778
71009
  await repo.updateById(id, {
71010
+ status: "active",
70779
71011
  lastSeenAt: /* @__PURE__ */ new Date(),
70780
71012
  lastSyncAt: /* @__PURE__ */ new Date(),
70781
71013
  lastSyncStatus: "completed",
@@ -71583,6 +71815,7 @@ function useHidAmicoController() {
71583
71815
  schemaPostFavorite,
71584
71816
  schemaServiceProvider,
71585
71817
  schemaServiceProviderBilling,
71818
+ schemaSignNfcPatrolLog,
71586
71819
  schemaSiteCamera,
71587
71820
  schemaSiteFacility,
71588
71821
  schemaSiteFacilityBooking,