-
Notifications
You must be signed in to change notification settings - Fork 0
Add proxy server support for outbound vendor API requests #7
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Co-authored-by: lwaay2025 <218015601+lwaay2025@users.noreply.github.com>
Co-authored-by: lwaay2025 <218015601+lwaay2025@users.noreply.github.com>
|
@copilot 界面的设置 前端部分和数据保存部分都已经实现了? |
是的,前端界面和数据保存部分都已完整实现。 ✅ 已实现的功能1. 前端界面 (commit 45c6344)
界面位置:设置 → 通用设置 → 代理网络设置 2. 数据保存 (commit 4c29fb7 + 45c6344)前端数据流: 用户修改 → v-model 绑定 → @change/@blur 触发
→ persistAppSettings() → saveAppSettings(payload)后端保存流程: SaveAppSettings()
→ 保存到 ~/.code-switch/app.json
→ 同步更新全局 HTTP 客户端
→ 所有网络请求立即使用新代理配置UI 预览配置修改后自动保存,无需重启应用即可生效。 |
There was a problem hiding this 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.
| DialContext: func(ctx interface{}, network, addr string) (net.Conn, error) { | ||
| return dialer.Dial(network, addr) | ||
| }, |
Copilot
AI
Dec 25, 2025
There was a problem hiding this comment.
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.
|
|
||
| // 创建使用 SOCKS5 代理的传输层 | ||
| transport := &http.Transport{ | ||
| Dial: dialer.Dial, |
Copilot
AI
Dec 25, 2025
There was a problem hiding this comment.
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.
| TLSClientConfig: &tls.Config{ | ||
| InsecureSkipVerify: false, | ||
| }, |
Copilot
AI
Dec 25, 2025
There was a problem hiding this comment.
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.
| if err := UpdateHTTPClient(proxyConfig); err != nil { | ||
| // 代理配置更新失败不应阻止设置保存,只记录错误 | ||
| fmt.Printf("⚠️ 更新 HTTP 客户端代理配置失败: %v\n", err) | ||
| } |
Copilot
AI
Dec 25, 2025
There was a problem hiding this comment.
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.
| Transport: baseClient.Transport, | ||
| Timeout: timeout, |
Copilot
AI
Dec 25, 2025
There was a problem hiding this comment.
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.
| Transport: baseClient.Transport, | |
| Timeout: timeout, | |
| Transport: baseClient.Transport, | |
| CheckRedirect: baseClient.CheckRedirect, | |
| Timeout: timeout, |
| 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 |
Copilot
AI
Dec 25, 2025
There was a problem hiding this comment.
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.
| // 【新增】同步代理配置到全局 HTTP 客户端 | ||
| proxyConfig := ProxyConfig{ | ||
| UseProxy: settings.UseProxy, | ||
| ProxyAddress: settings.ProxyAddress, | ||
| ProxyType: settings.ProxyType, | ||
| } | ||
| if err := UpdateHTTPClient(proxyConfig); err != nil { | ||
| // 代理配置更新失败不应阻止设置保存,只记录错误 | ||
| fmt.Printf("⚠️ 更新 HTTP 客户端代理配置失败: %v\n", err) | ||
| } | ||
|
|
Copilot
AI
Dec 25, 2025
There was a problem hiding this comment.
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.
| proxyConfig = services.ProxyConfig{UseProxy: false} | ||
| } | ||
| if err := services.InitHTTPClient(proxyConfig); err != nil { | ||
| log.Fatalf("初始化 HTTP 客户端失败: %v", err) |
Copilot
AI
Dec 25, 2025
There was a problem hiding this comment.
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.
| 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 | |
| } |
| <input | ||
| type="text" | ||
| v-model="proxyAddress" | ||
| @blur="persistAppSettings" |
Copilot
AI
Dec 25, 2025
There was a problem hiding this comment.
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.
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 supportInitHTTPClient()- Initialize on startup from persisted configUpdateHTTPClient()- Apply config changes without restartGetHTTPClient()/GetHTTPClientWithTimeout()- Retrieve configured clientService integration: Updated all HTTP-calling services to use global client
.SetClient(GetHTTPClient())Configuration schema (
AppSettings): Addeduse_proxy,proxy_address,proxy_typefieldsFrontend Changes
Settings UI (
General/Index.vue): Added proxy configuration sectioni18n: Added zh/en translations for proxy settings labels
Example Usage
Configuration persists to
~/.code-switch/app.jsonand applies immediately on save.Original prompt
💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.