96 lines
2.6 KiB
TypeScript
96 lines
2.6 KiB
TypeScript
import {
|
|
useCallback,
|
|
useEffect,
|
|
useMemo,
|
|
useState,
|
|
type ReactNode,
|
|
} from 'react';
|
|
import type { AuthResponse, User } from '../types';
|
|
import { api } from '../utils/api';
|
|
import { AuthContext } from './AuthContext';
|
|
|
|
const DEV_MOCK_USER: User = {
|
|
id: 1,
|
|
email: 'htlee@gcsc.co.kr',
|
|
name: '김개발 (DEV)',
|
|
avatarUrl: null,
|
|
status: 'ACTIVE',
|
|
isAdmin: true,
|
|
roles: [{ id: 1, name: 'ADMIN', description: '관리자', urlPatterns: ['/**'], defaultGrant: false }],
|
|
createdAt: new Date().toISOString(),
|
|
lastLoginAt: new Date().toISOString(),
|
|
};
|
|
|
|
function isDevMockSession(): boolean {
|
|
return import.meta.env.DEV && localStorage.getItem('dev-user') === 'true';
|
|
}
|
|
|
|
export function AuthProvider({ children }: { children: ReactNode }) {
|
|
const [user, setUser] = useState<User | null>(() =>
|
|
isDevMockSession() ? DEV_MOCK_USER : null,
|
|
);
|
|
const [token, setToken] = useState<string | null>(
|
|
() => localStorage.getItem('token'),
|
|
);
|
|
const [initialized, setInitialized] = useState(
|
|
() => isDevMockSession() || !localStorage.getItem('token'),
|
|
);
|
|
|
|
const logout = useCallback(() => {
|
|
const hadToken = !!localStorage.getItem('token') && !isDevMockSession();
|
|
localStorage.removeItem('token');
|
|
localStorage.removeItem('dev-user');
|
|
setToken(null);
|
|
setUser(null);
|
|
if (hadToken) {
|
|
api.post('/auth/logout').catch(() => {});
|
|
}
|
|
}, []);
|
|
|
|
const devLogin = useCallback(() => {
|
|
localStorage.setItem('dev-user', 'true');
|
|
localStorage.setItem('token', 'dev-mock-token');
|
|
setToken('dev-mock-token');
|
|
setUser(DEV_MOCK_USER);
|
|
setInitialized(true);
|
|
}, []);
|
|
|
|
const login = useCallback(async (googleToken: string) => {
|
|
const res = await api.post<AuthResponse>('/auth/google', {
|
|
idToken: googleToken,
|
|
});
|
|
localStorage.setItem('token', res.token);
|
|
setToken(res.token);
|
|
setUser(res.user);
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
if (!token || isDevMockSession()) return;
|
|
|
|
let cancelled = false;
|
|
api
|
|
.get<User>('/auth/me')
|
|
.then((data) => {
|
|
if (!cancelled) setUser(data);
|
|
})
|
|
.catch(() => {
|
|
if (!cancelled) logout();
|
|
})
|
|
.finally(() => {
|
|
if (!cancelled) setInitialized(true);
|
|
});
|
|
return () => {
|
|
cancelled = true;
|
|
};
|
|
}, [token, logout]);
|
|
|
|
const loading = !initialized;
|
|
|
|
const value = useMemo(
|
|
() => ({ user, token, loading, login, devLogin: import.meta.env.DEV ? devLogin : undefined, logout }),
|
|
[user, token, loading, login, devLogin, logout],
|
|
);
|
|
|
|
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
|
|
}
|