Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 2 additions & 57 deletions internal/handlers/ai/ai_handlers.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
package handlers

import (
"fmt"
"net/http"
"net/url"
"strings"
"time"

"MrRSS/internal/ai"
"MrRSS/internal/config"
"MrRSS/internal/handlers/core"
"MrRSS/internal/handlers/response"
"MrRSS/internal/utils/httputil"
)

// TestResult represents the result of AI configuration test
Expand Down Expand Up @@ -171,59 +170,5 @@ func HandleGetAITestInfo(h *core.Handler, w http.ResponseWriter, r *http.Request

// createHTTPClientWithProxy creates an HTTP client with global proxy settings if enabled
func createHTTPClientWithProxy(h *core.Handler) (*http.Client, error) {
// Check if global proxy is enabled
proxyEnabled, _ := h.DB.GetSetting("proxy_enabled")
if proxyEnabled != "true" {
return &http.Client{}, nil
}

// Build proxy URL from global settings
proxyType, _ := h.DB.GetSetting("proxy_type")
proxyHost, _ := h.DB.GetSetting("proxy_host")
proxyPort, _ := h.DB.GetSetting("proxy_port")
proxyUsername, _ := h.DB.GetEncryptedSetting("proxy_username")
proxyPassword, _ := h.DB.GetEncryptedSetting("proxy_password")

// Build proxy URL
proxyURL := buildProxyURL(proxyType, proxyHost, proxyPort, proxyUsername, proxyPassword)

if proxyURL == "" {
return &http.Client{}, nil
}

// Parse proxy URL
u, err := url.Parse(proxyURL)
if err != nil {
return nil, fmt.Errorf("invalid proxy URL: %w", err)
}

return &http.Client{
Transport: &http.Transport{
Proxy: http.ProxyURL(u),
},
}, nil
}

// buildProxyURL builds a proxy URL from components
func buildProxyURL(proxyType, proxyHost, proxyPort, proxyUsername, proxyPassword string) string {
if proxyHost == "" || proxyPort == "" {
return ""
}

var urlBuilder strings.Builder
urlBuilder.WriteString(strings.ToLower(proxyType))
urlBuilder.WriteString("://")

if proxyUsername != "" && proxyPassword != "" {
urlBuilder.WriteString(url.QueryEscape(proxyUsername))
urlBuilder.WriteString(":")
urlBuilder.WriteString(url.QueryEscape(proxyPassword))
urlBuilder.WriteString("@")
}

urlBuilder.WriteString(proxyHost)
urlBuilder.WriteString(":")
urlBuilder.WriteString(proxyPort)

return urlBuilder.String()
return httputil.CreateHTTPClientWithProxySettings(h.DB, 30*time.Second)
}
48 changes: 2 additions & 46 deletions internal/handlers/ai/ai_profiles_handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"MrRSS/internal/handlers/core"
"MrRSS/internal/handlers/response"
"MrRSS/internal/models"
"MrRSS/internal/utils/httputil"
)

// ProfileRequest represents the request body for creating/updating an AI profile
Expand Down Expand Up @@ -546,50 +547,5 @@ func testAIProfileConnection(h *core.Handler, profile *models.AIProfile) Profile

// createHTTPClientWithProxyForProfile creates an HTTP client with global proxy settings
func createHTTPClientWithProxyForProfile(h *core.Handler) (*http.Client, error) {
proxyEnabled, _ := h.DB.GetSetting("proxy_enabled")
if proxyEnabled != "true" {
return &http.Client{}, nil
}

proxyType, _ := h.DB.GetSetting("proxy_type")
proxyHost, _ := h.DB.GetSetting("proxy_host")
proxyPort, _ := h.DB.GetSetting("proxy_port")
proxyUsername, _ := h.DB.GetEncryptedSetting("proxy_username")
proxyPassword, _ := h.DB.GetEncryptedSetting("proxy_password")

proxyURL := buildProxyURLForProfile(proxyType, proxyHost, proxyPort, proxyUsername, proxyPassword)
if proxyURL == "" {
return &http.Client{}, nil
}

u, err := url.Parse(proxyURL)
if err != nil {
return nil, fmt.Errorf("invalid proxy URL: %w", err)
}

return &http.Client{
Transport: &http.Transport{
Proxy: http.ProxyURL(u),
},
}, nil
}

