mirror of
https://github.com/black-ant/Ant-Browser.git
synced 2026-07-14 18:48:55 +08:00
channel: master version: 1.0.0 source-ref: master published-at-utc: 2026-03-13T15:19:28Z
94 lines
2.6 KiB
Go
94 lines
2.6 KiB
Go
package backend
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"net"
|
|
"net/http"
|
|
"net/url"
|
|
"strings"
|
|
"time"
|
|
|
|
"ant-chrome/backend/internal/logger"
|
|
)
|
|
|
|
const maxRemoteProfileConfigBytes = 512 * 1024
|
|
|
|
// FetchRemoteAuthorProfile 拉取远程作者配置 JSON,供前端与本地默认配置合并。
|
|
func (a *App) FetchRemoteAuthorProfile(rawURL string, timeoutMs int) (map[string]interface{}, error) {
|
|
targetURL := strings.TrimSpace(rawURL)
|
|
if targetURL == "" {
|
|
return nil, fmt.Errorf("远程作者配置地址不能为空")
|
|
}
|
|
|
|
parsedURL, err := url.Parse(targetURL)
|
|
if err != nil || parsedURL.Scheme == "" || parsedURL.Host == "" {
|
|
return nil, fmt.Errorf("远程作者配置地址无效")
|
|
}
|
|
if parsedURL.Scheme != "http" && parsedURL.Scheme != "https" {
|
|
return nil, fmt.Errorf("远程作者配置仅支持 HTTP/HTTPS 地址")
|
|
}
|
|
|
|
if timeoutMs <= 0 {
|
|
timeoutMs = 3000
|
|
}
|
|
if timeoutMs > 15000 {
|
|
timeoutMs = 15000
|
|
}
|
|
|
|
timeout := time.Duration(timeoutMs) * time.Millisecond
|
|
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
|
defer cancel()
|
|
|
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, targetURL, nil)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("创建远程作者配置请求失败: %w", err)
|
|
}
|
|
req.Header.Set("Accept", "application/json")
|
|
req.Header.Set("User-Agent", "AntBrowser/1.0 profile-fetch")
|
|
|
|
client := &http.Client{}
|
|
resp, err := client.Do(req)
|
|
if err != nil {
|
|
log := logger.New("ProfileConfig")
|
|
if isTimeoutError(err) {
|
|
log.Warn("远程作者配置请求超时", logger.F("url", targetURL), logger.F("timeout_ms", timeoutMs))
|
|
return nil, fmt.Errorf("远程作者配置请求超时")
|
|
}
|
|
log.Warn("远程作者配置请求失败", logger.F("url", targetURL), logger.F("error", err))
|
|
return nil, fmt.Errorf("拉取远程作者配置失败: %w", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode < http.StatusOK || resp.StatusCode >= http.StatusMultipleChoices {
|
|
return nil, fmt.Errorf("远程作者配置返回异常状态码: %d", resp.StatusCode)
|
|
}
|
|
|
|
decoder := json.NewDecoder(io.LimitReader(resp.Body, maxRemoteProfileConfigBytes))
|
|
decoder.UseNumber()
|
|
|
|
var payload map[string]interface{}
|
|
if err := decoder.Decode(&payload); err != nil {
|
|
return nil, fmt.Errorf("解析远程作者配置失败: %w", err)
|
|
}
|
|
if len(payload) == 0 {
|
|
return nil, fmt.Errorf("远程作者配置为空")
|
|
}
|
|
|
|
return payload, nil
|
|
}
|
|
|
|
func isTimeoutError(err error) bool {
|
|
if err == nil {
|
|
return false
|
|
}
|
|
if errors.Is(err, context.DeadlineExceeded) {
|
|
return true
|
|
}
|
|
var netErr net.Error
|
|
return errors.As(err, &netErr) && netErr.Timeout()
|
|
}
|