"""Gitea webhook handler""" import hmac import hashlib from fastapi import HTTPException from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy import select from app.models import Repository, PullRequest, Review from app.models.pull_request import PRStatusEnum from app.models.review import ReviewStatusEnum from app.schemas.webhook import GiteaWebhook def verify_gitea_signature(payload: bytes, signature: str, secret: str) -> bool: """Verify Gitea webhook signature""" if not signature: return False expected_signature = hmac.new( secret.encode(), payload, hashlib.sha256 ).hexdigest() return hmac.compare_digest(signature, expected_signature) async def handle_gitea_webhook( webhook_data: GiteaWebhook, signature: str, raw_payload: bytes, db: AsyncSession ) -> dict: """Handle Gitea webhook""" # Find repository by URL repo_url = webhook_data.repository.get("html_url", "") result = await db.execute( select(Repository).where(Repository.url == repo_url) ) repository = result.scalar_one_or_none() if not repository: raise HTTPException(status_code=404, detail="Repository not found") # Verify signature if not verify_gitea_signature(raw_payload, signature, repository.webhook_secret): raise HTTPException(status_code=403, detail="Invalid signature") # Check if repository is active if not repository.is_active: return {"message": "Repository is not active"} # Handle PR events if webhook_data.action in ["opened", "synchronized", "reopened"]: # Create or update PR result = await db.execute( select(PullRequest).where( PullRequest.repository_id == repository.id, PullRequest.pr_number == webhook_data.number ) ) pr = result.scalar_one_or_none() if not pr: pr = PullRequest( repository_id=repository.id, pr_number=webhook_data.number, title=webhook_data.pull_request.title, author=webhook_data.pull_request.user.get("login", ""), source_branch=webhook_data.pull_request.head.get("ref", ""), target_branch=webhook_data.pull_request.base.get("ref", ""), url=webhook_data.pull_request.html_url, status=PRStatusEnum.OPEN ) db.add(pr) await db.commit() await db.refresh(pr) else: pr.title = webhook_data.pull_request.title pr.status = PRStatusEnum.OPEN await db.commit() # Create review review = Review( pull_request_id=pr.id, status=ReviewStatusEnum.PENDING ) db.add(review) await db.commit() await db.refresh(review) return { "message": "Review created", "review_id": review.id, "pr_id": pr.id } elif webhook_data.action == "closed": # Mark PR as closed result = await db.execute( select(PullRequest).where( PullRequest.repository_id == repository.id, PullRequest.pr_number == webhook_data.number ) ) pr = result.scalar_one_or_none() if pr: pr.status = PRStatusEnum.CLOSED await db.commit() return {"message": "PR closed"} return {"message": "Event not handled"}