新增 DB/Redis 连接池配置与校验,并补充单测 网关请求体大小限制与 413 处理 HTTP/req 客户端池化并调整上游连接池默认值 并发槽位改为 ZSET+Lua 与指数退避 用量统计改 SQL 聚合并新增索引迁移 计费缓存写入改工作池并补测试/基准 测试: 在 backend/ 下运行 go test ./...
90 lines
2.2 KiB
Go
90 lines
2.2 KiB
Go
package repository
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/url"
|
|
"time"
|
|
|
|
"github.com/Wei-Shaw/sub2api/internal/pkg/openai"
|
|
"github.com/Wei-Shaw/sub2api/internal/service"
|
|
"github.com/imroc/req/v3"
|
|
)
|
|
|
|
// NewOpenAIOAuthClient creates a new OpenAI OAuth client
|
|
func NewOpenAIOAuthClient() service.OpenAIOAuthClient {
|
|
return &openaiOAuthService{tokenURL: openai.TokenURL}
|
|
}
|
|
|
|
type openaiOAuthService struct {
|
|
tokenURL string
|
|
}
|
|
|
|
func (s *openaiOAuthService) ExchangeCode(ctx context.Context, code, codeVerifier, redirectURI, proxyURL string) (*openai.TokenResponse, error) {
|
|
client := createOpenAIReqClient(proxyURL)
|
|
|
|
if redirectURI == "" {
|
|
redirectURI = openai.DefaultRedirectURI
|
|
}
|
|
|
|
formData := url.Values{}
|
|
formData.Set("grant_type", "authorization_code")
|
|
formData.Set("client_id", openai.ClientID)
|
|
formData.Set("code", code)
|
|
formData.Set("redirect_uri", redirectURI)
|
|
formData.Set("code_verifier", codeVerifier)
|
|
|
|
var tokenResp openai.TokenResponse
|
|
|
|
resp, err := client.R().
|
|
SetContext(ctx).
|
|
SetFormDataFromValues(formData).
|
|
SetSuccessResult(&tokenResp).
|
|
Post(s.tokenURL)
|
|
|
|
if err != nil {
|
|
return nil, fmt.Errorf("request failed: %w", err)
|
|
}
|
|
|
|
if !resp.IsSuccessState() {
|
|
return nil, fmt.Errorf("token exchange failed: status %d, body: %s", resp.StatusCode, resp.String())
|
|
}
|
|
|
|
return &tokenResp, nil
|
|
}
|
|
|
|
func (s *openaiOAuthService) RefreshToken(ctx context.Context, refreshToken, proxyURL string) (*openai.TokenResponse, error) {
|
|
client := createOpenAIReqClient(proxyURL)
|
|
|
|
formData := url.Values{}
|
|
formData.Set("grant_type", "refresh_token")
|
|
formData.Set("refresh_token", refreshToken)
|
|
formData.Set("client_id", openai.ClientID)
|
|
formData.Set("scope", openai.RefreshScopes)
|
|
|
|
var tokenResp openai.TokenResponse
|
|
|
|
resp, err := client.R().
|
|
SetContext(ctx).
|
|
SetFormDataFromValues(formData).
|
|
SetSuccessResult(&tokenResp).
|
|
Post(s.tokenURL)
|
|
|
|
if err != nil {
|
|
return nil, fmt.Errorf("request failed: %w", err)
|
|
}
|
|
|
|
if !resp.IsSuccessState() {
|
|
return nil, fmt.Errorf("token refresh failed: status %d, body: %s", resp.StatusCode, resp.String())
|
|
}
|
|
|
|
return &tokenResp, nil
|
|
}
|
|
|
|
func createOpenAIReqClient(proxyURL string) *req.Client {
|
|
return getSharedReqClient(reqClientOptions{
|
|
ProxyURL: proxyURL,
|
|
Timeout: 60 * time.Second,
|
|
})
|
|
}
|