arbret/backend/main.py

100 lines
3 KiB
Python
Raw Normal View History

2025-12-20 22:18:14 +01:00
"""FastAPI application entry point."""
import asyncio
import logging
2025-12-18 21:48:41 +01:00
from contextlib import asynccontextmanager
2025-12-18 23:33:32 +01:00
2025-12-20 22:18:14 +01:00
from fastapi import FastAPI
2025-12-18 21:37:28 +01:00
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
2025-12-20 22:18:14 +01:00
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
2025-12-20 23:06:05 +01:00
from validate_constants import validate_shared_constants
2025-12-18 21:48:41 +01:00
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)
2025-12-18 21:48:41 +01:00
@asynccontextmanager
async def lifespan(app: FastAPI):
2025-12-20 23:06:05 +01:00
"""Create database tables on startup and validate constants."""
global _price_fetch_task
2025-12-20 23:06:05 +01:00
# Validate shared constants match backend definitions
validate_shared_constants()
2025-12-18 21:48:41 +01:00
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())
2025-12-18 21:48:41 +01:00
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")
2025-12-18 21:48:41 +01:00
app = FastAPI(lifespan=lifespan)
2025-12-18 21:37:28 +01:00
app.add_middleware(
CORSMiddleware,
allow_origins=["http://localhost:3000"],
allow_methods=["*"],
allow_headers=["*"],
2025-12-18 22:08:31 +01:00
allow_credentials=True,
2025-12-18 21:37:28 +01:00
)
# Include routers - modules with single router
2025-12-20 22:18:14 +01:00
app.include_router(auth_routes.router)
app.include_router(audit_routes.router)
app.include_router(profile_routes.router)
app.include_router(availability_routes.router)
2025-12-20 23:06:05 +01:00
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)