Skip to content

Commit ca0de4f

Browse files
committed
fix: recovery issue with facesearch:
1 parent 985d4c8 commit ca0de4f

9 files changed

Lines changed: 712 additions & 345 deletions

File tree

.env.example

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,11 @@ PUBLIC_APP_STORE_EID_WALLET=""
7272
PUBLIC_PLAY_STORE_EID_WALLET=""
7373
NOTIFICATION_SHARED_SECRET=your-notification-secret-key
7474

75+
# Shared secret between eid-wallet frontend and evault-core provisioner
76+
# Backend reads PROVISIONER_SHARED_SECRET, wallet reads PUBLIC_PROVISIONER_SHARED_SECRET
77+
PROVISIONER_SHARED_SECRET="your-provisioner-shared-secret"
78+
PUBLIC_PROVISIONER_SHARED_SECRET="your-provisioner-shared-secret"
79+
7580
PUBLIC_ESIGNER_BASE_URL="http://localhost:3004"
7681
PUBLIC_FILE_MANAGER_BASE_URL="http://localhost:3005"
7782

infrastructure/eid-wallet/src/env.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,5 @@ declare module "$env/static/public" {
66
export const PUBLIC_REGISTRY_URL: string;
77
export const PUBLIC_PROVISIONER_URL: string;
88
export const PUBLIC_EID_WALLET_TOKEN: string;
9+
export const PUBLIC_PROVISIONER_SHARED_SECRET: string;
910
}

infrastructure/eid-wallet/src/lib/global/controllers/evault.ts

Lines changed: 175 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -11,26 +11,92 @@ import NotificationService from "../../services/NotificationService";
1111
import type { KeyService } from "./key";
1212
import type { UserController } from "./user";
1313

