新增全渠道网关的 API 路由层和网关端点。 主要变更: - channel_router.py: 渠道管理 API 路由(CRUD、配置、绑定等) - gateway_sse_router.py: SSE 网关端点 - gateway_ws_router.py: WebSocket 网关端点 - webhook_router.py: Webhook 回调路由 - auth_router.py: 认证路由更新 - routers/__init__.py: 路由注册更新 - main.py: 主入口更新 - lifespan.py: 应用生命周期更新,渠道扩展加载与卸载
57 lines
1.5 KiB
Python
57 lines
1.5 KiB
Python
import logging
|
|
|
|
from fastapi import APIRouter, Request, Response
|
|
|
|
from yuxi.channel.gateway.routes import webhook_registry
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
router = APIRouter(prefix="/webhook", tags=["webhook"])
|
|
|
|
|
|
def _extract_signature(headers: dict[str, str]) -> str | None:
|
|
sig = headers.get("x-hub-signature-256") or headers.get("x-signature")
|
|
if sig and sig.startswith("sha256="):
|
|
sig = sig[7:]
|
|
return sig
|
|
|
|
|
|
@router.api_route(
|
|
"/{channel_type}",
|
|
methods=["POST", "PUT", "PATCH"],
|
|
summary="接收渠道 Webhook 回调",
|
|
)
|
|
async def webhook_receive(
|
|
channel_type: str,
|
|
request: Request,
|
|
):
|
|
body = await request.body()
|
|
method = request.method
|
|
content_type = request.headers.get("content-type")
|
|
headers = {k.lower(): v for k, v in request.headers.items()}
|
|
|
|
guard_result, handler_result = await webhook_registry.dispatch_guarded(
|
|
channel_type=channel_type,
|
|
method=method,
|
|
content_type=content_type,
|
|
body=body,
|
|
signature=_extract_signature(headers),
|
|
extra_headers=headers,
|
|
)
|
|
|
|
if guard_result is not None:
|
|
logger.warning(
|
|
"Webhook guard blocked: channel=%s method=%s status=%d error=%s",
|
|
channel_type,
|
|
method,
|
|
guard_result.http_status,
|
|
guard_result.error_code,
|
|
)
|
|
return Response(
|
|
content=guard_result.error_message or "",
|
|
status_code=guard_result.http_status,
|
|
media_type="text/plain",
|
|
)
|
|
|
|
return handler_result or {"status": "ok"}
|