Files
warmbly/internal/api/handler/campaign.go
T
Matthew Meszaros e0ced97ba1 feat: add organization audit trail
Move audit logs into org-scoped Postgres storage, wire audit events across backend handlers, and surface actor details in the dashboard activity log.

Add realtime audit invalidation and retention pruning so the trail stays current and bounded.
2026-06-01 04:23:00 +02:00

185 lines
4.2 KiB
Go

package handler
import (
"net/http"
"strconv"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
"github.com/warmbly/warmbly/internal/api/middleware"
"github.com/warmbly/warmbly/internal/errx"
"github.com/warmbly/warmbly/internal/models"
)
func (h *Handler) CreateCampaign(c *gin.Context) {
userIDStr := middleware.GetUserID(c)
orgID := middleware.GetOrganizationID(c)
var data models.CreateCampaign
if err := c.ShouldBindJSON(&data); err != nil {
errx.JSON(c, errx.ErrInvalid)
return
}
resp, err := h.CampaignService.Create(c.Request.Context(), userIDStr, orgID, &data)
if err != nil {
errx.JSON(c, err)
return
}
// Audit log
h.auditOrg(c, models.AuditActionCreate, models.AuditEntityCampaign, &resp.ID, nil, map[string]string{"name": resp.Name})
c.JSON(http.StatusOK, resp)
}
func (h *Handler) GetCampaign(c *gin.Context) {
userID := middleware.GetUserID(c)
id := c.Param("id")
resp, err := h.CampaignService.Get(c.Request.Context(), userID, id)
if err != nil {
errx.JSON(c, err)
return
}
c.JSON(http.StatusOK, resp)
}
func (h *Handler) SearchCampaigns(c *gin.Context) {
userID := middleware.GetUserID(c)
query := c.Query("q")
cursor := c.Query("cursor")
folder := c.Query("folder")
limit := c.Query("limit")
resp, err := h.CampaignService.Search(c.Request.Context(), userID, query, cursor, folder, limit)
if err != nil {
errx.JSON(c, err)
return
}
c.JSON(http.StatusOK, resp)
}
func (h *Handler) UpdateCampaign(c *gin.Context) {
userIDStr := middleware.GetUserID(c)
id := c.Param("id")
var data models.UpdateCampaign
if err := c.ShouldBindJSON(&data); err != nil {
errx.JSON(c, errx.ErrInvalid)
return
}
resp, err := h.CampaignService.Update(c.Request.Context(), userIDStr, id, &data)
if err != nil {
errx.JSON(c, err)
return
}
// Audit log
if campaignID, err := uuid.Parse(id); err == nil {
h.auditOrg(c, models.AuditActionUpdate, models.AuditEntityCampaign, &campaignID, nil, nil)
}
c.JSON(http.StatusOK, resp)
}
func (h *Handler) DeleteCampaign(c *gin.Context) {
userIDStr := middleware.GetUserID(c)
id := c.Param("id")
if err := h.CampaignService.Delete(c.Request.Context(), userIDStr, id); err != nil {
errx.JSON(c, err)
return
}
// Audit log
if campaignID, err := uuid.Parse(id); err == nil {
h.auditOrg(c, models.AuditActionDelete, models.AuditEntityCampaign, &campaignID, nil, nil)
}
c.Status(http.StatusNoContent)
}
// StartCampaign starts a campaign
// POST /campaigns/:id/start
func (h *Handler) StartCampaign(c *gin.Context) {
orgID := middleware.GetOrganizationID(c)
if orgID == nil {
errx.JSON(c, errx.New(errx.BadRequest, "no organization selected"))
return
}
id := c.Param("id")
if xerr := h.CampaignService.StartCampaign(c.Request.Context(), *orgID, id); xerr != nil {
errx.JSON(c, xerr)
return
}
if campaignID, err := uuid.Parse(id); err == nil {
h.auditOrg(c, models.AuditActionStart, models.AuditEntityCampaign, &campaignID, nil, nil)
}
c.JSON(http.StatusOK, gin.H{"status": "started"})
}
// StopCampaign stops a campaign
// POST /campaigns/:id/stop
func (h *Handler) StopCampaign(c *gin.Context) {
orgID := middleware.GetOrganizationID(c)
if orgID == nil {
errx.JSON(c, errx.New(errx.BadRequest, "no organization selected"))
return
}
id := c.Param("id")
if xerr := h.CampaignService.StopCampaign(c.Request.Context(), *orgID, id); xerr != nil {
errx.JSON(c, xerr)
return
}
if campaignID, err := uuid.Parse(id); err == nil {
h.auditOrg(c, models.AuditActionStop, models.AuditEntityCampaign, &campaignID, nil, nil)
}
c.JSON(http.StatusOK, gin.H{"status": "stopped"})
}
// GetCampaignLogs returns campaign activity logs
// GET /campaigns/:id/logs
func (h *Handler) GetCampaignLogs(c *gin.Context) {
userID := middleware.GetUserID(c)
id := c.Param("id")
cursorStr := c.Query("cursor")
var cursor *string
if cursorStr != "" {
cursor = &cursorStr
}
limit := 50
if limitStr := c.DefaultQuery("limit", "50"); limitStr != "" {
if n, err := strconv.Atoi(limitStr); err == nil && n > 0 && n <= 100 {
limit = n
}
}
result, xerr := h.CampaignService.GetLogs(c.Request.Context(), userID, id, limit, cursor)
if xerr != nil {
errx.JSON(c, xerr)
return
}
c.JSON(http.StatusOK, result)
}