@strands.gg/accui 1.9.0 → 1.10.1
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/accui.css +1 -5802
- package/dist/nuxt/module.cjs.js +1 -109
- package/dist/nuxt/module.cjs.js.map +1 -1
- package/dist/nuxt/module.es.js +9 -1
- package/dist/nuxt/module.es.js.map +1 -1
- package/dist/nuxt/runtime/composables/useStrandsAuth.cjs.js +1 -57
- package/dist/nuxt/runtime/composables/useStrandsAuth.cjs.js.map +1 -1
- package/dist/nuxt/runtime/composables/useStrandsAuth.es.js +1 -2
- package/dist/nuxt/runtime/middleware/auth.global.cjs.js +1 -42
- package/dist/nuxt/runtime/middleware/auth.global.cjs.js.map +1 -1
- package/dist/nuxt/runtime/middleware/auth.global.es.js +0 -1
- package/dist/nuxt/runtime/plugin.client.cjs.js +1 -26
- package/dist/nuxt/runtime/plugin.client.cjs.js.map +1 -1
- package/dist/nuxt/runtime/plugin.client.es.js +2 -1
- package/dist/nuxt/runtime/plugin.client.es.js.map +1 -1
- package/dist/nuxt/runtime/plugin.server.cjs.js +1 -17
- package/dist/nuxt/runtime/plugin.server.cjs.js.map +1 -1
- package/dist/nuxt/runtime/plugin.server.es.js +0 -1
- package/dist/nuxt-v4/module.cjs.js +1 -119
- package/dist/nuxt-v4/module.cjs.js.map +1 -1
- package/dist/nuxt-v4/module.es.js +9 -1
- package/dist/nuxt-v4/module.es.js.map +1 -1
- package/dist/nuxt-v4/runtime/composables/useStrandsAuth.cjs.js +1 -70
- package/dist/nuxt-v4/runtime/composables/useStrandsAuth.cjs.js.map +1 -1
- package/dist/nuxt-v4/runtime/composables/useStrandsAuth.es.js +1 -2
- package/dist/nuxt-v4/runtime/middleware/auth.global.cjs.js +1 -42
- package/dist/nuxt-v4/runtime/middleware/auth.global.cjs.js.map +1 -1
- package/dist/nuxt-v4/runtime/middleware/auth.global.es.js +0 -1
- package/dist/nuxt-v4/runtime/plugin.client.cjs.js +1 -26
- package/dist/nuxt-v4/runtime/plugin.client.cjs.js.map +1 -1
- package/dist/nuxt-v4/runtime/plugin.client.es.js +2 -1
- package/dist/nuxt-v4/runtime/plugin.client.es.js.map +1 -1
- package/dist/nuxt-v4/runtime/plugin.server.cjs.js +1 -22
- package/dist/nuxt-v4/runtime/plugin.server.cjs.js.map +1 -1
- package/dist/nuxt-v4/runtime/plugin.server.es.js +0 -1
- package/dist/strands-auth-ui.cjs.js +1 -9840
- package/dist/strands-auth-ui.cjs.js.map +1 -1
- package/dist/strands-auth-ui.es.js +1398 -808
- package/dist/strands-auth-ui.es.js.map +1 -1
- package/dist/useStrandsAuth-CTlaiFqK.cjs +1 -0
- package/dist/useStrandsAuth-CTlaiFqK.cjs.map +1 -0
- package/dist/{useStrandsAuth-BA8qEUcp.js → useStrandsAuth-Cev-PTun.js} +289 -127
- package/dist/useStrandsAuth-Cev-PTun.js.map +1 -0
- package/dist/useStrandsConfig-Cxb360Os.js +0 -1
- package/dist/useStrandsConfig-Z9_36OcV.cjs +1 -0
- package/dist/{useStrandsConfig-Dms13Zd0.cjs.map → useStrandsConfig-Z9_36OcV.cjs.map} +1 -1
- package/package.json +1 -1
- package/dist/useStrandsAuth-BA8qEUcp.js.map +0 -1
- package/dist/useStrandsAuth-Co2lLH4X.cjs +0 -736
- package/dist/useStrandsAuth-Co2lLH4X.cjs.map +0 -1
- package/dist/useStrandsConfig-Dms13Zd0.cjs +0 -179
|
@@ -1,5 +1,89 @@
|
|
|
1
|
-
import { ref, computed } from "vue";
|
|
1
|
+
import { ref, computed, onUnmounted } from "vue";
|
|
2
2
|
import { u as useStrandsConfig } from "./useStrandsConfig-Cxb360Os.js";
|
|
3
|
+
class RequestCache {
|
|
4
|
+
cache = /* @__PURE__ */ new Map();
|
|
5
|
+
DEFAULT_TTL = 5 * 60 * 1e3;
|
|
6
|
+
// 5 minutes
|
|
7
|
+
/**
|
|
8
|
+
* Memoized fetch - prevents duplicate requests and caches results
|
|
9
|
+
*/
|
|
10
|
+
async fetch(key, fetcher, ttl = this.DEFAULT_TTL) {
|
|
11
|
+
const now = Date.now();
|
|
12
|
+
const entry = this.cache.get(key);
|
|
13
|
+
if (entry && now - entry.timestamp < entry.ttl) {
|
|
14
|
+
return entry.promise;
|
|
15
|
+
}
|
|
16
|
+
this.cleanExpired();
|
|
17
|
+
const promise = fetcher().finally(() => {
|
|
18
|
+
setTimeout(() => {
|
|
19
|
+
this.cache.delete(key);
|
|
20
|
+
}, ttl);
|
|
21
|
+
});
|
|
22
|
+
this.cache.set(key, {
|
|
23
|
+
promise,
|
|
24
|
+
timestamp: now,
|
|
25
|
+
ttl
|
|
26
|
+
});
|
|
27
|
+
return promise;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Clear all cached entries
|
|
31
|
+
*/
|
|
32
|
+
clear() {
|
|
33
|
+
this.cache.clear();
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Remove a specific cache entry
|
|
37
|
+
*/
|
|
38
|
+
invalidate(key) {
|
|
39
|
+
this.cache.delete(key);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Clean expired cache entries
|
|
43
|
+
*/
|
|
44
|
+
cleanExpired() {
|
|
45
|
+
const now = Date.now();
|
|
46
|
+
for (const [key, entry] of this.cache.entries()) {
|
|
47
|
+
if (now - entry.timestamp > entry.ttl) {
|
|
48
|
+
this.cache.delete(key);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Get cache statistics (for debugging)
|
|
54
|
+
*/
|
|
55
|
+
getStats() {
|
|
56
|
+
return {
|
|
57
|
+
size: this.cache.size,
|
|
58
|
+
entries: Array.from(this.cache.keys())
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
const requestCache = new RequestCache();
|
|
63
|
+
function useRequestCache() {
|
|
64
|
+
return {
|
|
65
|
+
fetch: requestCache.fetch.bind(requestCache),
|
|
66
|
+
clear: requestCache.clear.bind(requestCache),
|
|
67
|
+
invalidate: requestCache.invalidate.bind(requestCache),
|
|
68
|
+
getStats: requestCache.getStats.bind(requestCache)
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
function debounce(func, wait) {
|
|
72
|
+
let timeout = null;
|
|
73
|
+
return (...args) => {
|
|
74
|
+
if (timeout) {
|
|
75
|
+
clearTimeout(timeout);
|
|
76
|
+
}
|
|
77
|
+
timeout = setTimeout(() => {
|
|
78
|
+
func(...args);
|
|
79
|
+
}, wait);
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
const debouncedSetItem = debounce((key, value) => {
|
|
83
|
+
if (typeof window !== "undefined") {
|
|
84
|
+
localStorage.setItem(key, value);
|
|
85
|
+
}
|
|
86
|
+
}, 300);
|
|
3
87
|
const mapApiUserToFrontend = (apiUser) => {
|
|
4
88
|
return {
|
|
5
89
|
id: apiUser.id,
|
|
@@ -20,39 +104,59 @@ const mapApiUserToFrontend = (apiUser) => {
|
|
|
20
104
|
updatedAt: apiUser.updated_at || apiUser.updatedAt || (/* @__PURE__ */ new Date()).toISOString()
|
|
21
105
|
};
|
|
22
106
|
};
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
if (isRefreshingToken.value) return "Refreshing session...";
|
|
42
|
-
if (isSendingMfaEmail.value) return "Sending verification code...";
|
|
43
|
-
if (isVerifyingMfa.value) return "Verifying code...";
|
|
44
|
-
return "Loading...";
|
|
45
|
-
});
|
|
46
|
-
const isInitialized = ref(false);
|
|
47
|
-
const mfaRequired = ref(false);
|
|
48
|
-
const mfaSessionId = ref(null);
|
|
49
|
-
const availableMfaMethods = ref([]);
|
|
107
|
+
const globalState = {
|
|
108
|
+
currentUser: ref(null),
|
|
109
|
+
currentSession: ref(null),
|
|
110
|
+
loadingStates: ref({
|
|
111
|
+
initializing: true,
|
|
112
|
+
signingIn: false,
|
|
113
|
+
signingUp: false,
|
|
114
|
+
signingOut: false,
|
|
115
|
+
refreshingToken: false,
|
|
116
|
+
sendingMfaEmail: false,
|
|
117
|
+
verifyingMfa: false,
|
|
118
|
+
loadingProfile: false
|
|
119
|
+
}),
|
|
120
|
+
isInitialized: ref(false),
|
|
121
|
+
mfaRequired: ref(false),
|
|
122
|
+
mfaSessionId: ref(null),
|
|
123
|
+
availableMfaMethods: ref([])
|
|
124
|
+
};
|
|
50
125
|
let refreshTimer = null;
|
|
126
|
+
let refreshPromise = null;
|
|
51
127
|
function useStrandsAuth() {
|
|
128
|
+
const { getUrl } = useStrandsConfig();
|
|
129
|
+
const { fetch: cachedFetch, clear: clearCache, invalidate } = useRequestCache();
|
|
130
|
+
const { currentUser, currentSession, loadingStates, isInitialized, mfaRequired, mfaSessionId, availableMfaMethods } = globalState;
|
|
131
|
+
const isInitializing = computed(() => loadingStates.value.initializing);
|
|
132
|
+
const isSigningIn = computed(() => loadingStates.value.signingIn);
|
|
133
|
+
const isSigningUp = computed(() => loadingStates.value.signingUp);
|
|
134
|
+
const isSigningOut = computed(() => loadingStates.value.signingOut);
|
|
135
|
+
const isRefreshingToken = computed(() => loadingStates.value.refreshingToken);
|
|
136
|
+
const isSendingMfaEmail = computed(() => loadingStates.value.sendingMfaEmail);
|
|
137
|
+
const isVerifyingMfa = computed(() => loadingStates.value.verifyingMfa);
|
|
138
|
+
computed(() => loadingStates.value.loadingProfile);
|
|
139
|
+
const loading = computed(
|
|
140
|
+
() => loadingStates.value.signingIn || loadingStates.value.signingUp || loadingStates.value.signingOut || loadingStates.value.refreshingToken || loadingStates.value.sendingMfaEmail || loadingStates.value.verifyingMfa || loadingStates.value.loadingProfile
|
|
141
|
+
);
|
|
142
|
+
const isLoading = computed(() => loadingStates.value.initializing || loading.value);
|
|
143
|
+
const loadingMessage = computed(() => {
|
|
144
|
+
const states = loadingStates.value;
|
|
145
|
+
if (states.initializing) return "Checking authentication...";
|
|
146
|
+
if (states.signingIn) return "Signing you in...";
|
|
147
|
+
if (states.signingUp) return "Creating your account...";
|
|
148
|
+
if (states.signingOut) return "Signing you out...";
|
|
149
|
+
if (states.refreshingToken) return "Refreshing session...";
|
|
150
|
+
if (states.sendingMfaEmail) return "Sending verification code...";
|
|
151
|
+
if (states.verifyingMfa) return "Verifying code...";
|
|
152
|
+
if (states.loadingProfile) return "Loading profile...";
|
|
153
|
+
return "Loading...";
|
|
154
|
+
});
|
|
52
155
|
const getAuthHeaders = () => {
|
|
53
156
|
if (!currentSession.value?.accessToken) {
|
|
54
157
|
throw new Error("No access token available");
|
|
55
158
|
}
|
|
159
|
+
currentSession.value.expiresAt;
|
|
56
160
|
const headers = {
|
|
57
161
|
"Authorization": `Bearer ${currentSession.value.accessToken}`
|
|
58
162
|
};
|
|
@@ -119,16 +223,20 @@ function useStrandsAuth() {
|
|
|
119
223
|
};
|
|
120
224
|
const isAuthenticated = computed(() => currentUser.value !== null);
|
|
121
225
|
const signIn = async (credentials) => {
|
|
122
|
-
|
|
226
|
+
loadingStates.value.signingIn = true;
|
|
123
227
|
try {
|
|
124
228
|
mfaRequired.value = false;
|
|
125
229
|
mfaSessionId.value = null;
|
|
126
230
|
availableMfaMethods.value = [];
|
|
231
|
+
const headers = {
|
|
232
|
+
"Content-Type": "application/json"
|
|
233
|
+
};
|
|
234
|
+
if (typeof window !== "undefined" && window.location) {
|
|
235
|
+
headers["Origin"] = window.location.origin;
|
|
236
|
+
}
|
|
127
237
|
const response = await fetch(getUrl("signIn"), {
|
|
128
238
|
method: "POST",
|
|
129
|
-
headers
|
|
130
|
-
"Content-Type": "application/json"
|
|
131
|
-
},
|
|
239
|
+
headers,
|
|
132
240
|
body: JSON.stringify(credentials)
|
|
133
241
|
});
|
|
134
242
|
if (!response.ok) {
|
|
@@ -166,7 +274,7 @@ function useStrandsAuth() {
|
|
|
166
274
|
};
|
|
167
275
|
});
|
|
168
276
|
availableMfaMethods.value = methods;
|
|
169
|
-
|
|
277
|
+
loadingStates.value.signingIn = false;
|
|
170
278
|
return authData;
|
|
171
279
|
}
|
|
172
280
|
await setAuthData(authData);
|
|
@@ -174,21 +282,23 @@ function useStrandsAuth() {
|
|
|
174
282
|
} catch (error) {
|
|
175
283
|
throw error;
|
|
176
284
|
} finally {
|
|
177
|
-
|
|
285
|
+
loadingStates.value.signingIn = false;
|
|
178
286
|
}
|
|
179
287
|
};
|
|
180
288
|
const signUp = async (userData) => {
|
|
181
|
-
|
|
289
|
+
loadingStates.value.signingUp = true;
|
|
182
290
|
try {
|
|
183
291
|
throw new Error("Sign up not implemented - please integrate with auth SDK");
|
|
184
292
|
} finally {
|
|
185
|
-
|
|
293
|
+
loadingStates.value.signingUp = false;
|
|
186
294
|
}
|
|
187
295
|
};
|
|
188
296
|
const signOut = async () => {
|
|
189
|
-
|
|
297
|
+
loadingStates.value.signingOut = true;
|
|
190
298
|
try {
|
|
191
299
|
stopTokenRefreshTimer();
|
|
300
|
+
refreshPromise = null;
|
|
301
|
+
clearCache();
|
|
192
302
|
currentUser.value = null;
|
|
193
303
|
currentSession.value = null;
|
|
194
304
|
mfaRequired.value = false;
|
|
@@ -199,91 +309,106 @@ function useStrandsAuth() {
|
|
|
199
309
|
localStorage.removeItem("strands_auth_user");
|
|
200
310
|
}
|
|
201
311
|
} finally {
|
|
202
|
-
|
|
312
|
+
loadingStates.value.signingOut = false;
|
|
203
313
|
}
|
|
204
314
|
};
|
|
205
315
|
const refreshToken = async () => {
|
|
206
316
|
if (!currentSession.value?.refreshToken) {
|
|
207
317
|
return false;
|
|
208
318
|
}
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
319
|
+
if (refreshPromise) {
|
|
320
|
+
console.log("Token refresh already in progress, waiting for completion...");
|
|
321
|
+
return await refreshPromise;
|
|
322
|
+
}
|
|
323
|
+
refreshPromise = (async () => {
|
|
324
|
+
loadingStates.value.refreshingToken = true;
|
|
325
|
+
try {
|
|
326
|
+
const response = await fetch(getUrl("refresh"), {
|
|
327
|
+
method: "POST",
|
|
328
|
+
headers: {
|
|
329
|
+
"Content-Type": "application/json"
|
|
330
|
+
},
|
|
331
|
+
body: JSON.stringify({
|
|
332
|
+
refresh_token: currentSession.value.refreshToken
|
|
333
|
+
})
|
|
334
|
+
});
|
|
335
|
+
if (!response.ok) {
|
|
336
|
+
if (response.status === 401) {
|
|
337
|
+
await signOut();
|
|
338
|
+
return false;
|
|
339
|
+
}
|
|
340
|
+
throw new Error(`Token refresh failed: ${response.status} ${response.statusText}`);
|
|
224
341
|
}
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
342
|
+
const tokenData = await response.json();
|
|
343
|
+
if (tokenData.user) {
|
|
344
|
+
currentUser.value = mapApiUserToFrontend(tokenData.user);
|
|
345
|
+
if (currentUser.value && typeof window !== "undefined") {
|
|
346
|
+
localStorage.setItem("strands_auth_user", JSON.stringify(currentUser.value));
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
const newSession = {
|
|
350
|
+
accessToken: tokenData.access_token,
|
|
351
|
+
refreshToken: tokenData.refresh_token,
|
|
352
|
+
expiresAt: new Date(Date.now() + 5 * 60 * 1e3),
|
|
353
|
+
// 5 minutes from now
|
|
354
|
+
userId: tokenData.user?.id || currentUser.value?.id
|
|
355
|
+
};
|
|
356
|
+
currentSession.value = newSession;
|
|
230
357
|
if (typeof window !== "undefined") {
|
|
231
|
-
localStorage.setItem("
|
|
358
|
+
localStorage.setItem("strands_auth_session", JSON.stringify(newSession));
|
|
232
359
|
}
|
|
360
|
+
startTokenRefreshTimer();
|
|
361
|
+
invalidate(`sessions:${currentSession.value.accessToken.slice(0, 20)}`);
|
|
362
|
+
return true;
|
|
363
|
+
} catch (error) {
|
|
364
|
+
await signOut();
|
|
365
|
+
return false;
|
|
366
|
+
} finally {
|
|
367
|
+
loadingStates.value.refreshingToken = false;
|
|
233
368
|
}
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
// 5 minutes from now
|
|
239
|
-
userId: tokenData.user?.id || currentUser.value?.id
|
|
240
|
-
};
|
|
241
|
-
currentSession.value = newSession;
|
|
242
|
-
if (typeof window !== "undefined") {
|
|
243
|
-
localStorage.setItem("strands_auth_session", JSON.stringify(newSession));
|
|
244
|
-
}
|
|
245
|
-
startTokenRefreshTimer();
|
|
246
|
-
return true;
|
|
247
|
-
} catch (error) {
|
|
248
|
-
await signOut();
|
|
249
|
-
return false;
|
|
250
|
-
}
|
|
369
|
+
})();
|
|
370
|
+
const result = await refreshPromise;
|
|
371
|
+
refreshPromise = null;
|
|
372
|
+
return result;
|
|
251
373
|
};
|
|
252
374
|
const fetchProfile = async () => {
|
|
253
375
|
if (!currentSession.value?.accessToken) {
|
|
254
376
|
throw new Error("No access token available");
|
|
255
377
|
}
|
|
256
|
-
|
|
378
|
+
const cacheKey = `profile:${currentSession.value.accessToken.slice(0, 20)}`;
|
|
379
|
+
loadingStates.value.loadingProfile = true;
|
|
257
380
|
try {
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
381
|
+
return await cachedFetch(cacheKey, async () => {
|
|
382
|
+
const response = await fetch(getUrl("profile"), {
|
|
383
|
+
method: "GET",
|
|
384
|
+
headers: {
|
|
385
|
+
"Content-Type": "application/json",
|
|
386
|
+
"Authorization": `Bearer ${currentSession.value?.accessToken}`
|
|
387
|
+
}
|
|
388
|
+
});
|
|
389
|
+
if (!response.ok) {
|
|
390
|
+
if (response.status === 401) {
|
|
391
|
+
throw new Error("Authentication expired. Please sign in again.");
|
|
392
|
+
} else {
|
|
393
|
+
throw new Error(`Failed to fetch profile: ${response.status} ${response.statusText}`);
|
|
394
|
+
}
|
|
263
395
|
}
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
if (
|
|
267
|
-
|
|
268
|
-
} else {
|
|
269
|
-
throw new Error(`Failed to fetch profile: ${response.status} ${response.statusText}`);
|
|
396
|
+
const userData = await response.json();
|
|
397
|
+
currentUser.value = mapApiUserToFrontend(userData);
|
|
398
|
+
if (currentUser.value && typeof window !== "undefined") {
|
|
399
|
+
localStorage.setItem("strands_auth_user", JSON.stringify(currentUser.value));
|
|
270
400
|
}
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
currentUser.value = mapApiUserToFrontend(userData);
|
|
274
|
-
if (typeof window !== "undefined") {
|
|
275
|
-
localStorage.setItem("strands_auth_user", JSON.stringify(currentUser.value));
|
|
276
|
-
}
|
|
277
|
-
return currentUser.value;
|
|
401
|
+
return currentUser.value;
|
|
402
|
+
});
|
|
278
403
|
} finally {
|
|
279
|
-
|
|
404
|
+
loadingStates.value.loadingProfile = false;
|
|
280
405
|
}
|
|
281
406
|
};
|
|
282
407
|
const updateProfile = async (profileData) => {
|
|
283
408
|
if (!currentSession.value?.accessToken) {
|
|
284
409
|
throw new Error("No access token available");
|
|
285
410
|
}
|
|
286
|
-
|
|
411
|
+
loadingStates.value.loadingProfile = true;
|
|
287
412
|
try {
|
|
288
413
|
const response = await fetch(getUrl("profile"), {
|
|
289
414
|
method: "POST",
|
|
@@ -305,19 +430,19 @@ function useStrandsAuth() {
|
|
|
305
430
|
}
|
|
306
431
|
const updatedUserData = await response.json();
|
|
307
432
|
currentUser.value = mapApiUserToFrontend(updatedUserData);
|
|
308
|
-
if (
|
|
309
|
-
|
|
433
|
+
if (currentUser.value) {
|
|
434
|
+
debouncedSetItem("strands_auth_user", JSON.stringify(currentUser.value));
|
|
310
435
|
}
|
|
311
436
|
return currentUser.value;
|
|
312
437
|
} finally {
|
|
313
|
-
|
|
438
|
+
loadingStates.value.loadingProfile = false;
|
|
314
439
|
}
|
|
315
440
|
};
|
|
316
441
|
const updateUserSettings = async (settings) => {
|
|
317
442
|
if (!currentSession.value?.accessToken) {
|
|
318
443
|
throw new Error("No access token available");
|
|
319
444
|
}
|
|
320
|
-
|
|
445
|
+
loadingStates.value.loadingProfile = true;
|
|
321
446
|
try {
|
|
322
447
|
const response = await fetch(getUrl("settings"), {
|
|
323
448
|
method: "POST",
|
|
@@ -338,19 +463,19 @@ function useStrandsAuth() {
|
|
|
338
463
|
}
|
|
339
464
|
const updatedUserData = await response.json();
|
|
340
465
|
currentUser.value = mapApiUserToFrontend(updatedUserData);
|
|
341
|
-
if (
|
|
342
|
-
|
|
466
|
+
if (currentUser.value) {
|
|
467
|
+
debouncedSetItem("strands_auth_user", JSON.stringify(currentUser.value));
|
|
343
468
|
}
|
|
344
469
|
return currentUser.value;
|
|
345
470
|
} finally {
|
|
346
|
-
|
|
471
|
+
loadingStates.value.loadingProfile = false;
|
|
347
472
|
}
|
|
348
473
|
};
|
|
349
474
|
const changeEmail = async (newEmail, password) => {
|
|
350
475
|
if (!currentSession.value?.accessToken) {
|
|
351
476
|
throw new Error("No access token available");
|
|
352
477
|
}
|
|
353
|
-
|
|
478
|
+
loadingStates.value.loadingProfile = true;
|
|
354
479
|
try {
|
|
355
480
|
const response = await fetch(getUrl("changeEmail"), {
|
|
356
481
|
method: "POST",
|
|
@@ -386,14 +511,14 @@ function useStrandsAuth() {
|
|
|
386
511
|
}
|
|
387
512
|
return result;
|
|
388
513
|
} finally {
|
|
389
|
-
|
|
514
|
+
loadingStates.value.loadingProfile = false;
|
|
390
515
|
}
|
|
391
516
|
};
|
|
392
517
|
const verifyMfa = async (deviceId, code, isBackupCode = false) => {
|
|
393
518
|
if (!mfaSessionId.value) {
|
|
394
519
|
throw new Error("No MFA session available");
|
|
395
520
|
}
|
|
396
|
-
|
|
521
|
+
loadingStates.value.verifyingMfa = true;
|
|
397
522
|
try {
|
|
398
523
|
const endpoint = isBackupCode ? getUrl("mfaBackupCodeVerify") : getUrl("mfaSigninVerify");
|
|
399
524
|
const body = isBackupCode ? { mfa_session_id: mfaSessionId.value, backup_code: code } : { mfa_session_id: mfaSessionId.value, device_id: deviceId, code };
|
|
@@ -420,14 +545,14 @@ function useStrandsAuth() {
|
|
|
420
545
|
await setAuthData(authData);
|
|
421
546
|
return authData;
|
|
422
547
|
} finally {
|
|
423
|
-
|
|
548
|
+
loadingStates.value.verifyingMfa = false;
|
|
424
549
|
}
|
|
425
550
|
};
|
|
426
551
|
const sendMfaEmailCode = async (deviceId) => {
|
|
427
552
|
if (!mfaSessionId.value) {
|
|
428
553
|
throw new Error("No MFA session available");
|
|
429
554
|
}
|
|
430
|
-
|
|
555
|
+
loadingStates.value.sendingMfaEmail = true;
|
|
431
556
|
try {
|
|
432
557
|
const response = await fetch(getUrl("mfaSigninSendEmail"), {
|
|
433
558
|
method: "POST",
|
|
@@ -451,7 +576,7 @@ function useStrandsAuth() {
|
|
|
451
576
|
const result = await response.json();
|
|
452
577
|
return result;
|
|
453
578
|
} finally {
|
|
454
|
-
|
|
579
|
+
loadingStates.value.sendingMfaEmail = false;
|
|
455
580
|
}
|
|
456
581
|
};
|
|
457
582
|
const getMfaWebAuthnChallenge = async (deviceId) => {
|
|
@@ -500,7 +625,6 @@ function useStrandsAuth() {
|
|
|
500
625
|
}
|
|
501
626
|
startTokenRefreshTimer();
|
|
502
627
|
} catch (error) {
|
|
503
|
-
console.error("Error setting auth data:", error);
|
|
504
628
|
}
|
|
505
629
|
};
|
|
506
630
|
const startTokenRefreshTimer = () => {
|
|
@@ -508,6 +632,9 @@ function useStrandsAuth() {
|
|
|
508
632
|
clearTimeout(refreshTimer);
|
|
509
633
|
}
|
|
510
634
|
if (!currentSession.value) return;
|
|
635
|
+
if (typeof document !== "undefined" && document.visibilityState === "hidden") {
|
|
636
|
+
return;
|
|
637
|
+
}
|
|
511
638
|
const now = /* @__PURE__ */ new Date();
|
|
512
639
|
const expiresAt = currentSession.value.expiresAt;
|
|
513
640
|
const timeUntilRefresh = expiresAt.getTime() - now.getTime() - 1 * 60 * 1e3;
|
|
@@ -516,9 +643,11 @@ function useStrandsAuth() {
|
|
|
516
643
|
return;
|
|
517
644
|
}
|
|
518
645
|
refreshTimer = setTimeout(async () => {
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
646
|
+
if (typeof document === "undefined" || document.visibilityState === "visible") {
|
|
647
|
+
const success = await refreshToken();
|
|
648
|
+
if (success) {
|
|
649
|
+
startTokenRefreshTimer();
|
|
650
|
+
}
|
|
522
651
|
}
|
|
523
652
|
}, timeUntilRefresh);
|
|
524
653
|
};
|
|
@@ -530,7 +659,7 @@ function useStrandsAuth() {
|
|
|
530
659
|
};
|
|
531
660
|
const initialize = async () => {
|
|
532
661
|
if (isInitialized.value) return;
|
|
533
|
-
|
|
662
|
+
loadingStates.value.initializing = true;
|
|
534
663
|
try {
|
|
535
664
|
if (typeof window !== "undefined") {
|
|
536
665
|
const storedSession = localStorage.getItem("strands_auth_session");
|
|
@@ -558,14 +687,14 @@ function useStrandsAuth() {
|
|
|
558
687
|
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
559
688
|
} catch (error) {
|
|
560
689
|
} finally {
|
|
561
|
-
|
|
690
|
+
loadingStates.value.initializing = false;
|
|
562
691
|
}
|
|
563
692
|
};
|
|
564
693
|
const changeUsername = async (newUsername) => {
|
|
565
694
|
if (!currentSession.value?.accessToken) {
|
|
566
695
|
throw new Error("No access token available");
|
|
567
696
|
}
|
|
568
|
-
|
|
697
|
+
loadingStates.value.loadingProfile = true;
|
|
569
698
|
try {
|
|
570
699
|
const response = await fetch(getUrl("changeUsername"), {
|
|
571
700
|
method: "POST",
|
|
@@ -600,7 +729,7 @@ function useStrandsAuth() {
|
|
|
600
729
|
}
|
|
601
730
|
return result;
|
|
602
731
|
} finally {
|
|
603
|
-
|
|
732
|
+
loadingStates.value.loadingProfile = false;
|
|
604
733
|
}
|
|
605
734
|
};
|
|
606
735
|
const getUsernameCooldown = async () => {
|
|
@@ -632,14 +761,26 @@ function useStrandsAuth() {
|
|
|
632
761
|
return response.json();
|
|
633
762
|
};
|
|
634
763
|
const getUserSessions = async () => {
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
}
|
|
639
|
-
|
|
640
|
-
|
|
764
|
+
if (!currentSession.value?.accessToken) {
|
|
765
|
+
throw new Error("No access token available");
|
|
766
|
+
}
|
|
767
|
+
const cacheKey = `sessions:${currentSession.value.accessToken.slice(0, 20)}`;
|
|
768
|
+
try {
|
|
769
|
+
return await cachedFetch(cacheKey, async () => {
|
|
770
|
+
const headers = getAuthHeaders();
|
|
771
|
+
const response = await fetch(getUrl("sessions"), {
|
|
772
|
+
method: "GET",
|
|
773
|
+
headers
|
|
774
|
+
});
|
|
775
|
+
if (!response.ok) {
|
|
776
|
+
const errorText = await response.text();
|
|
777
|
+
throw new Error(`Failed to get user sessions: ${response.status} ${response.statusText}`);
|
|
778
|
+
}
|
|
779
|
+
return response.json();
|
|
780
|
+
}, 2 * 60 * 1e3);
|
|
781
|
+
} catch (error) {
|
|
782
|
+
throw error;
|
|
641
783
|
}
|
|
642
|
-
return response.json();
|
|
643
784
|
};
|
|
644
785
|
const getSessionStats = async () => {
|
|
645
786
|
const response = await fetch(getUrl("sessionsStats"), {
|
|
@@ -672,6 +813,27 @@ function useStrandsAuth() {
|
|
|
672
813
|
}
|
|
673
814
|
return response.status === 200;
|
|
674
815
|
};
|
|
816
|
+
if (typeof document !== "undefined") {
|
|
817
|
+
document.addEventListener("visibilitychange", () => {
|
|
818
|
+
if (document.visibilityState === "visible" && currentSession.value) {
|
|
819
|
+
startTokenRefreshTimer();
|
|
820
|
+
} else if (document.visibilityState === "hidden") {
|
|
821
|
+
stopTokenRefreshTimer();
|
|
822
|
+
}
|
|
823
|
+
});
|
|
824
|
+
}
|
|
825
|
+
const cleanup = () => {
|
|
826
|
+
stopTokenRefreshTimer();
|
|
827
|
+
clearCache();
|
|
828
|
+
if (typeof document !== "undefined") {
|
|
829
|
+
document.removeEventListener("visibilitychange", () => {
|
|
830
|
+
});
|
|
831
|
+
}
|
|
832
|
+
};
|
|
833
|
+
try {
|
|
834
|
+
onUnmounted(cleanup);
|
|
835
|
+
} catch (e) {
|
|
836
|
+
}
|
|
675
837
|
if (!isInitialized.value) {
|
|
676
838
|
initialize();
|
|
677
839
|
}
|
|
@@ -723,10 +885,11 @@ function useStrandsAuth() {
|
|
|
723
885
|
// Token management
|
|
724
886
|
startTokenRefreshTimer,
|
|
725
887
|
stopTokenRefreshTimer,
|
|
888
|
+
getAuthHeaders,
|
|
726
889
|
// Force re-initialization (useful for testing or navigation)
|
|
727
890
|
forceReInit: () => {
|
|
728
891
|
isInitialized.value = false;
|
|
729
|
-
|
|
892
|
+
loadingStates.value.initializing = true;
|
|
730
893
|
initialize();
|
|
731
894
|
}
|
|
732
895
|
};
|
|
@@ -734,4 +897,3 @@ function useStrandsAuth() {
|
|
|
734
897
|
export {
|
|
735
898
|
useStrandsAuth as u
|
|
736
899
|
};
|
|
737
|
-
//# sourceMappingURL=useStrandsAuth-BA8qEUcp.js.map
|