4a6a211775
Guard updateStateFromHeaders emission with value-change detection to avoid unnecessary computeAggregatedUtilization scans on every proxied response. Replace statusAggregateStateLocked two-value return with comparable statusSnapshot struct. Define statusPayload type for the status wire format, replacing anonymous structs and map literals.
199 lines
5.9 KiB
Go
199 lines
5.9 KiB
Go
package ocm
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"net/http"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/sagernet/sing-box/option"
|
|
)
|
|
|
|
type statusPayload struct {
|
|
FiveHourUtilization float64 `json:"five_hour_utilization"`
|
|
WeeklyUtilization float64 `json:"weekly_utilization"`
|
|
PlanWeight float64 `json:"plan_weight"`
|
|
}
|
|
|
|
func (s *Service) handleStatusEndpoint(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodGet {
|
|
writeJSONError(w, r, http.StatusMethodNotAllowed, "invalid_request_error", "method not allowed")
|
|
return
|
|
}
|
|
|
|
var provider credentialProvider
|
|
var userConfig *option.OCMUser
|
|
if len(s.options.Users) > 0 {
|
|
if r.Header.Get("X-Api-Key") != "" || r.Header.Get("Api-Key") != "" {
|
|
writeJSONError(w, r, http.StatusBadRequest, "invalid_request_error",
|
|
"API key authentication is not supported; use Authorization: Bearer with an OCM user token")
|
|
return
|
|
}
|
|
|
|
authHeader := r.Header.Get("Authorization")
|
|
if authHeader == "" {
|
|
writeJSONError(w, r, http.StatusUnauthorized, "authentication_error", "missing api key")
|
|
return
|
|
}
|
|
clientToken := strings.TrimPrefix(authHeader, "Bearer ")
|
|
if clientToken == authHeader {
|
|
writeJSONError(w, r, http.StatusUnauthorized, "authentication_error", "invalid api key format")
|
|
return
|
|
}
|
|
username, ok := s.userManager.Authenticate(clientToken)
|
|
if !ok {
|
|
writeJSONError(w, r, http.StatusUnauthorized, "authentication_error", "invalid api key")
|
|
return
|
|
}
|
|
|
|
userConfig = s.userConfigMap[username]
|
|
var err error
|
|
provider, err = credentialForUser(s.userConfigMap, s.providers, username)
|
|
if err != nil {
|
|
writeJSONError(w, r, http.StatusInternalServerError, "api_error", err.Error())
|
|
return
|
|
}
|
|
} else {
|
|
provider = s.providers[s.options.Credentials[0].Tag]
|
|
}
|
|
if provider == nil {
|
|
writeJSONError(w, r, http.StatusInternalServerError, "api_error", "no credential available")
|
|
return
|
|
}
|
|
|
|
if r.URL.Query().Get("watch") == "true" {
|
|
s.handleStatusStream(w, r, provider, userConfig)
|
|
return
|
|
}
|
|
|
|
provider.pollIfStale(r.Context())
|
|
avgFiveHour, avgWeekly, totalWeight := s.computeAggregatedUtilization(provider, userConfig)
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.WriteHeader(http.StatusOK)
|
|
json.NewEncoder(w).Encode(statusPayload{
|
|
FiveHourUtilization: avgFiveHour,
|
|
WeeklyUtilization: avgWeekly,
|
|
PlanWeight: totalWeight,
|
|
})
|
|
}
|
|
|
|
func (s *Service) handleStatusStream(w http.ResponseWriter, r *http.Request, provider credentialProvider, userConfig *option.OCMUser) {
|
|
flusher, ok := w.(http.Flusher)
|
|
if !ok {
|
|
writeJSONError(w, r, http.StatusInternalServerError, "api_error", "streaming not supported")
|
|
return
|
|
}
|
|
|
|
subscription, done, err := s.statusObserver.Subscribe()
|
|
if err != nil {
|
|
writeJSONError(w, r, http.StatusInternalServerError, "api_error", "service closing")
|
|
return
|
|
}
|
|
defer s.statusObserver.UnSubscribe(subscription)
|
|
|
|
provider.pollIfStale(r.Context())
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.Header().Set(statusStreamHeader, "true")
|
|
w.WriteHeader(http.StatusOK)
|
|
|
|
lastFiveHour, lastWeekly, lastWeight := s.computeAggregatedUtilization(provider, userConfig)
|
|
buf := &bytes.Buffer{}
|
|
json.NewEncoder(buf).Encode(statusPayload{
|
|
FiveHourUtilization: lastFiveHour,
|
|
WeeklyUtilization: lastWeekly,
|
|
PlanWeight: lastWeight,
|
|
})
|
|
_, writeErr := w.Write(buf.Bytes())
|
|
if writeErr != nil {
|
|
return
|
|
}
|
|
flusher.Flush()
|
|
|
|
for {
|
|
select {
|
|
case <-r.Context().Done():
|
|
return
|
|
case <-done:
|
|
return
|
|
case <-subscription:
|
|
for {
|
|
select {
|
|
case <-subscription:
|
|
default:
|
|
goto drained
|
|
}
|
|
}
|
|
drained:
|
|
fiveHour, weekly, weight := s.computeAggregatedUtilization(provider, userConfig)
|
|
if fiveHour == lastFiveHour && weekly == lastWeekly && weight == lastWeight {
|
|
continue
|
|
}
|
|
lastFiveHour = fiveHour
|
|
lastWeekly = weekly
|
|
lastWeight = weight
|
|
buf.Reset()
|
|
json.NewEncoder(buf).Encode(statusPayload{
|
|
FiveHourUtilization: fiveHour,
|
|
WeeklyUtilization: weekly,
|
|
PlanWeight: weight,
|
|
})
|
|
_, writeErr = w.Write(buf.Bytes())
|
|
if writeErr != nil {
|
|
return
|
|
}
|
|
flusher.Flush()
|
|
}
|
|
}
|
|
}
|
|
|
|
func (s *Service) computeAggregatedUtilization(provider credentialProvider, userConfig *option.OCMUser) (float64, float64, float64) {
|
|
var totalWeightedRemaining5h, totalWeightedRemainingWeekly, totalWeight float64
|
|
for _, credential := range provider.allCredentials() {
|
|
if !credential.isAvailable() {
|
|
continue
|
|
}
|
|
if userConfig != nil && userConfig.ExternalCredential != "" && credential.tagName() == userConfig.ExternalCredential {
|
|
continue
|
|
}
|
|
if userConfig != nil && !userConfig.AllowExternalUsage && credential.isExternal() {
|
|
continue
|
|
}
|
|
weight := credential.planWeight()
|
|
remaining5h := credential.fiveHourCap() - credential.fiveHourUtilization()
|
|
if remaining5h < 0 {
|
|
remaining5h = 0
|
|
}
|
|
remainingWeekly := credential.weeklyCap() - credential.weeklyUtilization()
|
|
if remainingWeekly < 0 {
|
|
remainingWeekly = 0
|
|
}
|
|
totalWeightedRemaining5h += remaining5h * weight
|
|
totalWeightedRemainingWeekly += remainingWeekly * weight
|
|
totalWeight += weight
|
|
}
|
|
if totalWeight == 0 {
|
|
return 100, 100, 0
|
|
}
|
|
return 100 - totalWeightedRemaining5h/totalWeight,
|
|
100 - totalWeightedRemainingWeekly/totalWeight,
|
|
totalWeight
|
|
}
|
|
|
|
func (s *Service) rewriteResponseHeadersForExternalUser(headers http.Header, provider credentialProvider, userConfig *option.OCMUser) {
|
|
avgFiveHour, avgWeekly, totalWeight := s.computeAggregatedUtilization(provider, userConfig)
|
|
|
|
activeLimitIdentifier := normalizeRateLimitIdentifier(headers.Get("x-codex-active-limit"))
|
|
if activeLimitIdentifier == "" {
|
|
activeLimitIdentifier = "codex"
|
|
}
|
|
|
|
headers.Set("x-"+activeLimitIdentifier+"-primary-used-percent", strconv.FormatFloat(avgFiveHour, 'f', 2, 64))
|
|
headers.Set("x-"+activeLimitIdentifier+"-secondary-used-percent", strconv.FormatFloat(avgWeekly, 'f', 2, 64))
|
|
if totalWeight > 0 {
|
|
headers.Set("X-OCM-Plan-Weight", strconv.FormatFloat(totalWeight, 'f', -1, 64))
|
|
}
|
|
}
|