mirror of
https://github.com/btouchard/ackify-ce.git
synced 2026-02-09 07:18:36 -06:00
Enable importing backend packages in SaaS project by aligning module path with its location in the repository.
150 lines
5.3 KiB
Go
150 lines
5.3 KiB
Go
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
package admin
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"net/http"
|
|
"strconv"
|
|
|
|
"github.com/btouchard/ackify-ce/backend/internal/domain/models"
|
|
"github.com/btouchard/ackify-ce/backend/internal/presentation/api/shared"
|
|
"github.com/go-chi/chi/v5"
|
|
)
|
|
|
|
// webhookService defines webhook management operations
|
|
type webhookService interface {
|
|
CreateWebhook(ctx context.Context, input models.WebhookInput) (*models.Webhook, error)
|
|
UpdateWebhook(ctx context.Context, id int64, input models.WebhookInput) (*models.Webhook, error)
|
|
SetWebhookActive(ctx context.Context, id int64, active bool) error
|
|
DeleteWebhook(ctx context.Context, id int64) error
|
|
GetWebhookByID(ctx context.Context, id int64) (*models.Webhook, error)
|
|
ListWebhooks(ctx context.Context, limit, offset int) ([]*models.Webhook, error)
|
|
ListDeliveries(ctx context.Context, webhookID int64, limit, offset int) ([]*models.WebhookDelivery, error)
|
|
}
|
|
|
|
// WebhooksHandler groups operations on webhooks
|
|
type WebhooksHandler struct {
|
|
service webhookService
|
|
}
|
|
|
|
func NewWebhooksHandler(service webhookService) *WebhooksHandler {
|
|
return &WebhooksHandler{service: service}
|
|
}
|
|
|
|
type CreateWebhookRequest struct {
|
|
Title string `json:"title"`
|
|
TargetURL string `json:"targetUrl"`
|
|
Secret string `json:"secret"`
|
|
Active bool `json:"active"`
|
|
Events []string `json:"events"`
|
|
Headers map[string]string `json:"headers,omitempty"`
|
|
Description string `json:"description,omitempty"`
|
|
}
|
|
|
|
func (h *WebhooksHandler) HandleCreateWebhook(w http.ResponseWriter, r *http.Request) {
|
|
ctx := r.Context()
|
|
var req CreateWebhookRequest
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
shared.WriteError(w, http.StatusBadRequest, shared.ErrCodeBadRequest, "Invalid request body", nil)
|
|
return
|
|
}
|
|
if req.Title == "" || req.TargetURL == "" || req.Secret == "" || len(req.Events) == 0 {
|
|
shared.WriteError(w, http.StatusBadRequest, shared.ErrCodeBadRequest, "title, targetUrl, secret and events are required", nil)
|
|
return
|
|
}
|
|
user, _ := shared.GetUserFromContext(ctx)
|
|
input := models.WebhookInput{Title: req.Title, TargetURL: req.TargetURL, Secret: req.Secret, Active: req.Active, Events: req.Events, Headers: req.Headers, Description: req.Description}
|
|
if user != nil {
|
|
input.CreatedBy = user.Email
|
|
}
|
|
wh, err := h.service.CreateWebhook(ctx, input)
|
|
if err != nil {
|
|
shared.WriteInternalError(w)
|
|
return
|
|
}
|
|
shared.WriteJSON(w, http.StatusCreated, wh)
|
|
}
|
|
|
|
func (h *WebhooksHandler) HandleListWebhooks(w http.ResponseWriter, r *http.Request) {
|
|
ctx := r.Context()
|
|
limit := 100
|
|
offset := 0
|
|
list, err := h.service.ListWebhooks(ctx, limit, offset)
|
|
if err != nil {
|
|
shared.WriteInternalError(w)
|
|
return
|
|
}
|
|
meta := map[string]interface{}{"total": len(list), "limit": limit, "offset": offset}
|
|
shared.WriteJSONWithMeta(w, http.StatusOK, list, meta)
|
|
}
|
|
|
|
func (h *WebhooksHandler) HandleGetWebhook(w http.ResponseWriter, r *http.Request) {
|
|
ctx := r.Context()
|
|
id, _ := strconv.ParseInt(chi.URLParam(r, "id"), 10, 64)
|
|
wh, err := h.service.GetWebhookByID(ctx, id)
|
|
if err != nil {
|
|
shared.WriteError(w, http.StatusNotFound, shared.ErrCodeNotFound, "Webhook not found", nil)
|
|
return
|
|
}
|
|
shared.WriteJSON(w, http.StatusOK, wh)
|
|
}
|
|
|
|
func (h *WebhooksHandler) HandleUpdateWebhook(w http.ResponseWriter, r *http.Request) {
|
|
ctx := r.Context()
|
|
id, _ := strconv.ParseInt(chi.URLParam(r, "id"), 10, 64)
|
|
var req CreateWebhookRequest
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
shared.WriteError(w, http.StatusBadRequest, shared.ErrCodeBadRequest, "Invalid request body", nil)
|
|
return
|
|
}
|
|
// For updates, allow empty secret to keep current value
|
|
if req.Title == "" || req.TargetURL == "" || len(req.Events) == 0 {
|
|
shared.WriteError(w, http.StatusBadRequest, shared.ErrCodeBadRequest, "title, targetUrl and events are required", nil)
|
|
return
|
|
}
|
|
input := models.WebhookInput{Title: req.Title, TargetURL: req.TargetURL, Secret: req.Secret, Active: req.Active, Events: req.Events, Headers: req.Headers, Description: req.Description}
|
|
wh, err := h.service.UpdateWebhook(ctx, id, input)
|
|
if err != nil {
|
|
shared.WriteInternalError(w)
|
|
return
|
|
}
|
|
shared.WriteJSON(w, http.StatusOK, wh)
|
|
}
|
|
|
|
func (h *WebhooksHandler) HandleToggleWebhook(w http.ResponseWriter, r *http.Request) {
|
|
ctx := r.Context()
|
|
id, _ := strconv.ParseInt(chi.URLParam(r, "id"), 10, 64)
|
|
enable := chi.URLParam(r, "action") == "enable"
|
|
if err := h.service.SetWebhookActive(ctx, id, enable); err != nil {
|
|
shared.WriteInternalError(w)
|
|
return
|
|
}
|
|
status := "disabled"
|
|
if enable {
|
|
status = "enabled"
|
|
}
|
|
shared.WriteJSON(w, http.StatusOK, map[string]string{"message": "Webhook " + status})
|
|
}
|
|
|
|
func (h *WebhooksHandler) HandleDeleteWebhook(w http.ResponseWriter, r *http.Request) {
|
|
ctx := r.Context()
|
|
id, _ := strconv.ParseInt(chi.URLParam(r, "id"), 10, 64)
|
|
if err := h.service.DeleteWebhook(ctx, id); err != nil {
|
|
shared.WriteInternalError(w)
|
|
return
|
|
}
|
|
shared.WriteJSON(w, http.StatusOK, map[string]string{"message": "Webhook deleted"})
|
|
}
|
|
|
|
func (h *WebhooksHandler) HandleListDeliveries(w http.ResponseWriter, r *http.Request) {
|
|
ctx := r.Context()
|
|
id, _ := strconv.ParseInt(chi.URLParam(r, "id"), 10, 64)
|
|
deliveries, err := h.service.ListDeliveries(ctx, id, 100, 0)
|
|
if err != nil {
|
|
shared.WriteInternalError(w)
|
|
return
|
|
}
|
|
shared.WriteJSON(w, http.StatusOK, deliveries)
|
|
}
|