Files
dashboard-cpsp-executive/context/AuthContext.tsx
T
2026-09-08 16:23:05 +07:00

89 lines
2.5 KiB
TypeScript

import React, { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react';
import { api, ApiError, AUTH_UNAUTHORIZED_EVENT } from '../services/apiClient.ts';
import type { User } from '../types/api.ts';
type AuthContextValue = {
user: User | null;
loading: boolean;
login: (userLogin: string, password: string) => Promise<void>;
logout: () => Promise<void>;
refreshUser: () => Promise<User | null>;
};
const AuthContext = createContext<AuthContextValue | null>(null);
export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
const [user, setUser] = useState<User | null>(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
const onUnauthorized = () => setUser(null);
window.addEventListener(AUTH_UNAUTHORIZED_EVENT, onUnauthorized);
return () => window.removeEventListener(AUTH_UNAUTHORIZED_EVENT, onUnauthorized);
}, []);
useEffect(() => {
let cancelled = false;
(async () => {
try {
const me = await api.auth.me();
if (!cancelled) setUser(me);
} catch (err) {
if (
!cancelled &&
!(err instanceof ApiError && (err.status === 401 || err.status === 403))
) {
console.error(err);
}
if (!cancelled) setUser(null);
} finally {
if (!cancelled) setLoading(false);
}
})();
return () => {
cancelled = true;
};
}, []);
const login = useCallback(async (userLogin: string, password: string) => {
await api.auth.csrf();
const next = await api.auth.login(userLogin, password);
setUser(next);
}, []);
const logout = useCallback(async () => {
try {
await api.auth.logout();
} finally {
setUser(null);
}
}, []);
const refreshUser = useCallback(async () => {
try {
const me = await api.auth.me();
setUser(me);
return me;
} catch (err) {
if (err instanceof ApiError && (err.status === 401 || err.status === 403)) {
setUser(null);
return null;
}
throw err;
}
}, []);
const value = useMemo(
() => ({ user, loading, login, logout, refreshUser }),
[user, loading, login, logout, refreshUser]
);
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
};
export function useAuth(): AuthContextValue {
const ctx = useContext(AuthContext);
if (!ctx) throw new Error('useAuth must be used within AuthProvider');
return ctx;
}