package geminicli import ( "context" "encoding/json" "fmt" "math/rand" "net/http" "strconv" "time" "github.com/Wei-Shaw/sub2api/internal/pkg/httpclient" ) // DriveStorageInfo represents Google Drive storage quota information type DriveStorageInfo struct { Limit int64 `json:"limit"` // Storage limit in bytes Usage int64 `json:"usage"` // Current usage in bytes } // DriveClient interface for Google Drive API operations type DriveClient interface { GetStorageQuota(ctx context.Context, accessToken, proxyURL string) (*DriveStorageInfo, error) } type driveClient struct{} // NewDriveClient creates a new Drive API client func NewDriveClient() DriveClient { return &driveClient{} } // GetStorageQuota fetches storage quota from Google Drive API func (c *driveClient) GetStorageQuota(ctx context.Context, accessToken, proxyURL string) (*DriveStorageInfo, error) { const driveAPIURL = "https://www.googleapis.com/drive/v3/about?fields=storageQuota" req, err := http.NewRequestWithContext(ctx, "GET", driveAPIURL, nil) if err != nil { return nil, fmt.Errorf("failed to create request: %w", err) } req.Header.Set("Authorization", "Bearer "+accessToken) // Get HTTP client with proxy support client, err := httpclient.GetClient(httpclient.Options{ ProxyURL: proxyURL, Timeout: 10 * time.Second, }) if err != nil { return nil, fmt.Errorf("failed to create HTTP client: %w", err) } sleepWithContext := func(d time.Duration) error { timer := time.NewTimer(d) defer timer.Stop() select { case <-ctx.Done(): return ctx.Err() case <-timer.C: return nil } } // Retry logic with exponential backoff (+ jitter) for rate limits and transient failures var resp *http.Response maxRetries := 3 rng := rand.New(rand.NewSource(time.Now().UnixNano())) for attempt := 0; attempt < maxRetries; attempt++ { if ctx.Err() != nil { return nil, fmt.Errorf("request cancelled: %w", ctx.Err()) } resp, err = client.Do(req) if err != nil { // Network error retry if attempt < maxRetries-1 { backoff := time.Duration(1<