auto-sync per-repo webhooks on startup + lifespan fix

This commit is contained in:
Fam Zheng 2026-04-07 10:13:04 +01:00
parent 12b411a8b5
commit 9c9e634d65
2 changed files with 73 additions and 1 deletions

View File

@ -114,6 +114,47 @@ class GiteaClient:
return downloaded
async def list_user_repos(self, owner: str) -> list[dict]:
repos = []
page = 1
async with httpx.AsyncClient() as c:
while True:
r = await c.get(
f"{self.base}/users/{owner}/repos?page={page}&limit=50",
headers=self.headers,
)
r.raise_for_status()
batch = r.json()
if not batch:
break
repos.extend(batch)
page += 1
return repos
async def ensure_webhook(self, owner: str, repo: str, webhook_url: str):
"""Ensure a webhook exists for this repo pointing to webhook_url."""
async with httpx.AsyncClient() as c:
r = await c.get(
f"{self.base}/repos/{owner}/{repo}/hooks",
headers=self.headers,
)
if r.status_code == 200:
for h in r.json():
if h.get("config", {}).get("url") == webhook_url:
return False # already exists
r = await c.post(
f"{self.base}/repos/{owner}/{repo}/hooks",
headers=self.headers,
json={
"type": "gitea",
"active": True,
"events": ["issues", "issue_comment", "pull_request", "pull_request_comment"],
"config": {"url": webhook_url, "content_type": "json", "secret": ""},
},
)
r.raise_for_status()
return True # created
async def _download_inline_images(self, texts: list[str], dest_dir: Path) -> list[dict]:
downloaded = []
seen = set()

33
main.py
View File

@ -4,6 +4,8 @@ import hmac
import logging
import os
from contextlib import asynccontextmanager
from fastapi import FastAPI, Request, HTTPException
import config
@ -13,12 +15,14 @@ from workspace import ensure_repo, repo_path, attachments_path
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
log = logging.getLogger("gitea-bot")
app = FastAPI()
app = FastAPI(lifespan=lifespan)
gitea = GiteaClient()
ALLOWED_OWNER = "fam"
MAX_RESPONSE_LEN = 60000
CLAUDE_TIMEOUT = 600 # 10 minutes
WEBHOOK_SYNC_INTERVAL = 300 # 5 minutes
WEBHOOK_URL = "http://100.65.168.42:9880/webhook"
def verify_signature(payload: bytes, signature: str) -> bool:
@ -251,6 +255,33 @@ async def webhook(request: Request):
return {"status": "skip", "reason": f"unhandled {event}/{action}"}
async def sync_webhooks():
"""Ensure all repos under ALLOWED_OWNER have our webhook."""
try:
repos = await gitea.list_user_repos(ALLOWED_OWNER)
for r in repos:
name = r["name"]
created = await gitea.ensure_webhook(ALLOWED_OWNER, name, WEBHOOK_URL)
if created:
log.info(f"Added webhook to {ALLOWED_OWNER}/{name}")
except Exception:
log.exception("Failed to sync webhooks")
async def webhook_sync_loop():
"""Periodically sync webhooks for new repos."""
while True:
await sync_webhooks()
await asyncio.sleep(WEBHOOK_SYNC_INTERVAL)
@asynccontextmanager
async def lifespan(app):
task = asyncio.create_task(webhook_sync_loop())
yield
task.cancel()
@app.get("/health")
async def health():
return {"status": "ok"}