sub2api/backend/internal/service/windsurf_token_provider.go
win 21325afb33
Some checks failed
CI / test (push) Failing after 10s
CI / frontend (push) Failing after 8s
CI / golangci-lint (push) Failing after 5s
Security Scan / backend-security (push) Failing after 5s
Security Scan / frontend-security (push) Failing after 4s
feat(windsurf): 补全ops日志记录与endpoint派生,对齐其他平台
- windsurf_gateway_service: 添加上游延迟/TTFT/错误上下文记录
- endpoint: DeriveUpstreamEndpoint 添加 PlatformWindsurf 分支
- ops_error_logger: guessPlatformFromPath 添加 /windsurf/ 识别
2026-04-23 20:46:27 +08:00

115 lines
3.1 KiB
Go

package service
import (
"context"
"fmt"
"time"
"github.com/Wei-Shaw/sub2api/internal/config"
"github.com/Wei-Shaw/sub2api/internal/domain"
"github.com/Wei-Shaw/sub2api/internal/pkg/windsurf"
)
type WindsurfTokenProvider struct {
cfg config.WindsurfConfig
accountRepo AccountRepository
proxyRepo ProxyRepository
authClient *windsurf.AuthClient
}
func NewWindsurfTokenProvider(
cfg config.WindsurfConfig,
accountRepo AccountRepository,
proxyRepo ProxyRepository,
authClient *windsurf.AuthClient,
) *WindsurfTokenProvider {
return &WindsurfTokenProvider{
cfg: cfg,
accountRepo: accountRepo,
proxyRepo: proxyRepo,
authClient: authClient,
}
}
type WindsurfToken struct {
APIKey string
ProxyURL string
AccountID int64
Tier string
LSBinding WindsurfLSBinding
}
func (p *WindsurfTokenProvider) GetToken(ctx context.Context, accountID int64) (*WindsurfToken, error) {
account, err := p.accountRepo.GetByID(ctx, accountID)
if err != nil {
return nil, fmt.Errorf("get account: %w", err)
}
if account.Platform != domain.PlatformWindsurf {
return nil, fmt.Errorf("account %d is not a windsurf account", accountID)
}
creds := LoadWindsurfCredentials(account.Credentials)
if creds.APIKey == "" {
return nil, fmt.Errorf("account %d has no api_key", accountID)
}
proxyURL := ""
if account.ProxyID != nil {
proxy, err := p.proxyRepo.GetByID(ctx, *account.ProxyID)
if err == nil {
proxyURL = proxy.URL()
}
}
if creds.NeedsRefresh(p.cfg.Refresh.RefreshBeforeExpiry) {
if refreshErr := p.refreshInline(ctx, account, &creds, proxyURL); refreshErr != nil {
if !creds.IsExpired() {
extra := LoadWindsurfExtra(account.Extra)
return &WindsurfToken{
APIKey: creds.APIKey,
ProxyURL: proxyURL,
AccountID: accountID,
Tier: creds.Tier,
LSBinding: extra.LSBinding,
}, nil
}
return nil, fmt.Errorf("token expired and refresh failed: %w", refreshErr)
}
}
extra := LoadWindsurfExtra(account.Extra)
return &WindsurfToken{
APIKey: creds.APIKey,
ProxyURL: proxyURL,
AccountID: accountID,
Tier: creds.Tier,
LSBinding: extra.LSBinding,
}, nil
}
func (p *WindsurfTokenProvider) refreshInline(ctx context.Context, account *Account, creds *WindsurfCredentials, proxyURL string) error {
if creds.AuthMethod != "firebase" || creds.RefreshToken == "" {
return fmt.Errorf("cannot refresh: auth_method=%s", creds.AuthMethod)
}
result, err := p.authClient.RefreshFirebaseToken(ctx, creds.RefreshToken, proxyURL)
if err != nil {
return err
}
creds.IDToken = result.IDToken
creds.RefreshToken = result.RefreshToken
creds.ExpiresAt = time.Now().Add(time.Duration(result.ExpiresIn) * time.Second).Format(time.RFC3339)
creds.LastRefreshAt = time.Now().Format(time.RFC3339)
regResult, err := p.authClient.ReRegisterWithCodeium(ctx, result.IDToken, proxyURL)
if err == nil {
creds.APIKey = regResult.APIKey
creds.LastReregisterAt = time.Now().Format(time.RFC3339)
}
account.Credentials = StoreWindsurfCredentials(*creds)
return p.accountRepo.Update(ctx, account)
}