@7365admin1/core 3.32.2-staging.78 → 3.32.2-staging.79

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.d.ts CHANGED
@@ -9209,7 +9209,7 @@ type HidObjectBatch = {
9209
9209
  };
9210
9210
  type HidObjectQuery = {
9211
9211
  object: string;
9212
- where?: Record<string, any>;
9212
+ where?: UnknownRecord;
9213
9213
  fields?: string[];
9214
9214
  order?: string[];
9215
9215
  limit?: number;
@@ -9241,14 +9241,19 @@ declare function useHidAmicoService(): {
9241
9241
  message: string;
9242
9242
  results: Record<string, any>[];
9243
9243
  }>;
9244
- receiveNotification: (readerId: string, type: string, payload: Record<string, any>) => Promise<{
9245
- _id: bson.ObjectId;
9246
- reader: bson.ObjectId;
9247
- site: bson.ObjectId | undefined;
9248
- type: string;
9249
- payload: Record<string, any>;
9250
- status: "failed" | "received" | "processed";
9251
- createdAt: string | Date;
9244
+ receiveNotification: (readerId: string, type: string, payload: UnknownRecord) => Promise<{
9245
+ event: {
9246
+ _id: bson.ObjectId;
9247
+ reader: bson.ObjectId;
9248
+ site: bson.ObjectId | undefined;
9249
+ type: string;
9250
+ payload: Record<string, any>;
9251
+ status: "failed" | "received" | "processed";
9252
+ createdAt: string | Date;
9253
+ };
9254
+ response: {
9255
+ result: UnknownRecord;
9256
+ } | null;
9252
9257
  }>;
9253
9258
  listLogs: (value: {
9254
9259
  reader: string;
package/dist/index.js CHANGED
@@ -74438,6 +74438,8 @@ var schemaHidAmicoNotificationParams = import_joi153.default.object({
74438
74438
  "secbox",
74439
74439
  "access_photo",
74440
74440
  "new_user_identified",
74441
+ "new_qrcode",
74442
+ "new_card",
74441
74443
  "push_result"
74442
74444
  ).required()
74443
74445
  });
@@ -75269,6 +75271,8 @@ async function configureVisitorQrReader(client, qrFormat) {
75269
75271
  }
75270
75272
  async function ensureVisitorAccessAuthorization(client, readerId, readerLocation, hidUserId) {
75271
75273
  const accessRuleId = createStableHidId(`visitor-access-rule:${readerId}`);
75274
+ const timeZoneId = createStableHidId(`visitor-time-zone:${readerId}`);
75275
+ const timeSpanId = createStableHidId(`visitor-time-span:${readerId}`);
75272
75276
  const existingRuleResponse = await client.loadObjects({
75273
75277
  object: "access_rules",
75274
75278
  where: { access_rules: { id: accessRuleId } },
@@ -75296,6 +75300,74 @@ async function ensureVisitorAccessAuthorization(client, readerId, readerLocation
75296
75300
  }]
75297
75301
  });
75298
75302
  }
75303
+ const existingTimeZoneResponse = await client.loadObjects({
75304
+ object: "time_zones",
75305
+ where: { time_zones: { id: timeZoneId } },
75306
+ limit: 1,
75307
+ offset: 0
75308
+ });
75309
+ if (getHidObjectRows(existingTimeZoneResponse, "time_zones").length) {
75310
+ await client.modifyObjects({
75311
+ object: "time_zones",
75312
+ where: { time_zones: { id: timeZoneId } },
75313
+ values: { name: "iService365 Visitor Schedule" }
75314
+ });
75315
+ } else {
75316
+ await client.createObjects({
75317
+ object: "time_zones",
75318
+ values: [{ id: timeZoneId, name: "iService365 Visitor Schedule" }]
75319
+ });
75320
+ }
75321
+ const visitorTimeSpan = {
75322
+ time_zone_id: timeZoneId,
75323
+ start: 0,
75324
+ end: 86399,
75325
+ sun: 1,
75326
+ mon: 1,
75327
+ tue: 1,
75328
+ wed: 1,
75329
+ thu: 1,
75330
+ fri: 1,
75331
+ sat: 1,
75332
+ hol1: 1,
75333
+ hol2: 1,
75334
+ hol3: 1
75335
+ };
75336
+ const existingTimeSpanResponse = await client.loadObjects({
75337
+ object: "time_spans",
75338
+ where: { time_spans: { id: timeSpanId } },
75339
+ limit: 1,
75340
+ offset: 0
75341
+ });
75342
+ if (getHidObjectRows(existingTimeSpanResponse, "time_spans").length) {
75343
+ await client.modifyObjects({
75344
+ object: "time_spans",
75345
+ where: { time_spans: { id: timeSpanId } },
75346
+ values: visitorTimeSpan
75347
+ });
75348
+ } else {
75349
+ await client.createObjects({
75350
+ object: "time_spans",
75351
+ values: [{ id: timeSpanId, ...visitorTimeSpan }]
75352
+ });
75353
+ }
75354
+ const accessRuleTimeZoneResponse = await client.loadObjects({
75355
+ object: "access_rule_time_zones",
75356
+ where: {
75357
+ access_rule_time_zones: {
75358
+ access_rule_id: accessRuleId,
75359
+ time_zone_id: timeZoneId
75360
+ }
75361
+ },
75362
+ limit: 1,
75363
+ offset: 0
75364
+ });
75365
+ if (!getHidObjectRows(accessRuleTimeZoneResponse, "access_rule_time_zones").length) {
75366
+ await client.createObjects({
75367
+ object: "access_rule_time_zones",
75368
+ values: [{ access_rule_id: accessRuleId, time_zone_id: timeZoneId }]
75369
+ });
75370
+ }
75299
75371
  const portalsResponse = await client.loadObjects({
75300
75372
  object: "portals",
75301
75373
  limit: 100,
@@ -75350,7 +75422,7 @@ async function ensureVisitorAccessAuthorization(client, readerId, readerLocation
75350
75422
  values: missingPortalLinks
75351
75423
  });
75352
75424
  }
75353
- return { accessRuleId, portalIds };
75425
+ return { accessRuleId, portalIds, timeZoneId };
75354
75426
  }
75355
75427
  function getRelayAuth() {
75356
75428
  const username = process.env.HID_AMICO_RELAY_USERNAME;
@@ -75688,19 +75760,95 @@ function normalizeBatches(payload) {
75688
75760
  function firstValue(payload, keys) {
75689
75761
  for (const key of keys) {
75690
75762
  const value = payload?.[key];
75691
- if (value !== void 0 && value !== null && value !== "") {
75763
+ if ((typeof value === "string" || typeof value === "number") && value !== "") {
75692
75764
  return value;
75693
75765
  }
75694
75766
  }
75695
75767
  return void 0;
75696
75768
  }
75697
75769
  function extractIdentityKeys(payload) {
75698
- const nested = payload?.user || payload?.person || payload?.data || {};
75770
+ const nestedCandidate = payload.user || payload.person || payload.data;
75771
+ const nested = isUnknownRecord(nestedCandidate) ? nestedCandidate : {};
75699
75772
  const merged = { ...payload, ...nested };
75773
+ const hidUserId = firstValue(merged, ["hidUserId", "hid_user_id", "user_id", "userId", "id", "user"]);
75774
+ const registration = firstValue(merged, ["registration", "register", "employeeNo", "staffNo", "memberNo"]);
75775
+ const cardNo = firstValue(merged, [
75776
+ "qrcode_value",
75777
+ "qrCodeValue",
75778
+ "qr_code_value",
75779
+ "card_value",
75780
+ "cardNo",
75781
+ "card_no",
75782
+ "card",
75783
+ "cardNumber",
75784
+ "value"
75785
+ ]);
75786
+ return {
75787
+ hidUserId,
75788
+ registration: registration === void 0 ? void 0 : String(registration),
75789
+ cardNo: cardNo === void 0 ? void 0 : String(cardNo)
75790
+ };
75791
+ }
75792
+ function toPositiveInteger(value) {
75793
+ const parsed = Number(value);
75794
+ return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : null;
75795
+ }
75796
+ function toDateTimestamp(value) {
75797
+ if (value instanceof Date)
75798
+ return value.getTime();
75799
+ if (typeof value !== "string" && typeof value !== "number")
75800
+ return null;
75801
+ const parsed = new Date(value).getTime();
75802
+ return Number.isFinite(parsed) ? parsed : null;
75803
+ }
75804
+ function buildOnlineIdentificationResponse(identity, payload) {
75805
+ const metadata = identity && isUnknownRecord(identity.metadata) ? identity.metadata : {};
75806
+ const requestedPortalId = toPositiveInteger(payload.portal_id ?? payload.portalId);
75807
+ const configuredPortalIds = Array.isArray(metadata.portalIds) ? metadata.portalIds.map(toPositiveInteger).filter((value) => value !== null) : [];
75808
+ const portalId = requestedPortalId ?? (configuredPortalIds.length === 1 ? configuredPortalIds[0] : null);
75809
+ const now = Date.now();
75810
+ const issuedAt = toDateTimestamp(metadata.issuedAt);
75811
+ const expiresAt = toDateTimestamp(metadata.expiresAt);
75812
+ let deniedReason = "User is not registered for this reader.";
75813
+ if (identity) {
75814
+ deniedReason = "Access denied.";
75815
+ if (identity.type === "visitor" && issuedAt !== null && now < issuedAt) {
75816
+ deniedReason = "Visitor pass is not active yet.";
75817
+ } else if (identity.type === "visitor" && expiresAt !== null && now > expiresAt) {
75818
+ deniedReason = "Visitor pass has expired.";
75819
+ } else if (identity.type === "visitor" && configuredPortalIds.length > 0 && requestedPortalId !== null && !configuredPortalIds.includes(requestedPortalId)) {
75820
+ deniedReason = "Visitor is not permitted to use this portal.";
75821
+ } else {
75822
+ const userId = toPositiveInteger(identity.hidUserId ?? payload.user_id);
75823
+ const userName = String(metadata.name ?? payload.user_name ?? "").trim();
75824
+ const result = {
75825
+ event: 7,
75826
+ user_id: userId ?? 0,
75827
+ user_name: userName,
75828
+ user_image: Boolean(metadata.facialEnrolled),
75829
+ portal_id: portalId === null ? "" : String(portalId),
75830
+ message: "Access granted"
75831
+ };
75832
+ if (portalId !== null) {
75833
+ result.actions = [{ action: "door", parameters: `door=${portalId}` }];
75834
+ }
75835
+ return { granted: true, reason: "Access granted", response: { result } };
75836
+ }
75837
+ }
75700
75838
  return {
75701
- hidUserId: firstValue(merged, ["hidUserId", "hid_user_id", "user_id", "userId", "id", "user"]),
75702
- registration: firstValue(merged, ["registration", "register", "employeeNo", "staffNo", "memberNo"]),
75703
- cardNo: firstValue(merged, ["cardNo", "card_no", "card", "cardNumber", "value"])
75839
+ granted: false,
75840
+ reason: deniedReason,
75841
+ response: {
75842
+ result: {
75843
+ event: 6,
75844
+ user_id: toPositiveInteger(identity?.hidUserId ?? payload.user_id) ?? 0,
75845
+ user_name: String(metadata.name ?? payload.user_name ?? "").trim(),
75846
+ user_image: Boolean(metadata.facialEnrolled),
75847
+ portal_id: portalId === null ? "" : String(portalId),
75848
+ actions: [],
75849
+ message: deniedReason
75850
+ }
75851
+ }
75704
75852
  };
75705
75853
  }
75706
75854
  function useHidAmicoService() {
@@ -75854,6 +76002,7 @@ function useHidAmicoService() {
75854
76002
  reader: readerId,
75855
76003
  ...identityKeys
75856
76004
  });
76005
+ const authorization = ["new_user_identified", "new_qrcode", "new_card"].includes(type) ? buildOnlineIdentificationResponse(identity, payload) : null;
75857
76006
  const enrichedPayload = identity ? {
75858
76007
  ...payload,
75859
76008
  identity: {
@@ -75866,20 +76015,33 @@ function useHidAmicoService() {
75866
76015
  user: identity.user,
75867
76016
  member: identity.member,
75868
76017
  visitor: identity.visitor
75869
- }
76018
+ },
76019
+ ...authorization ? {
76020
+ authorization: {
76021
+ granted: authorization.granted,
76022
+ reason: authorization.reason
76023
+ }
76024
+ } : {}
75870
76025
  } : {
75871
76026
  ...payload,
75872
76027
  identity: null,
75873
- identityLookup: identityKeys
76028
+ identityLookup: identityKeys,
76029
+ ...authorization ? {
76030
+ authorization: {
76031
+ granted: authorization.granted,
76032
+ reason: authorization.reason
76033
+ }
76034
+ } : {}
75874
76035
  };
75875
76036
  await repo.updateById(readerId, { lastSeenAt: /* @__PURE__ */ new Date() });
75876
- return repo.addEvent({
76037
+ const event = await repo.addEvent({
75877
76038
  reader: readerId,
75878
76039
  site: reader.site,
75879
76040
  type,
75880
76041
  payload: enrichedPayload,
75881
- status: identity ? "processed" : "received"
76042
+ status: authorization ? authorization.granted ? "processed" : "failed" : identity ? "processed" : "received"
75882
76043
  });
76044
+ return { event, response: authorization?.response ?? null };
75883
76045
  }
75884
76046
  async function listLogs(value) {
75885
76047
  return repo.listEvents(value);
@@ -76021,6 +76183,7 @@ function useHidAmicoService() {
76021
76183
  const identityPayload = {
76022
76184
  hidUserId: String(hidUserId),
76023
76185
  registration,
76186
+ cardNo: credential.qrValue,
76024
76187
  visitor: value.visitorId,
76025
76188
  type: "visitor",
76026
76189
  status: "active",
@@ -76030,8 +76193,10 @@ function useHidAmicoService() {
76030
76193
  qrFormat: value.qrFormat,
76031
76194
  credentialObject: credential.object,
76032
76195
  credentialId: credential.credentialId,
76196
+ qrValue: credential.qrValue,
76033
76197
  accessRuleId: authorization.accessRuleId,
76034
76198
  portalIds: authorization.portalIds,
76199
+ timeZoneId: authorization.timeZoneId,
76035
76200
  issuedAt: issuedAt.toISOString(),
76036
76201
  expiresAt: expiresAt.toISOString()
76037
76202
  }
@@ -76057,6 +76222,7 @@ function useHidAmicoService() {
76057
76222
  credentialObject: credential.object,
76058
76223
  accessRuleId: authorization.accessRuleId,
76059
76224
  portalIds: authorization.portalIds,
76225
+ timeZoneId: authorization.timeZoneId,
76060
76226
  issuedAt: issuedAt.toISOString(),
76061
76227
  expiresAt: expiresAt.toISOString()
76062
76228
  }
@@ -76828,18 +76994,28 @@ function useHidAmicoController() {
76828
76994
  }
76829
76995
  }
76830
76996
  async function receiveNotification(req, res, next) {
76831
- const validation = schemaHidAmicoNotificationParams.validate(req.params);
76997
+ const type = String(req.params.type ?? "").replace(/\.fcgi$/i, "");
76998
+ const validation = schemaHidAmicoNotificationParams.validate({
76999
+ ...req.params,
77000
+ type
77001
+ });
76832
77002
  if (validation.error) {
76833
77003
  next(new import_node_server_utils267.BadRequestError(validation.error.message));
76834
77004
  return;
76835
77005
  }
76836
77006
  try {
76837
- const event = await service.receiveNotification(
77007
+ const queryPayload = req.query && typeof req.query === "object" ? req.query : {};
77008
+ const bodyPayload = req.body && typeof req.body === "object" ? req.body : {};
77009
+ const notification = await service.receiveNotification(
76838
77010
  validation.value.readerId,
76839
77011
  validation.value.type,
76840
- req.body ?? {}
77012
+ { ...queryPayload, ...bodyPayload }
76841
77013
  );
76842
- res.status(202).json({ data: event });
77014
+ if (notification.response) {
77015
+ res.status(200).json(notification.response);
77016
+ return;
77017
+ }
77018
+ res.status(202).json({ data: notification.event });
76843
77019
  } catch (error) {
76844
77020
  next(error);
76845
77021
  }