ef385340e8
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
52 lines
1.7 KiB
Python
52 lines
1.7 KiB
Python
"""ecomm backend — FastAPI app factory + the REST BFF.
|
|
|
|
SLICE-1 mounts only /healthz; the auth and storefront endpoints (§6.4) grow here in
|
|
later slices. create_app() opens the connection pool and self-migrates the database
|
|
at startup (INV-1, INV-7): the app boots against empty persistence and applies its
|
|
own schema — there is no seed step.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
from contextlib import asynccontextmanager
|
|
|
|
import psycopg
|
|
from fastapi import Depends, FastAPI, Response
|
|
|
|
from app.platform import config, db
|
|
from app.platform.deps import get_conn
|
|
|
|
|
|
def create_app(database_url: str | None = None) -> FastAPI:
|
|
dsn = database_url or config.database_url()
|
|
|
|
@asynccontextmanager
|
|
async def lifespan(app: FastAPI):
|
|
app.state.pool = db.open_pool(dsn)
|
|
with app.state.pool.connection() as conn:
|
|
db.migrate(conn) # self-migrate at startup (INV-1, INV-7)
|
|
try:
|
|
yield
|
|
finally:
|
|
app.state.pool.close()
|
|
|
|
app = FastAPI(title="ecomm", version="0.1", lifespan=lifespan)
|
|
|
|
@app.get("/healthz")
|
|
def healthz(response: Response, conn: psycopg.Connection = Depends(get_conn)):
|
|
"""Liveness + readiness: process up, DB reachable, migrations current (§6.4)."""
|
|
try:
|
|
conn.execute("SELECT 1")
|
|
pending = db.pending_migrations(conn)
|
|
except Exception:
|
|
response.status_code = 503
|
|
return {"status": "unavailable", "reason": "database_unreachable"}
|
|
if pending:
|
|
response.status_code = 503
|
|
return {"status": "unavailable", "reason": "migrations_pending"}
|
|
return {"status": "ok"}
|
|
|
|
return app
|
|
|
|
|
|
app = create_app()
|