Files
warmbly/internal/api/handler/website_tracking.go
T

117 lines
3.6 KiB
Go

package handler
import (
"errors"
"net/http"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
"github.com/warmbly/warmbly/internal/api/middleware"
"github.com/warmbly/warmbly/internal/app/websitetracking"
"github.com/warmbly/warmbly/internal/errx"
"github.com/warmbly/warmbly/internal/models"
)
// Website tracking settings (JWT only, MANAGE_SETTINGS).
func (h *Handler) GetWebsiteTrackingSettings(c *gin.Context) {
orgID := middleware.GetOrganizationID(c)
if orgID == nil {
errx.JSON(c, errx.New(errx.BadRequest, "no organization selected"))
return
}
settings, xerr := h.WebsiteTrackingService.GetSettings(c.Request.Context(), *orgID)
if xerr != nil {
errx.JSON(c, xerr)
return
}
c.JSON(http.StatusOK, settings)
}
func (h *Handler) UpdateWebsiteTrackingSettings(c *gin.Context) {
orgID := middleware.GetOrganizationID(c)
if orgID == nil {
errx.JSON(c, errx.New(errx.BadRequest, "no organization selected"))
return
}
userID, err := middleware.GetUserUUID(c)
if err != nil {
errx.JSON(c, errx.New(errx.BadRequest, "invalid user id"))
return
}
var req models.UpdateWebsiteTrackingSettingsRequest
if err := c.ShouldBindJSON(&req); err != nil {
errx.JSON(c, errx.ErrInvalid)
return
}
settings, xerr := h.WebsiteTrackingService.UpdateSettings(c.Request.Context(), *orgID, userID, &req)
if xerr != nil {
errx.JSON(c, xerr)
return
}
h.auditOrg(c, models.AuditActionUpdate, models.AuditEntitySettings, nil, nil, map[string]string{
"scope": "website_tracking",
})
c.JSON(http.StatusOK, settings)
}
// RotateWebsiteTrackingKey issues a new site key; the old one stops at once.
func (h *Handler) RotateWebsiteTrackingKey(c *gin.Context) {
orgID := middleware.GetOrganizationID(c)
if orgID == nil {
errx.JSON(c, errx.New(errx.BadRequest, "no organization selected"))
return
}
userID, err := middleware.GetUserUUID(c)
if err != nil {
errx.JSON(c, errx.New(errx.BadRequest, "invalid user id"))
return
}
settings, xerr := h.WebsiteTrackingService.RotateSiteKey(c.Request.Context(), *orgID, userID)
if xerr != nil {
errx.JSON(c, xerr)
return
}
h.auditOrg(c, models.AuditActionUpdate, models.AuditEntitySettings, nil, nil, map[string]string{
"scope": "website_tracking",
"action": "rotate_site_key",
})
c.JSON(http.StatusOK, settings)
}
// InternalIngestPageHit receives a page view from the tracking service.
//
// POST /api/v1/internal/page-hits
// -> 204 accepted or quietly rejected | 200 {"new_visitor_key":...}
// -> 400 malformed | 404 unknown site key (cached negatively at the edge)
func (h *Handler) InternalIngestPageHit(c *gin.Context) {
var req models.WebsiteHitRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid body"})
return
}
result, err := h.WebsiteTrackingService.Ingest(c.Request.Context(), &req)
switch {
case errors.Is(err, websitetracking.ErrMalformed):
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid hit"})
case errors.Is(err, websitetracking.ErrUnknownSite):
c.Status(http.StatusNotFound)
case errors.Is(err, websitetracking.ErrRejected):
c.Status(http.StatusNoContent)
case err != nil:
c.JSON(http.StatusInternalServerError, gin.H{"error": "ingest failed"})
case result != nil && result.NewVisitorKey != "":
c.JSON(http.StatusOK, result)
default:
c.Status(http.StatusNoContent)
}
}
// websiteIdentifyForLink: may this click destination carry the identification ticket?
func (h *Handler) websiteIdentifyForLink(c *gin.Context, campaignID uuid.UUID, destination string) bool {
if h.WebsiteTrackingService == nil {
return false
}
return h.WebsiteTrackingService.ShouldIdentify(c.Request.Context(), campaignID, destination)
}