fix: 인증 미들웨어 활성화, lifespan 예외처리, Docker 네트워크 바인딩 수정 및 문서 전면 개선
- hydra/main.py: auth_guard 미들웨어에 실제 API 키 검증 로직 추가 - hydra/main.py: lifespan 초기화 블록 try-except 감싸기, finally에서 ohlcv_store None 체크 추가 - Dockerfile: --host 127.0.0.1 → 0.0.0.0 (컨테이너 간 통신 가능하도록) - hydra/config/settings.py: 기본 API 키 사용 시 경고 validator 추가 - README.md: 첫 사용자를 위한 상세 가이드로 전면 재작성 - docs/QUICKSTART_KO.md: 단계별 시작 가이드 개선 - docs/API_REFERENCE_KO.md: 전체 엔드포인트 응답 예시 및 파라미터 설명 추가 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,6 @@
|
||||
import warnings
|
||||
from functools import lru_cache
|
||||
from pydantic import field_validator
|
||||
from pydantic_settings import BaseSettings
|
||||
|
||||
|
||||
@@ -13,6 +15,17 @@ class Settings(BaseSettings):
|
||||
|
||||
model_config = {"env_file": ".env", "env_file_encoding": "utf-8"}
|
||||
|
||||
@field_validator("hydra_api_key")
|
||||
@classmethod
|
||||
def api_key_must_not_be_default(cls, v: str) -> str:
|
||||
if v == "change-me":
|
||||
warnings.warn(
|
||||
"[HYDRA] HYDRA_API_KEY가 기본값 'change-me'로 설정되어 있습니다. "
|
||||
".env 파일에서 안전한 값으로 변경하세요.",
|
||||
stacklevel=2,
|
||||
)
|
||||
return v
|
||||
|
||||
|
||||
@lru_cache
|
||||
def get_settings() -> Settings:
|
||||
|
||||
@@ -48,53 +48,60 @@ async def lifespan(app: FastAPI):
|
||||
configure_logging(settings.log_level)
|
||||
logger.info("hydra_starting", profile=settings.hydra_profile)
|
||||
|
||||
r = redis_lib.Redis.from_url(settings.redis_url, decode_responses=True)
|
||||
set_redis(r)
|
||||
ohlcv_store = None
|
||||
try:
|
||||
r = redis_lib.Redis.from_url(settings.redis_url, decode_responses=True)
|
||||
set_redis(r)
|
||||
|
||||
market_manager = MarketManager()
|
||||
key_manager = KeyManager()
|
||||
telegram = TelegramNotifier(settings.telegram_bot_token, settings.telegram_chat_id)
|
||||
position_tracker = PositionTracker(r)
|
||||
state_manager = StateManager(r)
|
||||
risk_engine = RiskEngine(r, position_tracker)
|
||||
pnl_tracker = PnlTracker(r)
|
||||
ohlcv_store = create_store()
|
||||
await ohlcv_store.init()
|
||||
exchanges = create_exchanges(market_manager, key_manager)
|
||||
market_manager = MarketManager()
|
||||
key_manager = KeyManager()
|
||||
telegram = TelegramNotifier(settings.telegram_bot_token, settings.telegram_chat_id)
|
||||
position_tracker = PositionTracker(r)
|
||||
state_manager = StateManager(r)
|
||||
risk_engine = RiskEngine(r, position_tracker)
|
||||
pnl_tracker = PnlTracker(r)
|
||||
ohlcv_store = create_store()
|
||||
await ohlcv_store.init()
|
||||
exchanges = create_exchanges(market_manager, key_manager)
|
||||
|
||||
kill_switch = KillSwitch(
|
||||
exchanges=exchanges,
|
||||
position_tracker=position_tracker,
|
||||
telegram=telegram,
|
||||
redis_client=r,
|
||||
)
|
||||
order_queue = OrderQueue(
|
||||
redis_client=r,
|
||||
risk_engine=risk_engine,
|
||||
position_tracker=position_tracker,
|
||||
exchanges=exchanges,
|
||||
)
|
||||
graceful = GracefulManager(order_queue, position_tracker, r)
|
||||
graceful.register_signals()
|
||||
kill_switch = KillSwitch(
|
||||
exchanges=exchanges,
|
||||
position_tracker=position_tracker,
|
||||
telegram=telegram,
|
||||
redis_client=r,
|
||||
)
|
||||
order_queue = OrderQueue(
|
||||
redis_client=r,
|
||||
risk_engine=risk_engine,
|
||||
position_tracker=position_tracker,
|
||||
exchanges=exchanges,
|
||||
)
|
||||
graceful = GracefulManager(order_queue, position_tracker, r)
|
||||
graceful.register_signals()
|
||||
|
||||
set_order_queue(order_queue)
|
||||
set_position_tracker(position_tracker)
|
||||
set_risk_deps(kill_switch, risk_engine)
|
||||
set_market_manager(market_manager)
|
||||
set_pnl_dependencies(pnl_tracker, position_tracker)
|
||||
set_store(ohlcv_store)
|
||||
set_redis_for_indicators(r)
|
||||
set_redis_for_regime(r)
|
||||
set_redis_for_signals(r)
|
||||
set_redis_for_supplemental(r)
|
||||
set_store_for_backtest(ohlcv_store)
|
||||
set_order_queue(order_queue)
|
||||
set_position_tracker(position_tracker)
|
||||
set_risk_deps(kill_switch, risk_engine)
|
||||
set_market_manager(market_manager)
|
||||
set_pnl_dependencies(pnl_tracker, position_tracker)
|
||||
set_store(ohlcv_store)
|
||||
set_redis_for_indicators(r)
|
||||
set_redis_for_regime(r)
|
||||
set_redis_for_signals(r)
|
||||
set_redis_for_supplemental(r)
|
||||
set_store_for_backtest(ohlcv_store)
|
||||
|
||||
logger.info("hydra_started")
|
||||
except Exception as exc:
|
||||
logger.error("hydra_startup_failed", error=str(exc))
|
||||
raise
|
||||
|
||||
logger.info("hydra_started")
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
logger.info("hydra_stopping")
|
||||
await ohlcv_store.close()
|
||||
if ohlcv_store is not None:
|
||||
await ohlcv_store.close()
|
||||
|
||||
|
||||
def create_app() -> FastAPI:
|
||||
@@ -104,6 +111,13 @@ def create_app() -> FastAPI:
|
||||
async def auth_guard(request: Request, call_next):
|
||||
if request.url.path == "/health":
|
||||
return await call_next(request)
|
||||
from fastapi.responses import JSONResponse
|
||||
api_key = request.headers.get("X-HYDRA-KEY", "")
|
||||
if not api_key or api_key != get_settings().hydra_api_key:
|
||||
return JSONResponse(
|
||||
status_code=403,
|
||||
content={"detail": "Invalid or missing API key. Set X-HYDRA-KEY header."},
|
||||
)
|
||||
return await call_next(request)
|
||||
|
||||
app.include_router(health.router)
|
||||
|
||||
Reference in New Issue
Block a user