14-
const STORE_META_ENVELOPE = `
15-
mutation StoreMetaEnvelope($input: MetaEnvelopeInput!) {
16-
storeMetaEnvelope(input: $input) {
14+
const USER_PROFILE_ONTOLOGY = "550e8400-e29b-41d4-a716-446655440000";
15+
16+
const FIND_USER_PROFILE = `
17+
query FindUserProfile($ontologyId: String!) {
18+
metaEnvelopes(filter: { ontologyId: $ontologyId }, first: 1) {
19+
edges {
20+
node {
21+
id
22+
ontology
23+
parsed
24+
}
25+
}
26+
}
27+
}
28+
`;
29+
30+
const CREATE_META_ENVELOPE = `
31+
mutation CreateMetaEnvelope($input: MetaEnvelopeInput!) {
32+
createMetaEnvelope(input: $input) {
33+
metaEnvelope {
34+
id
35+
ontology
36+
parsed
37+
}
38+
errors {
39+
message
40+
code
41+
}
42+
}
43+
}
44+
`;
45+
46+
const UPDATE_META_ENVELOPE = `
47+
mutation UpdateMetaEnvelope($id: ID!, $input: MetaEnvelopeInput!) {
48+
updateMetaEnvelope(id: $id, input: $input) {
1749
metaEnvelope {
1850
id
1951
ontology
2052
parsed
2153
}
54+
errors {
55+
message
56+
code
57+
}
2258
}
2359
}
2460
`;
2561

26-
interface MetaEnvelopeResponse {
27-
storeMetaEnvelope: {
62+
interface GraphQLErrorItem {
63+
message: string;
64+
code?: string;
65+
}
66+
67+
interface ExistingUserProfileEnvelope {
68+
id: string;
69+
ontology: string;
70+
parsed?: Partial<UserProfile>;
71+
}
72+
73+
interface FindUserProfileResponse {
74+
metaEnvelopes: {
75+
edges: Array<{
76+
node: ExistingUserProfileEnvelope;
77+
}>;
78+
};
79+
}
80+
81+
interface CreateMetaEnvelopeResponse {
82+
createMetaEnvelope: {
2883
metaEnvelope: {
2984
id: string;
3085
ontology: string;
31-
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
32-
parsed: any;
33-
};
86+
parsed: UserProfile;
87+
} | null;
88+
errors: GraphQLErrorItem[] | null;
89+
};
90+
}
91+
92+
interface UpdateMetaEnvelopeResponse {
93+
updateMetaEnvelope: {
94+
metaEnvelope: {
95+
id: string;
96+
ontology: string;
97+
parsed: UserProfile;
98+
} | null;
99+
errors: GraphQLErrorItem[] | null;
34100
};
35101
}
36102

@@ -158,7 +224,7 @@ export class VaultController {
158224
const userData = await this.#userController.user;
159225
const displayName = userData?.name || vault.ename;
160226

161-
await this.createUserProfileInEVault(
227+
await this.upsertUserProfileInEVault(
162228
vault.ename,
163229
displayName,
164230
vault.ename,
@@ -167,7 +233,7 @@ export class VaultController {
167233
this.profileCreationStatus = "success";
168234
} catch (error) {
169235
console.error(
170-
"Failed to create UserProfile in eVault (retry):",
236+
"Failed to upsert UserProfile in eVault (retry):",
171237
error,
172238
);
173239
this.profileCreationStatus = "failed";
@@ -278,71 +344,133 @@ export class VaultController {
278344
return this.#client;
279345
}
280346

281-
/**
282-
* Create UserProfile in eVault with retry mechanism
283-
*/
284-
private async createUserProfileInEVault(
347+
private buildUserProfilePayload(
285348
ename: string,
286349
displayName: string,
287-
w3id: string,
288-
maxRetries = 10,
289-
): Promise<void> {
290-
console.log("attempting");
350+
now: string,
351+
existingProfile?: Partial<UserProfile>,
352+
): UserProfile {
291353
const username = ename.replace("@", "");
292-
const now = new Date().toISOString();
293-
294-
const userProfile: UserProfile = {
295-
username,
354+
return {
355+
username: existingProfile?.username ?? username,
296356
displayName,
297-
bio: null,
298-
avatarUrl: null,
299-
bannerUrl: null,
300-
ename,
301-
isVerified: false,
302-
isPrivate: false,
303-
createdAt: now,
357+
bio: existingProfile?.bio ?? null,
358+
avatarUrl: existingProfile?.avatarUrl ?? null,
359+
bannerUrl: existingProfile?.bannerUrl ?? null,
360+
ename: existingProfile?.ename ?? ename,
361+
isVerified: existingProfile?.isVerified ?? false,
362+
isPrivate: existingProfile?.isPrivate ?? false,
363+
createdAt: existingProfile?.createdAt ?? now,
304364
updatedAt: now,
305-
isArchived: false,
365+
isArchived: existingProfile?.isArchived ?? false,
306366
};
367+
}
368+
369+
private async findExistingUserProfile(
370+
client: GraphQLClient,
371+
): Promise<ExistingUserProfileEnvelope | undefined> {
372+
const response = await client.request<FindUserProfileResponse>(
373+
FIND_USER_PROFILE,
374+
{
375+
ontologyId: USER_PROFILE_ONTOLOGY,
376+
},
377+
);
378+
return response.metaEnvelopes.edges[0]?.node;
379+
}
380+
381+
private throwIfGraphQLErrors(
382+
errors: GraphQLErrorItem[] | null | undefined,
383+
operation: "create" | "update",
384+
) {
385+
if (!errors?.length) return;
386+
const message = errors[0]?.message ?? "Unknown GraphQL error";
387+
throw new Error(`[UserProfile ${operation}] ${message}`);
388+
}
307389

390+
/**
391+
* Upsert UserProfile in eVault with retry mechanism.
392+
* Lookup is performed on each attempt to avoid creating duplicates.
393+
*/
394+
private async upsertUserProfileInEVault(
395+
ename: string,
396+
displayName: string,
397+
w3id: string,
398+
maxRetries = 10,
399+
): Promise<void> {
308400
for (let attempt = 1; attempt <= maxRetries; attempt++) {
309401
try {
402+
const now = new Date().toISOString();
310403
const client = await this.ensureClient(w3id, ename);
404+
const existingProfile = await this.findExistingUserProfile(client);
405+
const payload = this.buildUserProfilePayload(
406+
ename,
407+
displayName,
408+
now,
409+
existingProfile?.parsed,
410+
);
411+
412+
if (existingProfile?.id) {
413+
console.log(
414+
`Attempting to update existing UserProfile in eVault (attempt ${attempt}/${maxRetries})`,
415+
);
416+
const response = await client.request<UpdateMetaEnvelopeResponse>(
417+
UPDATE_META_ENVELOPE,
418+
{
419+
id: existingProfile.id,
420+
input: {
421+
ontology: USER_PROFILE_ONTOLOGY,
422+
payload,
423+
acl: ["*"],
424+
},
425+
},
426+
);
427+
this.throwIfGraphQLErrors(
428+
response.updateMetaEnvelope.errors,
429+
"update",
430+
);
431+
console.log(
432+
"UserProfile updated successfully in eVault:",
433+
response.updateMetaEnvelope.metaEnvelope?.id ??
434+
existingProfile.id,
435+
);
436+
return;
437+
}
311438

312439
console.log(
313440
`Attempting to create UserProfile in eVault (attempt ${attempt}/${maxRetries})`,
314441
);
315-
316-
const response = await client.request<MetaEnvelopeResponse>(
317-
STORE_META_ENVELOPE,
442+
const response = await client.request<CreateMetaEnvelopeResponse>(
443+
CREATE_META_ENVELOPE,
318444
{
319445
input: {
320-
ontology: "550e8400-e29b-41d4-a716-446655440000",
321-
payload: userProfile,
446+
ontology: USER_PROFILE_ONTOLOGY,
447+
payload,
322448
acl: ["*"],
323449
},
324450
},
325451
);
326-
452+
this.throwIfGraphQLErrors(
453+
response.createMetaEnvelope.errors,
454+
"create",
455+
);
327456
console.log(
328457
"UserProfile created successfully in eVault:",
329-
response.storeMetaEnvelope.metaEnvelope.id,
458+
response.createMetaEnvelope.metaEnvelope?.id ?? "unknown-id",
330459
);
331460
return;
332461
} catch (error) {
333462
console.error(
334-
`Failed to create UserProfile in eVault (attempt ${attempt}/${maxRetries}):`,
463+
`Failed to upsert UserProfile in eVault (attempt ${attempt}/${maxRetries}):`,
335464
error,
336465
);
337466

338467
if (attempt === maxRetries) {
339468
console.error(
340-
"Max retries reached, giving up on UserProfile creation",
469+
"Max retries reached, giving up on UserProfile upsert",
341470
);
342471
throw error;
343472
}
344473

345-
// Wait before retrying (exponential backoff)
346474
const delay = Math.min(1000 * 2 ** (attempt - 1), 10000);
347475
console.log(`Waiting ${delay}ms before retry...`);
348476
await new Promise((resolve) => setTimeout(resolve, delay));
@@ -375,15 +503,15 @@ export class VaultController {
375503
if (this.profileCreationStatus === "success")
376504
return;
377505
this.profileCreationStatus = "loading";
378-
await this.createUserProfileInEVault(
506+
await this.upsertUserProfileInEVault(
379507
resolvedUser?.ename as string,
380508
displayName as string,
381509
resolvedUser?.ename as string,
382510
);
383511
this.profileCreationStatus = "success";
384512
} catch (error) {
385513
console.error(
386-
"Failed to create UserProfile in eVault:",
514+
"Failed to upsert UserProfile in eVault:",
387515
error,
388516
);
389517
this.profileCreationStatus = "failed";
@@ -407,34 +535,34 @@ export class VaultController {
407535
// Set loading status
408536
this.profileCreationStatus = "loading";
409537

410-
// Get user data for display name and create UserProfile
538+
// Get user data for display name and upsert UserProfile
411539
(async () => {
412540
try {
413541
const userData = await this.#userController.user;
414542
const displayName = userData?.name || vault.ename;
415543

416-
await this.createUserProfileInEVault(
544+
await this.upsertUserProfileInEVault(
417545
vault.ename,
418546
displayName,
419547
vault.ename,
420548
);
421549
this.profileCreationStatus = "success";
422550
} catch (error) {
423551
console.error(
424-
"Failed to get user data or create UserProfile:",
552+
"Failed to get user data or upsert UserProfile:",
425553
error,
426554
);
427555
// Fallback to using ename as display name
428556
try {
429-
await this.createUserProfileInEVault(
557+
await this.upsertUserProfileInEVault(
430558
vault.ename,
431559
vault.ename,
432560
vault.ename,
433561
);
434562
this.profileCreationStatus = "success";
435563
} catch (fallbackError) {
436564
console.error(
437-
"Failed to create UserProfile in eVault (fallback):",
565+
"Failed to upsert UserProfile in eVault (fallback):",
438566
fallbackError,
439567
);
440568
this.profileCreationStatus = "failed";

0 commit comments

Comments
 (0)