6113a0039c
Backend:
- WebhookPayloadLog model (provider_id, method, headers, body, status, extracted_fields, error_message)
- Auto-log payloads in generic_webhook() with matched/unmatched/error status
- Auto-prune beyond max_stored_payloads per provider
- Header filtering (only Content-Type, User-Agent, X-* stored; no Authorization)
- GET/DELETE /api/providers/{id}/webhook-logs endpoints
- store_payloads + max_stored_payloads in WebhookProviderConfig
Frontend:
- WebhookPayloadHistory.svelte — expandable log viewer with status badges, JSON body, headers, extracted fields
- payloadHistory flag on webhook provider descriptor
- max_stored_payloads config field (0 = disabled)
- Password confirmation field on change password modal
- i18n keys for webhook logs (en + ru)
77 lines
2.3 KiB
Python
77 lines
2.3 KiB
Python
"""Webhook payload log API routes."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException
|
|
from sqlalchemy import delete as sa_delete
|
|
from sqlmodel import select
|
|
from sqlmodel.ext.asyncio.session import AsyncSession
|
|
|
|
from ..auth.dependencies import get_current_user
|
|
from ..database.engine import get_session
|
|
from ..database.models import ServiceProvider, User, WebhookPayloadLog
|
|
from .helpers import get_owned_entity
|
|
|
|
_LOGGER = logging.getLogger(__name__)
|
|
|
|
router = APIRouter(prefix="/api/providers", tags=["webhook-logs"])
|
|
|
|
|
|
@router.get("/{provider_id}/webhook-logs")
|
|
async def list_webhook_logs(
|
|
provider_id: int,
|
|
limit: int = 20,
|
|
offset: int = 0,
|
|
session: AsyncSession = Depends(get_session),
|
|
user: User = Depends(get_current_user),
|
|
):
|
|
"""List recent webhook payload logs for a provider."""
|
|
provider = await get_owned_entity(
|
|
session, ServiceProvider, provider_id, user.id,
|
|
not_found_msg="Provider not found",
|
|
)
|
|
if provider.type != "webhook":
|
|
raise HTTPException(status_code=400, detail="Not a webhook provider")
|
|
|
|
result = await session.exec(
|
|
select(WebhookPayloadLog)
|
|
.where(WebhookPayloadLog.provider_id == provider_id)
|
|
.order_by(WebhookPayloadLog.created_at.desc())
|
|
.offset(offset)
|
|
.limit(min(limit, 100))
|
|
)
|
|
return [
|
|
{
|
|
"id": log.id,
|
|
"provider_id": log.provider_id,
|
|
"method": log.method,
|
|
"headers": log.headers,
|
|
"body": log.body,
|
|
"status": log.status,
|
|
"extracted_fields": log.extracted_fields,
|
|
"error_message": log.error_message,
|
|
"created_at": log.created_at.isoformat() if log.created_at else None,
|
|
}
|
|
for log in result.all()
|
|
]
|
|
|
|
|
|
@router.delete("/{provider_id}/webhook-logs", status_code=204)
|
|
async def clear_webhook_logs(
|
|
provider_id: int,
|
|
session: AsyncSession = Depends(get_session),
|
|
user: User = Depends(get_current_user),
|
|
):
|
|
"""Clear all webhook payload logs for a provider."""
|
|
await get_owned_entity(
|
|
session, ServiceProvider, provider_id, user.id,
|
|
not_found_msg="Provider not found",
|
|
)
|
|
await session.execute(
|
|
sa_delete(WebhookPayloadLog)
|
|
.where(WebhookPayloadLog.provider_id == provider_id)
|
|
)
|
|
await session.commit()
|