feat: add HTTP Management API, migrate CLI from Akka ClusterClient to HTTP

Replace the CLI's Akka.NET ClusterClient transport with a simple HTTP client
targeting a new POST /management endpoint on the Central Host. The endpoint
handles Basic Auth, LDAP authentication, role resolution, and ManagementActor
dispatch in a single round-trip — eliminating the CLI's Akka, LDAP, and
Security dependencies.

Also fixes DCL ReSubscribeAll losing subscriptions on repeated reconnect by
deriving the tag list from _subscriptionsByInstance instead of _subscriptionIds.
This commit is contained in:
Joseph Doherty
2026-03-20 23:55:31 -04:00
parent 7740a3bcf9
commit 1a540f4f0a
38 changed files with 863 additions and 758 deletions

View File

@@ -4,13 +4,7 @@ namespace ScadaLink.CLI;
public class CliConfig
{
public List<string> ContactPoints { get; set; } = new();
public string? LdapServer { get; set; }
public int LdapPort { get; set; } = 636;
public bool LdapUseTls { get; set; } = true;
public string LdapSearchBase { get; set; } = string.Empty;
public string LdapServiceAccountDn { get; set; } = string.Empty;
public string LdapServiceAccountPassword { get; set; } = string.Empty;
public string? ManagementUrl { get; set; }
public string DefaultFormat { get; set; } = "json";
public static CliConfig Load()
@@ -28,51 +22,28 @@ public class CliConfig
new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
if (fileConfig != null)
{
if (fileConfig.ContactPoints?.Count > 0) config.ContactPoints = fileConfig.ContactPoints;
if (fileConfig.Ldap != null)
{
config.LdapServer = fileConfig.Ldap.Server;
config.LdapPort = fileConfig.Ldap.Port;
config.LdapUseTls = fileConfig.Ldap.UseTls;
if (!string.IsNullOrEmpty(fileConfig.Ldap.SearchBase))
config.LdapSearchBase = fileConfig.Ldap.SearchBase;
if (!string.IsNullOrEmpty(fileConfig.Ldap.ServiceAccountDn))
config.LdapServiceAccountDn = fileConfig.Ldap.ServiceAccountDn;
if (!string.IsNullOrEmpty(fileConfig.Ldap.ServiceAccountPassword))
config.LdapServiceAccountPassword = fileConfig.Ldap.ServiceAccountPassword;
}
if (!string.IsNullOrEmpty(fileConfig.DefaultFormat)) config.DefaultFormat = fileConfig.DefaultFormat;
if (!string.IsNullOrEmpty(fileConfig.ManagementUrl))
config.ManagementUrl = fileConfig.ManagementUrl;
if (!string.IsNullOrEmpty(fileConfig.DefaultFormat))
config.DefaultFormat = fileConfig.DefaultFormat;
}
}
// Override from environment variables
var envContacts = Environment.GetEnvironmentVariable("SCADALINK_CONTACT_POINTS");
if (!string.IsNullOrEmpty(envContacts))
config.ContactPoints = envContacts.Split(',', StringSplitOptions.RemoveEmptyEntries).ToList();
var envLdap = Environment.GetEnvironmentVariable("SCADALINK_LDAP_SERVER");
if (!string.IsNullOrEmpty(envLdap)) config.LdapServer = envLdap;
var envUrl = Environment.GetEnvironmentVariable("SCADALINK_MANAGEMENT_URL");
if (!string.IsNullOrEmpty(envUrl))
config.ManagementUrl = envUrl;
var envFormat = Environment.GetEnvironmentVariable("SCADALINK_FORMAT");
if (!string.IsNullOrEmpty(envFormat)) config.DefaultFormat = envFormat;
if (!string.IsNullOrEmpty(envFormat))
config.DefaultFormat = envFormat;
return config;
}
private class CliConfigFile
{
public List<string>? ContactPoints { get; set; }
public LdapConfig? Ldap { get; set; }
public string? ManagementUrl { get; set; }
public string? DefaultFormat { get; set; }
}
private class LdapConfig
{
public string? Server { get; set; }
public int Port { get; set; } = 636;
public bool UseTls { get; set; } = true;
public string? SearchBase { get; set; }
public string? ServiceAccountDn { get; set; }
public string? ServiceAccountPassword { get; set; }
}
}