mirror of
https://github.com/Wei-Shaw/sub2api.git
synced 2026-10-14 20:12:39 +08:00
POST /admin/accounts/:id/claude/reset-credits/redeem (Idempotency-Key required, same middleware chain as the Codex reset-quota route). The server re-queries eligibility and claims only the upstream next_grant_id when it is redeemable; grant/org IDs never reach the client. Claims are serialized by account and organization Redis leases, and a durable per-organization fence in the idempotency table blocks further redemption after an unconfirmed outcome (settles after 24h). Ported from upstream PR #7591. Co-authored-by: korkin25 <eugeny.kuzakov@gmail.com> Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
57 lines
1.6 KiB
Go
57 lines
1.6 KiB
Go
package admin
|
|
|
|
import (
|
|
"context"
|
|
"github.com/Wei-Shaw/sub2api/internal/pkg/response"
|
|
"github.com/Wei-Shaw/sub2api/internal/service"
|
|
"github.com/gin-gonic/gin"
|
|
"strconv"
|
|
)
|
|
|
|
type claudeResetReader interface {
|
|
Query(context.Context, int64) (*service.ClaudeResetCredits, error)
|
|
Redeem(context.Context, int64, string) (*service.ClaudeResetOutcome, error)
|
|
}
|
|
|
|
func (h *AccountHandler) SetClaudeResetCreditService(s *service.ClaudeResetCreditService) {
|
|
h.claudeResetCredits = s
|
|
}
|
|
func (h *AccountHandler) ClaudeResetCredits(c *gin.Context) {
|
|
id, err := strconv.ParseInt(c.Param("id"), 10, 64)
|
|
if err != nil || id <= 0 {
|
|
response.BadRequest(c, "Invalid account ID")
|
|
return
|
|
}
|
|
if h.claudeResetCredits == nil {
|
|
response.Error(c, 503, "Claude reset service unavailable")
|
|
return
|
|
}
|
|
status, err := h.claudeResetCredits.Query(c.Request.Context(), id)
|
|
if err != nil {
|
|
response.ErrorFrom(c, err)
|
|
return
|
|
}
|
|
response.Success(c, status)
|
|
}
|
|
|
|
// RedeemClaudeResetCredit consumes the upstream next reset credit. The request has
|
|
// no body: the server picks the grant; the Idempotency-Key header identifies one
|
|
// operator confirmation and replays its outcome.
|
|
func (h *AccountHandler) RedeemClaudeResetCredit(c *gin.Context) {
|
|
id, err := strconv.ParseInt(c.Param("id"), 10, 64)
|
|
if err != nil || id <= 0 {
|
|
response.BadRequest(c, "Invalid account ID")
|
|
return
|
|
}
|
|
if h.claudeResetCredits == nil {
|
|
response.Error(c, 503, "Claude reset service unavailable")
|
|
return
|
|
}
|
|
result, err := h.claudeResetCredits.Redeem(c.Request.Context(), id, c.GetHeader("Idempotency-Key"))
|
|
if err != nil {
|
|
response.ErrorFrom(c, err)
|
|
return
|
|
}
|
|
response.Success(c, result)
|
|
}
|