package handlers import ( "fmt" "net/http" "time" "github.com/gin-gonic/gin" "github.com/google/uuid" "go.uber.org/zap" apperrors "veza-backend-api/internal/errors" "veza-backend-api/internal/services" "veza-backend-api/internal/workers" ) // WebhookHandler gère les handlers de webhooks type WebhookHandler struct { webhookService *services.WebhookService webhookWorker *workers.WebhookWorker logger *zap.Logger commonHandler *CommonHandler } // NewWebhookHandler crée un nouveau handler de webhooks func NewWebhookHandler( webhookService *services.WebhookService, webhookWorker *workers.WebhookWorker, logger *zap.Logger, ) *WebhookHandler { return &WebhookHandler{ webhookService: webhookService, webhookWorker: webhookWorker, logger: logger, commonHandler: NewCommonHandler(logger), } } // RegisterWebhook gère l'enregistrement d'un webhook func (h *WebhookHandler) RegisterWebhook() gin.HandlerFunc { return func(c *gin.Context) { // Récupérer l'ID utilisateur userIDInterface, exists := c.Get("user_id") if !exists { c.JSON(http.StatusUnauthorized, gin.H{"error": "User not authenticated"}) return } userID, ok := userIDInterface.(uuid.UUID) if !ok { c.JSON(http.StatusInternalServerError, gin.H{"error": "Invalid user ID type"}) return } var req struct { URL string `json:"url" binding:"required,url"` Events []string `json:"events" binding:"required,min=1"` } if appErr := h.commonHandler.BindAndValidateJSON(c, &req); appErr != nil { RespondWithAppError(c, appErr) return } webhook, err := h.webhookService.RegisterWebhook(c.Request.Context(), userID, req.URL, req.Events) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to register webhook"}) return } RespondSuccess(c, http.StatusCreated, webhook) } } // ListWebhooks liste les webhooks d'un utilisateur func (h *WebhookHandler) ListWebhooks() gin.HandlerFunc { return func(c *gin.Context) { userIDInterface, exists := c.Get("user_id") if !exists { c.JSON(http.StatusUnauthorized, gin.H{"error": "User not authenticated"}) return } userID, ok := userIDInterface.(uuid.UUID) if !ok { c.JSON(http.StatusInternalServerError, gin.H{"error": "Invalid user ID type"}) return } webhooks, err := h.webhookService.ListWebhooks(c.Request.Context(), userID) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to list webhooks"}) return } RespondSuccess(c, http.StatusOK, webhooks) } } // DeleteWebhook supprime un webhook func (h *WebhookHandler) DeleteWebhook() gin.HandlerFunc { return func(c *gin.Context) { userIDInterface, exists := c.Get("user_id") if !exists { c.JSON(http.StatusUnauthorized, gin.H{"error": "User not authenticated"}) return } userID, ok := userIDInterface.(uuid.UUID) if !ok { c.JSON(http.StatusInternalServerError, gin.H{"error": "Invalid user ID type"}) return } webhookIDStr := c.Param("id") webhookID, err := uuid.Parse(webhookIDStr) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid webhook ID"}) return } err = h.webhookService.DeleteWebhook(c.Request.Context(), webhookID, userID) if err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "Webhook not found"}) return } RespondSuccess(c, http.StatusOK, gin.H{"message": "Webhook deleted successfully"}) } } // GetWebhookStats retourne les statistiques des webhooks // GET /api/v1/webhooks/stats // BE-API-033: Implement webhook stats endpoint validation func (h *WebhookHandler) GetWebhookStats() gin.HandlerFunc { return func(c *gin.Context) { // Récupérer l'ID utilisateur depuis le contexte (pour cohérence avec les autres endpoints protégés) userID, ok := GetUserIDUUID(c) if !ok { return // Erreur déjà envoyée par GetUserIDUUID } // Récupérer les statistiques du worker if h.webhookWorker == nil { h.logger.Error("WebhookWorker not available") RespondWithAppError(c, apperrors.Wrap(apperrors.ErrCodeInternal, "Webhook stats service not available", nil)) return } stats := h.webhookWorker.GetStats() // BE-API-033: Standardize response format RespondSuccess(c, http.StatusOK, gin.H{ "user_id": userID, "stats": stats, }) } } // TestWebhook teste un webhook func (h *WebhookHandler) TestWebhook() gin.HandlerFunc { return func(c *gin.Context) { userIDInterface, exists := c.Get("user_id") if !exists { c.JSON(http.StatusUnauthorized, gin.H{"error": "User not authenticated"}) return } userID, ok := userIDInterface.(uuid.UUID) if !ok { c.JSON(http.StatusInternalServerError, gin.H{"error": "Invalid user ID type"}) return } webhookIDStr := c.Param("id") webhookID, err := uuid.Parse(webhookIDStr) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid webhook ID"}) return } webhook, err := h.webhookService.GetWebhook(c.Request.Context(), webhookID, userID) if err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "Webhook not found"}) return } job := workers.WebhookJob{ Webhook: webhook, Event: "ping", Data: map[string]interface{}{ "message": "This is a test webhook from Veza", "timestamp": time.Now(), "test_id": uuid.New().String(), }, Retries: 0, } h.webhookWorker.Enqueue(job) h.logger.Info("Test webhook queued", zap.String("webhook_id", webhookID.String())) RespondSuccess(c, http.StatusOK, gin.H{"message": fmt.Sprintf("Webhook test queued for %s", webhookID)}) } }