feat(host): add NodeName to NodeOptions + INodeIdentityProvider

- NodeName: semantic role-within-cluster identifier (node-a/node-b on sites,
  central-a/central-b on central). Bound from ScadaLink:Node:NodeName.
- INodeIdentityProvider exposes the trimmed name (null if unconfigured) so
  downstream audit writers can stamp the new SourceNode column.
This commit is contained in:
Joseph Doherty
2026-05-23 15:38:27 -04:00
parent 9e5e32d0f2
commit 2e10cbe42d
6 changed files with 110 additions and 0 deletions

View File

@@ -0,0 +1,20 @@
using Microsoft.Extensions.Options;
using ScadaLink.Commons.Interfaces.Services;
namespace ScadaLink.Host;
/// <summary>
/// Binds <see cref="INodeIdentityProvider"/> to <see cref="NodeOptions.NodeName"/>.
/// Empty or whitespace values are normalised to <c>null</c>; otherwise the value
/// is returned trimmed.
/// </summary>
internal sealed class NodeIdentityProvider : INodeIdentityProvider
{
public string? NodeName { get; }
public NodeIdentityProvider(IOptions<NodeOptions> nodeOptions)
{
var configured = nodeOptions.Value.NodeName;
NodeName = string.IsNullOrWhiteSpace(configured) ? null : configured.Trim();
}
}

View File

@@ -4,6 +4,14 @@ public class NodeOptions
{
public string Role { get; set; } = string.Empty;
public string NodeHostname { get; set; } = string.Empty;
/// <summary>
/// Operator-configured semantic node name used to stamp the SourceNode
/// column on audit rows. Conventional values are <c>node-a</c>/<c>node-b</c>
/// on site nodes and <c>central-a</c>/<c>central-b</c> on central nodes,
/// but the value is a free-form label — no validation is enforced.
/// </summary>
public string NodeName { get; set; } = string.Empty;
public string? SiteId { get; set; }
public int RemotingPort { get; set; } = 8081;
public int GrpcPort { get; set; } = 8083;

View File

@@ -7,6 +7,10 @@
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<ItemGroup>
<InternalsVisibleTo Include="ScadaLink.Host.Tests" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Akka.Cluster.Hosting" />
<PackageReference Include="Akka.Cluster.Tools" />

View File

@@ -1,6 +1,7 @@
using ScadaLink.AuditLog;
using ScadaLink.ClusterInfrastructure;
using ScadaLink.Communication;
using ScadaLink.Commons.Interfaces.Services;
using ScadaLink.DataConnectionLayer;
using ScadaLink.ExternalSystemGateway;
using ScadaLink.HealthMonitoring;
@@ -96,5 +97,10 @@ public static class SiteServiceRegistration
services.Configure<HealthMonitoringOptions>(config.GetSection("ScadaLink:HealthMonitoring"));
services.Configure<NotificationOptions>(config.GetSection("ScadaLink:Notification"));
services.Configure<LoggingOptions>(config.GetSection("ScadaLink:Logging"));
// Audit Log (#23) — exposes ScadaLink:Node:NodeName to downstream audit
// writers so they can stamp the SourceNode column. Registered here in
// shared bootstrap because every node (central + site) needs it.
services.AddSingleton<INodeIdentityProvider, NodeIdentityProvider>();
}
}