diff --git a/backend/src/MoShiro.Backend.Application/Agents/MoShiroFleetHooks.cs b/backend/src/MoShiro.Backend.Application/Agents/MoShiroFleetHooks.cs index 8a7f80d..75010cf 100644 --- a/backend/src/MoShiro.Backend.Application/Agents/MoShiroFleetHooks.cs +++ b/backend/src/MoShiro.Backend.Application/Agents/MoShiroFleetHooks.cs @@ -118,6 +118,7 @@ public sealed class MoShiroNodeEventSink : INodeEventSink await ApplyHelloAsync(id, hello, ct); break; case NodeEventKind.Heartbeat when nodeEvent.Extra is { } extra: + await _nodes.SetVersionAsync(id.Value, NodeKinds.Agent, nodeEvent.Node.NodeVersion, ct); try { AgentRuntimeState state = _runtime.Get(id); @@ -172,6 +173,8 @@ public sealed class MoShiroNodeEventSink : INodeEventSink } } + await _nodes.SetVersionAsync(id.Value, NodeKinds.Probe, nodeEvent.Node.NodeVersion, ct); + await _releases.OfferToNodeAsync(id, ct); await _schedule.SendToNodeAsync(id, ct); break; @@ -182,6 +185,7 @@ public sealed class MoShiroNodeEventSink : INodeEventSink _quicRuntime.MarkProbeOffline(id.Value, nodeEvent.AtMs); break; case NodeEventKind.Heartbeat when nodeEvent.Extra is { } extra: + await _nodes.SetVersionAsync(id.Value, NodeKinds.Probe, nodeEvent.Node.NodeVersion, ct); try { ProbeHeartbeatExtra? heartbeat = extra.Deserialize(MoShiroJsonContext.Default.ProbeHeartbeatExtra); @@ -254,6 +258,11 @@ public sealed class MoShiroNodeEventSink : INodeEventSink } _owners.Report(id.Value, meta.Owners); + if (meta.Platform is { } platform) + { + platform.Version = hello.NodeVersion; + } + await _nodes.SetVersionAsync(id.Value, NodeKinds.Agent, hello.NodeVersion, ct); AgentRecord? before = await _agents.GetAsync(id, ct); string? limitsJson = meta.LocalLimits is null ? null : JsonSerializer.Serialize(meta.LocalLimits, LocalLimitsJson.Default.LocalLimits); string? ipv6Json = meta.Ipv6 is null ? null : JsonSerializer.Serialize(meta.Ipv6, MoShiroJsonContext.Default.Ipv6Diagnostic); diff --git a/backend/src/MoShiro.Backend.Application/Nodes/NodeInfoService.cs b/backend/src/MoShiro.Backend.Application/Nodes/NodeInfoService.cs index 45d2926..4270f83 100644 --- a/backend/src/MoShiro.Backend.Application/Nodes/NodeInfoService.cs +++ b/backend/src/MoShiro.Backend.Application/Nodes/NodeInfoService.cs @@ -93,6 +93,25 @@ public sealed class NodeInfoService await SaveAsync(current with { Info = i with { Os = os, OsName = name, Arch = arch, Version = version, Tag = tag, Commit = commit, BuiltAt = builtAt }, UpdatedAtMs = Now() }, ct); } + /// Keep the displayed build version aligned with the authenticated Fleet hello, even if platform metadata is absent. + public async ValueTask SetVersionAsync(string nodeId, string kind, string? version, CancellationToken ct) + { + string? clean = Clean(version, 32); + if (clean is null) + { + return; + } + + await EnsureLoadedAsync(ct); + NodeMeta current = _cache.TryGetValue(nodeId, out NodeMeta? found) ? found : Empty(nodeId, kind); + if (current.Info.Version == clean) + { + return; + } + + await SaveAsync(current with { Info = current.Info with { Version = clean }, UpdatedAtMs = Now() }, ct); + } + /// /// 节点从某个地址连上来了:查一次它的国家和运营商。已经查过同一个地址(并且没过期)就什么也不做; /// 私有地址不查;查询失败或服务关着,不影响别的,隔一阵再试。