CodeReview/backend/app/api/v1/endpoints/webhooks.py

81 lines
3.0 KiB
Python
Raw Normal View History

from fastapi import APIRouter, Header, Request, HTTPException, Depends, BackgroundTasks
from sqlalchemy.ext.asyncio import AsyncSession
import hmac
import hashlib
import logging
import json
from app.api.deps import get_db
from app.db.session import AsyncSessionLocal
from app.core.config import settings
from app.services.ci_service import CIService
router = APIRouter()
logger = logging.getLogger(__name__)
async def process_gitea_event(event_type: str, payload: dict):
"""
Background task to process Gitea events without blocking the webhook response.
"""
async with AsyncSessionLocal() as db:
try:
ci_service = CIService(db)
if event_type == "pull_request":
action = payload.get("action")
if action in ["opened", "synchronize", "reopened"]:
logger.info(f"Starting background PR processing for action: {action}")
await ci_service.handle_pr_event(payload)
else:
logger.info(f"Ignoring PR Action in background: {action}")
elif event_type == "issue_comment":
logger.info(f"Starting background comment processing")
await ci_service.handle_comment_event(payload)
except Exception as e:
logger.error(f"Background Webhook Processing Error: {e}", exc_info=True)
@router.get("/health")
async def webhook_health():
return {"status": "ok", "module": "webhooks"}
@router.post("/gitea")
async def handle_gitea_webhook(
request: Request,
background_tasks: BackgroundTasks,
x_gitea_signature: str = Header(None),
x_gitea_event: str = Header(None),
):
"""
Handle incoming Gitea webhook events with background processing to prevent timeouts.
"""
# 1. Capture Raw Body for Signature Verification
payload_bytes = await request.body()
# 2. Verify Signature
if settings.GITEA_WEBHOOK_SECRET:
if not x_gitea_signature:
logger.warning("Missing Gitea Signature")
raise HTTPException(status_code=401, detail="Missing Signature")
computed_signature = hmac.new(
key=settings.GITEA_WEBHOOK_SECRET.encode(),
msg=payload_bytes,
digestmod=hashlib.sha256
).hexdigest()
if not hmac.compare_digest(computed_signature, x_gitea_signature):
logger.warning(f"Invalid Gitea Signature. Expected: {computed_signature}, Got: {x_gitea_signature}")
raise HTTPException(status_code=401, detail="Invalid Signature")
# 3. Parse Payload
try:
payload = json.loads(payload_bytes.decode())
except json.JSONDecodeError:
raise HTTPException(status_code=400, detail="Invalid JSON")
# 4. Enqueue Background Task
logger.info(f"Enqueuing Gitea Event for background processing: {x_gitea_event}")
background_tasks.add_task(process_gitea_event, x_gitea_event, payload)
# 5. Return Success Immediately
return {"status": "accepted", "event": x_gitea_event, "message": "Task enqueued"}