- POST /api/v1/webhooks/github endpoint - 處理 pull_request 和 push 事件 - 驗證 X-Hub-Signature-256 - Telegram 通知整合 - GitHubWebhookService 封裝 Redis 操作 (leWOOOgo 合規) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
122 lines
3.1 KiB
Python
122 lines
3.1 KiB
Python
"""
|
|
GitHub Webhook Service - Phase 13.1
|
|
====================================
|
|
封裝 GitHub Webhook 相關的 Redis 操作
|
|
|
|
遵循 leWOOOgo 積木化原則:
|
|
- Router 層不直接存取 Redis
|
|
- 透過 Service 層封裝資料存取邏輯
|
|
"""
|
|
|
|
import json
|
|
from typing import Protocol
|
|
|
|
import structlog
|
|
|
|
from src.core.redis_client import get_redis
|
|
|
|
logger = structlog.get_logger(__name__)
|
|
|
|
|
|
# Redis TTL: 7 天
|
|
GITHUB_REVIEW_TTL_SECONDS = 7 * 24 * 60 * 60
|
|
|
|
|
|
class IGitHubReviewRepository(Protocol):
|
|
"""GitHub Review Repository Interface"""
|
|
|
|
async def save_review(self, review_id: str, review_data: dict) -> bool:
|
|
"""儲存審查結果"""
|
|
...
|
|
|
|
async def get_review(self, review_id: str) -> dict | None:
|
|
"""取得審查結果"""
|
|
...
|
|
|
|
|
|
class GitHubReviewRedisRepository:
|
|
"""Redis 實作的 GitHub Review Repository"""
|
|
|
|
KEY_PREFIX = "github_review:"
|
|
|
|
async def save_review(self, review_id: str, review_data: dict) -> bool:
|
|
"""
|
|
儲存審查結果到 Redis
|
|
|
|
Args:
|
|
review_id: 審查 ID
|
|
review_data: 審查結果資料
|
|
|
|
Returns:
|
|
bool: 儲存是否成功
|
|
"""
|
|
try:
|
|
redis_client = get_redis()
|
|
key = f"{self.KEY_PREFIX}{review_id}"
|
|
await redis_client.set(
|
|
key,
|
|
json.dumps(review_data, ensure_ascii=False),
|
|
ex=GITHUB_REVIEW_TTL_SECONDS,
|
|
)
|
|
logger.debug("github_review_saved", review_id=review_id)
|
|
return True
|
|
except Exception as e:
|
|
logger.error("github_review_save_failed", review_id=review_id, error=str(e))
|
|
return False
|
|
|
|
async def get_review(self, review_id: str) -> dict | None:
|
|
"""
|
|
取得審查結果
|
|
|
|
Args:
|
|
review_id: 審查 ID
|
|
|
|
Returns:
|
|
審查結果資料,或 None
|
|
"""
|
|
try:
|
|
redis_client = get_redis()
|
|
key = f"{self.KEY_PREFIX}{review_id}"
|
|
result = await redis_client.get(key)
|
|
if result:
|
|
return json.loads(result)
|
|
return None
|
|
except Exception as e:
|
|
logger.error("github_review_get_failed", review_id=review_id, error=str(e))
|
|
return None
|
|
|
|
|
|
class GitHubWebhookService:
|
|
"""
|
|
GitHub Webhook 服務
|
|
|
|
封裝審查結果的儲存與查詢
|
|
"""
|
|
|
|
def __init__(self, repository: IGitHubReviewRepository | None = None):
|
|
self._repository = repository or GitHubReviewRedisRepository()
|
|
|
|
async def save_review_result(
|
|
self,
|
|
review_id: str,
|
|
review_data: dict,
|
|
) -> bool:
|
|
"""儲存審查結果"""
|
|
return await self._repository.save_review(review_id, review_data)
|
|
|
|
async def get_review_result(self, review_id: str) -> dict | None:
|
|
"""取得審查結果"""
|
|
return await self._repository.get_review(review_id)
|
|
|
|
|
|
# 單例
|
|
_service: GitHubWebhookService | None = None
|
|
|
|
|
|
def get_github_webhook_service() -> GitHubWebhookService:
|
|
"""取得 GitHubWebhookService 單例"""
|
|
global _service
|
|
if _service is None:
|
|
_service = GitHubWebhookService()
|
|
return _service
|