// buildProxyURLForProfile builds a proxy URL from components
func buildProxyURLForProfile(proxyType, proxyHost, proxyPort, proxyUsername, proxyPassword string) string {
if proxyHost == "" || proxyPort == "" {
return ""
}

scheme := "http"
switch proxyType {
case "socks5":
scheme = "socks5"
case "https":
scheme = "http" // HTTPS proxies use HTTP CONNECT
}

if proxyUsername != "" && proxyPassword != "" {
return fmt.Sprintf("%s://%s:%s@%s:%s", scheme, proxyUsername, proxyPassword, proxyHost, proxyPort)
}
return fmt.Sprintf("%s://%s:%s", scheme, proxyHost, proxyPort)
return httputil.CreateHTTPClientWithProxySettings(h.DB, 30*time.Second)
}
64 changes: 3 additions & 61 deletions internal/handlers/chat/chat_handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ import (
"fmt"
"log"
"net/http"
"net/url"
"strings"
"time"

"MrRSS/internal/ai"
"MrRSS/internal/handlers/core"
"MrRSS/internal/handlers/response"
"MrRSS/internal/utils/httputil"
"MrRSS/internal/utils/textutil"
)

Expand Down Expand Up @@ -285,65 +285,7 @@ func estimateChatTokens(messages []ChatMessage, response string) int {
return totalChars / 4
}

// createHTTPClientWithProxy creates an HTTP client with global proxy settings if enabled
// createHTTPClientWithProxy creates the canonical HTTP client with global proxy settings.
func createHTTPClientWithProxy(h *core.Handler) (*http.Client, error) {
// Check if global proxy is enabled
proxyEnabled, _ := h.DB.GetSetting("proxy_enabled")
if proxyEnabled != "true" {
return &http.Client{Timeout: 60 * time.Second}, nil
}

// Build proxy URL from global settings
proxyType, _ := h.DB.GetSetting("proxy_type")
proxyHost, _ := h.DB.GetSetting("proxy_host")
proxyPort, _ := h.DB.GetSetting("proxy_port")
proxyUsername, _ := h.DB.GetEncryptedSetting("proxy_username")
proxyPassword, _ := h.DB.GetEncryptedSetting("proxy_password")

// Build proxy URL
proxyURL := buildProxyURL(proxyType, proxyHost, proxyPort, proxyUsername, proxyPassword)

// Create HTTP client with proxy
return createHTTPClient(proxyURL, 60*time.Second)
}

// buildProxyURL builds a proxy URL from components
func buildProxyURL(proxyType, proxyHost, proxyPort, proxyUsername, proxyPassword string) string {
if proxyHost == "" || proxyPort == "" {
return ""
}

var urlBuilder strings.Builder
urlBuilder.WriteString(strings.ToLower(proxyType))
urlBuilder.WriteString("://")

if proxyUsername != "" && proxyPassword != "" {
urlBuilder.WriteString(url.QueryEscape(proxyUsername))
urlBuilder.WriteString(":")
urlBuilder.WriteString(url.QueryEscape(proxyPassword))
urlBuilder.WriteString("@")
}

urlBuilder.WriteString(proxyHost)
urlBuilder.WriteString(":")
urlBuilder.WriteString(proxyPort)

return urlBuilder.String()
}

// createHTTPClient creates an HTTP client with optional proxy
func createHTTPClient(proxyURL string, timeout time.Duration) (*http.Client, error) {
client := &http.Client{Timeout: timeout}

if proxyURL != "" {
u, err := url.Parse(proxyURL)
if err != nil {
return nil, fmt.Errorf("invalid proxy URL: %w", err)
}
client.Transport = &http.Transport{
Proxy: http.ProxyURL(u),
}
}

return client, nil
return httputil.CreateHTTPClientWithProxySettings(h.DB, 60*time.Second)
}
76 changes: 15 additions & 61 deletions internal/service/ai_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ import (
"fmt"
"net/http"
"net/url"
"strings"
"time"

"MrRSS/internal/ai"
"MrRSS/internal/config"
"MrRSS/internal/database"
"MrRSS/internal/models"
"MrRSS/internal/utils/httputil"
)

