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>
74 lines
2.1 KiB
Python
74 lines
2.1 KiB
Python
from fastapi import APIRouter, Depends, HTTPException
|
|
from pydantic import BaseModel
|
|
from typing import Optional
|
|
import exchange_keys
|
|
import exchange_adapters
|
|
from ..auth import require_user
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
class CredIn(BaseModel):
|
|
exchange: str
|
|
label: str = ""
|
|
api_key: str
|
|
api_secret: str
|
|
passphrase: Optional[str] = None
|
|
testnet: bool = False
|
|
|
|
|
|
class CredUpdateIn(BaseModel):
|
|
exchange: Optional[str] = None
|
|
label: Optional[str] = None
|
|
api_key: Optional[str] = None
|
|
api_secret: Optional[str] = None
|
|
passphrase: Optional[str] = None
|
|
testnet: Optional[bool] = None
|
|
enabled: Optional[bool] = None
|
|
|
|
|
|
def _serialize_cred(c):
|
|
d = dict(c)
|
|
for k in ["created_at", "updated_at"]:
|
|
if d.get(k) is not None:
|
|
d[k] = str(d[k])
|
|
return d
|
|
|
|
|
|
@router.get("/credentials")
|
|
def list_creds(_: dict = Depends(require_user)):
|
|
return [_serialize_cred(c) for c in exchange_keys.list_credentials()]
|
|
|
|
|
|
@router.post("/credentials")
|
|
def add_cred(body: CredIn, _: dict = Depends(require_user)):
|
|
cid = exchange_keys.add_credential(
|
|
body.exchange, body.label, body.api_key, body.api_secret,
|
|
body.passphrase, body.testnet, True,
|
|
)
|
|
if not cid:
|
|
raise HTTPException(status_code=400, detail="등록 실패")
|
|
return {"id": cid}
|
|
|
|
|
|
@router.put("/credentials/{cred_id}")
|
|
def update_cred(cred_id: int, body: CredUpdateIn, _: dict = Depends(require_user)):
|
|
fields = {k: v for k, v in body.dict().items() if v is not None}
|
|
if not fields:
|
|
raise HTTPException(status_code=400, detail="변경 항목 없음")
|
|
if not exchange_keys.update_credential(cred_id, **fields):
|
|
raise HTTPException(status_code=404, detail="not found")
|
|
return {"ok": True}
|
|
|
|
|
|
@router.delete("/credentials/{cred_id}")
|
|
def delete_cred(cred_id: int, _: dict = Depends(require_user)):
|
|
if not exchange_keys.delete_credential(cred_id):
|
|
raise HTTPException(status_code=404, detail="not found")
|
|
return {"ok": True}
|
|
|
|
|
|
@router.get("/exchanges")
|
|
def supported_exchanges(_: dict = Depends(require_user)):
|
|
return exchange_keys.SUPPORTED_EXCHANGES
|