Files
eth_hedge_sim/backend/app/main.py
T
2026-07-24 16:33:25 +08:00

110 lines
2.7 KiB
Python

from __future__ import annotations
import logging
from contextlib import asynccontextmanager
from pathlib import Path
from fastapi import FastAPI, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import FileResponse
from fastapi.staticfiles import StaticFiles
from .api import router as api_router
from .config import get_settings
from .market import MarketGateway, set_gateway
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s %(levelname)s [%(name)s] %(message)s",
)
logger = logging.getLogger("eth_hedge_sim")
def resolve_frontend_dist() -> Path:
here = Path(__file__).resolve()
# backend/app/main.py -> repo root is parents[2]
repo_root = here.parents[2]
return repo_root / "frontend" / "dist"
@asynccontextmanager
async def lifespan(app: FastAPI):
settings = get_settings()
if not settings.is_sim:
logger.warning("MODE=%s — still read-only market in current phase", settings.mode)
gw = MarketGateway(settings)
set_gateway(gw)
try:
await gw.start()
logger.info("market gateway started (SIM read-only)")
except Exception:
logger.exception("market gateway failed to start")
yield
await gw.stop()
set_gateway(None)
app = FastAPI(
title="eth_hedge_sim",
version="0.2.0",
description="ETH 自动对冲模拟盘",
lifespan=lifespan,
)
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
app.include_router(api_router)
@app.get("/health")
async def health() -> dict:
from .market import get_gateway
settings = get_settings()
gw = get_gateway()
snap = gw.snapshot()
return {
"ok": True,
"mode": settings.mode,
"env_name": settings.env_name,
"sim": settings.is_sim,
"market_connected": snap.connected,
"pair": snap.pair.to_dict() if snap.pair else None,
"updated_at_ms": snap.updated_at_ms,
}
_DIST = resolve_frontend_dist()
if (_DIST / "assets").is_dir():
app.mount("/assets", StaticFiles(directory=str(_DIST / "assets")), name="assets")
@app.get("/")
async def index_page():
index = _DIST / "index.html"
if index.exists():
return FileResponse(index)
return {
"ok": True,
"msg": "frontend not built yet; run: cd frontend && npm ci && npm run build",
"health": "/health",
}
@app.get("/app/{full_path:path}")
@app.get("/plan")
@app.get("/trades")
@app.get("/stats")
@app.get("/settings")
@app.get("/login")
async def spa_pages(full_path: str = ""):
index = _DIST / "index.html"
if not index.exists():
raise HTTPException(status_code=404, detail="frontend not built")
return FileResponse(index)