Fix auth, Bootstrap, Blazor nav, LDAP, and deployment pipeline for working Central UI

Bootstrap served locally with absolute paths and <base href="/">.
LDAP auth uses search-then-bind with service account for GLAuth compatibility.
CookieAuthenticationStateProvider reads HttpContext.User instead of parsing JWT.
Login/logout forms opt out of Blazor enhanced nav (data-enhance="false").
Nav links use absolute paths; seed data includes Design/Deployment group mappings.
DataConnections page loads all connections (not just site-assigned).
Site appsettings configured for Test Plant A; Site registers with Central on startup.
DeploymentService resolves string site identifier for Akka routing.
Instances page gains Create Instance form.
This commit is contained in:
Joseph Doherty
2026-03-17 10:03:06 -04:00
parent 6fa4c101ab
commit 4879c4e01e
21 changed files with 265 additions and 92 deletions

View File

@@ -232,5 +232,21 @@ akka {{
_logger.LogInformation(
"Site actors registered. DeploymentManager singleton scoped to role={SiteRole}, SiteCommunicationActor created.",
siteRole);
// Register with Central if configured — tells Central where to send deployment commands
if (!string.IsNullOrWhiteSpace(_communicationOptions.CentralActorPath))
{
var siteCommActor = _actorSystem.ActorSelection("/user/site-communication");
siteCommActor.Tell(new RegisterCentralPath(_communicationOptions.CentralActorPath));
// Also register this site with Central so it knows our address
var centralSelection = _actorSystem.ActorSelection(_communicationOptions.CentralActorPath);
var localSiteCommPath = $"akka.tcp://scadalink@{_nodeOptions.NodeHostname}:{_nodeOptions.RemotingPort}/user/site-communication";
centralSelection.Tell(new RegisterSite(_nodeOptions.SiteId!, localSiteCommPath));
_logger.LogInformation(
"Registered with Central at {CentralPath} as site {SiteId}",
_communicationOptions.CentralActorPath, _nodeOptions.SiteId);
}
}
}

View File

@@ -3,11 +3,9 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<base href="/" />
<title>ScadaLink</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css"
rel="stylesheet"
integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YcnS/1p0TQGL3BNgcree90f9QM0jB1zDTkM6"
crossorigin="anonymous" />
<link href="/lib/bootstrap/css/bootstrap.min.css" rel="stylesheet" />
<style>
.sidebar {
min-width: 220px;
@@ -82,7 +80,7 @@
</div>
</div>
<script src="_framework/blazor.web.js"></script>
<script src="/_framework/blazor.web.js"></script>
<script>
// Reconnection overlay for failover behavior
if (Blazor) {
@@ -91,8 +89,6 @@
});
}
</script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"
integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz"
crossorigin="anonymous"></script>
<script src="/lib/bootstrap/js/bootstrap.bundle.min.js"></script>
</body>
</html>

View File

@@ -152,6 +152,13 @@ try
services.AddExternalSystemGateway();
services.AddNotificationService();
// Configuration database (read-only access for external system definitions, notification lists)
var configDbConnectionString = context.Configuration["ScadaLink:Database:ConfigurationDb"];
if (!string.IsNullOrWhiteSpace(configDbConnectionString))
{
services.AddConfigurationDatabase(configDbConnectionString);
}
// Site-only components — AddSiteRuntime registers SiteStorageService with SQLite path
var siteDbPath = context.Configuration["ScadaLink:Database:SiteDbPath"] ?? "site.db";
services.AddSiteRuntime($"Data Source={siteDbPath}");

View File

@@ -26,6 +26,8 @@
"LdapUseTls": false,
"AllowInsecureLdap": true,
"LdapSearchBase": "dc=scadalink,dc=local",
"LdapServiceAccountDn": "cn=admin,dc=scadalink,dc=local",
"LdapServiceAccountPassword": "password",
"JwtSigningKey": "scadalink-dev-jwt-signing-key-must-be-at-least-32-characters-long",
"JwtExpiryMinutes": 15,
"IdleTimeoutMinutes": 30

View File

@@ -2,14 +2,14 @@
"ScadaLink": {
"Node": {
"Role": "Site",
"NodeHostname": "site-a-node1",
"SiteId": "SiteA",
"NodeHostname": "localhost",
"SiteId": "site-a",
"RemotingPort": 8082
},
"Cluster": {
"SeedNodes": [
"akka.tcp://scadalink@site-a-node1:8082",
"akka.tcp://scadalink@site-a-node2:8082"
"akka.tcp://scadalink@localhost:8082",
"akka.tcp://scadalink@localhost:8083"
],
"SplitBrainResolverStrategy": "keep-oldest",
"StableAfter": "00:00:15",
@@ -18,7 +18,8 @@
"MinNrOfMembers": 1
},
"Database": {
"SiteDbPath": "./data/scadalink.db"
"SiteDbPath": "./data/scadalink.db",
"ConfigurationDb": "Server=localhost,1433;Database=ScadaLinkConfig;User Id=scadalink_app;Password=ScadaLink_Dev1#;TrustServerCertificate=true"
},
"DataConnection": {
"ReconnectInterval": "00:00:05",
@@ -30,6 +31,7 @@
"ReplicationEnabled": true
},
"Communication": {
"CentralActorPath": "akka.tcp://scadalink@localhost:8081/user/central-communication",
"DeploymentTimeout": "00:02:00",
"LifecycleTimeout": "00:00:30",
"QueryTimeout": "00:00:30",

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long