From 7e75394ab8ceca845bc8c735dca7bb24d12fa16d Mon Sep 17 00:00:00 2001 From: Matthew Meszaros Date: Sun, 28 Jun 2026 05:58:25 +0000 Subject: [PATCH] feat: decode the admin discount list and redemption cursors as opaque offset tokens, returning 400 on a malformed cursor instead of silently resetting to the first page --- internal/api/handler/discount.go | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/internal/api/handler/discount.go b/internal/api/handler/discount.go index 1dfc78b1..c7b3337e 100644 --- a/internal/api/handler/discount.go +++ b/internal/api/handler/discount.go @@ -8,6 +8,7 @@ import ( "github.com/warmbly/warmbly/internal/api/middleware" "github.com/warmbly/warmbly/internal/errx" "github.com/warmbly/warmbly/internal/models" + "github.com/warmbly/warmbly/internal/utils/paging" ) // --- Admin discount-code management --- @@ -20,6 +21,15 @@ func (h *Handler) AdminListDiscounts(c *gin.Context) { return } + // The wire cursor is an opaque offset token; a malformed one is a 400, not + // a silent reset to the first page. + offset, xerr := paging.DecodeOffsetCursor(c.Query("cursor")) + if xerr != nil { + errx.JSON(c, xerr) + return + } + search.Offset = offset + result, xerr := h.DiscountService.List(c.Request.Context(), &search) if xerr != nil { errx.JSON(c, xerr) @@ -129,10 +139,14 @@ func (h *Handler) AdminListDiscountRedemptions(c *gin.Context) { return } - cursor := parseCursor(c.Query("cursor")) + offset, xerr := paging.DecodeOffsetCursor(c.Query("cursor")) + if xerr != nil { + errx.JSON(c, xerr) + return + } limit := parseLimit(c.Query("limit"), 50) - result, xerr := h.DiscountService.ListRedemptions(c.Request.Context(), id, cursor, limit) + result, xerr := h.DiscountService.ListRedemptions(c.Request.Context(), id, offset, limit) if xerr != nil { errx.JSON(c, xerr) return