mirror of
https://github.com/warmbly/warmbly.git
synced 2026-08-24 08:00:37 +00:00
e0ced97ba1
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.
175 lines
4.2 KiB
Go
175 lines
4.2 KiB
Go
package handler
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/google/uuid"
|
|
"github.com/warmbly/warmbly/internal/api/middleware"
|
|
"github.com/warmbly/warmbly/internal/config"
|
|
"github.com/warmbly/warmbly/internal/errx"
|
|
"github.com/warmbly/warmbly/internal/models"
|
|
)
|
|
|
|
const maxBulkOperationSize = 1000
|
|
|
|
func (h *Handler) AddContacts(c *gin.Context) {
|
|
userIDStr := middleware.GetUserID(c)
|
|
|
|
var data []models.AddContact
|
|
|
|
if err := c.ShouldBindJSON(&data); err != nil {
|
|
errx.Handle(c, errx.ErrInvalid)
|
|
return
|
|
}
|
|
|
|
if len(data) == 0 {
|
|
errx.Handle(c, errx.New(errx.BadRequest, "no contacts provided"))
|
|
return
|
|
}
|
|
if len(data) > config.MaxContactSize {
|
|
errx.Handle(c, errx.New(errx.BadRequest, fmt.Sprintf("too many contacts, maximum is %d", config.MaxContactSize)))
|
|
return
|
|
}
|
|
|
|
resp, err := h.ContactService.Add(c.Request.Context(), userIDStr, data)
|
|
if err != nil {
|
|
errx.Handle(c, err)
|
|
return
|
|
}
|
|
|
|
// Audit log - bulk import
|
|
h.auditOrg(c, models.AuditActionImport, models.AuditEntityContact, nil, nil, map[string]string{"count": fmt.Sprintf("%d", len(data))})
|
|
|
|
c.JSON(http.StatusOK, resp)
|
|
}
|
|
|
|
func (h *Handler) SearchContacts(c *gin.Context) {
|
|
userID := middleware.GetUserID(c)
|
|
|
|
cursor := c.Query("cursor")
|
|
category := c.Query("category")
|
|
limit := c.Query("limit")
|
|
|
|
var data models.SearchContacts
|
|
|
|
if err := c.ShouldBindJSON(&data); err != nil {
|
|
errx.Handle(c, errx.ErrInvalid)
|
|
return
|
|
}
|
|
|
|
resp, err := h.ContactService.Search(c.Request.Context(), userID, cursor, category, limit, data)
|
|
if err != nil {
|
|
errx.Handle(c, err)
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, resp)
|
|
}
|
|
|
|
func (h *Handler) UpdateContactBulk(c *gin.Context) {
|
|
userIDStr := middleware.GetUserID(c)
|
|
|
|
var data models.BulkEditContactsData
|
|
|
|
if err := c.ShouldBindJSON(&data); err != nil {
|
|
errx.Handle(c, errx.ErrInvalid)
|
|
return
|
|
}
|
|
|
|
if len(data.Contacts) == 0 {
|
|
errx.Handle(c, errx.New(errx.BadRequest, "no contacts provided"))
|
|
return
|
|
}
|
|
if len(data.Contacts) > maxBulkOperationSize {
|
|
errx.Handle(c, errx.New(errx.BadRequest, fmt.Sprintf("too many contacts, maximum is %d per batch", maxBulkOperationSize)))
|
|
return
|
|
}
|
|
|
|
resp, err := h.ContactService.BulkUpdate(c.Request.Context(), userIDStr, &data)
|
|
if err != nil {
|
|
errx.Handle(c, err)
|
|
return
|
|
}
|
|
|
|
// Audit log - bulk update
|
|
h.auditOrg(c, models.AuditActionUpdate, models.AuditEntityContact, nil, nil, map[string]string{"bulk": "true", "count": fmt.Sprintf("%d", len(data.Contacts))})
|
|
|
|
c.JSON(http.StatusOK, resp)
|
|
}
|
|
|
|
func (h *Handler) UpdateContact(c *gin.Context) {
|
|
userIDStr := middleware.GetUserID(c)
|
|
|
|
id := c.Param("id")
|
|
|
|
var data models.UpdateContact
|
|
|
|
if err := c.ShouldBindJSON(&data); err != nil {
|
|
errx.Handle(c, errx.ErrInvalid)
|
|
return
|
|
}
|
|
|
|
resp, err := h.ContactService.Update(c.Request.Context(), userIDStr, id, &data)
|
|
if err != nil {
|
|
errx.Handle(c, err)
|
|
return
|
|
}
|
|
|
|
// Audit log
|
|
if contactID, err := uuid.Parse(id); err == nil {
|
|
h.auditOrg(c, models.AuditActionUpdate, models.AuditEntityContact, &contactID, nil, nil)
|
|
}
|
|
|
|
c.JSON(http.StatusOK, resp)
|
|
}
|
|
|
|
func (h *Handler) DeleteContactBulk(c *gin.Context) {
|
|
userIDStr := middleware.GetUserID(c)
|
|
|
|
var data []string
|
|
|
|
if err := c.ShouldBindJSON(&data); err != nil {
|
|
errx.Handle(c, errx.ErrInvalid)
|
|
return
|
|
}
|
|
|
|
if len(data) == 0 {
|
|
errx.Handle(c, errx.New(errx.BadRequest, "no contacts provided"))
|
|
return
|
|
}
|
|
if len(data) > maxBulkOperationSize {
|
|
errx.Handle(c, errx.New(errx.BadRequest, fmt.Sprintf("too many contacts, maximum is %d per batch", maxBulkOperationSize)))
|
|
return
|
|
}
|
|
|
|
if err := h.ContactService.BulkDelete(c.Request.Context(), userIDStr, data); err != nil {
|
|
errx.Handle(c, err)
|
|
return
|
|
}
|
|
|
|
// Audit log - bulk delete
|
|
h.auditOrg(c, models.AuditActionDelete, models.AuditEntityContact, nil, nil, map[string]string{"bulk": "true", "count": fmt.Sprintf("%d", len(data))})
|
|
|
|
c.Status(http.StatusNoContent)
|
|
}
|
|
|
|
func (h *Handler) DeleteContact(c *gin.Context) {
|
|
userIDStr := middleware.GetUserID(c)
|
|
|
|
id := c.Param("id")
|
|
|
|
if err := h.ContactService.Delete(c.Request.Context(), userIDStr, id); err != nil {
|
|
errx.Handle(c, err)
|
|
return
|
|
}
|
|
|
|
// Audit log
|
|
if contactID, err := uuid.Parse(id); err == nil {
|
|
h.auditOrg(c, models.AuditActionDelete, models.AuditEntityContact, &contactID, nil, nil)
|
|
}
|
|
|
|
c.Status(http.StatusNoContent)
|
|
}
|