mirror of
https://github.com/router-for-me/CLIProxyAPI.git
synced 2026-02-02 20:40:52 +08:00
Add SOCKS5 and HTTP/HTTPS proxy support
- Updated `GetAuthenticatedClient` to handle proxy configuration via `proxy-url`. - Extended `Config` to include `proxy-url` property. - Adjusted error handling and removed unused JSON error response logic for API handlers. - Updated documentation and configuration examples to reflect new proxy settings.
This commit is contained in:
13
README.md
13
README.md
@@ -146,12 +146,13 @@ The server uses a YAML configuration file (`config.yaml`) located in the project
|
|||||||
|
|
||||||
### Configuration Options
|
### Configuration Options
|
||||||
|
|
||||||
| Parameter | Type | Default | Description |
|
| Parameter | Type | Default | Description |
|
||||||
|-----------|------|--------------------|-------------|
|
|-------------|----------|--------------------|----------------------------------------------------------------------------------------------|
|
||||||
| `port` | integer | 8317 | The port number on which the server will listen |
|
| `port` | integer | 8317 | The port number on which the server will listen |
|
||||||
| `auth_dir` | string | "~/.cli-proxy-api" | Directory where authentication tokens are stored. Supports using `~` for home directory |
|
| `auth_dir` | string | "~/.cli-proxy-api" | Directory where authentication tokens are stored. Supports using `~` for home directory |
|
||||||
| `debug` | boolean | false | Enable debug mode for verbose logging |
|
| `proxy-url` | string | "" | Proxy url, support socks5/http/https protocol, example: socks5://user:pass@192.168.1.1:1080/ |
|
||||||
| `api_keys` | string[] | [] | List of API keys that can be used to authenticate requests |
|
| `debug` | boolean | false | Enable debug mode for verbose logging |
|
||||||
|
| `api_keys` | string[] | [] | List of API keys that can be used to authenticate requests |
|
||||||
|
|
||||||
### Example Configuration File
|
### Example Configuration File
|
||||||
|
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ func main() {
|
|||||||
clientCtx := context.Background()
|
clientCtx := context.Background()
|
||||||
|
|
||||||
log.Info("Initializing authentication...")
|
log.Info("Initializing authentication...")
|
||||||
httpClient, errGetClient := auth.GetAuthenticatedClient(clientCtx, &ts, cfg.AuthDir)
|
httpClient, errGetClient := auth.GetAuthenticatedClient(clientCtx, &ts, cfg)
|
||||||
if errGetClient != nil {
|
if errGetClient != nil {
|
||||||
log.Fatalf("failed to get authenticated client: %v", errGetClient)
|
log.Fatalf("failed to get authenticated client: %v", errGetClient)
|
||||||
return
|
return
|
||||||
@@ -165,7 +165,7 @@ func main() {
|
|||||||
clientCtx := context.Background()
|
clientCtx := context.Background()
|
||||||
|
|
||||||
log.Info("Initializing authentication...")
|
log.Info("Initializing authentication...")
|
||||||
httpClient, errGetClient := auth.GetAuthenticatedClient(clientCtx, &ts, cfg.AuthDir)
|
httpClient, errGetClient := auth.GetAuthenticatedClient(clientCtx, &ts, cfg)
|
||||||
if errGetClient != nil {
|
if errGetClient != nil {
|
||||||
log.Fatalf("failed to get authenticated client: %v", errGetClient)
|
log.Fatalf("failed to get authenticated client: %v", errGetClient)
|
||||||
return errGetClient
|
return errGetClient
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
port: 8317
|
port: 8317
|
||||||
auth_dir: "~/.cli-proxy-api"
|
auth_dir: "~/.cli-proxy-api"
|
||||||
debug: false
|
debug: false
|
||||||
|
proxy-url: ""
|
||||||
api_keys:
|
api_keys:
|
||||||
- "12345"
|
- "12345"
|
||||||
- "23456"
|
- "23456"
|
||||||
@@ -429,12 +429,15 @@ func (h *APIHandlers) handleNonStreamingResponse(c *gin.Context, rawJson []byte)
|
|||||||
}
|
}
|
||||||
case err, okError := <-errChan:
|
case err, okError := <-errChan:
|
||||||
if okError {
|
if okError {
|
||||||
c.JSON(http.StatusInternalServerError, ErrorResponse{
|
c.Status(http.StatusInternalServerError)
|
||||||
Error: ErrorDetail{
|
_, _ = fmt.Fprint(c.Writer, err.Error())
|
||||||
Message: err.Error(),
|
flusher.Flush()
|
||||||
Type: "server_error",
|
// c.JSON(http.StatusInternalServerError, ErrorResponse{
|
||||||
},
|
// Error: ErrorDetail{
|
||||||
})
|
// Message: err.Error(),
|
||||||
|
// Type: "server_error",
|
||||||
|
// },
|
||||||
|
// })
|
||||||
cliCancel()
|
cliCancel()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -523,12 +526,15 @@ func (h *APIHandlers) handleStreamingResponse(c *gin.Context, rawJson []byte) {
|
|||||||
}
|
}
|
||||||
case err, okError := <-errChan:
|
case err, okError := <-errChan:
|
||||||
if okError {
|
if okError {
|
||||||
c.JSON(http.StatusInternalServerError, ErrorResponse{
|
c.Status(http.StatusInternalServerError)
|
||||||
Error: ErrorDetail{
|
_, _ = fmt.Fprint(c.Writer, err.Error())
|
||||||
Message: err.Error(),
|
flusher.Flush()
|
||||||
Type: "server_error",
|
// c.JSON(http.StatusInternalServerError, ErrorResponse{
|
||||||
},
|
// Error: ErrorDetail{
|
||||||
})
|
// Message: err.Error(),
|
||||||
|
// Type: "server_error",
|
||||||
|
// },
|
||||||
|
// })
|
||||||
cliCancel()
|
cliCancel()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,10 +5,14 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/luispater/CLIProxyAPI/internal/config"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"github.com/tidwall/gjson"
|
"github.com/tidwall/gjson"
|
||||||
|
"golang.org/x/net/proxy"
|
||||||
"io"
|
"io"
|
||||||
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"time"
|
"time"
|
||||||
@@ -39,7 +43,42 @@ type TokenStorage struct {
|
|||||||
|
|
||||||
// GetAuthenticatedClient configures and returns an HTTP client with OAuth2 tokens.
|
// GetAuthenticatedClient configures and returns an HTTP client with OAuth2 tokens.
|
||||||
// It handles the entire flow: loading, refreshing, and fetching new tokens.
|
// It handles the entire flow: loading, refreshing, and fetching new tokens.
|
||||||
func GetAuthenticatedClient(ctx context.Context, ts *TokenStorage, authDir string) (*http.Client, error) {
|
func GetAuthenticatedClient(ctx context.Context, ts *TokenStorage, cfg *config.Config) (*http.Client, error) {
|
||||||
|
proxyURL, err := url.Parse(cfg.ProxyUrl)
|
||||||
|
if err == nil {
|
||||||
|
if proxyURL.Scheme == "socks5" {
|
||||||
|
username := proxyURL.User.Username()
|
||||||
|
password, _ := proxyURL.User.Password()
|
||||||
|
auth := &proxy.Auth{
|
||||||
|
User: username,
|
||||||
|
Password: password,
|
||||||
|
}
|
||||||
|
dialer, errSOCKS5 := proxy.SOCKS5("tcp", proxyURL.Host, auth, proxy.Direct)
|
||||||
|
if errSOCKS5 != nil {
|
||||||
|
log.Fatalf("create SOCKS5 dialer failed: %v", errSOCKS5)
|
||||||
|
}
|
||||||
|
|
||||||
|
transport := &http.Transport{
|
||||||
|
DialContext: func(ctx context.Context, network, addr string) (c net.Conn, err error) {
|
||||||
|
return dialer.Dial(network, addr)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
proxyClient := &http.Client{
|
||||||
|
Transport: transport,
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx = context.WithValue(ctx, oauth2.HTTPClient, proxyClient)
|
||||||
|
} else if proxyURL.Scheme == "http" || proxyURL.Scheme == "https" {
|
||||||
|
transport := &http.Transport{
|
||||||
|
Proxy: http.ProxyURL(proxyURL),
|
||||||
|
}
|
||||||
|
proxyClient := &http.Client{
|
||||||
|
Transport: transport,
|
||||||
|
}
|
||||||
|
ctx = context.WithValue(ctx, oauth2.HTTPClient, proxyClient)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
conf := &oauth2.Config{
|
conf := &oauth2.Config{
|
||||||
ClientID: oauthClientID,
|
ClientID: oauthClientID,
|
||||||
ClientSecret: oauthClientSecret,
|
ClientSecret: oauthClientSecret,
|
||||||
@@ -49,7 +88,6 @@ func GetAuthenticatedClient(ctx context.Context, ts *TokenStorage, authDir strin
|
|||||||
}
|
}
|
||||||
|
|
||||||
var token *oauth2.Token
|
var token *oauth2.Token
|
||||||
var err error
|
|
||||||
|
|
||||||
if ts.Token == nil {
|
if ts.Token == nil {
|
||||||
log.Info("Could not load token from file, starting OAuth flow.")
|
log.Info("Could not load token from file, starting OAuth flow.")
|
||||||
@@ -57,7 +95,7 @@ func GetAuthenticatedClient(ctx context.Context, ts *TokenStorage, authDir strin
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to get token from web: %w", err)
|
return nil, fmt.Errorf("failed to get token from web: %w", err)
|
||||||
}
|
}
|
||||||
ts, err = saveTokenToFile(ctx, conf, token, ts.ProjectID, authDir)
|
ts, err = saveTokenToFile(ctx, conf, token, ts.ProjectID, cfg.AuthDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Log the error but proceed, as we have a valid token for the session.
|
// Log the error but proceed, as we have a valid token for the session.
|
||||||
log.Errorf("Warning: failed to save token to file: %v", err)
|
log.Errorf("Warning: failed to save token to file: %v", err)
|
||||||
|
|||||||
@@ -284,7 +284,9 @@ func (c *Client) StreamAPIRequest(ctx context.Context, endpoint string, body int
|
|||||||
_ = resp.Body.Close()
|
_ = resp.Body.Close()
|
||||||
}()
|
}()
|
||||||
bodyBytes, _ := io.ReadAll(resp.Body)
|
bodyBytes, _ := io.ReadAll(resp.Body)
|
||||||
return nil, fmt.Errorf("api streaming request failed with status %d: %s", resp.StatusCode, string(bodyBytes))
|
|
||||||
|
return nil, fmt.Errorf(string(bodyBytes))
|
||||||
|
// return nil, fmt.Errorf("api streaming request failed with status %d: %s", resp.StatusCode, string(bodyBytes))
|
||||||
}
|
}
|
||||||
|
|
||||||
return resp.Body, nil
|
return resp.Body, nil
|
||||||
|
|||||||
@@ -8,10 +8,11 @@ import (
|
|||||||
|
|
||||||
// Config represents the application's configuration
|
// Config represents the application's configuration
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Port int `yaml:"port"`
|
Port int `yaml:"port"`
|
||||||
AuthDir string `yaml:"auth_dir"`
|
AuthDir string `yaml:"auth_dir"`
|
||||||
Debug bool `yaml:"debug"`
|
Debug bool `yaml:"debug"`
|
||||||
ApiKeys []string `yaml:"api_keys"`
|
ProxyUrl string `yaml:"proxy-url"`
|
||||||
|
ApiKeys []string `yaml:"api_keys"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// / LoadConfig loads the configuration from the specified file
|
// / LoadConfig loads the configuration from the specified file
|
||||||
|
|||||||
Reference in New Issue
Block a user