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>
55 lines
1.7 KiB
Python
55 lines
1.7 KiB
Python
from fastapi import APIRouter, HTTPException, Depends
|
|
from pydantic import BaseModel
|
|
import users_db
|
|
from ..auth import create_token, require_user
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
class LoginIn(BaseModel):
|
|
username: str
|
|
password: str
|
|
|
|
|
|
class LoginOut(BaseModel):
|
|
access_token: str
|
|
token_type: str = "bearer"
|
|
user: dict
|
|
|
|
|
|
@router.post("/login", response_model=LoginOut)
|
|
def login(body: LoginIn):
|
|
user = users_db.authenticate(body.username.strip(), body.password)
|
|
if not user:
|
|
raise HTTPException(status_code=401, detail="아이디 또는 비밀번호가 올바르지 않습니다.")
|
|
token = create_token(user["username"], user.get("role", "user"))
|
|
# datetime 직렬화 위해 string 변환
|
|
user_safe = {
|
|
"id": user.get("id"),
|
|
"username": user.get("username"),
|
|
"role": user.get("role"),
|
|
"created_at": str(user.get("created_at")) if user.get("created_at") else None,
|
|
"last_login_at": str(user.get("last_login_at")) if user.get("last_login_at") else None,
|
|
}
|
|
return {"access_token": token, "user": user_safe}
|
|
|
|
|
|
@router.get("/me")
|
|
def me(payload: dict = Depends(require_user)):
|
|
return {"username": payload.get("sub"), "role": payload.get("role")}
|
|
|
|
|
|
class ChangePasswordIn(BaseModel):
|
|
old_password: str
|
|
new_password: str
|
|
|
|
|
|
@router.put("/password")
|
|
def change_password(body: ChangePasswordIn, payload: dict = Depends(require_user)):
|
|
username = payload.get("sub")
|
|
if len(body.new_password) < 6:
|
|
raise HTTPException(status_code=400, detail="새 비밀번호는 6자 이상")
|
|
if not users_db.change_password(username, body.old_password, body.new_password):
|
|
raise HTTPException(status_code=400, detail="현재 비밀번호 불일치")
|
|
return {"ok": True}
|