Tracker de vie auto-hébergé : suivi poids/calories/sport avec planning de pesées, sevrage tabac (vape) avec modèle de coût DIY et économies, et finances personnelles avec import de relevés bancaires. Architecture : FastAPI + SQLAlchemy 2.0 + PostgreSQL 16, React 18 + TS + Vite + Tailwind + ECharts, déploiement Docker Compose. Modules auto-découverts des deux côtés (pkgutil / import.meta.glob) et framework de connecteurs à deux voies (importeurs de fichiers + ingestion JSON) pour brancher de nouvelles sources sans toucher au noyau. Validé : 292 tests pytest, tsc + vite build, contrat API/web vérifié contre le schéma OpenAPI, et déploiement Docker réel sur PostgreSQL 16 (28 tables, SPA servie par nginx, wizard de premier démarrage). Documentation : README.md, docs/GUIDE.md, CONVENTIONS.md, et les documents de conception et de recherche dans docs/. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
64 lines
1.9 KiB
Python
64 lines
1.9 KiB
Python
from contextlib import asynccontextmanager
|
|
from typing import Any
|
|
|
|
from fastapi import FastAPI
|
|
from fastapi.middleware.cors import CORSMiddleware
|
|
from fastapi.responses import JSONResponse
|
|
|
|
from app.core.config import get_settings
|
|
from app.core.database import Base, engine
|
|
from app.core.errors import register_error_handlers
|
|
from app.core.module_loader import import_side_modules, register_routers
|
|
|
|
|
|
@asynccontextmanager
|
|
async def lifespan(app: FastAPI):
|
|
# Import every module's models/importers/ingest so that:
|
|
# - Base.metadata knows all tables before create_all
|
|
# - importer & ingest registries are populated (decorator side effects)
|
|
import_side_modules()
|
|
# v1 table-creation strategy: create_all on startup (Alembic later).
|
|
Base.metadata.create_all(bind=engine)
|
|
yield
|
|
|
|
|
|
def create_app() -> FastAPI:
|
|
settings = get_settings()
|
|
app = FastAPI(
|
|
title="LifeTrack API",
|
|
version="1.0.0",
|
|
docs_url="/api/docs",
|
|
redoc_url=None,
|
|
openapi_url="/api/openapi.json",
|
|
lifespan=lifespan,
|
|
)
|
|
if settings.cors_origins:
|
|
app.add_middleware(
|
|
CORSMiddleware,
|
|
allow_origins=settings.cors_origins,
|
|
allow_credentials=True,
|
|
allow_methods=["*"],
|
|
allow_headers=["*"],
|
|
)
|
|
register_error_handlers(app)
|
|
register_routers(app) # auto-discovery, see architecture §3.4
|
|
|
|
@app.get("/api/healthz", include_in_schema=False)
|
|
def healthz() -> dict[str, str]:
|
|
return {"status": "ok"}
|
|
|
|
# Bare aliases (no /api prefix) for container/orchestrator probes.
|
|
@app.get("/healthz", include_in_schema=False)
|
|
def healthz_alias() -> dict[str, str]:
|
|
return {"status": "ok"}
|
|
|
|
@app.get("/openapi.json", include_in_schema=False)
|
|
def openapi_alias() -> JSONResponse:
|
|
schema: dict[str, Any] = app.openapi()
|
|
return JSONResponse(schema)
|
|
|
|
return app
|
|
|
|
|
|
app = create_app()
|