66 lines
2.1 KiB
Python
66 lines
2.1 KiB
Python
import logging
|
|
from contextlib import asynccontextmanager
|
|
|
|
from fastapi import FastAPI, Request
|
|
from fastapi.responses import JSONResponse
|
|
|
|
from app.config import settings
|
|
from app.routers import analytics, auth, distractions, proactive, sessions, steps, tasks
|
|
from app.services.db import close_pool, get_pool
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
@asynccontextmanager
|
|
async def lifespan(app: FastAPI):
|
|
await get_pool()
|
|
print(f"APNs config → KEY_ID={settings.APNS_KEY_ID or '(empty)'!r} "
|
|
f"TEAM_ID={settings.APNS_TEAM_ID or '(empty)'!r} "
|
|
f"P8_PATH={settings.APNS_P8_PATH or '(empty)'!r} "
|
|
f"SANDBOX={settings.APNS_SANDBOX} "
|
|
f"BUNDLE={settings.APPLE_BUNDLE_ID}")
|
|
yield
|
|
await close_pool()
|
|
|
|
|
|
app = FastAPI(
|
|
title="LockInBro API",
|
|
version="1.0.0",
|
|
root_path="/api/v1",
|
|
lifespan=lifespan,
|
|
)
|
|
|
|
@app.exception_handler(Exception)
|
|
async def llm_error_handler(request: Request, exc: Exception):
|
|
# Surface LLM provider errors as 502 instead of 500
|
|
exc_name = type(exc).__name__
|
|
if "ClientError" in exc_name or "APIError" in exc_name or "APIConnectionError" in exc_name:
|
|
return JSONResponse(status_code=502, content={"detail": f"LLM provider error: {exc}"})
|
|
if isinstance(exc, RuntimeError) and "No LLM API key" in str(exc):
|
|
return JSONResponse(status_code=503, content={"detail": str(exc)})
|
|
raise exc
|
|
|
|
|
|
@app.middleware("http")
|
|
async def log_client_info(request: Request, call_next):
|
|
real_ip = request.headers.get("cf-connecting-ip", request.headers.get("x-forwarded-for", "unknown"))
|
|
ua = request.headers.get("user-agent", "unknown")
|
|
response = await call_next(request)
|
|
if request.url.path != "/api/v1/health":
|
|
print(f"[REQ] {request.method} {request.url.path} → {response.status_code} | ip={real_ip} ua={ua[:80]}")
|
|
return response
|
|
|
|
|
|
app.include_router(auth.router)
|
|
app.include_router(tasks.router)
|
|
app.include_router(steps.router)
|
|
app.include_router(sessions.router)
|
|
app.include_router(distractions.router)
|
|
app.include_router(proactive.router)
|
|
app.include_router(analytics.router)
|
|
|
|
|
|
@app.get("/health")
|
|
async def health():
|
|
return {"status": "ok"}
|