1
1
import type { Store } from "@tauri-apps/plugin-store" ;
2
+ import { GraphQLClient } from "graphql-request" ;
3
+ import axios from "axios" ;
4
+ import { PUBLIC_REGISTRY_URL } from "$env/static/public" ;
5
+ import { UserController } from "./user" ;
6
+
7
+ const STORE_META_ENVELOPE = `
8
+ mutation StoreMetaEnvelope($input: MetaEnvelopeInput!) {
9
+ storeMetaEnvelope(input: $input) {
10
+ metaEnvelope {
11
+ id
12
+ ontology
13
+ parsed
14
+ }
15
+ }
16
+ }
17
+ ` ;
18
+
19
+ interface MetaEnvelopeResponse {
20
+ storeMetaEnvelope : {
21
+ metaEnvelope : {
22
+ id : string ;
23
+ ontology : string ;
24
+ parsed : any ;
25
+ } ;
26
+ } ;
27
+ }
28
+
29
+ interface UserProfile {
30
+ username : string ;
31
+ displayName : string ;
32
+ bio : string | null ;
33
+ avatarUrl : string | null ;
34
+ bannerUrl : string | null ;
35
+ ename : string ;
36
+ isVerified : boolean ;
37
+ isPrivate : boolean ;
38
+ createdAt : string ;
39
+ updatedAt : string ;
40
+ isArchived : boolean ;
41
+ }
2
42
3
43
export class VaultController {
4
44
#store: Store ;
5
- constructor ( store : Store ) {
45
+ #client: GraphQLClient | null = null ;
46
+ #endpoint: string | null = null ;
47
+ #userController: UserController ;
48
+
49
+ constructor ( store : Store , userController : UserController ) {
6
50
this . #store = store ;
51
+ this . #userController = userController ;
52
+ }
53
+
54
+ /**
55
+ * Resolve eVault endpoint from registry
56
+ */
57
+ private async resolveEndpoint ( w3id : string ) : Promise < string > {
58
+ try {
59
+ const response = await axios . get (
60
+ new URL ( `resolve?w3id=${ w3id } ` , PUBLIC_REGISTRY_URL ) . toString ( )
61
+ ) ;
62
+ return new URL ( "/graphql" , response . data . uri ) . toString ( ) ;
63
+ } catch ( error ) {
64
+ console . error ( "Error resolving eVault endpoint:" , error ) ;
65
+ throw new Error ( "Failed to resolve eVault endpoint" ) ;
66
+ }
67
+ }
68
+
69
+ /**
70
+ * Ensure we have a valid GraphQL client
71
+ */
72
+ private async ensureClient ( w3id : string ) : Promise < GraphQLClient > {
73
+ if ( ! this . #endpoint || ! this . #client) {
74
+ this . #endpoint = await this . resolveEndpoint ( w3id ) ;
75
+ this . #client = new GraphQLClient ( this . #endpoint) ;
76
+ }
77
+ return this . #client;
78
+ }
79
+
80
+ /**
81
+ * Create UserProfile in eVault with retry mechanism
82
+ */
83
+ private async createUserProfileInEVault (
84
+ ename : string ,
85
+ displayName : string ,
86
+ w3id : string ,
87
+ maxRetries : number = 10
88
+ ) : Promise < void > {
89
+ console . log ( "attempting" )
90
+ const username = ename . replace ( '@' , '' ) ;
91
+ const now = new Date ( ) . toISOString ( ) ;
92
+
93
+ const userProfile : UserProfile = {
94
+ username,
95
+ displayName,
96
+ bio : null ,
97
+ avatarUrl : null ,
98
+ bannerUrl : null ,
99
+ ename,
100
+ isVerified : false ,
101
+ isPrivate : false ,
102
+ createdAt : now ,
103
+ updatedAt : now ,
104
+ isArchived : false
105
+ } ;
106
+
107
+ for ( let attempt = 1 ; attempt <= maxRetries ; attempt ++ ) {
108
+ try {
109
+ const client = await this . ensureClient ( w3id ) ;
110
+
111
+ console . log ( `Attempting to create UserProfile in eVault (attempt ${ attempt } /${ maxRetries } )` ) ;
112
+
113
+ const response = await client . request < MetaEnvelopeResponse > (
114
+ STORE_META_ENVELOPE ,
115
+ {
116
+ input : {
117
+ ontology : "550e8400-e29b-41d4-a716-446655440000" ,
118
+ payload : userProfile ,
119
+ acl : [ "*" ] ,
120
+ } ,
121
+ }
122
+ ) ;
123
+
124
+ console . log ( "UserProfile created successfully in eVault:" , response . storeMetaEnvelope . metaEnvelope . id ) ;
125
+ return ;
126
+ } catch ( error ) {
127
+ console . error ( `Failed to create UserProfile in eVault (attempt ${ attempt } /${ maxRetries } ):` , error ) ;
128
+
129
+ if ( attempt === maxRetries ) {
130
+ console . error ( "Max retries reached, giving up on UserProfile creation" ) ;
131
+ throw error ;
132
+ }
133
+
134
+ // Wait before retrying (exponential backoff)
135
+ const delay = Math . min ( 1000 * Math . pow ( 2 , attempt - 1 ) , 10000 ) ;
136
+ console . log ( `Waiting ${ delay } ms before retry...` ) ;
137
+ await new Promise ( resolve => setTimeout ( resolve , delay ) ) ;
138
+ }
139
+ }
7
140
}
8
141
9
142
set vault (
@@ -14,14 +147,59 @@ export class VaultController {
14
147
) {
15
148
if ( vault instanceof Promise ) {
16
149
vault
17
- . then ( ( resolvedUser ) => {
18
- this . #store. set ( "vault" , resolvedUser ) ;
150
+ . then ( async ( resolvedUser ) => {
151
+ if ( resolvedUser ?. ename ) {
152
+ this . #store. set ( "vault" , resolvedUser ) ;
153
+
154
+ // Get user data for display name
155
+ const userData = await this . #userController. user ;
156
+ const displayName = userData ?. name || resolvedUser . ename ;
157
+
158
+ try {
159
+ await this . createUserProfileInEVault (
160
+ resolvedUser . ename ,
161
+ displayName ,
162
+ resolvedUser . ename
163
+ ) ;
164
+ } catch ( error ) {
165
+ console . error ( "Failed to create UserProfile in eVault:" , error ) ;
166
+ // Don't throw here to avoid breaking the vault setting
167
+ }
168
+ }
19
169
} )
20
170
. catch ( ( error ) => {
21
171
console . error ( "Failed to set vault:" , error ) ;
22
172
} ) ;
23
173
} else {
24
- this . #store. set ( "vault" , vault ) ;
174
+ if ( vault ?. ename ) {
175
+ this . #store. set ( "vault" , vault ) ;
176
+
177
+ // Get user data for display name and create UserProfile
178
+ ( async ( ) => {
179
+ try {
180
+ const userData = await this . #userController. user ;
181
+ const displayName = userData ?. name || vault . ename ;
182
+
183
+ await this . createUserProfileInEVault (
184
+ vault . ename ,
185
+ displayName ,
186
+ vault . ename
187
+ ) ;
188
+ } catch ( error ) {
189
+ console . error ( "Failed to get user data or create UserProfile:" , error ) ;
190
+ // Fallback to using ename as display name
191
+ try {
192
+ await this . createUserProfileInEVault (
193
+ vault . ename ,
194
+ vault . ename ,
195
+ vault . ename
196
+ ) ;
197
+ } catch ( fallbackError ) {
198
+ console . error ( "Failed to create UserProfile in eVault (fallback):" , fallbackError ) ;
199
+ }
200
+ }
201
+ } ) ( ) ;
202
+ }
25
203
}
26
204
}
27
205
@@ -39,4 +217,13 @@ export class VaultController {
39
217
return undefined ;
40
218
} ) ;
41
219
}
220
+
221
+ // Getters for internal properties
222
+ get client ( ) {
223
+ return this . #client;
224
+ }
225
+
226
+ get endpoint ( ) {
227
+ return this . #endpoint;
228
+ }
42
229
}
0 commit comments