"""Webhook 管理 API(admin):/admin/api/webhooks - GET /admin/api/webhooks 列出全部 - POST /admin/api/webhooks 创建 - GET /admin/api/webhooks/{id} 获取 - PUT /admin/api/webhooks/{id} 更新 - DELETE /admin/api/webhooks/{id} 删除 - POST /admin/api/webhooks/{id}/test 发送测试事件 """ from __future__ import annotations from fastapi import APIRouter, Body, Depends from ..errors import HttpError from ..services import webhook_store from ._common import ok_with_cors from .admin import _require_admin router = APIRouter(prefix="/admin/api/webhooks", tags=["webhooks"]) @router.get("") async def list_wh(_admin: str = Depends(_require_admin)): return ok_with_cors({"items": webhook_store.list_webhooks()}) @router.post("") async def create_wh( body: dict = Body(default={}), _admin: str = Depends(_require_admin), ): name = body.get("name") url = body.get("url") events = body.get("events") or [] if not name or not url: raise HttpError("name and url are required", status=400, code="bad_request") if not isinstance(events, list): raise HttpError("events must be a list", status=400, code="bad_request") wh = webhook_store.create_webhook( name=name, url=url, events=events, enabled=bool(body.get("enabled", True)), ) return ok_with_cors({"webhook": wh}) @router.get("/{webhook_id}") async def get_wh(webhook_id: int, _admin: str = Depends(_require_admin)): wh = webhook_store.get_webhook(webhook_id) if not wh: raise HttpError(f"webhook not found: {webhook_id}", status=404, code="not_found") return ok_with_cors({"webhook": wh}) @router.put("/{webhook_id}") async def update_wh( webhook_id: int, body: dict = Body(default={}), _admin: str = Depends(_require_admin), ): events = body.get("events") if events is not None and not isinstance(events, list): raise HttpError("events must be a list", status=400, code="bad_request") wh = webhook_store.update_webhook( webhook_id, name=body.get("name"), url=body.get("url"), events=events, enabled=body.get("enabled"), ) if not wh: raise HttpError(f"webhook not found: {webhook_id}", status=404, code="not_found") return ok_with_cors({"webhook": wh}) @router.delete("/{webhook_id}") async def delete_wh(webhook_id: int, _admin: str = Depends(_require_admin)): ok = webhook_store.delete_webhook(webhook_id) return ok_with_cors({"deleted": ok, "id": webhook_id}) @router.post("/{webhook_id}/test") async def test_wh(webhook_id: int, _admin: str = Depends(_require_admin)): wh = webhook_store.get_webhook(webhook_id) if not wh: raise HttpError(f"webhook not found: {webhook_id}", status=404, code="not_found") await webhook_store.notify( "test.ping", {"webhook_id": webhook_id, "name": wh["name"], "message": "test from admin"}, ) return ok_with_cors({"sent": True, "id": webhook_id})