Skip to content

Conversation

Copy link

Copilot AI commented Dec 25, 2025

Due to network restrictions, outbound requests to vendor APIs may require routing through a proxy server. This adds configurable HTTP/HTTPS and SOCKS5 proxy support for all external API calls.

Backend Changes

  • Global HTTP client management (services/httpclient.go): Centralized client with proxy configuration support

    • InitHTTPClient() - Initialize on startup from persisted config
    • UpdateHTTPClient() - Apply config changes without restart
    • GetHTTPClient() / GetHTTPClientWithTimeout() - Retrieve configured client
  • Service integration: Updated all HTTP-calling services to use global client

    • Provider relay via xrequest: .SetClient(GetHTTPClient())
    • Update service, connectivity tests, health checks, speed tests, skill service
  • Configuration schema (AppSettings): Added use_proxy, proxy_address, proxy_type fields

Frontend Changes

  • Settings UI (General/Index.vue): Added proxy configuration section

    • Toggle for enable/disable
    • Dropdown for protocol selection (HTTP/HTTPS, SOCKS5)
    • Input for proxy address with format hints
  • i18n: Added zh/en translations for proxy settings labels

Example Usage

// Backend: All services now use the global client
req := xrequest.New().
    SetClient(GetHTTPClient()).  // Applies proxy if configured
    SetHeaders(headers).
    Post(targetURL)
// Frontend: Proxy settings in AppSettings
const settings: AppSettings = {
  use_proxy: true,
  proxy_address: 'http://127.0.0.1:1080',
  proxy_type: 'http',
  // ... other settings
}

Configuration persists to ~/.code-switch/app.json and applies immediately on save.

Original prompt

因网络环境限制,工具监听模式下调用供应商 API 时,外发网络请求可能无法直连,需要统一通过代理服务器转发。请在系统/应用的「代理网络设置」中开启 “使用代理服务器” 选项,并配置代理地址(HTTP/HTTPS 或 SOCKS5)。后续所有发往供应商 API 的请求,都必须在请求层套用该代理配置(包括域名解析、HTTPS 连接与请求转发),以保证请求可达与稳定性。


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

Copilot AI and others added 2 commits December 25, 2025 09:03
Co-authored-by: lwaay2025 <218015601+lwaay2025@users.noreply.github.com>
Co-authored-by: lwaay2025 <218015601+lwaay2025@users.noreply.github.com>
Copilot AI changed the title [WIP] Add proxy server configuration for API requests Add proxy server support for outbound vendor API requests Dec 25, 2025
Copilot AI requested a review from lwaay2025 December 25, 2025 09:11
@lwaay2025
Copy link
Owner

@copilot 界面的设置 前端部分和数据保存部分都已经实现了?

Copy link
Author

Copilot AI commented Dec 25, 2025

@copilot 界面的设置 前端部分和数据保存部分都已经实现了?

是的,前端界面和数据保存部分都已完整实现。

✅ 已实现的功能

1. 前端界面 (commit 45c6344)

  • 代理开关(macOS 风格切换按钮)
  • 代理类型选择器(HTTP/HTTPS 或 SOCKS5)
  • 代理地址输入框
  • 动态显示/隐藏(仅在启用时显示配置项)
  • 中英文翻译支持

界面位置:设置 → 通用设置 → 代理网络设置

2. 数据保存 (commit 4c29fb7 + 45c6344)

前端数据流:

用户修改  v-model 绑定  @change/@blur 触发
 persistAppSettings()  saveAppSettings(payload)

后端保存流程:

SaveAppSettings() 
→ 保存到 ~/.code-switch/app.json同步更新全局 HTTP 客户端所有网络请求立即使用新代理配置

UI 预览

