mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-10-06 23:56:37 +08:00
fix(openflare): Agent 上报 IP 后由 Server 自动解析节点地理位置
- 启动时按 of_options.GeoIPProvider 初始化 pkg/geoip(bootstrap + runtime) - mmdb 模式从内置 GeoLite2 种子到 data/;保存归属方式后热刷新 Provider - Agent/Relay 心跳在服务端根据 IP 写入 geo 字段,尊重 geo_manual_override - ipinfo 归属名称改为 City, Region, Country 可读格式
This commit is contained in:
@@ -82,7 +82,7 @@ func (e payloadError) Error() string { return string(e) }
|
||||
|
||||
func errPayload(message string) error { return payloadError(message) }
|
||||
|
||||
func applyNodeRuntime(node *model.OpenFlareNode, payload NodePayload, preserveName bool) {
|
||||
func applyNodeRuntime(ctx context.Context, node *model.OpenFlareNode, payload NodePayload, preserveName bool) {
|
||||
if !preserveName || strings.TrimSpace(node.Name) == "" {
|
||||
if strings.TrimSpace(payload.Name) != "" {
|
||||
node.Name = strings.TrimSpace(payload.Name)
|
||||
@@ -101,31 +101,7 @@ func applyNodeRuntime(node *model.OpenFlareNode, payload NodePayload, preserveNa
|
||||
node.LastSeenAt = &now
|
||||
node.LastError = truncateForDatabase(payload.LastError, maxDatabaseTextLength)
|
||||
if !node.GeoManualOverride {
|
||||
applyGeoInfoFromIP(node, node.IP)
|
||||
}
|
||||
}
|
||||
|
||||
func applyGeoInfoFromIP(node *model.OpenFlareNode, rawIP string) {
|
||||
if node == nil {
|
||||
return
|
||||
}
|
||||
node.GeoName = ""
|
||||
node.GeoLatitude = nil
|
||||
node.GeoLongitude = nil
|
||||
ip := net.ParseIP(strings.TrimSpace(rawIP))
|
||||
if ip == nil {
|
||||
return
|
||||
}
|
||||
info, err := ofgeoip.GeoInfoFromIP(ip)
|
||||
if err != nil || info == nil {
|
||||
return
|
||||
}
|
||||
if strings.TrimSpace(info.Name) != "" {
|
||||
node.GeoName = strings.TrimSpace(info.Name)
|
||||
}
|
||||
if info.Latitude != nil && info.Longitude != nil {
|
||||
node.GeoLatitude = cloneCoordinate(info.Latitude)
|
||||
node.GeoLongitude = cloneCoordinate(info.Longitude)
|
||||
ofgeoip.ApplyNodeGeoFromIP(ctx, node, node.IP)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,9 +4,11 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"testing"
|
||||
|
||||
ofgeoip "github.com/Rain-kl/Wavelet/internal/apps/openflare/geoip"
|
||||
"github.com/Rain-kl/Wavelet/internal/model"
|
||||
pkggeoip "github.com/Rain-kl/Wavelet/pkg/geoip"
|
||||
)
|
||||
@@ -48,7 +50,7 @@ func TestApplyGeoInfoFromIP(t *testing.T) {
|
||||
})
|
||||
|
||||
node := &model.OpenFlareNode{IP: "203.0.113.10"}
|
||||
applyGeoInfoFromIP(node, node.IP)
|
||||
ofgeoip.ApplyNodeGeoFromIP(context.Background(), node, node.IP)
|
||||
|
||||
if node.GeoName != "Shanghai" {
|
||||
t.Fatalf("expected geo_name Shanghai, got %q", node.GeoName)
|
||||
@@ -70,7 +72,7 @@ func TestApplyGeoInfoFromIPSkipsInvalidIP(t *testing.T) {
|
||||
GeoLatitude: geoipFloat(1),
|
||||
GeoLongitude: geoipFloat(2),
|
||||
}
|
||||
applyGeoInfoFromIP(node, "not-an-ip")
|
||||
ofgeoip.ApplyNodeGeoFromIP(context.Background(), node, "not-an-ip")
|
||||
|
||||
if node.GeoName != "" || node.GeoLatitude != nil || node.GeoLongitude != nil {
|
||||
t.Fatalf("expected geo fields to be cleared on invalid IP, got %+v", node)
|
||||
@@ -90,7 +92,7 @@ func TestApplyNodeRuntimeRespectsGeoManualOverride(t *testing.T) {
|
||||
GeoLatitude: geoipFloat(10),
|
||||
GeoLongitude: geoipFloat(20),
|
||||
}
|
||||
applyNodeRuntime(node, NodePayload{
|
||||
applyNodeRuntime(context.Background(), node, NodePayload{
|
||||
IP: "203.0.113.10",
|
||||
Version: "1.0.0",
|
||||
}, true)
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
ofgeoip "github.com/Rain-kl/Wavelet/internal/apps/openflare/geoip"
|
||||
"github.com/Rain-kl/Wavelet/internal/apps/openflare/node"
|
||||
"github.com/Rain-kl/Wavelet/internal/db"
|
||||
"github.com/Rain-kl/Wavelet/internal/model"
|
||||
@@ -17,6 +18,7 @@ import (
|
||||
|
||||
// RegisterWithAccessToken registers an agent on a reserved node token.
|
||||
func RegisterWithAccessToken(ctx context.Context, authNode *model.OpenFlareNode, payload NodePayload) (*RegistrationResponse, error) {
|
||||
_ = ofgeoip.EnsureRuntimeProvider(ctx)
|
||||
payload = normalizeNodePayload(payload)
|
||||
if authNode == nil {
|
||||
return nil, errors.New(errNodeNotFound)
|
||||
@@ -24,7 +26,7 @@ func RegisterWithAccessToken(ctx context.Context, authNode *model.OpenFlareNode,
|
||||
if err := validateNodePayload(payload); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
applyNodeRuntime(authNode, payload, true)
|
||||
applyNodeRuntime(ctx, authNode, payload, true)
|
||||
if err := model.SaveOpenFlareNode(ctx, authNode); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -38,6 +40,7 @@ func RegisterWithAccessToken(ctx context.Context, authNode *model.OpenFlareNode,
|
||||
|
||||
// RegisterWithDiscovery registers a new node using the global discovery token.
|
||||
func RegisterWithDiscovery(ctx context.Context, payload NodePayload) (*RegistrationResponse, error) {
|
||||
_ = ofgeoip.EnsureRuntimeProvider(ctx)
|
||||
payload = normalizeNodePayload(payload)
|
||||
if err := validateNodePayload(payload); err != nil {
|
||||
return nil, err
|
||||
@@ -66,7 +69,7 @@ func RegisterWithDiscovery(ctx context.Context, payload NodePayload) (*Registrat
|
||||
CapabilitiesJSON: "[]",
|
||||
UpdateChannel: releaseChannelStable,
|
||||
}
|
||||
applyNodeRuntime(record, payload, false)
|
||||
applyNodeRuntime(ctx, record, payload, false)
|
||||
|
||||
if err = model.CreateOpenFlareNode(ctx, record); err != nil {
|
||||
if isUniqueConstraintError(err) {
|
||||
@@ -84,6 +87,7 @@ func RegisterWithDiscovery(ctx context.Context, payload NodePayload) (*Registrat
|
||||
|
||||
// HeartbeatNode updates runtime state and returns agent settings.
|
||||
func HeartbeatNode(ctx context.Context, authNode *model.OpenFlareNode, payload NodePayload) (*HeartbeatResponse, error) {
|
||||
_ = ofgeoip.EnsureRuntimeProvider(ctx)
|
||||
if authNode == nil {
|
||||
return nil, errors.New(errNodeNotFound)
|
||||
}
|
||||
@@ -99,7 +103,7 @@ func HeartbeatNode(ctx context.Context, authNode *model.OpenFlareNode, payload N
|
||||
updateChannel := strings.TrimSpace(authNode.UpdateChannel)
|
||||
updateTag := strings.TrimSpace(authNode.UpdateTag)
|
||||
|
||||
applyNodeRuntime(authNode, payload, true)
|
||||
applyNodeRuntime(ctx, authNode, payload, true)
|
||||
authNode.UpdateRequested = false
|
||||
authNode.UpdateChannel = releaseChannelStable
|
||||
authNode.UpdateTag = ""
|
||||
|
||||
Reference in New Issue
Block a user