gc-wing/apps/web/src/shared/auth/authApi.ts
htlee 79b21c7d44 feat(auth): Google OAuth 로그인 구현
- shared/auth 모듈: AuthProvider, ProtectedRoute, useAuth, authApi
- 페이지: LoginPage(Google OAuth), PendingPage, DeniedPage
- WING_PERMIT 역할 기반 접근 제어
- Topbar에 사용자 이름 + 로그아웃 버튼 추가
- App.tsx에 react-router 라우팅 + AuthProvider 래핑
- DEV 모드 Mock 로그인 지원 (김개발)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 08:44:25 +09:00

35 lines
1009 B
TypeScript

const API_BASE = (import.meta.env.VITE_AUTH_API_URL || '').replace(/\/$/, '') + '/api';
async function request<T>(path: string, options?: RequestInit): Promise<T> {
const token = localStorage.getItem('token');
const headers: Record<string, string> = {
'Content-Type': 'application/json',
...(token ? { Authorization: `Bearer ${token}` } : {}),
};
const res = await fetch(`${API_BASE}${path}`, { ...options, headers });
if (res.status === 401) {
localStorage.removeItem('token');
window.location.href = '/login';
throw new Error('Unauthorized');
}
if (!res.ok) {
const body = await res.text();
throw new Error(body || `HTTP ${res.status}`);
}
if (res.status === 204 || res.headers.get('content-length') === '0') {
return undefined as T;
}
return res.json();
}
export const authApi = {
get: <T>(path: string) => request<T>(path),
post: <T>(path: string, body?: unknown) =>
request<T>(path, { method: 'POST', body: JSON.stringify(body) }),
};