c4e6aab7b2
- 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>
48 lines
1.4 KiB
Python
48 lines
1.4 KiB
Python
"""JWT 인증."""
|
|
import os
|
|
from datetime import datetime, timedelta
|
|
from typing import Optional
|
|
from fastapi import HTTPException, status, Depends
|
|
from fastapi.security import OAuth2PasswordBearer
|
|
from jose import jwt, JWTError
|
|
|
|
import users_db
|
|
|
|
JWT_SECRET = os.environ.get("JWT_SECRET", "change-me-in-production-please")
|
|
JWT_ALG = "HS256"
|
|
JWT_EXP_HOURS = 24 * 7
|
|
|
|
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/api/auth/login", auto_error=False)
|
|
|
|
|
|
def create_token(username: str, role: str) -> str:
|
|
payload = {
|
|
"sub": username,
|
|
"role": role,
|
|
"exp": datetime.utcnow() + timedelta(hours=JWT_EXP_HOURS),
|
|
"iat": datetime.utcnow(),
|
|
}
|
|
return jwt.encode(payload, JWT_SECRET, algorithm=JWT_ALG)
|
|
|
|
|
|
def decode_token(token: str) -> Optional[dict]:
|
|
try:
|
|
return jwt.decode(token, JWT_SECRET, algorithms=[JWT_ALG])
|
|
except JWTError:
|
|
return None
|
|
|
|
|
|
def require_user(token: Optional[str] = Depends(oauth2_scheme)) -> dict:
|
|
if not token:
|
|
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="not authenticated")
|
|
payload = decode_token(token)
|
|
if not payload:
|
|
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="invalid token")
|
|
return payload
|
|
|
|
|
|
def require_admin(payload: dict = Depends(require_user)) -> dict:
|
|
if payload.get("role") != "admin":
|
|
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="admin only")
|
|
return payload
|