diff --git a/ZB.MOM.WW.Overview/src/ZB.MOM.WW.Overview/appsettings.Wonder.json b/ZB.MOM.WW.Overview/src/ZB.MOM.WW.Overview/appsettings.Wonder.json new file mode 100644 index 0000000..eaa4024 --- /dev/null +++ b/ZB.MOM.WW.Overview/src/ZB.MOM.WW.Overview/appsettings.Wonder.json @@ -0,0 +1,127 @@ +{ + // Registry for the wonder-app-vd03 deployment (ASPNETCORE_ENVIRONMENT=Wonder), which runs as a + // Windows service under NSSM rather than in a container. Named "Wonder" and not "Production" on + // purpose, mirroring appsettings.Docker.json: this is ONE deployment's fleet, not a statement + // about what any production deployment looks like. A second box gets its own overlay. + // + // Every port below was established by probing the running box on 2026-08-11 — curl against each + // candidate port — NOT read off a compose file or inferred from the docker rig, whose numbers + // differ. The rig's ScadaBridge central serves health on :5000; here it is :8085. + // + // OtOpcUa :9000 service OtOpcUaHost (AdminUI on the same listener) + // ScadaBridge Central:8085 service ScadaBridge-Central (CentralUI on the same listener) + // ScadaBridge Site :8084 service ScadaBridge-Site (no UI: / returns 404) + // MxAccessGateway :5130 service MxAccessGw (dashboard; :5120 is gRPC) + // HistorianGateway :5220 service HistorianGateway (dashboard; :5221 is gRPC h2c) + // + // Ports NOT to probe, confirmed 400 (h2c gRPC, which rejects an HTTP/1.1 health GET): + // :8083 ScadaBridge central control plane, :8086. + + "Serilog": { + // Absolute, and deliberately OUTSIDE the Server directory the upgrade procedure replaces. + // A relative path resolves against the service's AppDirectory (…\Overview\Server), so every + // upgrade would silently discard the log history — the same class of defect that cost + // HistorianGateway its secrets store on a swap. + "WriteTo": [ + { "Name": "Console" }, + { + "Name": "File", + "Args": { + "path": "E:\\ApiInstall\\Overview\\logs\\overview-.log", + "rollingInterval": "Day" + } + } + ] + }, + + "Kestrel": { + // Bind explicitly; never also set ASPNETCORE_URLS, which would put Kestrel into + // explicit-endpoints mode with one of the two silently discarded. + "Endpoints": { + "Http": { + "Url": "http://+:5320" + } + } + }, + + "Overview": { + "PollIntervalSeconds": 10, + "TimeoutSeconds": 3, + "StaleAfterSeconds": 45, + "Applications": [ + { + "Name": "OtOpcUa", + "ManagementLabel": "AdminUI", + "Instances": [ + { + // A one-node cluster, so Group is still its real cluster identity: leader aggregation + // is scoped to it and the split-brain check cannot false-positive on a single member + // (it triggers on more than one DISTINCT reported address). + "Name": "wonder", + "Group": "MAIN", + // Probed by THIS service, on this box — localhost is the shortest path and is immune + // to any host firewall rule. ManagementUrl is the opposite case: the operator's + // browser follows it from their own machine, so it must carry the box address. + "BaseUrl": "http://localhost:9000", + "ManagementUrl": "http://10.220.157.247:9000/", + "HasActiveRole": true + } + ] + }, + { + "Name": "ScadaBridge", + "ManagementLabel": "CentralUI", + "Instances": [ + { + "Name": "central", + "Group": "central", + "BaseUrl": "http://localhost:8085", + "ManagementUrl": "http://10.220.157.247:8085/", + "HasActiveRole": true + }, + { + // Central and site are SEPARATE Akka clusters, so they are separate groups; putting + // them in one would make the dashboard compare two unrelated leaders and report a + // disagreement that does not exist. + "Name": "site", + "Group": "site", + "BaseUrl": "http://localhost:8084", + // No ManagementUrl: the site node serves no UI (/ returns 404), and a card linking to + // a 404 is worse than a card with no link. + "HasActiveRole": true + } + ] + }, + { + "Name": "MxAccessGateway", + "ManagementLabel": "Dashboard", + "Instances": [ + { + // HasActiveRole false: mxgw is a standalone gateway with no cluster. Its + // /health/active does answer 200, but only because the active tier runs an EMPTY + // check set — an Active badge here would assert a role that does not exist. + "Name": "wonder", + "BaseUrl": "http://localhost:5130", + "ManagementUrl": "http://10.220.157.247:5130/", + "HasActiveRole": false + } + ] + }, + { + "Name": "HistorianGateway", + "ManagementLabel": "Dashboard", + "Instances": [ + { + "Name": "wonder", + "BaseUrl": "http://localhost:5220", + "ManagementUrl": "http://10.220.157.247:5220/", + "HasActiveRole": false, + // Its readiness probe reaches the historian and the Galaxy SQL DB over the network, + // so it is the one instance that legitimately exceeds the 3s global timeout. + "TimeoutSeconds": 10 + } + ] + } + ] + } +}