feat(repository): 实现 Gemini OAuth 和 Token 缓存客户端
- 添加 Gemini OAuth 客户端实现 - 实现 Redis 基础的 Token 缓存 - 添加 gemini-cli Code Assist 客户端封装
This commit is contained in:
84
backend/internal/repository/gemini_oauth_client.go
Normal file
84
backend/internal/repository/gemini_oauth_client.go
Normal file
@@ -0,0 +1,84 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/Wei-Shaw/sub2api/internal/config"
|
||||
"github.com/Wei-Shaw/sub2api/internal/pkg/geminicli"
|
||||
"github.com/Wei-Shaw/sub2api/internal/service/ports"
|
||||
|
||||
"github.com/imroc/req/v3"
|
||||
)
|
||||
|
||||
type geminiOAuthClient struct {
|
||||
tokenURL string
|
||||
cfg *config.Config
|
||||
}
|
||||
|
||||
func NewGeminiOAuthClient(cfg *config.Config) ports.GeminiOAuthClient {
|
||||
return &geminiOAuthClient{
|
||||
tokenURL: geminicli.TokenURL,
|
||||
cfg: cfg,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *geminiOAuthClient) ExchangeCode(ctx context.Context, code, codeVerifier, redirectURI, proxyURL string) (*geminicli.TokenResponse, error) {
|
||||
client := createGeminiReqClient(proxyURL)
|
||||
|
||||
formData := url.Values{}
|
||||
formData.Set("grant_type", "authorization_code")
|
||||
formData.Set("client_id", c.cfg.Gemini.OAuth.ClientID)
|
||||
formData.Set("client_secret", c.cfg.Gemini.OAuth.ClientSecret)
|
||||
formData.Set("code", code)
|
||||
formData.Set("code_verifier", codeVerifier)
|
||||
formData.Set("redirect_uri", redirectURI)
|
||||
|
||||
var tokenResp geminicli.TokenResponse
|
||||
resp, err := client.R().
|
||||
SetContext(ctx).
|
||||
SetFormDataFromValues(formData).
|
||||
SetSuccessResult(&tokenResp).
|
||||
Post(c.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, geminicli.SanitizeBodyForLogs(resp.String()))
|
||||
}
|
||||
return &tokenResp, nil
|
||||
}
|
||||
|
||||
func (c *geminiOAuthClient) RefreshToken(ctx context.Context, refreshToken, proxyURL string) (*geminicli.TokenResponse, error) {
|
||||
client := createGeminiReqClient(proxyURL)
|
||||
|
||||
formData := url.Values{}
|
||||
formData.Set("grant_type", "refresh_token")
|
||||
formData.Set("refresh_token", refreshToken)
|
||||
formData.Set("client_id", c.cfg.Gemini.OAuth.ClientID)
|
||||
formData.Set("client_secret", c.cfg.Gemini.OAuth.ClientSecret)
|
||||
|
||||
var tokenResp geminicli.TokenResponse
|
||||
resp, err := client.R().
|
||||
SetContext(ctx).
|
||||
SetFormDataFromValues(formData).
|
||||
SetSuccessResult(&tokenResp).
|
||||
Post(c.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, geminicli.SanitizeBodyForLogs(resp.String()))
|
||||
}
|
||||
return &tokenResp, nil
|
||||
}
|
||||
|
||||
func createGeminiReqClient(proxyURL string) *req.Client {
|
||||
client := req.C().SetTimeout(60 * time.Second)
|
||||
if proxyURL != "" {
|
||||
client.SetProxyURL(proxyURL)
|
||||
}
|
||||
return client
|
||||
}
|
||||
Reference in New Issue
Block a user