auto-sync per-repo webhooks on startup + lifespan fix
This commit is contained in:
parent
12b411a8b5
commit
9c9e634d65
41
gitea.py
41
gitea.py
@ -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
33
main.py
@ -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"}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user