@riligar/auth-react 1.14.0 → 1.16.0
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.esm.js +441 -73
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +442 -70
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -235,7 +235,7 @@ const verifyMagicLink = async token => {
|
|
|
235
235
|
/*--- Password Reset -----------------------------*/
|
|
236
236
|
const forgotPassword = async (email, redirectTo) => {
|
|
237
237
|
const redirect = redirectTo || (typeof window !== 'undefined' ? `${window.location.origin}/auth/reset-password` : '/auth/reset-password');
|
|
238
|
-
return await api('/auth/
|
|
238
|
+
return await api('/auth/request-password-reset', {
|
|
239
239
|
method: 'POST',
|
|
240
240
|
body: JSON.stringify({
|
|
241
241
|
email,
|
|
@@ -273,6 +273,22 @@ const resendVerification = async (email, callbackURL) => {
|
|
|
273
273
|
const getSession = async () => {
|
|
274
274
|
return await api('/auth/session');
|
|
275
275
|
};
|
|
276
|
+
const listSessions = async () => {
|
|
277
|
+
return await api('/auth/list-sessions');
|
|
278
|
+
};
|
|
279
|
+
const revokeSession = async id => {
|
|
280
|
+
return await api('/auth/revoke-session-by-id', {
|
|
281
|
+
method: 'POST',
|
|
282
|
+
body: JSON.stringify({
|
|
283
|
+
id
|
|
284
|
+
})
|
|
285
|
+
});
|
|
286
|
+
};
|
|
287
|
+
const revokeOtherSessions = async () => {
|
|
288
|
+
return await api('/auth/revoke-other-sessions', {
|
|
289
|
+
method: 'POST'
|
|
290
|
+
});
|
|
291
|
+
};
|
|
276
292
|
|
|
277
293
|
/*--- Application Info ----------------------------*/
|
|
278
294
|
const getApplicationInfo = async () => {
|
|
@@ -324,6 +340,9 @@ const useAuthStore = zustand.create((set, get) => ({
|
|
|
324
340
|
user: null,
|
|
325
341
|
loading: true,
|
|
326
342
|
error: null,
|
|
343
|
+
// Session management
|
|
344
|
+
sessions: [],
|
|
345
|
+
currentSession: null,
|
|
327
346
|
// Loading states granulares
|
|
328
347
|
loadingStates: {
|
|
329
348
|
signIn: false,
|
|
@@ -336,7 +355,9 @@ const useAuthStore = zustand.create((set, get) => ({
|
|
|
336
355
|
resendVerification: false,
|
|
337
356
|
updateProfile: false,
|
|
338
357
|
changePassword: false,
|
|
339
|
-
changeEmail: false
|
|
358
|
+
changeEmail: false,
|
|
359
|
+
listSessions: false,
|
|
360
|
+
revokeSession: false
|
|
340
361
|
},
|
|
341
362
|
// Application info (logo, nome, etc)
|
|
342
363
|
applicationInfo: null,
|
|
@@ -379,9 +400,14 @@ const useAuthStore = zustand.create((set, get) => ({
|
|
|
379
400
|
// Precisamos buscar a sessão no servidor.
|
|
380
401
|
if (!user) {
|
|
381
402
|
try {
|
|
382
|
-
const
|
|
383
|
-
if (
|
|
384
|
-
user =
|
|
403
|
+
const sessionData = await getSession();
|
|
404
|
+
if (sessionData?.user) {
|
|
405
|
+
user = sessionData.user;
|
|
406
|
+
}
|
|
407
|
+
if (sessionData?.session) {
|
|
408
|
+
set({
|
|
409
|
+
currentSession: sessionData.session
|
|
410
|
+
});
|
|
385
411
|
}
|
|
386
412
|
} catch (sessionError) {
|
|
387
413
|
console.warn('[AuthStore] Failed to fetch session:', sessionError);
|
|
@@ -454,8 +480,13 @@ const useAuthStore = zustand.create((set, get) => ({
|
|
|
454
480
|
|
|
455
481
|
// Se não encontrou no token (sessão opaca), busca do servidor
|
|
456
482
|
if (!user) {
|
|
457
|
-
const
|
|
458
|
-
if (
|
|
483
|
+
const sessionData = await getSession();
|
|
484
|
+
if (sessionData?.user) user = sessionData.user;
|
|
485
|
+
if (sessionData?.session) {
|
|
486
|
+
set({
|
|
487
|
+
currentSession: sessionData.session
|
|
488
|
+
});
|
|
489
|
+
}
|
|
459
490
|
}
|
|
460
491
|
set({
|
|
461
492
|
user,
|
|
@@ -637,11 +668,86 @@ const useAuthStore = zustand.create((set, get) => ({
|
|
|
637
668
|
/* Session */
|
|
638
669
|
getSession: async () => {
|
|
639
670
|
try {
|
|
640
|
-
|
|
671
|
+
const sessionData = await getSession();
|
|
672
|
+
// Store current session for comparison (includes id)
|
|
673
|
+
if (sessionData?.session) {
|
|
674
|
+
set({
|
|
675
|
+
currentSession: sessionData.session
|
|
676
|
+
});
|
|
677
|
+
}
|
|
678
|
+
return sessionData;
|
|
679
|
+
} catch (err) {
|
|
680
|
+
set({
|
|
681
|
+
error: err
|
|
682
|
+
});
|
|
683
|
+
throw err;
|
|
684
|
+
}
|
|
685
|
+
},
|
|
686
|
+
listSessions: async () => {
|
|
687
|
+
const {
|
|
688
|
+
setLoading
|
|
689
|
+
} = get();
|
|
690
|
+
setLoading('listSessions', true);
|
|
691
|
+
set({
|
|
692
|
+
error: null
|
|
693
|
+
});
|
|
694
|
+
try {
|
|
695
|
+
const result = await listSessions();
|
|
696
|
+
set({
|
|
697
|
+
sessions: result || []
|
|
698
|
+
});
|
|
699
|
+
setLoading('listSessions', false);
|
|
700
|
+
return result;
|
|
701
|
+
} catch (err) {
|
|
702
|
+
set({
|
|
703
|
+
error: err,
|
|
704
|
+
sessions: []
|
|
705
|
+
});
|
|
706
|
+
setLoading('listSessions', false);
|
|
707
|
+
throw err;
|
|
708
|
+
}
|
|
709
|
+
},
|
|
710
|
+
revokeSession: async token => {
|
|
711
|
+
const {
|
|
712
|
+
setLoading,
|
|
713
|
+
listSessions
|
|
714
|
+
} = get();
|
|
715
|
+
setLoading('revokeSession', true);
|
|
716
|
+
set({
|
|
717
|
+
error: null
|
|
718
|
+
});
|
|
719
|
+
try {
|
|
720
|
+
await revokeSession(token);
|
|
721
|
+
// Refresh sessions list after revocation
|
|
722
|
+
await listSessions();
|
|
723
|
+
setLoading('revokeSession', false);
|
|
724
|
+
} catch (err) {
|
|
725
|
+
set({
|
|
726
|
+
error: err
|
|
727
|
+
});
|
|
728
|
+
setLoading('revokeSession', false);
|
|
729
|
+
throw err;
|
|
730
|
+
}
|
|
731
|
+
},
|
|
732
|
+
revokeOtherSessions: async () => {
|
|
733
|
+
const {
|
|
734
|
+
setLoading,
|
|
735
|
+
listSessions
|
|
736
|
+
} = get();
|
|
737
|
+
setLoading('revokeSession', true);
|
|
738
|
+
set({
|
|
739
|
+
error: null
|
|
740
|
+
});
|
|
741
|
+
try {
|
|
742
|
+
await revokeOtherSessions();
|
|
743
|
+
// Refresh sessions list after revocation
|
|
744
|
+
await listSessions();
|
|
745
|
+
setLoading('revokeSession', false);
|
|
641
746
|
} catch (err) {
|
|
642
747
|
set({
|
|
643
748
|
error: err
|
|
644
749
|
});
|
|
750
|
+
setLoading('revokeSession', false);
|
|
645
751
|
throw err;
|
|
646
752
|
}
|
|
647
753
|
},
|
|
@@ -821,8 +927,24 @@ function AuthProvider({
|
|
|
821
927
|
checkTokenValidity();
|
|
822
928
|
}
|
|
823
929
|
};
|
|
930
|
+
|
|
931
|
+
// Escuta evento de sessão revogada (quando o usuário revoga sua própria sessão)
|
|
932
|
+
const handleSessionRevoked = () => {
|
|
933
|
+
// Limpa o usuário do store
|
|
934
|
+
useAuthStore.setState({
|
|
935
|
+
user: null,
|
|
936
|
+
currentSession: null,
|
|
937
|
+
sessions: []
|
|
938
|
+
});
|
|
939
|
+
// Dispara evento de logout para sincronizar entre abas
|
|
940
|
+
localStorage.setItem('auth:logout', Date.now());
|
|
941
|
+
};
|
|
824
942
|
window.addEventListener('storage', handleStorageChange);
|
|
825
|
-
|
|
943
|
+
window.addEventListener('auth:session-revoked', handleSessionRevoked);
|
|
944
|
+
return () => {
|
|
945
|
+
window.removeEventListener('storage', handleStorageChange);
|
|
946
|
+
window.removeEventListener('auth:session-revoked', handleSessionRevoked);
|
|
947
|
+
};
|
|
826
948
|
}, [checkTokenValidity]);
|
|
827
949
|
|
|
828
950
|
// Verifica validade do token periodicamente
|
|
@@ -910,6 +1032,19 @@ const useUser = () => useAuthStore(shallow.useShallow(s => ({
|
|
|
910
1032
|
// Alias deprecado para backwards compatibility
|
|
911
1033
|
const useProfile = useUser;
|
|
912
1034
|
|
|
1035
|
+
// Sessions Management Hook
|
|
1036
|
+
const useSessions = () => useAuthStore(shallow.useShallow(s => ({
|
|
1037
|
+
currentSession: s.currentSession,
|
|
1038
|
+
sessions: s.sessions,
|
|
1039
|
+
getSession: s.getSession,
|
|
1040
|
+
listSessions: s.listSessions,
|
|
1041
|
+
revokeSession: s.revokeSession,
|
|
1042
|
+
revokeOtherSessions: s.revokeOtherSessions,
|
|
1043
|
+
loadingListSessions: s.loadingStates.listSessions,
|
|
1044
|
+
loadingRevokeSession: s.loadingStates.revokeSession,
|
|
1045
|
+
error: s.error
|
|
1046
|
+
})));
|
|
1047
|
+
|
|
913
1048
|
// Application Logo Hook
|
|
914
1049
|
const useApplicationLogo = () => {
|
|
915
1050
|
const applicationInfo = useAuthStore(s => s.applicationInfo);
|
|
@@ -1939,6 +2074,7 @@ function UserProfile({
|
|
|
1939
2074
|
showName = true,
|
|
1940
2075
|
showEmail = true,
|
|
1941
2076
|
showPassword = true,
|
|
2077
|
+
showSessions = true,
|
|
1942
2078
|
// Customização
|
|
1943
2079
|
labels = {},
|
|
1944
2080
|
title = 'Account',
|
|
@@ -1966,6 +2102,111 @@ function UserProfile({
|
|
|
1966
2102
|
loadingChangeEmail
|
|
1967
2103
|
} = useProfile();
|
|
1968
2104
|
|
|
2105
|
+
// Hook para sessions
|
|
2106
|
+
const {
|
|
2107
|
+
currentSession,
|
|
2108
|
+
sessions,
|
|
2109
|
+
listSessions,
|
|
2110
|
+
getSession,
|
|
2111
|
+
revokeSession,
|
|
2112
|
+
revokeOtherSessions,
|
|
2113
|
+
loadingListSessions,
|
|
2114
|
+
loadingRevokeSession
|
|
2115
|
+
} = useSessions();
|
|
2116
|
+
|
|
2117
|
+
// Load sessions when opened (modal) or component mounts (card), and when sessions section is opened
|
|
2118
|
+
react.useEffect(() => {
|
|
2119
|
+
if (showSessions && (variant === 'card' || opened)) {
|
|
2120
|
+
// Fetch current session first to get the session ID, then list all sessions
|
|
2121
|
+
getSession().catch(err => console.warn('Failed to get current session:', err));
|
|
2122
|
+
listSessions().catch(err => console.warn('Failed to load sessions:', err));
|
|
2123
|
+
}
|
|
2124
|
+
}, [opened, showSessions, variant]);
|
|
2125
|
+
|
|
2126
|
+
// Helper to parse user agent string
|
|
2127
|
+
const parseUserAgent = ua => {
|
|
2128
|
+
if (!ua) return {
|
|
2129
|
+
browser: 'Unknown Browser',
|
|
2130
|
+
os: 'Unknown OS'
|
|
2131
|
+
};
|
|
2132
|
+
let browser = 'Unknown Browser';
|
|
2133
|
+
let os = 'Unknown OS';
|
|
2134
|
+
|
|
2135
|
+
// Detect browser
|
|
2136
|
+
if (ua.includes('Chrome') && !ua.includes('Edg')) browser = 'Chrome';else if (ua.includes('Firefox')) browser = 'Firefox';else if (ua.includes('Safari') && !ua.includes('Chrome')) browser = 'Safari';else if (ua.includes('Edg')) browser = 'Edge';else if (ua.includes('Opera') || ua.includes('OPR')) browser = 'Opera';
|
|
2137
|
+
|
|
2138
|
+
// Detect OS
|
|
2139
|
+
if (ua.includes('Windows')) os = 'Windows';else if (ua.includes('Mac OS')) os = 'macOS';else if (ua.includes('Linux')) os = 'Linux';else if (ua.includes('Android')) os = 'Android';else if (ua.includes('iPhone') || ua.includes('iPad')) os = 'iOS';
|
|
2140
|
+
return {
|
|
2141
|
+
browser,
|
|
2142
|
+
os
|
|
2143
|
+
};
|
|
2144
|
+
};
|
|
2145
|
+
|
|
2146
|
+
// Session handlers
|
|
2147
|
+
const handleRevokeSession = async sessionId => {
|
|
2148
|
+
// Check if revoking current session
|
|
2149
|
+
const isCurrentSession = sessionId === currentSession?.id || sessions.length === 1;
|
|
2150
|
+
|
|
2151
|
+
// If revoking current session, handle logout immediately after revocation
|
|
2152
|
+
if (isCurrentSession) {
|
|
2153
|
+
try {
|
|
2154
|
+
await revokeSession(sessionId);
|
|
2155
|
+
} catch (error) {
|
|
2156
|
+
// Even if it fails, we're revoking our own session, so just logout
|
|
2157
|
+
// The server already revoked our session
|
|
2158
|
+
}
|
|
2159
|
+
// Clear auth state and redirect
|
|
2160
|
+
localStorage.removeItem('auth:token');
|
|
2161
|
+
window.dispatchEvent(new CustomEvent('auth:session-revoked'));
|
|
2162
|
+
if (variant === 'modal') onClose?.();
|
|
2163
|
+
return;
|
|
2164
|
+
}
|
|
2165
|
+
|
|
2166
|
+
// Revoking another session
|
|
2167
|
+
try {
|
|
2168
|
+
await revokeSession(sessionId);
|
|
2169
|
+
notifications.notifications.show({
|
|
2170
|
+
title: labels.successTitle || 'Sucesso',
|
|
2171
|
+
message: labels.sessionRevoked || 'Sessão encerrada com sucesso',
|
|
2172
|
+
color: 'green'
|
|
2173
|
+
});
|
|
2174
|
+
} catch (error) {
|
|
2175
|
+
// Check for 401 error (our SDK uses error.res, axios uses error.response)
|
|
2176
|
+
const status = error.res?.status || error.response?.status;
|
|
2177
|
+
if (status === 401) {
|
|
2178
|
+
// This means our session was revoked, not the target one - do logout
|
|
2179
|
+
localStorage.removeItem('auth:token');
|
|
2180
|
+
window.dispatchEvent(new CustomEvent('auth:session-revoked'));
|
|
2181
|
+
if (variant === 'modal') onClose?.();
|
|
2182
|
+
return;
|
|
2183
|
+
}
|
|
2184
|
+
notifications.notifications.show({
|
|
2185
|
+
title: labels.errorTitle || 'Erro',
|
|
2186
|
+
message: error.message || labels.sessionRevokeFailed || 'Falha ao encerrar sessão',
|
|
2187
|
+
color: 'red'
|
|
2188
|
+
});
|
|
2189
|
+
onError?.(error);
|
|
2190
|
+
}
|
|
2191
|
+
};
|
|
2192
|
+
const handleRevokeOtherSessions = async () => {
|
|
2193
|
+
try {
|
|
2194
|
+
await revokeOtherSessions();
|
|
2195
|
+
notifications.notifications.show({
|
|
2196
|
+
title: labels.successTitle || 'Sucesso',
|
|
2197
|
+
message: labels.otherSessionsRevoked || 'Todas as outras sessões foram encerradas',
|
|
2198
|
+
color: 'green'
|
|
2199
|
+
});
|
|
2200
|
+
} catch (error) {
|
|
2201
|
+
notifications.notifications.show({
|
|
2202
|
+
title: labels.errorTitle || 'Erro',
|
|
2203
|
+
message: error.message || labels.otherSessionsRevokeFailed || 'Falha ao encerrar sessões',
|
|
2204
|
+
color: 'red'
|
|
2205
|
+
});
|
|
2206
|
+
onError?.(error);
|
|
2207
|
+
}
|
|
2208
|
+
};
|
|
2209
|
+
|
|
1969
2210
|
// Password form
|
|
1970
2211
|
const passwordForm = form.useForm({
|
|
1971
2212
|
initialValues: {
|
|
@@ -2497,7 +2738,7 @@ function UserProfile({
|
|
|
2497
2738
|
})]
|
|
2498
2739
|
})]
|
|
2499
2740
|
})]
|
|
2500
|
-
}), showPassword && /*#__PURE__*/jsxRuntime.jsxs(core.Box, {
|
|
2741
|
+
}), (showPassword || showSessions) && /*#__PURE__*/jsxRuntime.jsxs(core.Box, {
|
|
2501
2742
|
mb: "md",
|
|
2502
2743
|
children: [/*#__PURE__*/jsxRuntime.jsx(SectionHeader, {
|
|
2503
2744
|
icon: iconsReact.IconShield,
|
|
@@ -2505,69 +2746,196 @@ function UserProfile({
|
|
|
2505
2746
|
description: labels.securityDescription || 'Protect your account'
|
|
2506
2747
|
}), /*#__PURE__*/jsxRuntime.jsxs(core.Stack, {
|
|
2507
2748
|
gap: "sm",
|
|
2508
|
-
children: [/*#__PURE__*/jsxRuntime.
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2749
|
+
children: [showPassword && /*#__PURE__*/jsxRuntime.jsxs(jsxRuntime.Fragment, {
|
|
2750
|
+
children: [/*#__PURE__*/jsxRuntime.jsx(SettingRow, {
|
|
2751
|
+
label: labels.password || 'Password',
|
|
2752
|
+
action: labels.change || 'Change',
|
|
2753
|
+
actionLabel: labels.changePassword || 'Change your password',
|
|
2754
|
+
onClick: () => handleToggleSection('password'),
|
|
2755
|
+
expanded: editingSection === 'password',
|
|
2756
|
+
children: /*#__PURE__*/jsxRuntime.jsxs(core.Group, {
|
|
2757
|
+
gap: "xs",
|
|
2758
|
+
children: [/*#__PURE__*/jsxRuntime.jsx(iconsReact.IconKey, {
|
|
2759
|
+
size: 16,
|
|
2760
|
+
color: "var(--mantine-color-dimmed)"
|
|
2761
|
+
}), /*#__PURE__*/jsxRuntime.jsx(core.Text, {
|
|
2762
|
+
size: "sm",
|
|
2763
|
+
c: "dimmed",
|
|
2764
|
+
children: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022"
|
|
2765
|
+
})]
|
|
2766
|
+
})
|
|
2767
|
+
}), /*#__PURE__*/jsxRuntime.jsx(core.Collapse, {
|
|
2768
|
+
in: editingSection === 'password',
|
|
2769
|
+
children: /*#__PURE__*/jsxRuntime.jsx(core.Card, {
|
|
2770
|
+
p: "md",
|
|
2771
|
+
withBorder: true,
|
|
2772
|
+
children: /*#__PURE__*/jsxRuntime.jsx("form", {
|
|
2773
|
+
onSubmit: passwordForm.onSubmit(handleChangePassword),
|
|
2774
|
+
children: /*#__PURE__*/jsxRuntime.jsxs(core.Stack, {
|
|
2775
|
+
gap: "sm",
|
|
2776
|
+
children: [/*#__PURE__*/jsxRuntime.jsx(core.PasswordInput, {
|
|
2777
|
+
label: labels.currentPassword || 'Senha Atual',
|
|
2778
|
+
placeholder: labels.currentPasswordPlaceholder || 'Digite sua senha atual',
|
|
2779
|
+
...passwordForm.getInputProps('currentPassword')
|
|
2780
|
+
}), /*#__PURE__*/jsxRuntime.jsx(core.PasswordInput, {
|
|
2781
|
+
label: labels.newPassword || 'Nova Senha',
|
|
2782
|
+
placeholder: labels.newPasswordPlaceholder || 'Digite a nova senha',
|
|
2783
|
+
description: labels.passwordDescription || 'Mínimo 8 caracteres',
|
|
2784
|
+
...passwordForm.getInputProps('newPassword')
|
|
2785
|
+
}), /*#__PURE__*/jsxRuntime.jsx(core.PasswordInput, {
|
|
2786
|
+
label: labels.confirmPassword || 'Confirmar Nova Senha',
|
|
2787
|
+
placeholder: labels.confirmPasswordPlaceholder || 'Confirme a nova senha',
|
|
2788
|
+
...passwordForm.getInputProps('confirmPassword')
|
|
2789
|
+
}), /*#__PURE__*/jsxRuntime.jsxs(core.Group, {
|
|
2790
|
+
justify: "flex-end",
|
|
2791
|
+
gap: "xs",
|
|
2792
|
+
children: [/*#__PURE__*/jsxRuntime.jsx(core.Button, {
|
|
2793
|
+
variant: "default",
|
|
2794
|
+
size: "xs",
|
|
2795
|
+
onClick: () => handleToggleSection('password'),
|
|
2796
|
+
leftSection: /*#__PURE__*/jsxRuntime.jsx(iconsReact.IconX, {
|
|
2797
|
+
size: 14
|
|
2798
|
+
}),
|
|
2799
|
+
children: labels.cancel || 'Cancelar'
|
|
2800
|
+
}), /*#__PURE__*/jsxRuntime.jsx(core.Button, {
|
|
2801
|
+
type: "submit",
|
|
2802
|
+
size: "xs",
|
|
2803
|
+
loading: loadingChangePassword,
|
|
2804
|
+
leftSection: /*#__PURE__*/jsxRuntime.jsx(iconsReact.IconCheck, {
|
|
2805
|
+
size: 14
|
|
2806
|
+
}),
|
|
2807
|
+
children: labels.save || 'Salvar'
|
|
2808
|
+
})]
|
|
2566
2809
|
})]
|
|
2567
|
-
})
|
|
2810
|
+
})
|
|
2568
2811
|
})
|
|
2569
2812
|
})
|
|
2570
|
-
})
|
|
2813
|
+
})]
|
|
2814
|
+
}), showSessions && /*#__PURE__*/jsxRuntime.jsxs(jsxRuntime.Fragment, {
|
|
2815
|
+
children: [/*#__PURE__*/jsxRuntime.jsx(SettingRow, {
|
|
2816
|
+
label: labels.sessions || 'Sessions',
|
|
2817
|
+
action: editingSection === 'sessions' ? labels.close || 'Close' : labels.manage || 'Manage',
|
|
2818
|
+
actionLabel: labels.manageSessions || 'Manage your active sessions',
|
|
2819
|
+
onClick: () => handleToggleSection('sessions'),
|
|
2820
|
+
expanded: editingSection === 'sessions',
|
|
2821
|
+
children: /*#__PURE__*/jsxRuntime.jsxs(core.Group, {
|
|
2822
|
+
gap: "xs",
|
|
2823
|
+
children: [/*#__PURE__*/jsxRuntime.jsx(iconsReact.IconDevices, {
|
|
2824
|
+
size: 16,
|
|
2825
|
+
color: "var(--mantine-color-dimmed)"
|
|
2826
|
+
}), /*#__PURE__*/jsxRuntime.jsx(core.Text, {
|
|
2827
|
+
size: "sm",
|
|
2828
|
+
c: "dimmed",
|
|
2829
|
+
children: sessions.length > 0 ? `${sessions.length} active session${sessions.length > 1 ? 's' : ''}` : loadingListSessions ? 'Loading...' : 'No sessions'
|
|
2830
|
+
})]
|
|
2831
|
+
})
|
|
2832
|
+
}), /*#__PURE__*/jsxRuntime.jsx(core.Collapse, {
|
|
2833
|
+
in: editingSection === 'sessions',
|
|
2834
|
+
children: /*#__PURE__*/jsxRuntime.jsx(core.Card, {
|
|
2835
|
+
p: "md",
|
|
2836
|
+
withBorder: true,
|
|
2837
|
+
children: /*#__PURE__*/jsxRuntime.jsx(core.Stack, {
|
|
2838
|
+
gap: "md",
|
|
2839
|
+
children: loadingListSessions ? /*#__PURE__*/jsxRuntime.jsx(core.Text, {
|
|
2840
|
+
size: "sm",
|
|
2841
|
+
c: "dimmed",
|
|
2842
|
+
ta: "center",
|
|
2843
|
+
children: labels.loadingSessions || 'Carregando sessões...'
|
|
2844
|
+
}) : sessions.length === 0 ? /*#__PURE__*/jsxRuntime.jsx(core.Text, {
|
|
2845
|
+
size: "sm",
|
|
2846
|
+
c: "dimmed",
|
|
2847
|
+
ta: "center",
|
|
2848
|
+
children: labels.noSessionsFound || 'Nenhuma sessão encontrada'
|
|
2849
|
+
}) : /*#__PURE__*/jsxRuntime.jsxs(jsxRuntime.Fragment, {
|
|
2850
|
+
children: [sessions.map(sessionItem => {
|
|
2851
|
+
const isCurrentSession = sessionItem.id === currentSession?.id;
|
|
2852
|
+
const deviceInfo = parseUserAgent(sessionItem.userAgent);
|
|
2853
|
+
const createdDate = new Date(sessionItem.createdAt);
|
|
2854
|
+
return /*#__PURE__*/jsxRuntime.jsx(core.Card, {
|
|
2855
|
+
p: "sm",
|
|
2856
|
+
style: {
|
|
2857
|
+
borderColor: isCurrentSession ? 'var(--mantine-color-blue-5)' : undefined,
|
|
2858
|
+
backgroundColor: isCurrentSession ? 'var(--mantine-color-blue-light)' : undefined
|
|
2859
|
+
},
|
|
2860
|
+
children: /*#__PURE__*/jsxRuntime.jsxs(core.Group, {
|
|
2861
|
+
justify: "space-between",
|
|
2862
|
+
wrap: "nowrap",
|
|
2863
|
+
children: [/*#__PURE__*/jsxRuntime.jsxs(core.Group, {
|
|
2864
|
+
gap: "sm",
|
|
2865
|
+
wrap: "nowrap",
|
|
2866
|
+
style: {
|
|
2867
|
+
flex: 1
|
|
2868
|
+
},
|
|
2869
|
+
children: [/*#__PURE__*/jsxRuntime.jsx(core.ThemeIcon, {
|
|
2870
|
+
size: 36,
|
|
2871
|
+
variant: "light",
|
|
2872
|
+
color: isCurrentSession ? 'blue' : 'gray',
|
|
2873
|
+
children: /*#__PURE__*/jsxRuntime.jsx(iconsReact.IconDeviceMobile, {
|
|
2874
|
+
size: 18
|
|
2875
|
+
})
|
|
2876
|
+
}), /*#__PURE__*/jsxRuntime.jsxs(core.Box, {
|
|
2877
|
+
style: {
|
|
2878
|
+
flex: 1
|
|
2879
|
+
},
|
|
2880
|
+
children: [/*#__PURE__*/jsxRuntime.jsxs(core.Group, {
|
|
2881
|
+
gap: "xs",
|
|
2882
|
+
children: [/*#__PURE__*/jsxRuntime.jsx(core.Text, {
|
|
2883
|
+
size: "sm",
|
|
2884
|
+
fw: 500,
|
|
2885
|
+
children: deviceInfo.browser
|
|
2886
|
+
}), isCurrentSession && /*#__PURE__*/jsxRuntime.jsx(core.Badge, {
|
|
2887
|
+
size: "xs",
|
|
2888
|
+
variant: "filled",
|
|
2889
|
+
color: "blue",
|
|
2890
|
+
children: labels.thisDevice || 'Este dispositivo'
|
|
2891
|
+
})]
|
|
2892
|
+
}), /*#__PURE__*/jsxRuntime.jsxs(core.Text, {
|
|
2893
|
+
size: "xs",
|
|
2894
|
+
c: "dimmed",
|
|
2895
|
+
children: [deviceInfo.os, " \u2022 ", sessionItem.ipAddress || labels.unknownIP || 'IP desconhecido']
|
|
2896
|
+
}), /*#__PURE__*/jsxRuntime.jsxs(core.Text, {
|
|
2897
|
+
size: "xs",
|
|
2898
|
+
c: "dimmed",
|
|
2899
|
+
children: [labels.createdAt || 'Criada em', " ", createdDate.toLocaleDateString('pt-BR'), " ", labels.at || 'às', ' ', createdDate.toLocaleTimeString('pt-BR', {
|
|
2900
|
+
hour: '2-digit',
|
|
2901
|
+
minute: '2-digit'
|
|
2902
|
+
})]
|
|
2903
|
+
})]
|
|
2904
|
+
})]
|
|
2905
|
+
}), /*#__PURE__*/jsxRuntime.jsx(core.Tooltip, {
|
|
2906
|
+
label: isCurrentSession ? labels.signOutAndEnd || 'Encerrar e sair' : labels.endSession || 'Encerrar sessão',
|
|
2907
|
+
children: /*#__PURE__*/jsxRuntime.jsx(core.Button, {
|
|
2908
|
+
variant: "light",
|
|
2909
|
+
color: "red",
|
|
2910
|
+
size: "xs",
|
|
2911
|
+
onClick: () => handleRevokeSession(sessionItem.id),
|
|
2912
|
+
loading: loadingRevokeSession,
|
|
2913
|
+
leftSection: /*#__PURE__*/jsxRuntime.jsx(iconsReact.IconLogout, {
|
|
2914
|
+
size: 14
|
|
2915
|
+
}),
|
|
2916
|
+
children: labels.end || 'Encerrar'
|
|
2917
|
+
})
|
|
2918
|
+
})]
|
|
2919
|
+
})
|
|
2920
|
+
}, sessionItem.id);
|
|
2921
|
+
}), sessions.length > 1 && /*#__PURE__*/jsxRuntime.jsx(core.Group, {
|
|
2922
|
+
justify: "flex-end",
|
|
2923
|
+
children: /*#__PURE__*/jsxRuntime.jsx(core.Button, {
|
|
2924
|
+
variant: "light",
|
|
2925
|
+
color: "red",
|
|
2926
|
+
size: "xs",
|
|
2927
|
+
onClick: handleRevokeOtherSessions,
|
|
2928
|
+
loading: loadingRevokeSession,
|
|
2929
|
+
leftSection: /*#__PURE__*/jsxRuntime.jsx(iconsReact.IconLogout, {
|
|
2930
|
+
size: 14
|
|
2931
|
+
}),
|
|
2932
|
+
children: labels.endOtherSessions || 'Encerrar todas as outras sessões'
|
|
2933
|
+
})
|
|
2934
|
+
})]
|
|
2935
|
+
})
|
|
2936
|
+
})
|
|
2937
|
+
})
|
|
2938
|
+
})]
|
|
2571
2939
|
})]
|
|
2572
2940
|
})]
|
|
2573
2941
|
})]
|
|
@@ -2787,9 +3155,12 @@ exports.getApplicationInfo = getApplicationInfo;
|
|
|
2787
3155
|
exports.getCurrentUser = getCurrentUser;
|
|
2788
3156
|
exports.getSession = getSession;
|
|
2789
3157
|
exports.isAuthenticated = isAuthenticated;
|
|
3158
|
+
exports.listSessions = listSessions;
|
|
2790
3159
|
exports.refreshToken = refreshToken;
|
|
2791
3160
|
exports.resendVerification = resendVerification;
|
|
2792
3161
|
exports.resetPassword = resetPassword;
|
|
3162
|
+
exports.revokeOtherSessions = revokeOtherSessions;
|
|
3163
|
+
exports.revokeSession = revokeSession;
|
|
2793
3164
|
exports.sendMagicLink = sendMagicLink;
|
|
2794
3165
|
exports.signIn = signIn;
|
|
2795
3166
|
exports.signOut = signOut;
|
|
@@ -2806,6 +3177,7 @@ exports.useMagicLink = useMagicLink;
|
|
|
2806
3177
|
exports.usePasswordReset = usePasswordReset;
|
|
2807
3178
|
exports.useProfile = useUser;
|
|
2808
3179
|
exports.useSession = useSession;
|
|
3180
|
+
exports.useSessions = useSessions;
|
|
2809
3181
|
exports.useSignIn = useSignIn;
|
|
2810
3182
|
exports.useSignOut = useSignOut;
|
|
2811
3183
|
exports.useSignUp = useSignUp;
|