// aiService implements AIService interface
Expand Down Expand Up @@ -49,14 +49,18 @@ func (s *aiService) Summarize(ctx context.Context, content string) (string, erro
model = defaults.AIModel
}

// Create AI client
// Create AI client using the same transport as configuration tests.
httpClient, err := s.createHTTPClientWithProxy()
if err != nil {
return "", fmt.Errorf("failed to create HTTP client: %w", err)
}
clientConfig := ai.ClientConfig{
APIKey: apiKey,
Endpoint: endpoint,
Model: model,
Timeout: 30 * time.Second,
}
client := ai.NewClient(clientConfig)
client := ai.NewClientWithHTTPClient(clientConfig, httpClient)

// Generate summary
response, err := client.Request(content, "Summarize this article")
Expand Down Expand Up @@ -91,14 +95,18 @@ func (s *aiService) Chat(ctx context.Context, sessionID int64, message string) (
model = defaults.AIModel
}

// Create AI client
// Create AI client using the same transport as configuration tests.
httpClient, err := s.createHTTPClientWithProxy()
if err != nil {
return "", fmt.Errorf("failed to create HTTP client: %w", err)
}
clientConfig := ai.ClientConfig{
APIKey: apiKey,
Endpoint: endpoint,
Model: model,
Timeout: 30 * time.Second,
}
client := ai.NewClient(clientConfig)
client := ai.NewClientWithHTTPClient(clientConfig, httpClient)

// Send chat message
response, err := client.Request(message, "")
Expand Down Expand Up @@ -166,61 +174,7 @@ func (s *aiService) TestConfig(ctx context.Context) error {
return err
}

// createHTTPClientWithProxy creates an HTTP client with global proxy settings if enabled
// createHTTPClientWithProxy creates the canonical HTTP client with global proxy settings.
func (s *aiService) createHTTPClientWithProxy() (*http.Client, error) {
// Check if global proxy is enabled
proxyEnabled, _ := s.db.GetSetting("proxy_enabled")
if proxyEnabled != "true" {
return &http.Client{}, nil
}

// Build proxy URL from global settings
proxyType, _ := s.db.GetSetting("proxy_type")
proxyHost, _ := s.db.GetSetting("proxy_host")
proxyPort, _ := s.db.GetSetting("proxy_port")
proxyUsername, _ := s.db.GetEncryptedSetting("proxy_username")
proxyPassword, _ := s.db.GetEncryptedSetting("proxy_password")

// Build proxy URL
proxyURL := s.buildProxyURL(proxyType, proxyHost, proxyPort, proxyUsername, proxyPassword)

if proxyURL == "" {
return &http.Client{}, nil
}

// Parse proxy URL
u, err := url.Parse(proxyURL)
if err != nil {
return nil, fmt.Errorf("invalid proxy URL: %w", err)
}

return &http.Client{
Transport: &http.Transport{
Proxy: http.ProxyURL(u),
},
}, nil
}

// buildProxyURL builds a proxy URL from components
func (s *aiService) buildProxyURL(proxyType, proxyHost, proxyPort, proxyUsername, proxyPassword string) string {
if proxyHost == "" || proxyPort == "" {
return ""
}

var urlBuilder strings.Builder
urlBuilder.WriteString(strings.ToLower(proxyType))
urlBuilder.WriteString("://")

if proxyUsername != "" && proxyPassword != "" {
urlBuilder.WriteString(url.QueryEscape(proxyUsername))
urlBuilder.WriteString(":")
urlBuilder.WriteString(url.QueryEscape(proxyPassword))
urlBuilder.WriteString("@")
}

urlBuilder.WriteString(proxyHost)
urlBuilder.WriteString(":")
urlBuilder.WriteString(proxyPort)

return urlBuilder.String()
return httputil.CreateHTTPClientWithProxySettings(s.db, 30*time.Second)
}
Loading