template-monorepo/frontend/src/context/AuthContext.tsx

97 lines
2.2 KiB
TypeScript
Raw Normal View History

2026-05-26 09:32:32 +00:00
import {
createContext,
useCallback,
useContext,
useEffect,
useMemo,
useState,
type ReactNode,
} from 'react';
import { clearTokens, getToken } from '../api/http';
import * as permApi from '../api/permission';
interface AuthState {
ready: boolean;
token: string;
uid: string;
roles: string[];
isAdmin: boolean;
syncSession: () => void;
refreshRoles: () => Promise<void>;
signOut: () => void;
}
const AuthContext = createContext<AuthState | null>(null);
export function AuthProvider({ children }: { children: ReactNode }) {
const [ready, setReady] = useState(false);
const [token, setToken] = useState(getToken);
const [uid, setUid] = useState(() => localStorage.getItem('uid') ?? '');
const [roles, setRoles] = useState<string[]>(() => {
try {
return JSON.parse(localStorage.getItem('roles') ?? '[]') as string[];
} catch {
return [];
}
});
const syncSession = useCallback(() => {
setToken(getToken());
setUid(localStorage.getItem('uid') ?? '');
}, []);
const refreshRoles = useCallback(async () => {
if (!getToken()) {
setRoles([]);
return;
}
try {
const me = await permApi.getMyPermissions();
setRoles(me.roles ?? []);
localStorage.setItem('roles', JSON.stringify(me.roles ?? []));
if (me.uid) {
setUid(me.uid);
localStorage.setItem('uid', me.uid);
}
} catch {
setRoles([]);
}
}, []);
useEffect(() => {
(async () => {
if (getToken()) await refreshRoles();
setReady(true);
})();
}, [refreshRoles]);
const signOut = useCallback(() => {
clearTokens();
setToken('');
setUid('');
setRoles([]);
}, []);
const value = useMemo(
() => ({
ready,
token,
uid,
roles,
isAdmin: permApi.isAdminRole(roles),
syncSession,
refreshRoles,
signOut,
}),
[ready, token, uid, roles, syncSession, refreshRoles, signOut],
);
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
}
export function useAuth() {
const ctx = useContext(AuthContext);
if (!ctx) throw new Error('useAuth 需在 AuthProvider 內');
return ctx;
}