style: run prettier on entire codebase
This commit is contained in:
@@ -1,279 +1,397 @@
|
||||
import { createContext, useContext, useState, useEffect, useCallback, useMemo, useRef, type ReactNode } from 'react'
|
||||
import { setSessionExpired, setTokenGetter, setRefreshFn } from '../utils/api'
|
||||
import {
|
||||
createContext,
|
||||
useContext,
|
||||
useState,
|
||||
useEffect,
|
||||
useCallback,
|
||||
useMemo,
|
||||
useRef,
|
||||
type ReactNode,
|
||||
} from "react";
|
||||
import { setSessionExpired, setTokenGetter, setRefreshFn } from "../utils/api";
|
||||
|
||||
const API_BASE = '/api/admin'
|
||||
const API_BASE = "/api/admin";
|
||||
|
||||
interface User {
|
||||
id: number
|
||||
username: string
|
||||
email: string
|
||||
fullName: string
|
||||
roleDisplay: string
|
||||
isAdmin: boolean
|
||||
totpEnabled: boolean
|
||||
require2FA: boolean
|
||||
permissions: string[]
|
||||
[key: string]: unknown
|
||||
id: number;
|
||||
username: string;
|
||||
email: string;
|
||||
fullName: string;
|
||||
roleDisplay: string;
|
||||
isAdmin: boolean;
|
||||
totpEnabled: boolean;
|
||||
require2FA: boolean;
|
||||
permissions: string[];
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
interface AuthState {
|
||||
user: User | null
|
||||
loading: boolean
|
||||
error: string | null
|
||||
isAuthenticated: boolean
|
||||
isAdmin: boolean
|
||||
permissions: string[]
|
||||
hasPermission: (permission: string) => boolean
|
||||
user: User | null;
|
||||
loading: boolean;
|
||||
error: string | null;
|
||||
isAuthenticated: boolean;
|
||||
isAdmin: boolean;
|
||||
permissions: string[];
|
||||
hasPermission: (permission: string) => boolean;
|
||||
}
|
||||
|
||||
interface AuthActions {
|
||||
login: (username: string, password: string, remember?: boolean) => Promise<{ success: boolean; requires2FA?: boolean; loginToken?: string; error?: string; remember?: boolean }>
|
||||
verify2FA: (loginToken: string, code: string, remember?: boolean, isBackup?: boolean) => Promise<{ success: boolean; error?: string }>
|
||||
logout: () => Promise<void>
|
||||
checkSession: () => Promise<boolean>
|
||||
getAccessToken: () => string | null
|
||||
apiRequest: (endpoint: string, options?: RequestInit) => Promise<Response>
|
||||
silentRefresh: () => Promise<boolean>
|
||||
updateUser: (updates: Partial<User>) => void
|
||||
login: (
|
||||
username: string,
|
||||
password: string,
|
||||
remember?: boolean,
|
||||
) => Promise<{
|
||||
success: boolean;
|
||||
requires2FA?: boolean;
|
||||
loginToken?: string;
|
||||
error?: string;
|
||||
remember?: boolean;
|
||||
}>;
|
||||
verify2FA: (
|
||||
loginToken: string,
|
||||
code: string,
|
||||
remember?: boolean,
|
||||
isBackup?: boolean,
|
||||
) => Promise<{ success: boolean; error?: string }>;
|
||||
logout: () => Promise<void>;
|
||||
checkSession: () => Promise<boolean>;
|
||||
getAccessToken: () => string | null;
|
||||
apiRequest: (endpoint: string, options?: RequestInit) => Promise<Response>;
|
||||
silentRefresh: () => Promise<boolean>;
|
||||
updateUser: (updates: Partial<User>) => void;
|
||||
}
|
||||
|
||||
const AuthStateContext = createContext<AuthState | null>(null)
|
||||
const AuthActionsContext = createContext<AuthActions | null>(null)
|
||||
const AuthStateContext = createContext<AuthState | null>(null);
|
||||
const AuthActionsContext = createContext<AuthActions | null>(null);
|
||||
|
||||
function mapUser(u: Record<string, unknown> | null): User | null {
|
||||
if (!u) return null
|
||||
const id = (u.userId ?? u.id) as number
|
||||
const firstName = (u.firstName ?? u.first_name ?? '') as string
|
||||
const lastName = (u.lastName ?? u.last_name ?? '') as string
|
||||
const roleName = (u.roleName ?? u.role_name ?? '') as string
|
||||
if (!u) return null;
|
||||
const id = (u.userId ?? u.id) as number;
|
||||
const firstName = (u.firstName ?? u.first_name ?? "") as string;
|
||||
const lastName = (u.lastName ?? u.last_name ?? "") as string;
|
||||
const roleName = (u.roleName ?? u.role_name ?? "") as string;
|
||||
return {
|
||||
...u,
|
||||
id,
|
||||
fullName: (u.fullName ?? u.full_name ?? `${firstName} ${lastName}`.trim()) as string,
|
||||
fullName: (u.fullName ??
|
||||
u.full_name ??
|
||||
`${firstName} ${lastName}`.trim()) as string,
|
||||
roleDisplay: (u.roleDisplay ?? u.role_display ?? roleName) as string,
|
||||
isAdmin: (u.isAdmin ?? u.is_admin ?? roleName === 'admin') as boolean,
|
||||
isAdmin: (u.isAdmin ?? u.is_admin ?? roleName === "admin") as boolean,
|
||||
totpEnabled: (u.totpEnabled ?? u.totp_enabled ?? false) as boolean,
|
||||
require2FA: (u.require2FA ?? u.require_2fa ?? false) as boolean,
|
||||
permissions: (u.permissions ?? []) as string[],
|
||||
} as User
|
||||
} as User;
|
||||
}
|
||||
|
||||
let accessToken: string | null = null
|
||||
let tokenExpiresAt: number | null = null
|
||||
let cachedUser: User | null = null
|
||||
let sessionFetched = false
|
||||
let silentRefreshInFlight: Promise<boolean> | null = null
|
||||
let accessToken: string | null = null;
|
||||
let tokenExpiresAt: number | null = null;
|
||||
let cachedUser: User | null = null;
|
||||
let sessionFetched = false;
|
||||
let silentRefreshInFlight: Promise<boolean> | null = null;
|
||||
|
||||
export function AuthProvider({ children }: { children: ReactNode }) {
|
||||
const [user, setUser] = useState<User | null>(cachedUser)
|
||||
const [loading, setLoading] = useState(!sessionFetched)
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
const refreshTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null)
|
||||
const [user, setUser] = useState<User | null>(cachedUser);
|
||||
const [loading, setLoading] = useState(!sessionFetched);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const refreshTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||
|
||||
useEffect(() => { cachedUser = user }, [user])
|
||||
useEffect(() => {
|
||||
cachedUser = user;
|
||||
}, [user]);
|
||||
|
||||
const getAccessTokenFn = useCallback((): string | null => {
|
||||
if (!tokenExpiresAt || Date.now() > tokenExpiresAt - 30000) return null
|
||||
return accessToken
|
||||
}, [])
|
||||
if (!tokenExpiresAt || Date.now() > tokenExpiresAt - 30000) return null;
|
||||
return accessToken;
|
||||
}, []);
|
||||
|
||||
const setAccessTokenFn = useCallback((token: string | null, expiresIn?: number) => {
|
||||
const ttl = expiresIn ?? 900 // default 15 min matching backend config
|
||||
accessToken = token
|
||||
tokenExpiresAt = token ? Date.now() + ttl * 1000 : null
|
||||
if (refreshTimeoutRef.current) {
|
||||
clearTimeout(refreshTimeoutRef.current)
|
||||
refreshTimeoutRef.current = null
|
||||
}
|
||||
if (token && ttl > 60) {
|
||||
refreshTimeoutRef.current = setTimeout(() => silentRefresh(), (ttl - 60) * 1000)
|
||||
}
|
||||
}, []) // eslint-disable-line react-hooks/exhaustive-deps
|
||||
const setAccessTokenFn = useCallback(
|
||||
(token: string | null, expiresIn?: number) => {
|
||||
const ttl = expiresIn ?? 900; // default 15 min matching backend config
|
||||
accessToken = token;
|
||||
tokenExpiresAt = token ? Date.now() + ttl * 1000 : null;
|
||||
if (refreshTimeoutRef.current) {
|
||||
clearTimeout(refreshTimeoutRef.current);
|
||||
refreshTimeoutRef.current = null;
|
||||
}
|
||||
if (token && ttl > 60) {
|
||||
refreshTimeoutRef.current = setTimeout(
|
||||
() => silentRefresh(),
|
||||
(ttl - 60) * 1000,
|
||||
);
|
||||
}
|
||||
},
|
||||
[],
|
||||
); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
|
||||
const silentRefresh = useCallback(async (): Promise<boolean> => {
|
||||
// Deduplicate concurrent refresh calls — token rotation means only one call can succeed
|
||||
if (silentRefreshInFlight) return silentRefreshInFlight
|
||||
if (silentRefreshInFlight) return silentRefreshInFlight;
|
||||
|
||||
const promise = (async (): Promise<boolean> => {
|
||||
try {
|
||||
const response = await fetch(`${API_BASE}/refresh`, { method: 'POST', credentials: 'include' })
|
||||
const data = await response.json()
|
||||
const response = await fetch(`${API_BASE}/refresh`, {
|
||||
method: "POST",
|
||||
credentials: "include",
|
||||
});
|
||||
const data = await response.json();
|
||||
if (data.success && data.data?.access_token) {
|
||||
setAccessTokenFn(data.data.access_token, data.data.expires_in)
|
||||
setUser(mapUser(data.data.user))
|
||||
return true
|
||||
setAccessTokenFn(data.data.access_token, data.data.expires_in);
|
||||
setUser(mapUser(data.data.user));
|
||||
return true;
|
||||
}
|
||||
accessToken = null
|
||||
tokenExpiresAt = null
|
||||
setUser(null)
|
||||
cachedUser = null
|
||||
setSessionExpired()
|
||||
return false
|
||||
accessToken = null;
|
||||
tokenExpiresAt = null;
|
||||
setUser(null);
|
||||
cachedUser = null;
|
||||
setSessionExpired();
|
||||
return false;
|
||||
} catch {
|
||||
// Network error — don't kick the user out, just return false
|
||||
return false
|
||||
return false;
|
||||
} finally {
|
||||
silentRefreshInFlight = null
|
||||
silentRefreshInFlight = null;
|
||||
}
|
||||
})()
|
||||
})();
|
||||
|
||||
silentRefreshInFlight = promise
|
||||
return promise
|
||||
}, [setAccessTokenFn])
|
||||
silentRefreshInFlight = promise;
|
||||
return promise;
|
||||
}, [setAccessTokenFn]);
|
||||
|
||||
const checkSession = useCallback(async (): Promise<boolean> => {
|
||||
try {
|
||||
const token = getAccessTokenFn()
|
||||
const token = getAccessTokenFn();
|
||||
if (token) {
|
||||
const headers: Record<string, string> = { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` }
|
||||
const response = await fetch(`${API_BASE}/session`, { method: 'GET', credentials: 'include', headers })
|
||||
if (response.status === 429 || response.status >= 500) return !!cachedUser
|
||||
const data = await response.json()
|
||||
const headers: Record<string, string> = {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${token}`,
|
||||
};
|
||||
const response = await fetch(`${API_BASE}/session`, {
|
||||
method: "GET",
|
||||
credentials: "include",
|
||||
headers,
|
||||
});
|
||||
if (response.status === 429 || response.status >= 500)
|
||||
return !!cachedUser;
|
||||
const data = await response.json();
|
||||
if (data.success && data.data?.user) {
|
||||
if (data.data.access_token) setAccessTokenFn(data.data.access_token)
|
||||
setUser(mapUser(data.data.user))
|
||||
cachedUser = mapUser(data.data.user)
|
||||
return true
|
||||
if (data.data.access_token) setAccessTokenFn(data.data.access_token);
|
||||
setUser(mapUser(data.data.user));
|
||||
cachedUser = mapUser(data.data.user);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// No token or session invalid — try silent refresh via cookie
|
||||
const refreshed = await silentRefresh()
|
||||
if (refreshed) return true
|
||||
setUser(null)
|
||||
cachedUser = null
|
||||
accessToken = null
|
||||
tokenExpiresAt = null
|
||||
return false
|
||||
const refreshed = await silentRefresh();
|
||||
if (refreshed) return true;
|
||||
setUser(null);
|
||||
cachedUser = null;
|
||||
accessToken = null;
|
||||
tokenExpiresAt = null;
|
||||
return false;
|
||||
} catch {
|
||||
return !!cachedUser
|
||||
return !!cachedUser;
|
||||
} finally {
|
||||
setLoading(false)
|
||||
sessionFetched = true
|
||||
setLoading(false);
|
||||
sessionFetched = true;
|
||||
}
|
||||
}, [getAccessTokenFn, setAccessTokenFn, silentRefresh])
|
||||
}, [getAccessTokenFn, setAccessTokenFn, silentRefresh]);
|
||||
|
||||
useEffect(() => {
|
||||
setTokenGetter(getAccessTokenFn)
|
||||
setRefreshFn(silentRefresh)
|
||||
}, [getAccessTokenFn, silentRefresh])
|
||||
setTokenGetter(getAccessTokenFn);
|
||||
setRefreshFn(silentRefresh);
|
||||
}, [getAccessTokenFn, silentRefresh]);
|
||||
|
||||
useEffect(() => {
|
||||
checkSession()
|
||||
return () => { if (refreshTimeoutRef.current) clearTimeout(refreshTimeoutRef.current) }
|
||||
}, [checkSession])
|
||||
checkSession();
|
||||
return () => {
|
||||
if (refreshTimeoutRef.current) clearTimeout(refreshTimeoutRef.current);
|
||||
};
|
||||
}, [checkSession]);
|
||||
|
||||
const login = useCallback(async (username: string, password: string, remember = false) => {
|
||||
setError(null)
|
||||
try {
|
||||
const response = await fetch(`${API_BASE}/login`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
credentials: 'include',
|
||||
body: JSON.stringify({ username, password, remember_me: remember }),
|
||||
})
|
||||
const data = await response.json()
|
||||
if (data.success) {
|
||||
if (data.data?.totp_required) {
|
||||
return { success: false, requires2FA: true, loginToken: data.data.login_token, remember }
|
||||
const login = useCallback(
|
||||
async (username: string, password: string, remember = false) => {
|
||||
setError(null);
|
||||
try {
|
||||
const response = await fetch(`${API_BASE}/login`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
credentials: "include",
|
||||
body: JSON.stringify({ username, password, remember_me: remember }),
|
||||
});
|
||||
const data = await response.json();
|
||||
if (data.success) {
|
||||
if (data.data?.totp_required) {
|
||||
return {
|
||||
success: false,
|
||||
requires2FA: true,
|
||||
loginToken: data.data.login_token,
|
||||
remember,
|
||||
};
|
||||
}
|
||||
setAccessTokenFn(data.data.access_token, data.data.expires_in);
|
||||
setUser(mapUser(data.data.user));
|
||||
cachedUser = mapUser(data.data.user);
|
||||
sessionFetched = true;
|
||||
return { success: true };
|
||||
}
|
||||
setAccessTokenFn(data.data.access_token, data.data.expires_in)
|
||||
setUser(mapUser(data.data.user))
|
||||
cachedUser = mapUser(data.data.user)
|
||||
sessionFetched = true
|
||||
return { success: true }
|
||||
setError(data.error);
|
||||
return { success: false, error: data.error };
|
||||
} catch {
|
||||
const errorMsg =
|
||||
"Chyba pripojeni. Zkontrolujte prosim pripojeni k internetu a zkuste to znovu.";
|
||||
setError(errorMsg);
|
||||
return { success: false, error: errorMsg };
|
||||
}
|
||||
setError(data.error)
|
||||
return { success: false, error: data.error }
|
||||
} catch {
|
||||
const errorMsg = 'Chyba pripojeni. Zkontrolujte prosim pripojeni k internetu a zkuste to znovu.'
|
||||
setError(errorMsg)
|
||||
return { success: false, error: errorMsg }
|
||||
}
|
||||
}, [setAccessTokenFn])
|
||||
},
|
||||
[setAccessTokenFn],
|
||||
);
|
||||
|
||||
const verify2FA = useCallback(async (loginToken: string, code: string, remember = false, isBackup = false) => {
|
||||
setError(null)
|
||||
try {
|
||||
const response = await fetch(`${API_BASE}/login/totp`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
credentials: 'include',
|
||||
body: JSON.stringify({ login_token: loginToken, totp_code: code, remember_me: remember }),
|
||||
})
|
||||
const data = await response.json()
|
||||
if (data.success) {
|
||||
setAccessTokenFn(data.data.access_token, data.data.expires_in)
|
||||
setUser(mapUser(data.data.user))
|
||||
cachedUser = mapUser(data.data.user)
|
||||
sessionFetched = true
|
||||
return { success: true }
|
||||
const verify2FA = useCallback(
|
||||
async (
|
||||
loginToken: string,
|
||||
code: string,
|
||||
remember = false,
|
||||
isBackup = false,
|
||||
) => {
|
||||
setError(null);
|
||||
try {
|
||||
const response = await fetch(`${API_BASE}/login/totp`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
credentials: "include",
|
||||
body: JSON.stringify({
|
||||
login_token: loginToken,
|
||||
totp_code: code,
|
||||
remember_me: remember,
|
||||
}),
|
||||
});
|
||||
const data = await response.json();
|
||||
if (data.success) {
|
||||
setAccessTokenFn(data.data.access_token, data.data.expires_in);
|
||||
setUser(mapUser(data.data.user));
|
||||
cachedUser = mapUser(data.data.user);
|
||||
sessionFetched = true;
|
||||
return { success: true };
|
||||
}
|
||||
setError(data.error);
|
||||
return { success: false, error: data.error };
|
||||
} catch {
|
||||
const errorMsg = "Chyba pripojeni.";
|
||||
setError(errorMsg);
|
||||
return { success: false, error: errorMsg };
|
||||
}
|
||||
setError(data.error)
|
||||
return { success: false, error: data.error }
|
||||
} catch {
|
||||
const errorMsg = 'Chyba pripojeni.'
|
||||
setError(errorMsg)
|
||||
return { success: false, error: errorMsg }
|
||||
}
|
||||
}, [setAccessTokenFn])
|
||||
},
|
||||
[setAccessTokenFn],
|
||||
);
|
||||
|
||||
const logout = useCallback(async () => {
|
||||
try {
|
||||
const token = getAccessTokenFn()
|
||||
const token = getAccessTokenFn();
|
||||
await fetch(`${API_BASE}/logout`, {
|
||||
method: 'POST',
|
||||
method: "POST",
|
||||
headers: { ...(token && { Authorization: `Bearer ${token}` }) },
|
||||
credentials: 'include',
|
||||
})
|
||||
} catch { /* ignore */ } finally {
|
||||
accessToken = null
|
||||
tokenExpiresAt = null
|
||||
setUser(null)
|
||||
cachedUser = null
|
||||
sessionFetched = false
|
||||
if (refreshTimeoutRef.current) { clearTimeout(refreshTimeoutRef.current); refreshTimeoutRef.current = null }
|
||||
}
|
||||
}, [getAccessTokenFn])
|
||||
|
||||
const apiRequest = useCallback(async (endpoint: string, options: RequestInit = {}) => {
|
||||
let token = getAccessTokenFn()
|
||||
if (!token && user) {
|
||||
const refreshed = await silentRefresh()
|
||||
if (refreshed) token = getAccessTokenFn()
|
||||
}
|
||||
const headers: Record<string, string> = { 'Content-Type': 'application/json', ...(options.headers as Record<string, string>) }
|
||||
if (token) headers['Authorization'] = `Bearer ${token}`
|
||||
const response = await fetch(`${API_BASE}${endpoint}`, { ...options, headers, credentials: 'include' })
|
||||
if (response.status === 401 && user) {
|
||||
const refreshed = await silentRefresh()
|
||||
if (refreshed) {
|
||||
token = getAccessTokenFn()
|
||||
if (token) headers['Authorization'] = `Bearer ${token}`
|
||||
return fetch(`${API_BASE}${endpoint}`, { ...options, headers, credentials: 'include' })
|
||||
credentials: "include",
|
||||
});
|
||||
} catch {
|
||||
/* ignore */
|
||||
} finally {
|
||||
accessToken = null;
|
||||
tokenExpiresAt = null;
|
||||
setUser(null);
|
||||
cachedUser = null;
|
||||
sessionFetched = false;
|
||||
if (refreshTimeoutRef.current) {
|
||||
clearTimeout(refreshTimeoutRef.current);
|
||||
refreshTimeoutRef.current = null;
|
||||
}
|
||||
}
|
||||
return response
|
||||
}, [getAccessTokenFn, silentRefresh, user])
|
||||
}, [getAccessTokenFn]);
|
||||
|
||||
const apiRequest = useCallback(
|
||||
async (endpoint: string, options: RequestInit = {}) => {
|
||||
let token = getAccessTokenFn();
|
||||
if (!token && user) {
|
||||
const refreshed = await silentRefresh();
|
||||
if (refreshed) token = getAccessTokenFn();
|
||||
}
|
||||
const headers: Record<string, string> = {
|
||||
"Content-Type": "application/json",
|
||||
...(options.headers as Record<string, string>),
|
||||
};
|
||||
if (token) headers["Authorization"] = `Bearer ${token}`;
|
||||
const response = await fetch(`${API_BASE}${endpoint}`, {
|
||||
...options,
|
||||
headers,
|
||||
credentials: "include",
|
||||
});
|
||||
if (response.status === 401 && user) {
|
||||
const refreshed = await silentRefresh();
|
||||
if (refreshed) {
|
||||
token = getAccessTokenFn();
|
||||
if (token) headers["Authorization"] = `Bearer ${token}`;
|
||||
return fetch(`${API_BASE}${endpoint}`, {
|
||||
...options,
|
||||
headers,
|
||||
credentials: "include",
|
||||
});
|
||||
}
|
||||
}
|
||||
return response;
|
||||
},
|
||||
[getAccessTokenFn, silentRefresh, user],
|
||||
);
|
||||
|
||||
const updateUser = useCallback((updates: Partial<User>) => {
|
||||
setUser(prev => prev ? { ...prev, ...updates } : null)
|
||||
}, [])
|
||||
setUser((prev) => (prev ? { ...prev, ...updates } : null));
|
||||
}, []);
|
||||
|
||||
const hasPermission = useCallback((permission: string): boolean => {
|
||||
if (!user) return false
|
||||
if (user.isAdmin) return true
|
||||
return (user.permissions || []).includes(permission)
|
||||
}, [user])
|
||||
const hasPermission = useCallback(
|
||||
(permission: string): boolean => {
|
||||
if (!user) return false;
|
||||
if (user.isAdmin) return true;
|
||||
return (user.permissions || []).includes(permission);
|
||||
},
|
||||
[user],
|
||||
);
|
||||
|
||||
const permissions = useMemo(() => user?.permissions || [], [user])
|
||||
const permissions = useMemo(() => user?.permissions || [], [user]);
|
||||
|
||||
const stateValue = useMemo<AuthState>(() => ({
|
||||
user, loading, error, isAuthenticated: !!user, isAdmin: user?.isAdmin || false, permissions, hasPermission,
|
||||
}), [user, loading, error, permissions, hasPermission])
|
||||
const stateValue = useMemo<AuthState>(
|
||||
() => ({
|
||||
user,
|
||||
loading,
|
||||
error,
|
||||
isAuthenticated: !!user,
|
||||
isAdmin: user?.isAdmin || false,
|
||||
permissions,
|
||||
hasPermission,
|
||||
}),
|
||||
[user, loading, error, permissions, hasPermission],
|
||||
);
|
||||
|
||||
const actionsValue = useMemo<AuthActions>(() => ({
|
||||
login, verify2FA, logout, checkSession, getAccessToken: getAccessTokenFn, apiRequest, silentRefresh, updateUser,
|
||||
}), [login, verify2FA, logout, checkSession, getAccessTokenFn, apiRequest, silentRefresh, updateUser])
|
||||
const actionsValue = useMemo<AuthActions>(
|
||||
() => ({
|
||||
login,
|
||||
verify2FA,
|
||||
logout,
|
||||
checkSession,
|
||||
getAccessToken: getAccessTokenFn,
|
||||
apiRequest,
|
||||
silentRefresh,
|
||||
updateUser,
|
||||
}),
|
||||
[
|
||||
login,
|
||||
verify2FA,
|
||||
logout,
|
||||
checkSession,
|
||||
getAccessTokenFn,
|
||||
apiRequest,
|
||||
silentRefresh,
|
||||
updateUser,
|
||||
],
|
||||
);
|
||||
|
||||
return (
|
||||
<AuthActionsContext.Provider value={actionsValue}>
|
||||
@@ -281,26 +399,29 @@ export function AuthProvider({ children }: { children: ReactNode }) {
|
||||
{children}
|
||||
</AuthStateContext.Provider>
|
||||
</AuthActionsContext.Provider>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export function useAuth(): AuthState & AuthActions {
|
||||
const state = useContext(AuthStateContext)
|
||||
const actions = useContext(AuthActionsContext)
|
||||
if (!state || !actions) throw new Error('useAuth must be used within an AuthProvider')
|
||||
return { ...state, ...actions }
|
||||
const state = useContext(AuthStateContext);
|
||||
const actions = useContext(AuthActionsContext);
|
||||
if (!state || !actions)
|
||||
throw new Error("useAuth must be used within an AuthProvider");
|
||||
return { ...state, ...actions };
|
||||
}
|
||||
|
||||
export function useAuthState(): AuthState {
|
||||
const context = useContext(AuthStateContext)
|
||||
if (!context) throw new Error('useAuthState must be used within an AuthProvider')
|
||||
return context
|
||||
const context = useContext(AuthStateContext);
|
||||
if (!context)
|
||||
throw new Error("useAuthState must be used within an AuthProvider");
|
||||
return context;
|
||||
}
|
||||
|
||||
export function useAuthActions(): AuthActions {
|
||||
const context = useContext(AuthActionsContext)
|
||||
if (!context) throw new Error('useAuthActions must be used within an AuthProvider')
|
||||
return context
|
||||
const context = useContext(AuthActionsContext);
|
||||
if (!context)
|
||||
throw new Error("useAuthActions must be used within an AuthProvider");
|
||||
return context;
|
||||
}
|
||||
|
||||
export default AuthStateContext
|
||||
export default AuthStateContext;
|
||||
|
||||
Reference in New Issue
Block a user