"""Webhook configuration CRUD + test notification.""" from __future__ import annotations import json from datetime import datetime, timezone from typing import List from fastapi import APIRouter, Depends, HTTPException from sqlalchemy.orm import Session from app.database import get_db from app.models.webhook_config import WebhookConfig from app.schemas.webhook import WebhookCreate, WebhookUpdate, WebhookResponse from app.services.webhook_service import send_test_notification from app.utils.auth import get_current_user router = APIRouter(prefix="/api/webhooks", tags=["webhooks"]) MASK = "***" def _to_response(w: WebhookConfig) -> WebhookResponse: return WebhookResponse( id=w.id, name=w.name, type=w.type, url=w.url, secret_masked=MASK if w.secret else "", enabled=w.enabled, events=json.loads(w.events_json or "[]"), created_at=w.created_at, updated_at=w.updated_at, ) @router.get("", response_model=List[WebhookResponse]) def list_webhooks(db: Session = Depends(get_db), _=Depends(get_current_user)): return [_to_response(w) for w in db.query(WebhookConfig).order_by(WebhookConfig.id).all()] @router.post("", response_model=WebhookResponse, status_code=201) def create_webhook( body: WebhookCreate, db: Session = Depends(get_db), _=Depends(get_current_user), ): w = WebhookConfig( name=body.name, type=body.type, url=body.url, secret=body.secret, enabled=body.enabled, events_json=json.dumps(body.events, ensure_ascii=False), ) db.add(w) db.commit() db.refresh(w) return _to_response(w) @router.get("/{wid}", response_model=WebhookResponse) def get_webhook(wid: int, db: Session = Depends(get_db), _=Depends(get_current_user)): w = db.query(WebhookConfig).filter(WebhookConfig.id == wid).first() if not w: raise HTTPException(404, "webhook not found") return _to_response(w) @router.put("/{wid}", response_model=WebhookResponse) def update_webhook( wid: int, body: WebhookUpdate, db: Session = Depends(get_db), _=Depends(get_current_user), ): w = db.query(WebhookConfig).filter(WebhookConfig.id == wid).first() if not w: raise HTTPException(404, "webhook not found") data = body.model_dump(exclude_none=True) if "events" in data: w.events_json = json.dumps(data.pop("events"), ensure_ascii=False) if "secret" in data: if data["secret"] != MASK: # only update if not mask placeholder w.secret = data.pop("secret") else: data.pop("secret") for k, v in data.items(): setattr(w, k, v) w.updated_at = datetime.now(timezone.utc) db.commit() db.refresh(w) return _to_response(w) @router.delete("/{wid}", status_code=204) def delete_webhook(wid: int, db: Session = Depends(get_db), _=Depends(get_current_user)): w = db.query(WebhookConfig).filter(WebhookConfig.id == wid).first() if not w: raise HTTPException(404, "webhook not found") db.delete(w) db.commit() @router.post("/{wid}/test") def test_webhook(wid: int, db: Session = Depends(get_db), _=Depends(get_current_user)): w = db.query(WebhookConfig).filter(WebhookConfig.id == wid).first() if not w: raise HTTPException(404, "webhook not found") ok, msg = send_test_notification(db, w) return {"success": ok, "message": msg}