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
+60 -60
View File
@@ -93,7 +93,7 @@ async def create_token(request: Request, authorization: str | None = Header(defa
return data
@router.get("/tokens")
async def list_tokens(request: Request, authorization: str | None = Header(default=None)):
def list_tokens(request: Request, authorization: str | None = Header(default=None)):
user = get_bearer_user(request, authorization)
_v2_rate_check(request, user)
with get_conn() as conn:
@@ -109,7 +109,7 @@ async def list_tokens(request: Request, authorization: str | None = Header(defau
return {"tokens": out}
@router.delete("/tokens/{token_id}")
async def revoke_token(token_id: int, request: Request, authorization: str | None = Header(default=None)):
def revoke_token(token_id: int, request: Request, authorization: str | None = Header(default=None)):
user = require_scope("write")(request, authorization)
_v2_rate_check(request, user)
with get_conn() as conn:
@@ -124,7 +124,7 @@ async def revoke_token(token_id: int, request: Request, authorization: str | Non
return {"id": token_id, "status": "revoked"}
@router.post("/tokens/{token_id}/rotate")
async def rotate_token(token_id: int, request: Request, authorization: str | None = Header(default=None)):
def rotate_token(token_id: int, request: Request, authorization: str | None = Header(default=None)):
user = require_scope("write")(request, authorization)
_v2_rate_check(request, user)
with get_conn() as conn:
@@ -147,7 +147,7 @@ async def rotate_token(token_id: int, request: Request, authorization: str | Non
# ── Users ─────────────────────────────────────────────────────────────────
@router.get("/users/me")
async def get_me(request: Request, authorization: str | None = Header(default=None)):
def get_me(request: Request, authorization: str | None = Header(default=None)):
user = get_bearer_user(request, authorization)
_v2_rate_check(request, user)
with get_conn() as conn:
@@ -199,7 +199,7 @@ async def patch_me(request: Request, authorization: str | None = Header(default=
return row_to_dict(row)
@router.get("/users/search")
async def search_users(request: Request, q: str = "", authorization: str | None = Header(default=None)):
def search_users(request: Request, q: str = "", authorization: str | None = Header(default=None)):
user = get_bearer_user(request, authorization)
_v2_rate_check(request, user)
q = (q or request.query_params.get("q") or "").strip()
@@ -213,7 +213,7 @@ async def search_users(request: Request, q: str = "", authorization: str | None
# ── Workspaces ────────────────────────────────────────────────────────────
@router.get("/workspaces")
async def list_workspaces(request: Request, authorization: str | None = Header(default=None)):
def list_workspaces(request: Request, authorization: str | None = Header(default=None)):
user = get_bearer_user(request, authorization)
_v2_rate_check(request, user)
limit, offset = parse_pagination(request)
@@ -264,7 +264,7 @@ async def create_workspace(request: Request, authorization: str | None = Header(
return JSONResponse(content=data, status_code=201)
@router.get("/workspaces/{workspace_id}")
async def get_workspace(workspace_id: int, request: Request, authorization: str | None = Header(default=None)):
def get_workspace(workspace_id: int, request: Request, authorization: str | None = Header(default=None)):
user = get_bearer_user(request, authorization)
_v2_rate_check(request, user)
with get_conn() as conn:
@@ -310,7 +310,7 @@ async def patch_workspace(workspace_id: int, request: Request, authorization: st
return {"id": workspace_id, "status": "updated"}
@router.delete("/workspaces/{workspace_id}")
async def delete_workspace(workspace_id: int, request: Request, authorization: str | None = Header(default=None)):
def delete_workspace(workspace_id: int, request: Request, authorization: str | None = Header(default=None)):
user = require_scope("write")(request, authorization)
_v2_rate_check(request, user)
with get_conn() as conn:
@@ -325,7 +325,7 @@ async def delete_workspace(workspace_id: int, request: Request, authorization: s
return {"id": workspace_id, "status": "deleted"}
@router.get("/workspaces/{workspace_id}/members")
async def list_workspace_members(workspace_id: int, request: Request, authorization: str | None = Header(default=None)):
def list_workspace_members(workspace_id: int, request: Request, authorization: str | None = Header(default=None)):
user = get_bearer_user(request, authorization)
_v2_rate_check(request, user)
with get_conn() as conn:
@@ -400,7 +400,7 @@ async def update_member_role(workspace_id: int, uid: int, request: Request, auth
return {"workspace_id": workspace_id, "user_id": uid, "role": role, "status": "updated"}
@router.delete("/workspaces/{workspace_id}/members/{uid}")
async def remove_member(workspace_id: int, uid: int, request: Request, authorization: str | None = Header(default=None)):
def remove_member(workspace_id: int, uid: int, request: Request, authorization: str | None = Header(default=None)):
user = require_scope("write")(request, authorization)
_v2_rate_check(request, user)
with get_conn() as conn:
@@ -419,7 +419,7 @@ async def remove_member(workspace_id: int, uid: int, request: Request, authoriza
# ── Collections ───────────────────────────────────────────────────────────
@router.get("/collections")
async def list_collections_v2(request: Request, authorization: str | None = Header(default=None)):
def list_collections_v2(request: Request, authorization: str | None = Header(default=None)):
user = get_bearer_user(request, authorization)
_v2_rate_check(request, user)
limit, offset = parse_pagination(request)
@@ -494,7 +494,7 @@ async def create_collection_v2(request: Request, authorization: str | None = Hea
return JSONResponse(content=data, status_code=201)
@router.get("/collections/{collection_id}")
async def get_collection_v2(collection_id: int, request: Request, authorization: str | None = Header(default=None)):
def get_collection_v2(collection_id: int, request: Request, authorization: str | None = Header(default=None)):
user = get_bearer_user(request, authorization)
_v2_rate_check(request, user)
with get_conn() as conn:
@@ -532,7 +532,7 @@ async def patch_collection_v2(collection_id: int, request: Request, authorizatio
return {"id": collection_id, "status": "updated"}
@router.delete("/collections/{collection_id}")
async def delete_collection_v2(collection_id: int, request: Request, authorization: str | None = Header(default=None)):
def delete_collection_v2(collection_id: int, request: Request, authorization: str | None = Header(default=None)):
user = require_scope("write")(request, authorization)
_v2_rate_check(request, user)
with get_conn() as conn:
@@ -582,7 +582,7 @@ async def create_linked_db(collection_id: int, request: Request, authorization:
return {"id": nid, "name": name, "status": "created"}
@router.post("/collections/{collection_id}/task")
async def toggle_task(collection_id: int, request: Request, authorization: str | None = Header(default=None)):
def toggle_task(collection_id: int, request: Request, authorization: str | None = Header(default=None)):
user = require_scope("write")(request, authorization)
_v2_rate_check(request, user)
with get_conn() as conn:
@@ -597,7 +597,7 @@ async def toggle_task(collection_id: int, request: Request, authorization: str |
return {"id": collection_id, "is_task": bool(new_val)}
@router.get("/collections/{collection_id}/sources")
async def list_sources(collection_id: int, request: Request, authorization: str | None = Header(default=None)):
def list_sources(collection_id: int, request: Request, authorization: str | None = Header(default=None)):
user = get_bearer_user(request, authorization)
_v2_rate_check(request, user)
with get_conn() as conn:
@@ -631,7 +631,7 @@ async def add_source(collection_id: int, request: Request, authorization: str |
return {"collection_id": collection_id, "source_collection_id": src_id, "status": "added"}
@router.delete("/collections/{collection_id}/sources/{source_id}")
async def remove_source(collection_id: int, source_id: int, request: Request, authorization: str | None = Header(default=None)):
def remove_source(collection_id: int, source_id: int, request: Request, authorization: str | None = Header(default=None)):
user = require_scope("write")(request, authorization)
_v2_rate_check(request, user)
with get_conn() as conn:
@@ -643,7 +643,7 @@ async def remove_source(collection_id: int, source_id: int, request: Request, au
# ── Collection pages ──────────────────────────────────────────────────────
@router.get("/collections/{collection_id}/pages")
async def list_collection_pages_v2(collection_id: int, request: Request, authorization: str | None = Header(default=None)):
def list_collection_pages_v2(collection_id: int, request: Request, authorization: str | None = Header(default=None)):
user = get_bearer_user(request, authorization)
_v2_rate_check(request, user)
limit, offset = parse_pagination(request)
@@ -753,7 +753,7 @@ async def create_collection_page_v2(collection_id: int, request: Request, author
return JSONResponse(content=data, status_code=201)
@router.get("/pages/{page_id}")
async def get_page_v2(page_id: int, request: Request, authorization: str | None = Header(default=None)):
def get_page_v2(page_id: int, request: Request, authorization: str | None = Header(default=None)):
user = get_bearer_user(request, authorization)
_v2_rate_check(request, user)
with get_conn() as conn:
@@ -881,7 +881,7 @@ async def move_page_v2(page_id: int, request: Request, authorization: str | None
return {"id": page_id, "status": "moved"}
@router.get("/pages/{page_id}/sub-items")
async def list_sub_items_v2(page_id: int, request: Request, authorization: str | None = Header(default=None)):
def list_sub_items_v2(page_id: int, request: Request, authorization: str | None = Header(default=None)):
user = get_bearer_user(request, authorization)
_v2_rate_check(request, user)
with get_conn() as conn:
@@ -912,7 +912,7 @@ async def create_sub_item_v2(page_id: int, request: Request, authorization: str
return {"id": nid, "status": "created"}
@router.get("/pages/{page_id}/dependencies")
async def list_dependencies_v2(page_id: int, request: Request, authorization: str | None = Header(default=None)):
def list_dependencies_v2(page_id: int, request: Request, authorization: str | None = Header(default=None)):
user = get_bearer_user(request, authorization)
_v2_rate_check(request, user)
with get_conn() as conn:
@@ -945,7 +945,7 @@ async def add_dependency_v2(page_id: int, request: Request, authorization: str |
return {"status": "added"}
@router.delete("/pages/{page_id}/dependencies/{dep_id}")
async def remove_dependency_v2(page_id: int, dep_id: int, request: Request, authorization: str | None = Header(default=None)):
def remove_dependency_v2(page_id: int, dep_id: int, request: Request, authorization: str | None = Header(default=None)):
user = require_scope("write")(request, authorization)
_v2_rate_check(request, user)
with get_conn() as conn:
@@ -957,7 +957,7 @@ async def remove_dependency_v2(page_id: int, dep_id: int, request: Request, auth
# ── Properties ────────────────────────────────────────────────────────────
@router.get("/collections/{collection_id}/properties")
async def list_properties_v2(collection_id: int, request: Request, authorization: str | None = Header(default=None)):
def list_properties_v2(collection_id: int, request: Request, authorization: str | None = Header(default=None)):
user = get_bearer_user(request, authorization)
_v2_rate_check(request, user)
with get_conn() as conn:
@@ -1016,7 +1016,7 @@ async def patch_property_v2(prop_id: int, request: Request, authorization: str |
return {"id": prop_id, "status": "updated"}
@router.delete("/properties/{prop_id}")
async def delete_property_v2(prop_id: int, request: Request, authorization: str | None = Header(default=None)):
def delete_property_v2(prop_id: int, request: Request, authorization: str | None = Header(default=None)):
user = require_scope("write")(request, authorization)
_v2_rate_check(request, user)
with get_conn() as conn:
@@ -1093,7 +1093,7 @@ async def compute_rollup_v2(request: Request, authorization: str | None = Header
# ── Views & dashboards ────────────────────────────────────────────────────
@router.get("/collections/{collection_id}/views")
async def list_views_v2(collection_id: int, request: Request, authorization: str | None = Header(default=None)):
def list_views_v2(collection_id: int, request: Request, authorization: str | None = Header(default=None)):
user = get_bearer_user(request, authorization)
_v2_rate_check(request, user)
with get_conn() as conn:
@@ -1157,7 +1157,7 @@ async def patch_view_v2(view_id: int, request: Request, authorization: str | Non
return {"id": view_id, "status": "updated"}
@router.delete("/views/{view_id}")
async def delete_view_v2(view_id: int, request: Request, authorization: str | None = Header(default=None)):
def delete_view_v2(view_id: int, request: Request, authorization: str | None = Header(default=None)):
user = require_scope("write")(request, authorization)
_v2_rate_check(request, user)
with get_conn() as conn:
@@ -1188,7 +1188,7 @@ async def save_as_view_v2(view_id: int, request: Request, authorization: str | N
return {"id": nid, "name": name, "status": "created"}
@router.get("/collections/{collection_id}/dashboards")
async def list_dashboards_v2(collection_id: int, request: Request, authorization: str | None = Header(default=None)):
def list_dashboards_v2(collection_id: int, request: Request, authorization: str | None = Header(default=None)):
user = get_bearer_user(request, authorization)
_v2_rate_check(request, user)
with get_conn() as conn:
@@ -1234,7 +1234,7 @@ async def patch_dashboard_v2(dashboard_id: int, request: Request, authorization:
return {"id": dashboard_id, "status": "updated"}
@router.delete("/dashboards/{dashboard_id}")
async def delete_dashboard_v2(dashboard_id: int, request: Request, authorization: str | None = Header(default=None)):
def delete_dashboard_v2(dashboard_id: int, request: Request, authorization: str | None = Header(default=None)):
user = require_scope("write")(request, authorization)
_v2_rate_check(request, user)
with get_conn() as conn:
@@ -1245,7 +1245,7 @@ async def delete_dashboard_v2(dashboard_id: int, request: Request, authorization
# ── Comments & mentions ───────────────────────────────────────────────────
@router.get("/pages/{page_id}/comments")
async def list_comments_v2(page_id: int, request: Request, authorization: str | None = Header(default=None)):
def list_comments_v2(page_id: int, request: Request, authorization: str | None = Header(default=None)):
user = get_bearer_user(request, authorization)
_v2_rate_check(request, user)
limit, offset = parse_pagination(request)
@@ -1304,7 +1304,7 @@ async def patch_comment_v2(comment_id: int, request: Request, authorization: str
return {"id": comment_id, "status": "updated"}
@router.delete("/comments/{comment_id}")
async def delete_comment_v2(comment_id: int, request: Request, authorization: str | None = Header(default=None)):
def delete_comment_v2(comment_id: int, request: Request, authorization: str | None = Header(default=None)):
user = require_scope("write")(request, authorization)
_v2_rate_check(request, user)
with get_conn() as conn:
@@ -1349,7 +1349,7 @@ async def create_mention_v2(page_id: int, request: Request, authorization: str |
# ── Notifications ─────────────────────────────────────────────────────────
@router.get("/notifications")
async def list_notifications_v2(request: Request, authorization: str | None = Header(default=None)):
def list_notifications_v2(request: Request, authorization: str | None = Header(default=None)):
user = get_bearer_user(request, authorization)
_v2_rate_check(request, user)
limit, offset = parse_pagination(request)
@@ -1364,7 +1364,7 @@ async def list_notifications_v2(request: Request, authorization: str | None = He
return {"notifications": [row_to_dict(r) for r in rows], "total": total, "limit": limit, "offset": offset}
@router.get("/notifications/unread-count")
async def unread_count(request: Request, authorization: str | None = Header(default=None)):
def unread_count(request: Request, authorization: str | None = Header(default=None)):
user = get_bearer_user(request, authorization)
_v2_rate_check(request, user)
with get_conn() as conn:
@@ -1372,7 +1372,7 @@ async def unread_count(request: Request, authorization: str | None = Header(defa
return {"unread": cnt}
@router.post("/notifications/{notif_id}/read")
async def mark_read(notif_id: int, request: Request, authorization: str | None = Header(default=None)):
def mark_read(notif_id: int, request: Request, authorization: str | None = Header(default=None)):
user = get_bearer_user(request, authorization)
_v2_rate_check(request, user)
with get_conn() as conn:
@@ -1381,7 +1381,7 @@ async def mark_read(notif_id: int, request: Request, authorization: str | None =
return {"id": notif_id, "status": "read"}
@router.post("/notifications/read-all")
async def mark_all_read(request: Request, authorization: str | None = Header(default=None)):
def mark_all_read(request: Request, authorization: str | None = Header(default=None)):
user = get_bearer_user(request, authorization)
_v2_rate_check(request, user)
with get_conn() as conn:
@@ -1411,7 +1411,7 @@ async def patch_prefs(request: Request, authorization: str | None = Header(defau
# ── Favorites / Tags / Recents ────────────────────────────────────────────
@router.get("/favorites")
async def list_favorites_v2(request: Request, authorization: str | None = Header(default=None)):
def list_favorites_v2(request: Request, authorization: str | None = Header(default=None)):
user = get_bearer_user(request, authorization)
_v2_rate_check(request, user)
with get_conn() as conn:
@@ -1455,7 +1455,7 @@ async def remove_favorite_v2(page_id: int, request: Request, authorization: str
return {"page_id": page_id, "status": "removed"}
@router.get("/tags")
async def list_tags_v2(request: Request, authorization: str | None = Header(default=None)):
def list_tags_v2(request: Request, authorization: str | None = Header(default=None)):
user = get_bearer_user(request, authorization)
_v2_rate_check(request, user)
q = (request.query_params.get("q") or "").strip()
@@ -1506,7 +1506,7 @@ async def patch_tag_v2(tag_id: int, request: Request, authorization: str | None
return {"id": tag_id, "status": "updated"}
@router.delete("/tags/{tag_id}")
async def delete_tag_v2(tag_id: int, request: Request, authorization: str | None = Header(default=None)):
def delete_tag_v2(tag_id: int, request: Request, authorization: str | None = Header(default=None)):
user = require_scope("write")(request, authorization)
_v2_rate_check(request, user)
with get_conn() as conn:
@@ -1534,7 +1534,7 @@ async def attach_tag_v2(page_id: int, request: Request, authorization: str | Non
return {"page_id": page_id, "tag_id": tag_id, "status": "attached"}
@router.delete("/pages/{page_id}/tags/{tag_id}")
async def detach_tag_v2(page_id: int, tag_id: int, request: Request, authorization: str | None = Header(default=None)):
def detach_tag_v2(page_id: int, tag_id: int, request: Request, authorization: str | None = Header(default=None)):
user = require_scope("write")(request, authorization)
_v2_rate_check(request, user)
with get_conn() as conn:
@@ -1543,7 +1543,7 @@ async def detach_tag_v2(page_id: int, tag_id: int, request: Request, authorizati
return {"status": "detached"}
@router.get("/recents")
async def list_recents_v2(request: Request, authorization: str | None = Header(default=None)):
def list_recents_v2(request: Request, authorization: str | None = Header(default=None)):
user = get_bearer_user(request, authorization)
_v2_rate_check(request, user)
limit = int(request.query_params.get("limit", "20"))
@@ -1558,7 +1558,7 @@ async def list_recents_v2(request: Request, authorization: str | None = Header(d
# ── Sharing / publish ─────────────────────────────────────────────────────
@router.get("/pages/{page_id}/shares")
async def list_shares_v2(page_id: int, request: Request, authorization: str | None = Header(default=None)):
def list_shares_v2(page_id: int, request: Request, authorization: str | None = Header(default=None)):
user = get_bearer_user(request, authorization)
_v2_rate_check(request, user)
with get_conn() as conn:
@@ -1611,7 +1611,7 @@ async def patch_share_v2(share_id: int, request: Request, authorization: str | N
return {"id": share_id, "status": "updated"}
@router.delete("/shares/{share_id}")
async def delete_share_v2(share_id: int, request: Request, authorization: str | None = Header(default=None)):
def delete_share_v2(share_id: int, request: Request, authorization: str | None = Header(default=None)):
user = require_scope("write")(request, authorization)
_v2_rate_check(request, user)
with get_conn() as conn:
@@ -1651,7 +1651,7 @@ async def unpublish_page_v2(page_id: int, request: Request, authorization: str |
# ── History ───────────────────────────────────────────────────────────────
@router.get("/pages/{page_id}/history")
async def list_history_v2(page_id: int, request: Request, authorization: str | None = Header(default=None)):
def list_history_v2(page_id: int, request: Request, authorization: str | None = Header(default=None)):
user = get_bearer_user(request, authorization)
_v2_rate_check(request, user)
with get_conn() as conn:
@@ -1693,7 +1693,7 @@ async def restore_history_v2(page_id: int, request: Request, authorization: str
# ── Sprints ───────────────────────────────────────────────────────────────
@router.get("/collections/{collection_id}/sprints")
async def list_sprints_v2(collection_id: int, request: Request, authorization: str | None = Header(default=None)):
def list_sprints_v2(collection_id: int, request: Request, authorization: str | None = Header(default=None)):
user = get_bearer_user(request, authorization)
_v2_rate_check(request, user)
limit, offset = parse_pagination(request)
@@ -1751,7 +1751,7 @@ async def patch_sprint_v2(sprint_id: int, request: Request, authorization: str |
return {"id": sprint_id, "status": "updated"}
@router.delete("/sprints/{sprint_id}")
async def delete_sprint_v2(sprint_id: int, request: Request, authorization: str | None = Header(default=None)):
def delete_sprint_v2(sprint_id: int, request: Request, authorization: str | None = Header(default=None)):
user = require_scope("write")(request, authorization)
_v2_rate_check(request, user)
with get_conn() as conn:
@@ -1779,7 +1779,7 @@ async def assign_sprint_v2(sprint_id: int, request: Request, authorization: str
return {"sprint_id": sprint_id, "page_id": pid, "status": "assigned"}
@router.delete("/sprints/{sprint_id}/assign/{page_id}")
async def unassign_sprint_v2(sprint_id: int, page_id: int, request: Request, authorization: str | None = Header(default=None)):
def unassign_sprint_v2(sprint_id: int, page_id: int, request: Request, authorization: str | None = Header(default=None)):
user = require_scope("write")(request, authorization)
_v2_rate_check(request, user)
with get_conn() as conn:
@@ -1788,7 +1788,7 @@ async def unassign_sprint_v2(sprint_id: int, page_id: int, request: Request, aut
return {"status": "removed"}
@router.get("/sprints/{sprint_id}/burndown")
async def burndown_v2(sprint_id: int, request: Request, authorization: str | None = Header(default=None)):
def burndown_v2(sprint_id: int, request: Request, authorization: str | None = Header(default=None)):
user = get_bearer_user(request, authorization)
_v2_rate_check(request, user)
with get_conn() as conn:
@@ -1816,7 +1816,7 @@ async def burndown_v2(sprint_id: int, request: Request, authorization: str | Non
# ── Templates ─────────────────────────────────────────────────────────────
@router.get("/collections/{collection_id}/templates")
async def list_templates_v2(collection_id: int, request: Request, authorization: str | None = Header(default=None)):
def list_templates_v2(collection_id: int, request: Request, authorization: str | None = Header(default=None)):
user = get_bearer_user(request, authorization)
_v2_rate_check(request, user)
with get_conn() as conn:
@@ -1860,7 +1860,7 @@ async def patch_template_v2(template_id: int, request: Request, authorization: s
return {"id": template_id, "status": "updated"}
@router.delete("/templates/{template_id}")
async def delete_template_v2(template_id: int, request: Request, authorization: str | None = Header(default=None)):
def delete_template_v2(template_id: int, request: Request, authorization: str | None = Header(default=None)):
user = require_scope("write")(request, authorization)
_v2_rate_check(request, user)
with get_conn() as conn:
@@ -1869,7 +1869,7 @@ async def delete_template_v2(template_id: int, request: Request, authorization:
return {"id": template_id, "status": "deleted"}
@router.post("/templates/{template_id}/apply")
async def apply_template_v2(template_id: int, request: Request, authorization: str | None = Header(default=None)):
def apply_template_v2(template_id: int, request: Request, authorization: str | None = Header(default=None)):
user = require_scope("write")(request, authorization)
_v2_rate_check(request, user)
with get_conn() as conn:
@@ -1884,7 +1884,7 @@ async def apply_template_v2(template_id: int, request: Request, authorization: s
return {"template_id": template_id, "page_id": pid, "status": "applied"}
@router.get("/templates/database")
async def list_db_templates_v2(request: Request, authorization: str | None = Header(default=None)):
def list_db_templates_v2(request: Request, authorization: str | None = Header(default=None)):
user = get_bearer_user(request, authorization)
_v2_rate_check(request, user)
with get_conn() as conn:
@@ -1917,7 +1917,7 @@ async def apply_db_template_v2(template_id: int, request: Request, authorization
# ── Export / Import ───────────────────────────────────────────────────────
@router.get("/pages/{page_id}/export")
async def export_page_v2(page_id: int, request: Request, format: str = "markdown", authorization: str | None = Header(default=None)):
def export_page_v2(page_id: int, request: Request, format: str = "markdown", authorization: str | None = Header(default=None)):
user = get_bearer_user(request, authorization)
_v2_rate_check(request, user)
fmt = (format or request.query_params.get("format") or "markdown").lower()
@@ -1941,7 +1941,7 @@ async def export_page_v2(page_id: int, request: Request, format: str = "markdown
raise HTTPException(500, f"Export failed: {e}") from None
@router.get("/collections/{collection_id}/export/csv")
async def export_csv_v2(collection_id: int, request: Request, authorization: str | None = Header(default=None)):
def export_csv_v2(collection_id: int, request: Request, authorization: str | None = Header(default=None)):
user = get_bearer_user(request, authorization)
_v2_rate_check(request, user)
import csv
@@ -2003,7 +2003,7 @@ async def import_csv_v2(collection_id: int, request: Request, authorization: str
# ── Forges / Projects ─────────────────────────────────────────────────────
@router.get("/projects")
async def list_projects_v2(request: Request, authorization: str | None = Header(default=None)):
def list_projects_v2(request: Request, authorization: str | None = Header(default=None)):
user = get_bearer_user(request, authorization)
_v2_rate_check(request, user)
with get_conn() as conn:
@@ -2029,7 +2029,7 @@ async def project_tree_v2(owner: str, repo: str, request: Request, path: str = "
# ── Search (FTS5 + LIKE fallback) ─────────────────────────────────────────
@router.get("/search")
async def search_v2(request: Request, query: str = "", workspace_id: int | None = None, type: str = "all", authorization: str | None = Header(default=None)):
def search_v2(request: Request, query: str = "", workspace_id: int | None = None, type: str = "all", authorization: str | None = Header(default=None)):
user = get_bearer_user(request, authorization)
_v2_rate_check(request, user)
q = (query or request.query_params.get("query") or "").strip()
@@ -2053,7 +2053,7 @@ async def search_v2(request: Request, query: str = "", workspace_id: int | None
# ── Admin ─────────────────────────────────────────────────────────────────
@router.get("/admin/users")
async def admin_list_users_v2(request: Request, limit: int = 30, offset: int = 0, authorization: str | None = Header(default=None)):
def admin_list_users_v2(request: Request, limit: int = 30, offset: int = 0, authorization: str | None = Header(default=None)):
user = get_bearer_user(request, authorization)
_v2_rate_check(request, user)
if not user.get("is_admin") and not has_scope(user.get("_token_scopes"), "admin"):
@@ -2092,7 +2092,7 @@ async def admin_patch_user_v2(uid: int, request: Request, authorization: str | N
return {"id": uid, "status": "updated"}
@router.delete("/admin/users/{uid}")
async def admin_delete_user_v2(uid: int, request: Request, authorization: str | None = Header(default=None)):
def admin_delete_user_v2(uid: int, request: Request, authorization: str | None = Header(default=None)):
user = get_bearer_user(request, authorization)
_v2_rate_check(request, user)
if not user.get("is_admin") and not has_scope(user.get("_token_scopes"), "admin"):
@@ -2106,7 +2106,7 @@ async def admin_delete_user_v2(uid: int, request: Request, authorization: str |
return {"id": uid, "status": "deleted"}
@router.get("/admin/audit-logs")
async def admin_audit_logs_v2(request: Request, limit: int = 50, authorization: str | None = Header(default=None)):
def admin_audit_logs_v2(request: Request, limit: int = 50, authorization: str | None = Header(default=None)):
user = get_bearer_user(request, authorization)
_v2_rate_check(request, user)
if not user.get("is_admin") and not has_scope(user.get("_token_scopes"), "admin"):
@@ -2119,7 +2119,7 @@ async def admin_audit_logs_v2(request: Request, limit: int = 50, authorization:
# ── Webhooks (v6.4.0 prod) ────────────────────────────────────────────────
@router.get("/webhooks/events")
async def list_webhook_events_v2(request: Request, authorization: str | None = Header(default=None)):
def list_webhook_events_v2(request: Request, authorization: str | None = Header(default=None)):
"""Catalogue of deliverable events (+ wildcard syntax)."""
user = get_bearer_user(request, authorization)
_v2_rate_check(request, user)
@@ -2128,7 +2128,7 @@ async def list_webhook_events_v2(request: Request, authorization: str | None = H
@router.get("/webhooks")
async def list_webhooks_v2(request: Request, authorization: str | None = Header(default=None)):
def list_webhooks_v2(request: Request, authorization: str | None = Header(default=None)):
user = get_bearer_user(request, authorization)
_v2_rate_check(request, user)
limit, offset = parse_pagination(request)
@@ -2193,7 +2193,7 @@ async def patch_webhook_v2(webhook_id: int, request: Request, authorization: str
return {"id": webhook_id, "status": "updated"}
@router.delete("/webhooks/{webhook_id}")
async def delete_webhook_v2(webhook_id: int, request: Request, authorization: str | None = Header(default=None)):
def delete_webhook_v2(webhook_id: int, request: Request, authorization: str | None = Header(default=None)):
user = require_scope("write")(request, authorization)
_v2_rate_check(request, user)
with get_conn() as conn:
@@ -2220,7 +2220,7 @@ async def test_webhook_v2(webhook_id: int, request: Request, authorization: str
return {"webhook_id": webhook_id, "status": "tested", "delivered": ok}
@router.get("/webhooks/{webhook_id}/deliveries")
async def list_deliveries_v2(webhook_id: int, request: Request,
def list_deliveries_v2(webhook_id: int, request: Request,
status: str | None = None,
authorization: str | None = Header(default=None)):
user = get_bearer_user(request, authorization)