Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
Menu
Open sidebar
陈曦
sub2api
Commits
3dab7172
Commit
3dab7172
authored
Jan 19, 2026
by
shaw
Browse files
feat: usage接口支持TLS指纹和缓存User-Agent
parent
090c8981
Changes
3
Hide whitespace changes
Inline
Side-by-side
backend/cmd/server/wire_gen.go
View file @
3dab7172
...
...
@@ -105,14 +105,15 @@ func initializeApplication(buildInfo handler.BuildInfo) (*Application, error) {
geminiTokenCache
:=
repository
.
NewGeminiTokenCache
(
redisClient
)
compositeTokenCacheInvalidator
:=
service
.
NewCompositeTokenCacheInvalidator
(
geminiTokenCache
)
rateLimitService
:=
service
.
ProvideRateLimitService
(
accountRepository
,
usageLogRepository
,
configConfig
,
geminiQuotaService
,
tempUnschedCache
,
timeoutCounterCache
,
settingService
,
compositeTokenCacheInvalidator
)
claudeUsageFetcher
:=
repository
.
NewClaudeUsageFetcher
()
httpUpstream
:=
repository
.
NewHTTPUpstream
(
configConfig
)
claudeUsageFetcher
:=
repository
.
NewClaudeUsageFetcher
(
httpUpstream
)
antigravityQuotaFetcher
:=
service
.
NewAntigravityQuotaFetcher
(
proxyRepository
)
usageCache
:=
service
.
NewUsageCache
()
accountUsageService
:=
service
.
NewAccountUsageService
(
accountRepository
,
usageLogRepository
,
claudeUsageFetcher
,
geminiQuotaService
,
antigravityQuotaFetcher
,
usageCache
)
identityCache
:=
repository
.
NewIdentityCache
(
redisClient
)
accountUsageService
:=
service
.
NewAccountUsageService
(
accountRepository
,
usageLogRepository
,
claudeUsageFetcher
,
geminiQuotaService
,
antigravityQuotaFetcher
,
usageCache
,
identityCache
)
geminiTokenProvider
:=
service
.
NewGeminiTokenProvider
(
accountRepository
,
geminiTokenCache
,
geminiOAuthService
)
gatewayCache
:=
repository
.
NewGatewayCache
(
redisClient
)
antigravityTokenProvider
:=
service
.
NewAntigravityTokenProvider
(
accountRepository
,
geminiTokenCache
,
antigravityOAuthService
)
httpUpstream
:=
repository
.
NewHTTPUpstream
(
configConfig
)
antigravityGatewayService
:=
service
.
NewAntigravityGatewayService
(
accountRepository
,
gatewayCache
,
antigravityTokenProvider
,
rateLimitService
,
httpUpstream
,
settingService
)
accountTestService
:=
service
.
NewAccountTestService
(
accountRepository
,
geminiTokenProvider
,
antigravityGatewayService
,
httpUpstream
,
configConfig
)
concurrencyCache
:=
repository
.
ProvideConcurrencyCache
(
redisClient
,
configConfig
)
...
...
@@ -137,7 +138,6 @@ func initializeApplication(buildInfo handler.BuildInfo) (*Application, error) {
return
nil
,
err
}
billingService
:=
service
.
NewBillingService
(
configConfig
,
pricingService
)
identityCache
:=
repository
.
NewIdentityCache
(
redisClient
)
identityService
:=
service
.
NewIdentityService
(
identityCache
)
deferredService
:=
service
.
ProvideDeferredService
(
accountRepository
,
timingWheelService
)
claudeTokenProvider
:=
service
.
NewClaudeTokenProvider
(
accountRepository
,
geminiTokenCache
,
oAuthService
)
...
...
backend/internal/repository/claude_usage_service.go
View file @
3dab7172
...
...
@@ -14,37 +14,82 @@ import (
const
defaultClaudeUsageURL
=
"https://api.anthropic.com/api/oauth/usage"
// 默认 User-Agent,与用户抓包的请求一致
const
defaultUsageUserAgent
=
"claude-code/2.1.7"
type
claudeUsageService
struct
{
usageURL
string
allowPrivateHosts
bool
httpUpstream
service
.
HTTPUpstream
}
func
NewClaudeUsageFetcher
()
service
.
ClaudeUsageFetcher
{
return
&
claudeUsageService
{
usageURL
:
defaultClaudeUsageURL
}
// NewClaudeUsageFetcher 创建 Claude 用量获取服务
// httpUpstream: 可选,如果提供则支持 TLS 指纹伪装
func
NewClaudeUsageFetcher
(
httpUpstream
service
.
HTTPUpstream
)
service
.
ClaudeUsageFetcher
{
return
&
claudeUsageService
{
usageURL
:
defaultClaudeUsageURL
,
httpUpstream
:
httpUpstream
,
}
}
// FetchUsage 简单版本,不支持 TLS 指纹(向后兼容)
func
(
s
*
claudeUsageService
)
FetchUsage
(
ctx
context
.
Context
,
accessToken
,
proxyURL
string
)
(
*
service
.
ClaudeUsageResponse
,
error
)
{
client
,
err
:=
httpclient
.
GetClient
(
httpclient
.
Options
{
ProxyURL
:
proxyURL
,
Timeout
:
30
*
time
.
Second
,
ValidateResolvedIP
:
true
,
AllowPrivateHosts
:
s
.
allowPrivateHosts
,
return
s
.
FetchUsageWithOptions
(
ctx
,
&
service
.
ClaudeUsageFetchOptions
{
AccessToken
:
accessToken
,
ProxyURL
:
proxyURL
,
})
if
err
!=
nil
{
client
=
&
http
.
Client
{
Timeout
:
30
*
time
.
Second
}
}
// FetchUsageWithOptions 完整版本,支持 TLS 指纹和自定义 User-Agent
func
(
s
*
claudeUsageService
)
FetchUsageWithOptions
(
ctx
context
.
Context
,
opts
*
service
.
ClaudeUsageFetchOptions
)
(
*
service
.
ClaudeUsageResponse
,
error
)
{
if
opts
==
nil
{
return
nil
,
fmt
.
Errorf
(
"options is nil"
)
}
// 创建请求
req
,
err
:=
http
.
NewRequestWithContext
(
ctx
,
"GET"
,
s
.
usageURL
,
nil
)
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"create request failed: %w"
,
err
)
}
req
.
Header
.
Set
(
"Authorization"
,
"Bearer "
+
accessToken
)
// 设置请求头(与抓包一致,但不设置 Accept-Encoding,让 Go 自动处理压缩)
req
.
Header
.
Set
(
"Accept"
,
"application/json, text/plain, */*"
)
req
.
Header
.
Set
(
"Content-Type"
,
"application/json"
)
req
.
Header
.
Set
(
"Authorization"
,
"Bearer "
+
opts
.
AccessToken
)
req
.
Header
.
Set
(
"anthropic-beta"
,
"oauth-2025-04-20"
)
resp
,
err
:=
client
.
Do
(
req
)
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"request failed: %w"
,
err
)
// 设置 User-Agent(优先使用缓存的 Fingerprint,否则使用默认值)
userAgent
:=
defaultUsageUserAgent
if
opts
.
Fingerprint
!=
nil
&&
opts
.
Fingerprint
.
UserAgent
!=
""
{
userAgent
=
opts
.
Fingerprint
.
UserAgent
}
req
.
Header
.
Set
(
"User-Agent"
,
userAgent
)
var
resp
*
http
.
Response
// 如果启用 TLS 指纹且有 HTTPUpstream,使用 DoWithTLS
if
opts
.
EnableTLSFingerprint
&&
s
.
httpUpstream
!=
nil
{
// accountConcurrency 传 0 使用默认连接池配置,usage 请求不需要特殊的并发设置
resp
,
err
=
s
.
httpUpstream
.
DoWithTLS
(
req
,
opts
.
ProxyURL
,
opts
.
AccountID
,
0
,
true
)
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"request with TLS fingerprint failed: %w"
,
err
)
}
}
else
{
// 不启用 TLS 指纹,使用普通 HTTP 客户端
client
,
err
:=
httpclient
.
GetClient
(
httpclient
.
Options
{
ProxyURL
:
opts
.
ProxyURL
,
Timeout
:
30
*
time
.
Second
,
ValidateResolvedIP
:
true
,
AllowPrivateHosts
:
s
.
allowPrivateHosts
,
})
if
err
!=
nil
{
client
=
&
http
.
Client
{
Timeout
:
30
*
time
.
Second
}
}
resp
,
err
=
client
.
Do
(
req
)
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"request failed: %w"
,
err
)
}
}
defer
func
()
{
_
=
resp
.
Body
.
Close
()
}()
...
...
backend/internal/service/account_usage_service.go
View file @
3dab7172
...
...
@@ -157,9 +157,20 @@ type ClaudeUsageResponse struct {
}
`json:"seven_day_sonnet"`
}
// ClaudeUsageFetchOptions 包含获取 Claude 用量数据所需的所有选项
type
ClaudeUsageFetchOptions
struct
{
AccessToken
string
// OAuth access token
ProxyURL
string
// 代理 URL(可选)
AccountID
int64
// 账号 ID(用于 TLS 指纹选择)
EnableTLSFingerprint
bool
// 是否启用 TLS 指纹伪装
Fingerprint
*
Fingerprint
// 缓存的指纹信息(User-Agent 等)
}
// ClaudeUsageFetcher fetches usage data from Anthropic OAuth API
type
ClaudeUsageFetcher
interface
{
FetchUsage
(
ctx
context
.
Context
,
accessToken
,
proxyURL
string
)
(
*
ClaudeUsageResponse
,
error
)
// FetchUsageWithOptions 使用完整选项获取用量数据,支持 TLS 指纹和自定义 User-Agent
FetchUsageWithOptions
(
ctx
context
.
Context
,
opts
*
ClaudeUsageFetchOptions
)
(
*
ClaudeUsageResponse
,
error
)
}
// AccountUsageService 账号使用量查询服务
...
...
@@ -170,6 +181,7 @@ type AccountUsageService struct {
geminiQuotaService
*
GeminiQuotaService
antigravityQuotaFetcher
*
AntigravityQuotaFetcher
cache
*
UsageCache
identityCache
IdentityCache
}
// NewAccountUsageService 创建AccountUsageService实例
...
...
@@ -180,6 +192,7 @@ func NewAccountUsageService(
geminiQuotaService
*
GeminiQuotaService
,
antigravityQuotaFetcher
*
AntigravityQuotaFetcher
,
cache
*
UsageCache
,
identityCache
IdentityCache
,
)
*
AccountUsageService
{
return
&
AccountUsageService
{
accountRepo
:
accountRepo
,
...
...
@@ -188,6 +201,7 @@ func NewAccountUsageService(
geminiQuotaService
:
geminiQuotaService
,
antigravityQuotaFetcher
:
antigravityQuotaFetcher
,
cache
:
cache
,
identityCache
:
identityCache
,
}
}
...
...
@@ -424,6 +438,8 @@ func (s *AccountUsageService) GetAccountUsageStats(ctx context.Context, accountI
}
// fetchOAuthUsageRaw 从 Anthropic API 获取原始响应(不构建 UsageInfo)
// 如果账号开启了 TLS 指纹,则使用 TLS 指纹伪装
// 如果有缓存的 Fingerprint,则使用缓存的 User-Agent 等信息
func
(
s
*
AccountUsageService
)
fetchOAuthUsageRaw
(
ctx
context
.
Context
,
account
*
Account
)
(
*
ClaudeUsageResponse
,
error
)
{
accessToken
:=
account
.
GetCredential
(
"access_token"
)
if
accessToken
==
""
{
...
...
@@ -435,7 +451,22 @@ func (s *AccountUsageService) fetchOAuthUsageRaw(ctx context.Context, account *A
proxyURL
=
account
.
Proxy
.
URL
()
}
return
s
.
usageFetcher
.
FetchUsage
(
ctx
,
accessToken
,
proxyURL
)
// 构建完整的选项
opts
:=
&
ClaudeUsageFetchOptions
{
AccessToken
:
accessToken
,
ProxyURL
:
proxyURL
,
AccountID
:
account
.
ID
,
EnableTLSFingerprint
:
account
.
IsTLSFingerprintEnabled
(),
}
// 尝试获取缓存的 Fingerprint(包含 User-Agent 等信息)
if
s
.
identityCache
!=
nil
{
if
fp
,
err
:=
s
.
identityCache
.
GetFingerprint
(
ctx
,
account
.
ID
);
err
==
nil
&&
fp
!=
nil
{
opts
.
Fingerprint
=
fp
}
}
return
s
.
usageFetcher
.
FetchUsageWithOptions
(
ctx
,
opts
)
}
// parseTime 尝试多种格式解析时间
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment