Files
Ant Browser Release Bot fc5a6fadbe publish: 1.1.0 snapshot (ff46de5)
channel: master
version: 1.1.0
source-ref: master
published-at-utc: 2026-03-20T14:14:07Z
2026-03-20 22:14:08 +08:00

209 lines
5.7 KiB
Go

package browser
import (
"strings"
"time"
)
func normalizeProxyBindValue(v string) string {
return strings.ToLower(strings.TrimSpace(v))
}
func (m *Manager) listProxyCatalog() []Proxy {
if m.ProxyDAO != nil {
if list, err := m.ProxyDAO.List(); err == nil && len(list) > 0 {
return append([]Proxy{}, list...)
}
}
return append([]Proxy{}, m.Config.Browser.Proxies...)
}
func findProxyByID(list []Proxy, proxyID string) (Proxy, bool) {
target := normalizeProxyBindValue(proxyID)
if target == "" {
return Proxy{}, false
}
for _, item := range list {
if normalizeProxyBindValue(item.ProxyId) == target {
return item, true
}
}
return Proxy{}, false
}
func uniqueProxyMatch(list []Proxy, match func(Proxy) bool) (Proxy, bool) {
var hit Proxy
matched := 0
for _, item := range list {
if !match(item) {
continue
}
hit = item
matched++
if matched > 1 {
return Proxy{}, false
}
}
return hit, matched == 1
}
// GetProxyByID 根据代理 ID 获取代理对象(优先 DAO)。
func (m *Manager) GetProxyByID(proxyID string) (Proxy, bool) {
return findProxyByID(m.listProxyCatalog(), proxyID)
}
// BindProfileToProxy 将实例绑定到指定代理并同步绑定快照。
// syncProxyConfig=true 时会同步更新 profile.ProxyConfig。
func BindProfileToProxy(profile *Profile, proxy Proxy, syncProxyConfig bool) bool {
if profile == nil {
return false
}
changed := false
if profile.ProxyId != strings.TrimSpace(proxy.ProxyId) {
profile.ProxyId = strings.TrimSpace(proxy.ProxyId)
changed = true
}
if syncProxyConfig {
proxyConfig := strings.TrimSpace(proxy.ProxyConfig)
if proxyConfig != "" && profile.ProxyConfig != proxyConfig {
profile.ProxyConfig = proxyConfig
changed = true
}
}
sourceID := strings.TrimSpace(proxy.SourceID)
sourceURL := strings.TrimSpace(proxy.SourceURL)
proxyName := strings.TrimSpace(proxy.ProxyName)
if profile.ProxyBindSourceID != sourceID {
profile.ProxyBindSourceID = sourceID
changed = true
}
if profile.ProxyBindSourceURL != sourceURL {
profile.ProxyBindSourceURL = sourceURL
changed = true
}
if profile.ProxyBindName != proxyName {
profile.ProxyBindName = proxyName
changed = true
}
if changed {
profile.ProxyBindUpdatedAt = time.Now().Format(time.RFC3339)
}
return changed
}
// ClearProfileProxyBinding 清空实例的代理绑定快照。
func ClearProfileProxyBinding(profile *Profile) bool {
if profile == nil {
return false
}
changed := false
if profile.ProxyBindSourceID != "" {
profile.ProxyBindSourceID = ""
changed = true
}
if profile.ProxyBindSourceURL != "" {
profile.ProxyBindSourceURL = ""
changed = true
}
if profile.ProxyBindName != "" {
profile.ProxyBindName = ""
changed = true
}
if changed {
profile.ProxyBindUpdatedAt = time.Now().Format(time.RFC3339)
}
return changed
}
// ResolveProfileProxyBinding 尝试修复实例代理绑定。
// 返回值: changed 是否修改实例, boundInPool 是否在代理池成功定位, mode 重关联命中模式
func (m *Manager) ResolveProfileProxyBinding(profile *Profile) (bool, bool, string) {
if profile == nil {
return false, false, ""
}
proxies := m.listProxyCatalog()
if len(proxies) == 0 {
return false, false, ""
}
if proxy, ok := findProxyByID(proxies, profile.ProxyId); ok {
changed := BindProfileToProxy(profile, proxy, true)
return changed, true, "proxy_id"
}
allowConfigFallback := strings.TrimSpace(profile.ProxyId) != "" ||
strings.TrimSpace(profile.ProxyBindSourceID) != "" ||
strings.TrimSpace(profile.ProxyBindSourceURL) != "" ||
strings.TrimSpace(profile.ProxyBindName) != ""
if proxy, ok, mode := matchProxyBySnapshot(profile, proxies, allowConfigFallback); ok {
changed := BindProfileToProxy(profile, proxy, true)
return changed, true, mode
}
return false, false, ""
}
func matchProxyBySnapshot(profile *Profile, proxies []Proxy, allowConfigFallback bool) (Proxy, bool, string) {
nameKey := normalizeProxyBindValue(profile.ProxyBindName)
sourceIDKey := normalizeProxyBindValue(profile.ProxyBindSourceID)
sourceURLKey := normalizeProxyBindValue(profile.ProxyBindSourceURL)
cfgKey := normalizeProxyBindValue(profile.ProxyConfig)
if sourceIDKey != "" && nameKey != "" {
if hit, ok := uniqueProxyMatch(proxies, func(item Proxy) bool {
return normalizeProxyBindValue(item.SourceID) == sourceIDKey &&
normalizeProxyBindValue(item.ProxyName) == nameKey
}); ok {
return hit, true, "source_id+name"
}
}
if sourceURLKey != "" && nameKey != "" {
if hit, ok := uniqueProxyMatch(proxies, func(item Proxy) bool {
return normalizeProxyBindValue(item.SourceURL) == sourceURLKey &&
normalizeProxyBindValue(item.ProxyName) == nameKey
}); ok {
return hit, true, "source_url+name"
}
}
if nameKey != "" {
if hit, ok := uniqueProxyMatch(proxies, func(item Proxy) bool {
return normalizeProxyBindValue(item.ProxyName) == nameKey
}); ok {
return hit, true, "name"
}
}
if sourceIDKey != "" && cfgKey != "" {
if hit, ok := uniqueProxyMatch(proxies, func(item Proxy) bool {
return normalizeProxyBindValue(item.SourceID) == sourceIDKey &&
normalizeProxyBindValue(item.ProxyConfig) == cfgKey
}); ok {
return hit, true, "source_id+config"
}
}
if sourceURLKey != "" && cfgKey != "" {
if hit, ok := uniqueProxyMatch(proxies, func(item Proxy) bool {
return normalizeProxyBindValue(item.SourceURL) == sourceURLKey &&
normalizeProxyBindValue(item.ProxyConfig) == cfgKey
}); ok {
return hit, true, "source_url+config"
}
}
if allowConfigFallback && cfgKey != "" {
if hit, ok := uniqueProxyMatch(proxies, func(item Proxy) bool {
return normalizeProxyBindValue(item.ProxyConfig) == cfgKey
}); ok {
return hit, true, "config"
}
}
return Proxy{}, false, ""
}