backend requirements onthe xustomer app

This commit is contained in:
2026-09-07 10:55:11 +05:30
parent 35675d8a9b
commit 1b2690b21a
56 changed files with 13342 additions and 1071 deletions

View File

@@ -2,6 +2,8 @@ package middlewares
import (
"context"
"crypto/sha256"
"encoding/hex"
"fmt"
"strconv"
"strings"
@@ -37,8 +39,7 @@ func Idempotency() fiber.Handler {
if key == "" || db.Rdb == nil {
return c.Next()
}
uid, _ := c.Locals("userid").(int)
base := fmt.Sprintf("idem:%d:%s", uid, key)
base := "idem:" + idempotencyScope(c) + ":" + key
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
@@ -80,3 +81,32 @@ func Idempotency() fiber.Handler {
return nil
}
}
// idempotencyScope namespaces a key so one caller's stored response can never
// be replayed to another.
//
// For an authenticated request the caller's own user id is the scope, which is
// what this middleware has always used.
//
// An UNAUTHENTICATED request has no user id, and defaulting to 0 would put
// every anonymous caller in one namespace: two customers who happened to pick
// the same Idempotency-Key on POST /customer/auth/otp/verify would collide, and
// the second would be handed the first's access token, refresh token and
// customer record. So an anonymous request is scoped by the request path and a
// hash of its body instead — the same body replays, a different body does not,
// and one person's session can never be served to another.
// The authenticated form is byte-identical to what this middleware has always
// produced ("idem:<uid>:<key>"). Changing it would orphan every in-flight key
// in Redis at deploy time, and a rider retrying a pickup-complete across that
// boundary would execute it a second time instead of replaying — the exact
// double-collection this middleware exists to prevent.
func idempotencyScope(c *fiber.Ctx) string {
if uid, ok := c.Locals("userid").(int); ok && uid != 0 {
return fmt.Sprintf("%d", uid)
}
// Anonymous callers get their own namespace shape, which no previous key
// can collide with: every key written before this change had a numeric
// scope, and this one never is.
sum := sha256.Sum256(append([]byte(c.Path()+"\x00"), c.Body()...))
return "anon-" + hex.EncodeToString(sum[:16])
}