╔════════════════════════════════════════════╗
║  代理网络设置                               ║
║ ┌──────────────────────────────────────┐  ║
║ │ 使用代理服务器          [●] ON      │  ║
║ │ 提示:启用后所有请求将通过代理转发   │  ║
║ │                                      │  ║
║ │ 代理类型    [HTTP/HTTPS ▼]          │  ║
║ │                                      │  ║
║ │ 代理地址                             │  ║
║ │ ┌──────────────────────────────────┐ │  ║
║ │ │ http://127.0.0.1:1080           │ │  ║
║ │ └──────────────────────────────────┘ │  ║
║ └──────────────────────────────────────┘  ║
╚════════════════════════════════════════════╝

配置修改后自动保存,无需重启应用即可生效。

@lwaay2025 lwaay2025 marked this pull request as ready for review December 25, 2025 09:18
Copilot AI review requested due to automatic review settings December 25, 2025 09:18
@lwaay2025 lwaay2025 merged commit 9a45745 into main Dec 25, 2025
1 check passed
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds configurable HTTP/HTTPS and SOCKS5 proxy support to enable outbound vendor API requests to route through proxy servers when network restrictions apply. The implementation introduces a centralized HTTP client management system with global configuration.

Key changes:

  • Centralized HTTP client management with proxy configuration support (initialization, updates, and retrieval)
  • Updated all HTTP-calling services to use the global client instead of creating local instances
  • Added proxy configuration fields to AppSettings with frontend UI for configuration

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
services/httpclient.go New file implementing global HTTP client with HTTP/HTTPS and SOCKS5 proxy support
services/appsettings.go Added proxy configuration fields (use_proxy, proxy_address, proxy_type) and synchronization logic
main.go Initialized global HTTP client during application startup with persisted proxy configuration
services/updateservice.go Replaced local HTTP client instantiation with global client retrieval
services/speedtestservice.go Updated to use global client with custom redirect policy
services/skillservice.go Changed to use global client in service initialization
services/providerrelay.go Integrated global client via xrequest SetClient method
services/healthcheckservice.go Replaced custom transport configuration with global client
services/connectivitytestservice.go Updated to use global client with 10-second timeout
frontend/src/services/appSettings.ts Added proxy-related fields to AppSettings TypeScript interface
frontend/src/components/General/Index.vue Added proxy configuration UI section with toggle, type dropdown, and address input
frontend/src/locales/zh.json Added Chinese translations for proxy settings
frontend/src/locales/en.json Added English translations for proxy settings

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +205 to +207
DialContext: func(ctx interface{}, network, addr string) (net.Conn, error) {
return dialer.Dial(network, addr)
},
Copy link

Copilot AI Dec 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The DialContext function signature is incorrect. The first parameter should be context.Context, not interface{}. This will cause a type mismatch error since http.Transport.DialContext expects func(ctx context.Context, network, addr string) (net.Conn, error). Change the function signature to use context.Context as the first parameter type.

Copilot uses AI. Check for mistakes.

