The agreed_price depends on trade direction (buy/sell) and must be calculated on the frontend. Returning a buy-side-only agreed_price from the API was misleading and unused. Frontend already calculates the direction-aware price correctly.
99 lines
3 KiB
Python
99 lines
3 KiB
Python
"""FastAPI application entry point."""
|
|
|
|
import asyncio
|
|
import logging
|
|
from contextlib import asynccontextmanager
|
|
|
|
from fastapi import FastAPI
|
|
from fastapi.middleware.cors import CORSMiddleware
|
|
|
|
from database import Base, async_session, engine
|
|
from models import PriceHistory
|
|
from price_fetcher import PAIR_BTC_EUR, SOURCE_BITFINEX, fetch_btc_eur_price
|
|
from routes import audit as audit_routes
|
|
from routes import auth as auth_routes
|
|
from routes import availability as availability_routes
|
|
from routes import exchange as exchange_routes
|
|
from routes import invites as invites_routes
|
|
from routes import meta as meta_routes
|
|
from routes import profile as profile_routes
|
|
from shared_constants import PRICE_REFRESH_SECONDS
|
|
from validate_constants import validate_shared_constants
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
# Background task handle
|
|
_price_fetch_task: asyncio.Task | None = None
|
|
|
|
|
|
async def periodic_price_fetcher():
|
|
"""Background task that fetches BTC/EUR price every minute."""
|
|
logger.info(
|
|
"Starting periodic price fetcher (every %d seconds)", PRICE_REFRESH_SECONDS
|
|
)
|
|
while True:
|
|
try:
|
|
price_value, timestamp = await fetch_btc_eur_price()
|
|
async with async_session() as db:
|
|
new_price = PriceHistory(
|
|
source=SOURCE_BITFINEX,
|
|
pair=PAIR_BTC_EUR,
|
|
price=price_value,
|
|
timestamp=timestamp,
|
|
)
|
|
db.add(new_price)
|
|
await db.commit()
|
|
logger.info("Fetched BTC/EUR price: €%.2f", price_value)
|
|
except Exception as e:
|
|
logger.error("Failed to fetch price: %s", e)
|
|
|
|
await asyncio.sleep(PRICE_REFRESH_SECONDS)
|
|
|
|
|
|
@asynccontextmanager
|
|
async def lifespan(app: FastAPI):
|
|
"""Create database tables on startup and validate constants."""
|
|
global _price_fetch_task
|
|
|
|
# Validate shared constants match backend definitions
|
|
validate_shared_constants()
|
|
|
|
async with engine.begin() as conn:
|
|
await conn.run_sync(Base.metadata.create_all)
|
|
|
|
# Start background price fetcher
|
|
_price_fetch_task = asyncio.create_task(periodic_price_fetcher())
|
|
|
|
yield
|
|
|
|
# Cancel background task on shutdown
|
|
if _price_fetch_task:
|
|
_price_fetch_task.cancel()
|
|
try:
|
|
await _price_fetch_task
|
|
except asyncio.CancelledError:
|
|
logger.info("Price fetcher task cancelled")
|
|
|
|
|
|
app = FastAPI(lifespan=lifespan)
|
|
|
|
app.add_middleware(
|
|
CORSMiddleware,
|
|
allow_origins=["http://localhost:3000"],
|
|
allow_methods=["*"],
|
|
allow_headers=["*"],
|
|
allow_credentials=True,
|
|
)
|
|
|
|
# Include routers - modules with single router
|
|
app.include_router(auth_routes.router)
|
|
app.include_router(audit_routes.router)
|
|
app.include_router(profile_routes.router)
|
|
app.include_router(availability_routes.router)
|
|
app.include_router(meta_routes.router)
|
|
|
|
# Include routers - modules with multiple routers
|
|
for r in invites_routes.routers:
|
|
app.include_router(r)
|
|
for r in exchange_routes.routers:
|
|
app.include_router(r)
|