feat(forge): fondation multi-forges — connexions chiffrées, registre, client GitHub (P0/P1)

- Tables forge_connections/users/workspaces (+ membres, projets,
  états, audit) ; migration des anciennes bases avec portées
  (workspace_id, connection_id) par défaut (1, 1).
- Jetons chiffrés Fernet (KANBANTE_MASTER_KEY ou clé générée en
  base), jamais réaffichés.
- ForgeRegistry : connexion héritée créée depuis l'environnement,
  espace Principal, instantanés préfixés par connexion.
- GitHubClient complet (Bearer, pagination Link, étiquettes par
  nom, PATCH archived, branches via git/refs).
- Paramètres surchargeables en base (résolveur config, P3).
This commit is contained in:
Spark
2026-10-21 14:48:29 -04:00
parent f1a05279b6
commit 735d7cdfd0
11 changed files with 1403 additions and 79 deletions
+15 -10
View File
@@ -67,22 +67,27 @@ async def refresh_snapshots(gitea, classify_repos) -> int:
return len(active)
async def snapshot_loop(gitea, classify_repos) -> None:
async def snapshot_loop(clients_provider, classify_repos) -> None:
"""Periodic refresher, started by the app lifespan.
Sleeps briefly first: hydration already serves the pages,
the loop only has to keep the snapshots warm.
the loop only has to keep the snapshots warm. Since 0.24.0
`clients_provider` is a zero-arg callable returning the live
forge clients (one per global connection); each is refreshed
in turn. The interval is re-read every cycle so the admin
settings panel can change it without a restart.
"""
interval = get_settings().snapshot_interval
if interval <= 0:
logger.info("snapshot refresher disabled (interval <= 0)")
return
await asyncio.sleep(10)
while True:
try:
await refresh_snapshots(gitea, classify_repos)
except Exception as exc:
logger.warning("snapshot refresh cycle failed: %s", exc)
interval = get_settings().snapshot_interval
if interval <= 0:
logger.info("snapshot refresher disabled (interval <= 0)")
return
for client in clients_provider():
try:
await refresh_snapshots(client, classify_repos)
except Exception as exc:
logger.warning("snapshot refresh cycle failed: %s", exc)
await asyncio.sleep(interval)