Files
chpark c4e6aab7b2 React + FastAPI 풀 마이그레이션 — Streamlit 제거
- backend/ — FastAPI + JWT + 모든 REST 엔드포인트
- frontend/ — Next.js 14 + Tailwind + 7페이지 (대시보드/트레이드/거래소/자동매매/설정/내정보/로그인)
- core_logic.py — 신호계산/알림 로직 분리 (기존 app_streamlit.py 에서 추출)
- users_db.py + bcrypt 인증, exchange_keys.py + Fernet 암호화
- trades_db.py — 진입/청산 lifecycle 추적, signal_events raw 로그
- settings_db.py — 모든 운영 파라미터 DB 영속 저장 (RSI/거래량/펀딩비 임계값 포함)
- docker-compose: frontend / backend / postgres + Traefik 라우팅
- assets/logo.svg — JUNGGOMOA 그라디언트 로고

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 17:27:11 +09:00

48 lines
1.3 KiB
TypeScript

'use client';
import { create } from 'zustand';
import { api, getToken, setToken, clearToken } from './api';
interface AuthUser { id?: number; username: string; role: string; created_at?: string; last_login_at?: string; }
interface AuthState {
user: AuthUser | null;
loading: boolean;
error: string | null;
login: (u: string, p: string) => Promise<boolean>;
logout: () => void;
fetchMe: () => Promise<void>;
}
export const useAuth = create<AuthState>((set) => ({
user: null,
loading: false,
error: null,
async login(u, p) {
set({ loading: true, error: null });
try {
const r = await api.post<{ access_token: string; user: AuthUser }>('/api/auth/login', { username: u, password: p });
setToken(r.access_token);
set({ user: r.user, loading: false });
return true;
} catch (e: any) {
set({ error: e?.message || '로그인 실패', loading: false });
return false;
}
},
logout() {
clearToken();
set({ user: null });
if (typeof window !== 'undefined') window.location.href = '/login';
},
async fetchMe() {
if (!getToken()) { set({ user: null }); return; }
try {
const me = await api.get<AuthUser>('/api/auth/me');
set({ user: me });
} catch {
clearToken();
set({ user: null });
}
},
}));