diff --git a/app/main.py b/app/main.py index 369c577..4c15c92 100644 --- a/app/main.py +++ b/app/main.py @@ -344,3 +344,65 @@ def update_user(user_id: int, db: Session = Depends(get_db), full_name: str = No db.commit() db.refresh(user) return user + + + +# ============ Webhooks API ============ + +from app.models.webhook import Webhook, WebhookLog +from app.schemas.webhook import WebhookCreate, WebhookUpdate, WebhookResponse, WebhookLogResponse + + +@app.post("/webhooks", response_model=WebhookResponse, status_code=status.HTTP_201_CREATED) +def create_webhook(wh: WebhookCreate, db: Session = Depends(get_db)): + db_wh = Webhook(**wh.model_dump()) + db.add(db_wh) + db.commit() + db.refresh(db_wh) + return db_wh + + +@app.get("/webhooks", response_model=List[WebhookResponse]) +def list_webhooks(project_id: int = None, db: Session = Depends(get_db)): + query = db.query(Webhook) + if project_id is not None: + query = query.filter(Webhook.project_id == project_id) + return query.all() + + +@app.get("/webhooks/{webhook_id}", response_model=WebhookResponse) +def get_webhook(webhook_id: int, db: Session = Depends(get_db)): + wh = db.query(Webhook).filter(Webhook.id == webhook_id).first() + if not wh: + raise HTTPException(status_code=404, detail="Webhook not found") + return wh + + +@app.patch("/webhooks/{webhook_id}", response_model=WebhookResponse) +def update_webhook(webhook_id: int, wh_update: WebhookUpdate, db: Session = Depends(get_db)): + wh = db.query(Webhook).filter(Webhook.id == webhook_id).first() + if not wh: + raise HTTPException(status_code=404, detail="Webhook not found") + for field, value in wh_update.model_dump(exclude_unset=True).items(): + setattr(wh, field, value) + db.commit() + db.refresh(wh) + return wh + + +@app.delete("/webhooks/{webhook_id}", status_code=status.HTTP_204_NO_CONTENT) +def delete_webhook(webhook_id: int, db: Session = Depends(get_db)): + wh = db.query(Webhook).filter(Webhook.id == webhook_id).first() + if not wh: + raise HTTPException(status_code=404, detail="Webhook not found") + db.delete(wh) + db.commit() + return None + + +@app.get("/webhooks/{webhook_id}/logs", response_model=List[WebhookLogResponse]) +def list_webhook_logs(webhook_id: int, limit: int = 50, db: Session = Depends(get_db)): + logs = db.query(WebhookLog).filter( + WebhookLog.webhook_id == webhook_id + ).order_by(WebhookLog.created_at.desc()).limit(limit).all() + return logs diff --git a/app/schemas/webhook.py b/app/schemas/webhook.py new file mode 100644 index 0000000..9318cfe --- /dev/null +++ b/app/schemas/webhook.py @@ -0,0 +1,43 @@ +from pydantic import BaseModel +from typing import Optional +from datetime import datetime + + +class WebhookCreate(BaseModel): + url: str + secret: Optional[str] = None + events: str # comma-separated: "issue.created,issue.updated" + project_id: Optional[int] = None + is_active: bool = True + + +class WebhookUpdate(BaseModel): + url: Optional[str] = None + secret: Optional[str] = None + events: Optional[str] = None + is_active: Optional[bool] = None + + +class WebhookResponse(BaseModel): + id: int + url: str + events: str + project_id: Optional[int] + is_active: bool + created_at: datetime + + class Config: + from_attributes = True + + +class WebhookLogResponse(BaseModel): + id: int + webhook_id: int + event: str + payload: str + response_status: Optional[int] + success: bool + created_at: datetime + + class Config: + from_attributes = True