// 创建使用 SOCKS5 代理的传输层
transport := &http.Transport{
Dial: dialer.Dial,
Copy link

Copilot AI Dec 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The deprecated Dial field is being used in the transport. The Dial field has been deprecated in favor of DialContext. Remove the deprecated Dial field assignment and only use DialContext to avoid potential conflicts and ensure proper context cancellation support.

Copilot uses AI. Check for mistakes.
Comment on lines +170 to +172
TLSClientConfig: &tls.Config{
InsecureSkipVerify: false,
},
Copy link

Copilot AI Dec 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The TLS configuration sets InsecureSkipVerify: false, which is correct. However, when using a proxy, the application should be aware that HTTPS traffic integrity depends on the proxy's trustworthiness. Consider adding documentation or user warnings that using an untrusted proxy can compromise the security of API communications, as the proxy can potentially intercept HTTPS traffic.

Copilot uses AI. Check for mistakes.
if err := UpdateHTTPClient(proxyConfig); err != nil {
// 代理配置更新失败不应阻止设置保存,只记录错误
fmt.Printf("⚠️ 更新 HTTP 客户端代理配置失败: %v\n", err)
}
Copy link

Copilot AI Dec 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using fmt.Printf for error logging is inconsistent with the logging pattern used elsewhere in the codebase, where log.Printf is the standard. This error should be logged using log.Printf instead to maintain consistency with the project's logging approach.

Copilot uses AI. Check for mistakes.
Comment on lines +89 to +90
Transport: baseClient.Transport,
Timeout: timeout,
Copy link

Copilot AI Dec 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The GetHTTPClientWithTimeout function creates a new client that shares the Transport with the base client but doesn't copy the CheckRedirect handler. This means any custom redirect logic set on the base client (like in speedtestservice.go) will be lost when using this function. The function should also copy the CheckRedirect field from the base client to preserve redirect behavior.

Suggested change
Transport: baseClient.Transport,
Timeout: timeout,
Transport: baseClient.Transport,
CheckRedirect: baseClient.CheckRedirect,
Timeout: timeout,

Copilot uses AI. Check for mistakes.
Comment on lines +148 to +157
client := GetHTTPClientWithTimeout(time.Duration(timeoutSecs) * time.Second)
// 配置重定向策略
client.CheckRedirect = func(req *http.Request, via []*http.Request) error {
// 限制重定向次数为 5
if len(via) >= 5 {
return fmt.Errorf("重定向次数过多")
}
return nil
}
return client
Copy link

Copilot AI Dec 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Modifying the CheckRedirect handler of a shared HTTP client creates a concurrency risk. The GetHTTPClientWithTimeout function returns a client with a shared Transport from the global client, and modifying its CheckRedirect field could cause race conditions if multiple goroutines call buildClient concurrently. The function should create a completely independent client instance rather than modifying a client that shares the global transport's configuration.

Copilot uses AI. Check for mistakes.
Comment on lines +183 to +193
// 【新增】同步代理配置到全局 HTTP 客户端
proxyConfig := ProxyConfig{
UseProxy: settings.UseProxy,
ProxyAddress: settings.ProxyAddress,
ProxyType: settings.ProxyType,
}
if err := UpdateHTTPClient(proxyConfig); err != nil {
// 代理配置更新失败不应阻止设置保存,只记录错误
fmt.Printf("⚠️ 更新 HTTP 客户端代理配置失败: %v\n", err)
}

Copy link

Copilot AI Dec 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The proxy address is saved without validation. Invalid proxy addresses (e.g., malformed URLs, missing ports) will only fail when attempting to use them, not at save time. This results in unclear error feedback to users. Consider adding validation to check if the proxy address is properly formatted before saving, and provide clear error messages if validation fails.

Copilot uses AI. Check for mistakes.
proxyConfig = services.ProxyConfig{UseProxy: false}
}
if err := services.InitHTTPClient(proxyConfig); err != nil {
log.Fatalf("初始化 HTTP 客户端失败: %v", err)
Copy link

Copilot AI Dec 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using log.Fatalf when HTTP client initialization fails is overly strict and prevents the application from starting even if the proxy configuration error is non-critical. Since proxy configuration errors are already handled gracefully when reading config (line 98), initialization should also be non-fatal. Consider logging the error and falling back to a default client instead of terminating the entire application.

Suggested change
log.Fatalf("初始化 HTTP 客户端失败: %v", err)
log.Printf("⚠️ 初始化 HTTP 客户端失败,使用默认配置重试: %v", err)
fallbackConfig := services.ProxyConfig{UseProxy: false}
if fallbackErr := services.InitHTTPClient(fallbackConfig); fallbackErr != nil {
log.Printf("❌ 使用默认配置初始化 HTTP 客户端也失败: %v", fallbackErr)
} else {
proxyConfig = fallbackConfig
}

Copilot uses AI. Check for mistakes.
Comment on lines +511 to +514
<input
type="text"
v-model="proxyAddress"
@blur="persistAppSettings"
Copy link

Copilot AI Dec 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The proxy address input uses @blur event for saving, which means changes are only persisted when the user clicks away from the input field. This can lead to data loss if the user enters a proxy address and immediately closes the settings without clicking elsewhere first. Consider using @input with debouncing or adding an explicit "Apply" button to ensure changes are saved reliably.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants