package stats import ( "context" "strings" "time" "lingniu-vehicle-ingest/go/vehicle-gateway/internal/envelope" ) type SourceIdentity struct { Protocol envelope.Protocol SourceIP string SourceEndpoint string } func NewSourceIdentity(protocol envelope.Protocol, endpoint string) (SourceIdentity, bool) { sourceIP := NormalizeSourceIP(endpoint) if sourceIP == "" { return SourceIdentity{}, false } return SourceIdentity{ Protocol: protocol, SourceIP: sourceIP, SourceEndpoint: strings.TrimSpace(endpoint), }, true } func NormalizeSourceIP(endpoint string) string { endpoint = strings.TrimSpace(endpoint) if endpoint == "" { return "" } if host, _, ok := strings.Cut(endpoint, ":"); ok { return strings.TrimSpace(host) } return endpoint } func UpsertDataSource(ctx context.Context, exec Execer, identity SourceIdentity, now time.Time) error { if exec == nil { panic("stats execer must not be nil") } if identity.SourceIP == "" { return nil } if now.IsZero() { now = time.Now() } _, err := exec.ExecContext(ctx, upsertDataSourceSQL, string(identity.Protocol), identity.SourceIP, identity.SourceEndpoint, now, now, ) return err } const upsertDataSourceSQL = ` INSERT INTO vehicle_data_source (protocol, source_ip, latest_source_endpoint, first_seen_at, latest_seen_at) VALUES (?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE latest_source_endpoint = VALUES(latest_source_endpoint), latest_seen_at = VALUES(latest_seen_at), updated_at = CURRENT_TIMESTAMP `