"""Webhook management endpoints.""" import json from typing import Any, List from fastapi import APIRouter, Depends, HTTPException, status, BackgroundTasks from sqlalchemy.orm import Session from app.dependencies import get_db, get_current_superuser from app.models.user import User from app import crud from app.schemas.webhook import ( WebhookCreate, WebhookUpdate, Webhook as WebhookSchema, WebhookWithSecret, WebhookDelivery as WebhookDeliverySchema, WebhookTest, WEBHOOK_EVENTS, ) router = APIRouter() @router.get("/", response_model=List[WebhookSchema]) def list_webhooks( db: Session = Depends(get_db), current_user: User = Depends(get_current_superuser), skip: int = 0, limit: int = 100, is_active: bool = None ): """ List all webhooks. Requires superuser permissions. """ webhooks = crud.webhook.get_multi(db, skip=skip, limit=limit, is_active=is_active) # Convert events from JSON string to list result = [] for webhook in webhooks: webhook_dict = { "id": webhook.id, "name": webhook.name, "url": webhook.url, "secret": webhook.secret, "events": json.loads(webhook.events) if webhook.events else [], "is_active": webhook.is_active, "retry_count": webhook.retry_count, "timeout_seconds": webhook.timeout_seconds, "created_by": webhook.created_by, "created_at": webhook.created_at, "updated_at": webhook.updated_at, "last_triggered_at": webhook.last_triggered_at, "success_count": webhook.success_count, "failure_count": webhook.failure_count, } result.append(webhook_dict) return result @router.post("/", response_model=WebhookWithSecret, status_code=status.HTTP_201_CREATED) def create_webhook( webhook_in: WebhookCreate, db: Session = Depends(get_db), current_user: User = Depends(get_current_superuser), ): """ Create a new webhook. Returns the webhook with its secret (only shown once at creation). Requires superuser permissions. """ webhook = crud.webhook.create(db, obj_in=webhook_in, created_by=current_user.id) # Log the action crud.audit_log.log_action( db, user_id=current_user.id, username=current_user.username, action="create", resource_type="webhook", resource_id=webhook.id, details={"name": webhook.name, "url": webhook.url}, status="success" ) return { "id": webhook.id, "name": webhook.name, "url": webhook.url, "secret": webhook.secret, "events": json.loads(webhook.events) if webhook.events else [], "is_active": webhook.is_active, "retry_count": webhook.retry_count, "timeout_seconds": webhook.timeout_seconds, "created_by": webhook.created_by, "created_at": webhook.created_at, "updated_at": webhook.updated_at, "last_triggered_at": webhook.last_triggered_at, "success_count": webhook.success_count, "failure_count": webhook.failure_count, } @router.get("/events", response_model=List[str]) def list_webhook_events( current_user: User = Depends(get_current_superuser), ): """ List all available webhook event types. """ return WEBHOOK_EVENTS @router.get("/{webhook_id}", response_model=WebhookSchema) def get_webhook( webhook_id: str, db: Session = Depends(get_db), current_user: User = Depends(get_current_superuser), ): """ Get a specific webhook. Requires superuser permissions. """ webhook = crud.webhook.get(db, id=webhook_id) if not webhook: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Webhook not found" ) return { "id": webhook.id, "name": webhook.name, "url": webhook.url, "secret": webhook.secret, "events": json.loads(webhook.events) if webhook.events else [], "is_active": webhook.is_active, "retry_count": webhook.retry_count, "timeout_seconds": webhook.timeout_seconds, "created_by": webhook.created_by, "created_at": webhook.created_at, "updated_at": webhook.updated_at, "last_triggered_at": webhook.last_triggered_at, "success_count": webhook.success_count, "failure_count": webhook.failure_count, } @router.put("/{webhook_id}", response_model=WebhookSchema) def update_webhook( webhook_id: str, webhook_in: WebhookUpdate, db: Session = Depends(get_db), current_user: User = Depends(get_current_superuser), ): """ Update a webhook. Requires superuser permissions. """ webhook = crud.webhook.get(db, id=webhook_id) if not webhook: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Webhook not found" ) webhook = crud.webhook.update(db, db_obj=webhook, obj_in=webhook_in) # Log the action crud.audit_log.log_action( db, user_id=current_user.id, username=current_user.username, action="update", resource_type="webhook", resource_id=webhook.id, details={"name": webhook.name}, status="success" ) return { "id": webhook.id, "name": webhook.name, "url": webhook.url, "secret": webhook.secret, "events": json.loads(webhook.events) if webhook.events else [], "is_active": webhook.is_active, "retry_count": webhook.retry_count, "timeout_seconds": webhook.timeout_seconds, "created_by": webhook.created_by, "created_at": webhook.created_at, "updated_at": webhook.updated_at, "last_triggered_at": webhook.last_triggered_at, "success_count": webhook.success_count, "failure_count": webhook.failure_count, } @router.delete("/{webhook_id}", status_code=status.HTTP_204_NO_CONTENT) def delete_webhook( webhook_id: str, db: Session = Depends(get_db), current_user: User = Depends(get_current_superuser), ): """ Delete a webhook. Requires superuser permissions. """ webhook = crud.webhook.get(db, id=webhook_id) if not webhook: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Webhook not found" ) # Log the action crud.audit_log.log_action( db, user_id=current_user.id, username=current_user.username, action="delete", resource_type="webhook", resource_id=webhook_id, details={"name": webhook.name}, status="success" ) crud.webhook.delete(db, id=webhook_id) return None @router.post("/{webhook_id}/regenerate-secret", response_model=WebhookWithSecret) def regenerate_webhook_secret( webhook_id: str, db: Session = Depends(get_db), current_user: User = Depends(get_current_superuser), ): """ Regenerate the secret for a webhook. Returns the new secret (only shown once). Requires superuser permissions. """ webhook = crud.webhook.get(db, id=webhook_id) if not webhook: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Webhook not found" ) webhook = crud.webhook.regenerate_secret(db, db_obj=webhook) # Log the action crud.audit_log.log_action( db, user_id=current_user.id, username=current_user.username, action="regenerate_secret", resource_type="webhook", resource_id=webhook.id, details={"name": webhook.name}, status="success" ) return { "id": webhook.id, "name": webhook.name, "url": webhook.url, "secret": webhook.secret, "events": json.loads(webhook.events) if webhook.events else [], "is_active": webhook.is_active, "retry_count": webhook.retry_count, "timeout_seconds": webhook.timeout_seconds, "created_by": webhook.created_by, "created_at": webhook.created_at, "updated_at": webhook.updated_at, "last_triggered_at": webhook.last_triggered_at, "success_count": webhook.success_count, "failure_count": webhook.failure_count, } @router.post("/{webhook_id}/test", response_model=WebhookDeliverySchema) async def test_webhook( webhook_id: str, test_data: WebhookTest = None, db: Session = Depends(get_db), current_user: User = Depends(get_current_superuser), ): """ Send a test delivery to a webhook. Requires superuser permissions. """ webhook = crud.webhook.get(db, id=webhook_id) if not webhook: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Webhook not found" ) event_type = test_data.event_type if test_data else "test.ping" payload = test_data.payload if test_data and test_data.payload else None delivery = await crud.webhook_service.test_webhook( db, webhook=webhook, event_type=event_type, payload=payload ) # Log the action crud.audit_log.log_action( db, user_id=current_user.id, username=current_user.username, action="test", resource_type="webhook", resource_id=webhook.id, details={"status": delivery.status}, status="success" ) return delivery @router.get("/{webhook_id}/deliveries", response_model=List[WebhookDeliverySchema]) def list_webhook_deliveries( webhook_id: str, db: Session = Depends(get_db), current_user: User = Depends(get_current_superuser), skip: int = 0, limit: int = 50 ): """ List deliveries for a specific webhook. Requires superuser permissions. """ webhook = crud.webhook.get(db, id=webhook_id) if not webhook: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Webhook not found" ) return crud.webhook_delivery.get_by_webhook( db, webhook_id=webhook_id, skip=skip, limit=limit ) @router.post("/{webhook_id}/deliveries/{delivery_id}/retry", response_model=WebhookDeliverySchema) async def retry_webhook_delivery( webhook_id: str, delivery_id: str, db: Session = Depends(get_db), current_user: User = Depends(get_current_superuser), ): """ Retry a failed webhook delivery. Requires superuser permissions. """ webhook = crud.webhook.get(db, id=webhook_id) if not webhook: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Webhook not found" ) delivery = crud.webhook_delivery.get(db, id=delivery_id) if not delivery or delivery.webhook_id != webhook_id: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Delivery not found" ) await crud.webhook_service.deliver(db, webhook, delivery) db.refresh(delivery) return delivery