@7365admin1/core 3.64.3-staging.285 → 3.64.3-staging.287
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/.changeset/hid-profile-qr-backend.md +31 -0
- package/.changeset/hid-reader-user-search.md +22 -0
- package/.changeset/site-update-ignores-derived-resident-modules.md +16 -0
- package/dist/index.d.ts +45 -0
- package/dist/index.js +273 -13
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +273 -13
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/test/site-update-derived-metadata.test.mjs +53 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
---
|
|
2
|
+
"@7365admin1/core": minor
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Let a signed-in member hold their own HID QR code.
|
|
6
|
+
|
|
7
|
+
`GET`, `POST` and `DELETE /readers/:readerId/profile-qr` read, issue and revoke
|
|
8
|
+
the caller's own QR credential on one reader. The caller comes from the session,
|
|
9
|
+
so there is no user id in the path to point at somebody else, and each handler
|
|
10
|
+
calls `authorizeReader` for itself rather than trusting the router - the site is
|
|
11
|
+
re-resolved from the stored reader record.
|
|
12
|
+
|
|
13
|
+
The credential is the visitor mechanism: an alphanumeric value in the reader's
|
|
14
|
+
`qrcodes` table, with the reader put into the QR-readable configuration first.
|
|
15
|
+
Unlike a visitor pass it carries no validity window, matching the member's
|
|
16
|
+
facial enrollment, which is standing until removed. Issuing replaces any earlier
|
|
17
|
+
code for that member on that reader, and the replacement is written and verified
|
|
18
|
+
before the old one is removed.
|
|
19
|
+
|
|
20
|
+
It creates no access rule, deliberately. `setProfileFacial` beside it does not
|
|
21
|
+
either: a member's door access comes from the HID permissions screen, and
|
|
22
|
+
minting a rule here would mean anyone who can press Generate admits themselves.
|
|
23
|
+
The read reports whether that screen admits them, resolved through
|
|
24
|
+
`resolvePermissionUserBindings` - the same resolver that writes the rules onto
|
|
25
|
+
the device - so the answer is the one the reader will actually be asked.
|
|
26
|
+
|
|
27
|
+
Two settings are now readable that were already writable: the QR identification
|
|
28
|
+
and legacy-mode flags `configureVisitorQrReader` sets on every issue, plus
|
|
29
|
+
`access_rule_time_zones` alongside its two sibling link tables. Whether a reader
|
|
30
|
+
was set up to accept a QR, and whether an access rule had a schedule, were
|
|
31
|
+
invisible to every screen and every diagnosis.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
---
|
|
2
|
+
"@7365admin1/core": patch
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Make the search on the HID reader user roster work.
|
|
6
|
+
|
|
7
|
+
`listReaderUsers` asked the device to filter. Amico's `where` takes a value or a
|
|
8
|
+
list of values per column and has no operator form, so the substring clause it
|
|
9
|
+
sent for a text term, `{name: {LIKE: "%x%"}}`, was answered
|
|
10
|
+
`400 Invalid value (string or array expected)`. Every search by name threw, and
|
|
11
|
+
the screen showed "Unable to load HID users from reader" with an empty table.
|
|
12
|
+
|
|
13
|
+
A numeric term took a different branch that matched the HID user id exactly.
|
|
14
|
+
That did not fail, it answered wrongly: searching a registration number like
|
|
15
|
+
`000004` resolved to `id = 4` and returned a different person.
|
|
16
|
+
|
|
17
|
+
Rows are already pulled in one page and filtered here for `userType`, so the
|
|
18
|
+
search now joins that filter as a case-insensitive substring over name, id and
|
|
19
|
+
registration - the same three fields, and the same "contains" semantics, the
|
|
20
|
+
identities search uses. Verified against a live reader: a name matches
|
|
21
|
+
partially and case-insensitively, a registration number returns its own user,
|
|
22
|
+
a full UID still works, and no match returns an empty page rather than an error.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
---
|
|
2
|
+
"@7365admin1/core": patch
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Stop refusing Site Settings saves that send back `metadata.residentAppModulesEffective`.
|
|
6
|
+
|
|
7
|
+
`GET /api/sites/:id` attaches the derived `residentAppModulesEffective` to
|
|
8
|
+
`metadata` on every read, and the Site Settings screens save by sending the
|
|
9
|
+
metadata they read straight back. `updateSiteSchema` does not allow that key, so
|
|
10
|
+
`PATCH /api/sites/id/:id` refused the whole save with "is not allowed" — the
|
|
11
|
+
resident-app configuration panel, the HID QR code pass and the HID face-reader
|
|
12
|
+
toggle all failed.
|
|
13
|
+
|
|
14
|
+
`updateById` now drops the derived key before validating. It is never stored and
|
|
15
|
+
is recomputed on the next read, so nothing is lost; every stored key, including
|
|
16
|
+
`residentAppModules`, is validated and saved exactly as before.
|
package/dist/index.d.ts
CHANGED
|
@@ -12642,6 +12642,48 @@ declare function useHidAmicoService(): {
|
|
|
12642
12642
|
facialEnrolled: boolean;
|
|
12643
12643
|
facialEnrolledAt: string;
|
|
12644
12644
|
}>;
|
|
12645
|
+
getProfileQr: (readerId: string, userId: string) => Promise<{
|
|
12646
|
+
readerId: string;
|
|
12647
|
+
userId: string;
|
|
12648
|
+
memberId: string;
|
|
12649
|
+
hidUserId: string;
|
|
12650
|
+
registration: string;
|
|
12651
|
+
readerName: string;
|
|
12652
|
+
portalName: string;
|
|
12653
|
+
qrEnabled: boolean;
|
|
12654
|
+
issued: boolean;
|
|
12655
|
+
qrValue: string;
|
|
12656
|
+
issuedAt: string;
|
|
12657
|
+
assigned: boolean | null;
|
|
12658
|
+
}>;
|
|
12659
|
+
issueProfileQr: (readerId: string, userId: string) => Promise<{
|
|
12660
|
+
readerId: string;
|
|
12661
|
+
userId: string;
|
|
12662
|
+
memberId: string;
|
|
12663
|
+
hidUserId: string;
|
|
12664
|
+
registration: string;
|
|
12665
|
+
readerName: string;
|
|
12666
|
+
portalName: string;
|
|
12667
|
+
qrEnabled: boolean;
|
|
12668
|
+
issued: boolean;
|
|
12669
|
+
qrValue: string;
|
|
12670
|
+
issuedAt: string;
|
|
12671
|
+
assigned: boolean | null;
|
|
12672
|
+
}>;
|
|
12673
|
+
deleteProfileQr: (readerId: string, userId: string) => Promise<{
|
|
12674
|
+
readerId: string;
|
|
12675
|
+
userId: string;
|
|
12676
|
+
memberId: string;
|
|
12677
|
+
hidUserId: string;
|
|
12678
|
+
registration: string;
|
|
12679
|
+
readerName: string;
|
|
12680
|
+
portalName: string;
|
|
12681
|
+
qrEnabled: boolean;
|
|
12682
|
+
issued: boolean;
|
|
12683
|
+
qrValue: string;
|
|
12684
|
+
issuedAt: string;
|
|
12685
|
+
assigned: boolean | null;
|
|
12686
|
+
}>;
|
|
12645
12687
|
setProfileFacial: (readerId: string, userId: string, image: Buffer, options?: {
|
|
12646
12688
|
timestamp?: number;
|
|
12647
12689
|
match?: boolean;
|
|
@@ -12891,6 +12933,9 @@ declare function useHidAmicoController(): {
|
|
|
12891
12933
|
revokeVisitorQr: (req: Request, res: Response, next: NextFunction) => Promise<void>;
|
|
12892
12934
|
getProfileFacial: (req: Request, res: Response, next: NextFunction) => Promise<void>;
|
|
12893
12935
|
setProfileFacial: (req: Request, res: Response, next: NextFunction) => Promise<void>;
|
|
12936
|
+
getProfileQr: (req: Request, res: Response, next: NextFunction) => Promise<void>;
|
|
12937
|
+
issueProfileQr: (req: Request, res: Response, next: NextFunction) => Promise<void>;
|
|
12938
|
+
deleteProfileQr: (req: Request, res: Response, next: NextFunction) => Promise<void>;
|
|
12894
12939
|
setVisitorImage: (req: Request, res: Response, next: NextFunction) => Promise<void>;
|
|
12895
12940
|
deleteVisitorImage: (req: Request, res: Response, next: NextFunction) => Promise<void>;
|
|
12896
12941
|
getIntercomStatus: (req: Request, res: Response, next: NextFunction) => Promise<void>;
|
package/dist/index.js
CHANGED
|
@@ -38517,6 +38517,13 @@ function residentAppModulesAboveCeiling(orgModules, requested) {
|
|
|
38517
38517
|
function residentAppModulesAboveCeilingMessage(rejected) {
|
|
38518
38518
|
return `This client has not been given ${rejected.join(", ")} in the resident app, so a site cannot switch it on. Ask Seven365 to add it to the organisation first.`;
|
|
38519
38519
|
}
|
|
38520
|
+
function withoutDerivedSiteMetadata(metadata) {
|
|
38521
|
+
if (!metadata || typeof metadata !== "object" || Array.isArray(metadata)) {
|
|
38522
|
+
return metadata;
|
|
38523
|
+
}
|
|
38524
|
+
const { residentAppModulesEffective: _derived, ...stored } = metadata;
|
|
38525
|
+
return stored;
|
|
38526
|
+
}
|
|
38520
38527
|
function asModules(value) {
|
|
38521
38528
|
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
38522
38529
|
}
|
|
@@ -38704,8 +38711,9 @@ function useSiteController() {
|
|
|
38704
38711
|
}
|
|
38705
38712
|
async function updateById(req, res, next) {
|
|
38706
38713
|
try {
|
|
38714
|
+
const body = req.body?.metadata ? { ...req.body, metadata: withoutDerivedSiteMetadata(req.body.metadata) } : req.body;
|
|
38707
38715
|
const { error, value } = updateSiteSchema.validate(
|
|
38708
|
-
{ _id: req.params.id, ...
|
|
38716
|
+
{ _id: req.params.id, ...body },
|
|
38709
38717
|
{ abortEarly: false }
|
|
38710
38718
|
);
|
|
38711
38719
|
if (error) {
|
|
@@ -48716,7 +48724,12 @@ var HID_READABLE_OBJECTS = /* @__PURE__ */ new Set([
|
|
|
48716
48724
|
"time_spans",
|
|
48717
48725
|
"group_access_rules",
|
|
48718
48726
|
"portal_access_rules",
|
|
48719
|
-
"user_access_rules"
|
|
48727
|
+
"user_access_rules",
|
|
48728
|
+
// The third leg of the authorization chain, alongside the two above. A rule
|
|
48729
|
+
// with no time zone never grants, so leaving this unreadable made the most
|
|
48730
|
+
// common misconfiguration the one nobody could see. Read-only: the mutable
|
|
48731
|
+
// set is unchanged.
|
|
48732
|
+
"access_rule_time_zones"
|
|
48720
48733
|
]);
|
|
48721
48734
|
var HID_WRITABLE_CONFIGURATION = /* @__PURE__ */ new Map([
|
|
48722
48735
|
["identifier", /* @__PURE__ */ new Set([
|
|
@@ -48727,10 +48740,18 @@ var HID_WRITABLE_CONFIGURATION = /* @__PURE__ */ new Map([
|
|
|
48727
48740
|
["pjsip", new Set(PJSIP_CONFIGURATION_KEYS)]
|
|
48728
48741
|
]);
|
|
48729
48742
|
var HID_READABLE_CONFIGURATION = /* @__PURE__ */ new Map([
|
|
48730
|
-
|
|
48743
|
+
// `local_identification` and the two QR settings below are written by
|
|
48744
|
+
// `configureVisitorQrReader` on every visitor-QR issue but could not be read
|
|
48745
|
+
// back, so whether a reader is actually set up to accept a QR was invisible
|
|
48746
|
+
// to every screen and every diagnosis. Reading a setting we already write is
|
|
48747
|
+
// not a widening of what this endpoint can change - the writable map is
|
|
48748
|
+
// untouched.
|
|
48749
|
+
["general", /* @__PURE__ */ new Set(["online", "local_identification"])],
|
|
48750
|
+
["face_id", /* @__PURE__ */ new Set(["qrcode_legacy_mode_enabled"])],
|
|
48731
48751
|
["identifier", /* @__PURE__ */ new Set([
|
|
48732
48752
|
"card_identification_enabled",
|
|
48733
48753
|
"pin_identification_enabled",
|
|
48754
|
+
"qrcode_identification_enabled",
|
|
48734
48755
|
"multi_factor_authentication"
|
|
48735
48756
|
])],
|
|
48736
48757
|
// A configured password must never be returned to a browser client.
|
|
@@ -51116,20 +51137,21 @@ function useHidAmicoService() {
|
|
|
51116
51137
|
const client = new HidAmicoClient(reader);
|
|
51117
51138
|
try {
|
|
51118
51139
|
await client.login();
|
|
51119
|
-
const normalizedSearch = search.trim();
|
|
51120
|
-
const
|
|
51121
|
-
|
|
51122
|
-
|
|
51123
|
-
|
|
51124
|
-
}
|
|
51125
|
-
userWhere.name = { LIKE: `%${normalizedSearch}%` };
|
|
51126
|
-
}
|
|
51140
|
+
const normalizedSearch = search.trim().toLowerCase();
|
|
51141
|
+
const matchesSearch = (row) => {
|
|
51142
|
+
if (!normalizedSearch)
|
|
51143
|
+
return true;
|
|
51144
|
+
return [row.name, row.id, row.registration].some((value) => String(value ?? "").toLowerCase().includes(normalizedSearch));
|
|
51145
|
+
};
|
|
51127
51146
|
let rows = [];
|
|
51128
51147
|
let reportedTotal = null;
|
|
51129
51148
|
let hasMore = false;
|
|
51130
51149
|
const response = await client.loadObjects({
|
|
51131
51150
|
object: "users",
|
|
51132
|
-
where: { users:
|
|
51151
|
+
where: { users: {} },
|
|
51152
|
+
// One page of the device's table, then everything below is decided
|
|
51153
|
+
// here. A reader holding more than this many users would need paging
|
|
51154
|
+
// added; the estate's readers hold single figures.
|
|
51133
51155
|
limit: 500,
|
|
51134
51156
|
offset: 0,
|
|
51135
51157
|
order: ["id", "ascending"]
|
|
@@ -51137,7 +51159,8 @@ function useHidAmicoService() {
|
|
|
51137
51159
|
const deviceRows = getHidObjectRows(response, "users");
|
|
51138
51160
|
const filteredRows = deviceRows.filter((row) => {
|
|
51139
51161
|
const isVisitor = Number(row.user_type_id) === 1;
|
|
51140
|
-
|
|
51162
|
+
const typeMatches = userType === "visitor" ? isVisitor : userType === "user" ? !isVisitor : true;
|
|
51163
|
+
return typeMatches && matchesSearch(row);
|
|
51141
51164
|
});
|
|
51142
51165
|
reportedTotal = filteredRows.length;
|
|
51143
51166
|
rows = filteredRows.slice(offset, offset + limit);
|
|
@@ -51821,6 +51844,198 @@ function useHidAmicoService() {
|
|
|
51821
51844
|
}
|
|
51822
51845
|
return null;
|
|
51823
51846
|
}
|
|
51847
|
+
function createProfileQrCredential(readerId, userId, qrValue) {
|
|
51848
|
+
return {
|
|
51849
|
+
credentialId: createStableHidId(`profile-qr:${readerId}:${userId}`),
|
|
51850
|
+
object: "qrcodes",
|
|
51851
|
+
value: qrValue || `USR${import_crypto3.default.randomBytes(12).toString("hex").toUpperCase()}`
|
|
51852
|
+
};
|
|
51853
|
+
}
|
|
51854
|
+
async function isProfileAssignedToReader(reader, userId) {
|
|
51855
|
+
try {
|
|
51856
|
+
const documents = await repo.listSitePermissionDocuments(String(reader.site));
|
|
51857
|
+
const assignments = documents.flatMap((document2) => Array.isArray(document2.assignments) ? document2.assignments : []);
|
|
51858
|
+
if (!assignments.length)
|
|
51859
|
+
return false;
|
|
51860
|
+
const bindings = await repo.resolvePermissionUserBindings(String(reader.site), assignments);
|
|
51861
|
+
return bindings.some((binding) => String(binding.userId) === String(userId));
|
|
51862
|
+
} catch {
|
|
51863
|
+
return null;
|
|
51864
|
+
}
|
|
51865
|
+
}
|
|
51866
|
+
async function getProfileQr(readerId, userId) {
|
|
51867
|
+
const reader = await getActiveReader(readerId);
|
|
51868
|
+
const context = await getProfileEnrollmentContext(readerId, reader, userId);
|
|
51869
|
+
const metadata = context.profileIdentity && isUnknownRecord2(context.profileIdentity.metadata) ? context.profileIdentity.metadata : {};
|
|
51870
|
+
return {
|
|
51871
|
+
readerId,
|
|
51872
|
+
userId,
|
|
51873
|
+
memberId: String(context.member._id),
|
|
51874
|
+
hidUserId: String(context.hidUserId),
|
|
51875
|
+
registration: context.registration,
|
|
51876
|
+
readerName: String(reader.name || ""),
|
|
51877
|
+
portalName: String(reader.portalName || ""),
|
|
51878
|
+
qrEnabled: readerSupportsCredential(reader, "qr"),
|
|
51879
|
+
issued: typeof metadata.profileQrValue === "string" && metadata.profileQrValue.length > 0,
|
|
51880
|
+
qrValue: typeof metadata.profileQrValue === "string" ? metadata.profileQrValue : "",
|
|
51881
|
+
issuedAt: typeof metadata.profileQrIssuedAt === "string" ? metadata.profileQrIssuedAt : "",
|
|
51882
|
+
assigned: await isProfileAssignedToReader(reader, userId)
|
|
51883
|
+
};
|
|
51884
|
+
}
|
|
51885
|
+
async function issueProfileQr(readerId, userId) {
|
|
51886
|
+
const reader = await getActiveReader(readerId);
|
|
51887
|
+
assertReaderCapability(reader, "qr");
|
|
51888
|
+
assertReaderPortal(reader, "issuing a QR code");
|
|
51889
|
+
const context = await getProfileEnrollmentContext(readerId, reader, userId);
|
|
51890
|
+
if (!context.profileName) {
|
|
51891
|
+
throw new import_node_server_utils137.BadRequestError("A profile name is required to issue a HID QR code.");
|
|
51892
|
+
}
|
|
51893
|
+
const issuedAt = /* @__PURE__ */ new Date();
|
|
51894
|
+
const credential = createProfileQrCredential(readerId, userId);
|
|
51895
|
+
const client = new HidAmicoClient(reader);
|
|
51896
|
+
try {
|
|
51897
|
+
await client.login();
|
|
51898
|
+
await configureVisitorQrReader(client);
|
|
51899
|
+
const existingUserResponse = await client.loadObjects({
|
|
51900
|
+
object: "users",
|
|
51901
|
+
where: { users: { id: context.hidUserId } },
|
|
51902
|
+
limit: 1,
|
|
51903
|
+
offset: 0
|
|
51904
|
+
});
|
|
51905
|
+
const hidUser = { name: context.profileName, registration: context.registration };
|
|
51906
|
+
if (getHidObjectRows(existingUserResponse, "users").length) {
|
|
51907
|
+
await client.modifyObjects({
|
|
51908
|
+
object: "users",
|
|
51909
|
+
where: { users: { id: context.hidUserId } },
|
|
51910
|
+
values: hidUser
|
|
51911
|
+
});
|
|
51912
|
+
} else {
|
|
51913
|
+
await client.createObjects({
|
|
51914
|
+
object: "users",
|
|
51915
|
+
values: [{ id: context.hidUserId, ...hidUser }]
|
|
51916
|
+
});
|
|
51917
|
+
}
|
|
51918
|
+
const existingQrs = getHidObjectRows(
|
|
51919
|
+
await client.loadObjects({
|
|
51920
|
+
object: credential.object,
|
|
51921
|
+
where: { [credential.object]: { user_id: context.hidUserId } },
|
|
51922
|
+
limit: 100,
|
|
51923
|
+
offset: 0
|
|
51924
|
+
}),
|
|
51925
|
+
credential.object
|
|
51926
|
+
);
|
|
51927
|
+
const credentialValues = { value: credential.value, user_id: context.hidUserId };
|
|
51928
|
+
if (existingQrs.some((row) => String(row.id) === String(credential.credentialId))) {
|
|
51929
|
+
await client.modifyObjects({
|
|
51930
|
+
object: credential.object,
|
|
51931
|
+
where: { [credential.object]: { id: credential.credentialId } },
|
|
51932
|
+
values: credentialValues
|
|
51933
|
+
});
|
|
51934
|
+
} else {
|
|
51935
|
+
await client.createObjects({
|
|
51936
|
+
object: credential.object,
|
|
51937
|
+
values: [{ id: credential.credentialId, ...credentialValues }]
|
|
51938
|
+
});
|
|
51939
|
+
}
|
|
51940
|
+
const savedCredential = getHidObjectRows(
|
|
51941
|
+
await client.loadObjects({
|
|
51942
|
+
object: credential.object,
|
|
51943
|
+
where: { [credential.object]: { id: credential.credentialId } },
|
|
51944
|
+
limit: 1,
|
|
51945
|
+
offset: 0
|
|
51946
|
+
}),
|
|
51947
|
+
credential.object
|
|
51948
|
+
)[0];
|
|
51949
|
+
if (!savedCredential || String(savedCredential.user_id) !== String(context.hidUserId) || String(savedCredential.value) !== String(credential.value)) {
|
|
51950
|
+
throw new import_node_server_utils137.BadRequestError("The HID reader did not retain the generated QR code.");
|
|
51951
|
+
}
|
|
51952
|
+
for (const staleQr of existingQrs) {
|
|
51953
|
+
const staleId = Number(staleQr.id);
|
|
51954
|
+
if (Number.isSafeInteger(staleId) && staleId > 0 && staleId !== credential.credentialId) {
|
|
51955
|
+
await client.destroyObjects({
|
|
51956
|
+
object: credential.object,
|
|
51957
|
+
where: { [credential.object]: { id: staleId } }
|
|
51958
|
+
});
|
|
51959
|
+
}
|
|
51960
|
+
}
|
|
51961
|
+
const existingIdentity = context.profileIdentity;
|
|
51962
|
+
const existingMetadata = existingIdentity && isUnknownRecord2(existingIdentity.metadata) ? existingIdentity.metadata : {};
|
|
51963
|
+
const identityPayload = {
|
|
51964
|
+
hidUserId: String(context.hidUserId),
|
|
51965
|
+
registration: context.registration,
|
|
51966
|
+
user: userId,
|
|
51967
|
+
member: context.member._id,
|
|
51968
|
+
type: context.identityType,
|
|
51969
|
+
status: "active",
|
|
51970
|
+
metadata: {
|
|
51971
|
+
...existingMetadata,
|
|
51972
|
+
name: context.profileName,
|
|
51973
|
+
profileQr: true,
|
|
51974
|
+
profileQrValue: credential.value,
|
|
51975
|
+
profileQrCredentialId: credential.credentialId,
|
|
51976
|
+
profileQrIssuedAt: issuedAt.toISOString()
|
|
51977
|
+
}
|
|
51978
|
+
};
|
|
51979
|
+
if (existingIdentity?._id) {
|
|
51980
|
+
await repo.updateIdentity(existingIdentity._id, identityPayload);
|
|
51981
|
+
} else {
|
|
51982
|
+
await repo.addIdentity({
|
|
51983
|
+
...identityPayload,
|
|
51984
|
+
reader: readerId,
|
|
51985
|
+
site: reader.site
|
|
51986
|
+
});
|
|
51987
|
+
}
|
|
51988
|
+
await repo.updateById(readerId, { lastSeenAt: /* @__PURE__ */ new Date() });
|
|
51989
|
+
await repo.addEvent({
|
|
51990
|
+
reader: readerId,
|
|
51991
|
+
site: reader.site,
|
|
51992
|
+
type: "profile_qr_issued",
|
|
51993
|
+
payload: {
|
|
51994
|
+
user: userId,
|
|
51995
|
+
member: String(context.member._id),
|
|
51996
|
+
hidUserId: context.hidUserId,
|
|
51997
|
+
credentialId: credential.credentialId
|
|
51998
|
+
}
|
|
51999
|
+
});
|
|
52000
|
+
return getProfileQr(readerId, userId);
|
|
52001
|
+
} catch (error) {
|
|
52002
|
+
await repo.addEvent({
|
|
52003
|
+
reader: readerId,
|
|
52004
|
+
site: reader.site,
|
|
52005
|
+
type: "profile_qr_issue_failed",
|
|
52006
|
+
payload: { user: userId, message: getErrorMessage2(error) }
|
|
52007
|
+
});
|
|
52008
|
+
throw error;
|
|
52009
|
+
} finally {
|
|
52010
|
+
await client.logout();
|
|
52011
|
+
}
|
|
52012
|
+
}
|
|
52013
|
+
async function deleteProfileQr(readerId, userId) {
|
|
52014
|
+
const reader = await getActiveReader(readerId);
|
|
52015
|
+
const context = await getProfileEnrollmentContext(readerId, reader, userId);
|
|
52016
|
+
const client = new HidAmicoClient(reader);
|
|
52017
|
+
try {
|
|
52018
|
+
await client.login();
|
|
52019
|
+
await client.destroyObjects({
|
|
52020
|
+
object: "qrcodes",
|
|
52021
|
+
where: { qrcodes: { user_id: context.hidUserId } }
|
|
52022
|
+
});
|
|
52023
|
+
if (context.profileIdentity?._id) {
|
|
52024
|
+
const existingMetadata = isUnknownRecord2(context.profileIdentity.metadata) ? context.profileIdentity.metadata : {};
|
|
52025
|
+
const { profileQr, profileQrValue, profileQrCredentialId, profileQrIssuedAt, ...rest } = existingMetadata;
|
|
52026
|
+
await repo.updateIdentity(context.profileIdentity._id, { metadata: rest });
|
|
52027
|
+
}
|
|
52028
|
+
await repo.addEvent({
|
|
52029
|
+
reader: readerId,
|
|
52030
|
+
site: reader.site,
|
|
52031
|
+
type: "profile_qr_revoked",
|
|
52032
|
+
payload: { user: userId, hidUserId: context.hidUserId }
|
|
52033
|
+
});
|
|
52034
|
+
return getProfileQr(readerId, userId);
|
|
52035
|
+
} finally {
|
|
52036
|
+
await client.logout();
|
|
52037
|
+
}
|
|
52038
|
+
}
|
|
51824
52039
|
async function getProfileFacial(readerId, userId) {
|
|
51825
52040
|
const reader = await getActiveReader(readerId);
|
|
51826
52041
|
const context = await getProfileEnrollmentContext(readerId, reader, userId);
|
|
@@ -53366,6 +53581,9 @@ function useHidAmicoService() {
|
|
|
53366
53581
|
queueVisitorQrJob,
|
|
53367
53582
|
processVisitorQrGatewayJobs,
|
|
53368
53583
|
getProfileFacial,
|
|
53584
|
+
getProfileQr,
|
|
53585
|
+
issueProfileQr,
|
|
53586
|
+
deleteProfileQr,
|
|
53369
53587
|
setProfileFacial,
|
|
53370
53588
|
setVisitorImage,
|
|
53371
53589
|
deleteVisitorImage,
|
|
@@ -93472,6 +93690,45 @@ function useHidAmicoController() {
|
|
|
93472
93690
|
next(error);
|
|
93473
93691
|
}
|
|
93474
93692
|
}
|
|
93693
|
+
async function getProfileQr(req, res, next) {
|
|
93694
|
+
const { error, value } = schemaHidAmicoReaderIdParams.validate(req.params);
|
|
93695
|
+
if (error) {
|
|
93696
|
+
next(new import_node_server_utils301.BadRequestError(error.message));
|
|
93697
|
+
return;
|
|
93698
|
+
}
|
|
93699
|
+
try {
|
|
93700
|
+
await authorizeReader(req, value.readerId);
|
|
93701
|
+
res.json({ data: await service.getProfileQr(value.readerId, getAuthenticatedUserId(req)) });
|
|
93702
|
+
} catch (error2) {
|
|
93703
|
+
next(error2);
|
|
93704
|
+
}
|
|
93705
|
+
}
|
|
93706
|
+
async function issueProfileQr(req, res, next) {
|
|
93707
|
+
const { error, value } = schemaHidAmicoReaderIdParams.validate(req.params);
|
|
93708
|
+
if (error) {
|
|
93709
|
+
next(new import_node_server_utils301.BadRequestError(error.message));
|
|
93710
|
+
return;
|
|
93711
|
+
}
|
|
93712
|
+
try {
|
|
93713
|
+
await authorizeReader(req, value.readerId);
|
|
93714
|
+
res.json({ data: await service.issueProfileQr(value.readerId, getAuthenticatedUserId(req)) });
|
|
93715
|
+
} catch (error2) {
|
|
93716
|
+
next(error2);
|
|
93717
|
+
}
|
|
93718
|
+
}
|
|
93719
|
+
async function deleteProfileQr(req, res, next) {
|
|
93720
|
+
const { error, value } = schemaHidAmicoReaderIdParams.validate(req.params);
|
|
93721
|
+
if (error) {
|
|
93722
|
+
next(new import_node_server_utils301.BadRequestError(error.message));
|
|
93723
|
+
return;
|
|
93724
|
+
}
|
|
93725
|
+
try {
|
|
93726
|
+
await authorizeReader(req, value.readerId);
|
|
93727
|
+
res.json({ data: await service.deleteProfileQr(value.readerId, getAuthenticatedUserId(req)) });
|
|
93728
|
+
} catch (error2) {
|
|
93729
|
+
next(error2);
|
|
93730
|
+
}
|
|
93731
|
+
}
|
|
93475
93732
|
async function setProfileFacial(req, res, next) {
|
|
93476
93733
|
const params = schemaHidAmicoReaderIdParams.validate(req.params);
|
|
93477
93734
|
const query2 = schemaHidAmicoUserImageUploadQuery.validate(req.query);
|
|
@@ -93698,6 +93955,9 @@ function useHidAmicoController() {
|
|
|
93698
93955
|
revokeVisitorQr,
|
|
93699
93956
|
getProfileFacial,
|
|
93700
93957
|
setProfileFacial,
|
|
93958
|
+
getProfileQr,
|
|
93959
|
+
issueProfileQr,
|
|
93960
|
+
deleteProfileQr,
|
|
93701
93961
|
setVisitorImage,
|
|
93702
93962
|
deleteVisitorImage,
|
|
93703
93963
|
getIntercomStatus,
|