fix: A21 phase 1 — 352 routes async sans await → threadpool (v7.8.0)
FlowDeck CI / lint (push) Canceled after 0s
FlowDeck CI / test (push) Canceled after 0s
FlowDeck CI / docker (push) Canceled after 0s

- Conversion `async def` → `def` de TOUTES les routes dont le corps ne contient
  ni `await`, ni `async with`, ni `async for`, ni `asyncio` (scan automatique
  corps par corps sur app/ : 352 converties, 0 dangereuses, vérifié
  `asyncio`/`run_coroutine`/`.result()` absents). FastAPI exécute ces handlers
  dans son threadpool → tout leur SQLite (`get_conn()` + `conn.execute`) quitte
  l'event loop, sans changer une ligne de logique.
- Répartition : api_v2 60, dashboard 40, collections 25, board 23,
  workspace 19, wiki 17, permissions 14, api 14, main.py 6, + 35 fichiers.
- Les 4 routers prioritaires de l'audit sont couverts par ce lot :
  api_v2 60 + dashboard 40 + collections 25 + board 23 = 148 conversions
  (le reste de leurs routes attend la phase 2 : elles ont de vrais `await`).
- Reste (phase 2) : les 311 routes avec de vrais `await` → enrouler les blocs
  DB dans `await anyio.to_thread.run_sync(...)` ; pas de wrapper partagé livré
  (rien ne l'appellerait — YAGNI jusqu'au premier usage).

suite **1037/1037** (233 s) · `ruff check app tests` OK · docs à jour
This commit is contained in:
2026-10-01 10:53:26 -04:00
parent c718fe06de
commit 224bda74d5
48 changed files with 379 additions and 358 deletions
+11 -11
View File
@@ -315,7 +315,7 @@ async def create_site(request: Request):
@router.get("/api/v2/sites")
async def list_sites(request: Request):
def list_sites(request: Request):
user = _auth_user(request)
limit, offset = parse_pagination(request)
with get_conn() as conn:
@@ -333,7 +333,7 @@ async def list_sites(request: Request):
@router.get("/api/v2/sites/{site_id}")
async def get_site(site_id: int, request: Request):
def get_site(site_id: int, request: Request):
user = _auth_user(request)
with get_conn() as conn:
row = conn.execute("SELECT * FROM sites WHERE id=?", (site_id,)).fetchone()
@@ -407,7 +407,7 @@ async def update_site(site_id: int, request: Request):
@router.delete("/api/v2/sites/{site_id}")
async def delete_site(site_id: int, request: Request):
def delete_site(site_id: int, request: Request):
user = _auth_user(request, require_write=True)
with get_conn() as conn:
row = conn.execute("SELECT * FROM sites WHERE id=?", (site_id,)).fetchone()
@@ -422,7 +422,7 @@ async def delete_site(site_id: int, request: Request):
@router.get("/api/v2/sites/{site_id}/pages")
async def list_site_pages(site_id: int, request: Request):
def list_site_pages(site_id: int, request: Request):
user = _auth_user(request)
with get_conn() as conn:
row = conn.execute("SELECT * FROM sites WHERE id=?", (site_id,)).fetchone()
@@ -461,7 +461,7 @@ async def add_site_page(site_id: int, request: Request):
@router.delete("/api/v2/sites/{site_id}/pages/{page_id}")
async def remove_site_page(site_id: int, page_id: int, request: Request):
def remove_site_page(site_id: int, page_id: int, request: Request):
user = _auth_user(request, require_write=True)
with get_conn() as conn:
row = conn.execute("SELECT * FROM sites WHERE id=?", (site_id,)).fetchone()
@@ -478,7 +478,7 @@ async def remove_site_page(site_id: int, page_id: int, request: Request):
@router.get("/api/v2/sites/{site_id}/stats")
async def site_stats(site_id: int, request: Request, days: int = 30):
def site_stats(site_id: int, request: Request, days: int = 30):
user = _auth_user(request)
days = max(1, min(int(days or 30), 365))
with get_conn() as conn:
@@ -516,7 +516,7 @@ def _public_guard(site: dict, request: Request):
@router.get("/s/{slug}", response_class=HTMLResponse)
async def public_site_home(request: Request, slug: str):
def public_site_home(request: Request, slug: str):
with get_conn() as conn:
site = _resolve_site(conn, slug=slug,
host=request.headers.get("host", ""))
@@ -537,7 +537,7 @@ async def public_site_home(request: Request, slug: str):
@router.get("/s/{slug}/sitemap.xml", response_class=PlainTextResponse)
async def site_sitemap(request: Request, slug: str):
def site_sitemap(request: Request, slug: str):
with get_conn() as conn:
site = _resolve_site(conn, slug=slug)
if not site or _site_expired(site) or site.get("password_hash"):
@@ -556,7 +556,7 @@ async def site_sitemap(request: Request, slug: str):
@router.get("/s/{slug}/{page_ref}", response_class=HTMLResponse)
async def public_site_page(request: Request, slug: str, page_ref: str):
def public_site_page(request: Request, slug: str, page_ref: str):
with get_conn() as conn:
site = _resolve_site(conn, slug=slug, host=request.headers.get("host", ""))
if not site:
@@ -614,7 +614,7 @@ async def public_site_auth(request: Request, slug: str):
# ── Public Forms ───────────────────────────────────────────────────────────
@router.get("/api/v2/collections/{collection_id}/form")
async def get_form_config(collection_id: int, request: Request):
def get_form_config(collection_id: int, request: Request):
_auth_user(request)
with get_conn() as conn:
info = _form_config(conn, collection_id)
@@ -662,7 +662,7 @@ def _collection_props(conn, collection_id: int) -> list[dict]:
@router.get("/f/{token}", response_class=HTMLResponse)
async def public_form(request: Request, token: str):
def public_form(request: Request, token: str):
embed = request.query_params.get("embed") == "1"
with get_conn() as conn:
row = conn.execute("SELECT * FROM collections").fetchone()