Release / update-version (push) Has been cancelled
Release / build-frontend (push) Has been cancelled
Release / release (push) Has been cancelled
Release / sync-version-file (push) Has been cancelled
CI / shell (push) Canceled after 0s
CI / test (push) Canceled after 0s
CI / frontend (push) Canceled after 0s
CI / golangci-lint (push) Canceled after 0s
Security Scan / backend-security (push) Canceled after 0s
Security Scan / frontend-security (push) Canceled after 0s
109 lines
4.0 KiB
Go
109 lines
4.0 KiB
Go
package handler
|
|
|
|
import (
|
|
"net/http"
|
|
"time"
|
|
|
|
"github.com/Wei-Shaw/sub2api/internal/config"
|
|
"github.com/Wei-Shaw/sub2api/internal/pkg/timezone"
|
|
middleware2 "github.com/Wei-Shaw/sub2api/internal/server/middleware"
|
|
"github.com/Wei-Shaw/sub2api/internal/service"
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
const keyBillingInfoSchemaVersion = 1
|
|
|
|
type keyBillingInfoResponse struct {
|
|
Object string `json:"object"`
|
|
SchemaVersion int `json:"schema_version"`
|
|
BillingScope string `json:"billing_scope"`
|
|
GroupRateMultiplier float64 `json:"group_rate_multiplier"`
|
|
UserRateMultiplier *float64 `json:"user_rate_multiplier,omitempty"`
|
|
ResolvedRateMultiplier float64 `json:"resolved_rate_multiplier"`
|
|
PeakRateEnabled bool `json:"peak_rate_enabled"`
|
|
PeakStart *string `json:"peak_start,omitempty"`
|
|
PeakEnd *string `json:"peak_end,omitempty"`
|
|
PeakRateMultiplier *float64 `json:"peak_rate_multiplier,omitempty"`
|
|
AppliedPeakMultiplier *float64 `json:"applied_peak_multiplier,omitempty"`
|
|
EffectiveRateMultiplier float64 `json:"effective_rate_multiplier"`
|
|
Timezone *string `json:"timezone,omitempty"`
|
|
ObservedAt time.Time `json:"observed_at"`
|
|
}
|
|
|
|
// KeyBillingInfo returns the token billing multiplier effective for the authenticated API key.
|
|
// GET /v1/sub2api/billing
|
|
func (h *GatewayHandler) KeyBillingInfo(c *gin.Context) {
|
|
apiKey, ok := middleware2.GetAPIKeyFromContext(c)
|
|
if !ok {
|
|
h.errorResponse(c, http.StatusUnauthorized, "authentication_error", "Invalid API key")
|
|
return
|
|
}
|
|
if h.cfg != nil && h.cfg.RunMode == config.RunModeSimple {
|
|
h.errorResponse(c, http.StatusNotFound, "not_found_error", "Billing information is not supported in simple mode")
|
|
return
|
|
}
|
|
if apiKey.GroupID == nil {
|
|
h.errorResponse(c, http.StatusForbidden, "permission_error", "API key is not assigned to a group")
|
|
return
|
|
}
|
|
if apiKey.Group == nil {
|
|
h.errorResponse(c, http.StatusInternalServerError, "api_error", "Billing information is unavailable")
|
|
return
|
|
}
|
|
|
|
resolvedRate, ok := h.resolveKeyBillingRate(c, apiKey)
|
|
if !ok {
|
|
h.errorResponse(c, http.StatusInternalServerError, "api_error", "Billing information is unavailable")
|
|
return
|
|
}
|
|
|
|
c.Header("Cache-Control", "no-store")
|
|
c.JSON(http.StatusOK, buildKeyBillingInfo(apiKey, resolvedRate, timezone.Now()))
|
|
}
|
|
|
|
func (h *GatewayHandler) resolveKeyBillingRate(c *gin.Context, apiKey *service.APIKey) (float64, bool) {
|
|
groupRate := apiKey.Group.RateMultiplier
|
|
switch apiKey.Group.Platform {
|
|
case service.PlatformOpenAI, service.PlatformGrok:
|
|
if h.openAIGatewayService == nil {
|
|
return 0, false
|
|
}
|
|
return h.openAIGatewayService.ResolveUserGroupRateMultiplier(c.Request.Context(), apiKey.UserID, *apiKey.GroupID, groupRate), true
|
|
default:
|
|
if h.gatewayService == nil {
|
|
return 0, false
|
|
}
|
|
return h.gatewayService.ResolveUserGroupRateMultiplier(c.Request.Context(), apiKey.UserID, *apiKey.GroupID, groupRate), true
|
|
}
|
|
}
|
|
|
|
func buildKeyBillingInfo(apiKey *service.APIKey, resolvedRate float64, now time.Time) keyBillingInfoResponse {
|
|
groupRate := apiKey.Group.RateMultiplier
|
|
var userRate *float64
|
|
if resolvedRate != groupRate {
|
|
userRate = &resolvedRate
|
|
}
|
|
appliedPeak := apiKey.Group.PeakMultiplierAt(now)
|
|
|
|
response := keyBillingInfoResponse{
|
|
Object: "sub2api.key_billing",
|
|
SchemaVersion: keyBillingInfoSchemaVersion,
|
|
BillingScope: "token",
|
|
GroupRateMultiplier: groupRate,
|
|
UserRateMultiplier: userRate,
|
|
ResolvedRateMultiplier: resolvedRate,
|
|
PeakRateEnabled: apiKey.Group.PeakRateEnabled,
|
|
EffectiveRateMultiplier: resolvedRate * appliedPeak,
|
|
ObservedAt: now.UTC(),
|
|
}
|
|
if apiKey.Group.PeakRateEnabled {
|
|
response.PeakStart = &apiKey.Group.PeakStart
|
|
response.PeakEnd = &apiKey.Group.PeakEnd
|
|
response.PeakRateMultiplier = &apiKey.Group.PeakRateMultiplier
|
|
response.AppliedPeakMultiplier = &appliedPeak
|
|
tz := timezone.Location().String()
|
|
response.Timezone = &tz
|
|
}
|
|
return response
|
|
}
|