Primakov Alexandr Alexandrovich 09cdd06307 init
2025-10-12 23:15:09 +03:00

117 lines
3.5 KiB
Python

"""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"}