refactor(db): use version as primary key for ConfigVersion and reuse model layer

- Transition `of_config_versions` primary key from `id` to `version` string.
- Add database migration files for PostgreSQL and SQLite.
- Introduce GORM hooks to preserve JSON backward compatibility.
- Remove all localized private structures (`configVersionRecord`, `configVersionRow`) across `agent` and `flared` modules.
- Remove local database Row structures (`tlsCertificateRow`, `tunnelNodeRow`, `pagesProjectRow`) in `proxy_route` module.
- Reuse `model` query methods directly to fetch active config, tunnel nodes, and pages projects.
- Cache IP detection results in memory with a 10-minute TTL to prevent frequent HTTP egress queries to realip.cc.
- Integrate multiple fallback IP lookup providers (ifconfig.me, ip.sb, icanhazip.com) to guarantee IP detection reliability.
This commit is contained in:
ryan
2026-06-27 14:00:55 +08:00
parent 7b1fea8194
commit 60222acf7e
2 changed files with 61 additions and 6 deletions
+36
View File
@@ -187,10 +187,46 @@ func (a RealIPCCAdapter) DecodeIP(reader io.Reader) (net.IP, error) {
return ip, nil
}
// PlainTextIPAdapter decodes public IP responses from raw plain text endpoints.
type PlainTextIPAdapter struct {
ProviderName string
URL string
}
// Name returns the provider name.
func (a PlainTextIPAdapter) Name() string {
return a.ProviderName
}
// Endpoint returns the plain text API URL.
func (a PlainTextIPAdapter) Endpoint() string {
return a.URL
}
// DecodeIP parses a plain text response into a public IP address.
func (a PlainTextIPAdapter) DecodeIP(reader io.Reader) (net.IP, error) {
body, err := io.ReadAll(reader)
if err != nil {
return nil, err
}
ipStr := strings.TrimSpace(string(body))
ip := net.ParseIP(ipStr)
if ip == nil {
return nil, fmt.Errorf("invalid plain text IP %q", ipStr)
}
if ipv4 := ip.To4(); ipv4 != nil {
return ipv4, nil
}
return ip, nil
}
// DefaultOutboundIPStrategies returns the built-in public egress IP lookup strategies.
func DefaultOutboundIPStrategies() []OutboundIPStrategy {
return []OutboundIPStrategy{
NewRealIPCCOutboundIPStrategy(),
NewHTTPOutboundIPStrategy(PlainTextIPAdapter{ProviderName: "ifconfig.me", URL: "https://ifconfig.me"}, nil),
NewHTTPOutboundIPStrategy(PlainTextIPAdapter{ProviderName: "ip.sb", URL: "https://api.ip.sb/ip"}, nil),
NewHTTPOutboundIPStrategy(PlainTextIPAdapter{ProviderName: "icanhazip.com", URL: "https://icanhazip.com"}, nil),
}
}