package middleware import ( "context" "encoding/json" "time" "a301_server/pkg/database" "github.com/gofiber/fiber/v2" ) const idempotencyTTL = 10 * time.Minute type cachedResponse struct { StatusCode int `json:"s"` Body json.RawMessage `json:"b"` } // Idempotency checks the Idempotency-Key header to prevent duplicate transactions. // If the same key is seen again within the TTL, the cached response is returned. func Idempotency(c *fiber.Ctx) error { key := c.Get("Idempotency-Key") if key == "" { return c.Next() } redisKey := "idempotency:" + key ctx := context.Background() // Check if this key was already processed cached, err := database.RDB.Get(ctx, redisKey).Bytes() if err == nil && len(cached) > 0 { var cr cachedResponse if json.Unmarshal(cached, &cr) == nil { c.Set("Content-Type", "application/json") c.Set("X-Idempotent-Replay", "true") return c.Status(cr.StatusCode).Send(cr.Body) } } // Process the request if err := c.Next(); err != nil { return err } // Cache successful responses (2xx) status := c.Response().StatusCode() if status >= 200 && status < 300 { cr := cachedResponse{StatusCode: status, Body: c.Response().Body()} if data, err := json.Marshal(cr); err == nil { database.RDB.Set(ctx, redisKey, data, idempotencyTTL) } } return nil }