docs(audit): apply global term/path substitutions across living docs
This commit is contained in:
@@ -19,7 +19,7 @@ The worker must do all MXAccess COM calls on its dedicated STA thread, and the S
|
|||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
# Full solution build (gateway, worker, contracts, tests)
|
# Full solution build (gateway, worker, contracts, tests)
|
||||||
dotnet build src/MxGateway.sln
|
dotnet build src/ZB.MOM.WW.MxGateway.slnx
|
||||||
|
|
||||||
# Worker must be built x86 — the gateway looks for MxGateway.Worker.exe under bin\x86
|
# Worker must be built x86 — the gateway looks for MxGateway.Worker.exe under bin\x86
|
||||||
dotnet build src/MxGateway.Worker/MxGateway.Worker.csproj -p:Platform=x86
|
dotnet build src/MxGateway.Worker/MxGateway.Worker.csproj -p:Platform=x86
|
||||||
@@ -29,10 +29,10 @@ dotnet test src/MxGateway.Tests/MxGateway.Tests.csproj
|
|||||||
dotnet test src/MxGateway.Worker.Tests/MxGateway.Worker.Tests.csproj -p:Platform=x86
|
dotnet test src/MxGateway.Worker.Tests/MxGateway.Worker.Tests.csproj -p:Platform=x86
|
||||||
|
|
||||||
# Run gateway locally (defaults bound under MxGateway:* in src/MxGateway.Server/appsettings.json)
|
# Run gateway locally (defaults bound under MxGateway:* in src/MxGateway.Server/appsettings.json)
|
||||||
dotnet run --project src/MxGateway.Server/MxGateway.Server.csproj
|
dotnet run --project src/ZB.MOM.WW.MxGateway.Server/ZB.MOM.WW.MxGateway.Server.csproj
|
||||||
|
|
||||||
# API-key admin CLI (same exe, "apikey" subcommand)
|
# API-key admin CLI (same exe, "apikey" subcommand)
|
||||||
dotnet run --project src/MxGateway.Server/MxGateway.Server.csproj -- apikey create --display-name "dev" --scopes session,invoke,event,metadata,admin
|
dotnet run --project src/ZB.MOM.WW.MxGateway.Server/ZB.MOM.WW.MxGateway.Server.csproj -- apikey create --display-name "dev" --scopes session,invoke,event,metadata,admin
|
||||||
```
|
```
|
||||||
|
|
||||||
Single test by name (xUnit `--filter`):
|
Single test by name (xUnit `--filter`):
|
||||||
@@ -54,7 +54,7 @@ Live LDAP tests use `MXGATEWAY_RUN_LIVE_LDAP_TESTS=1`. See `docs/GatewayTesting.
|
|||||||
|
|
||||||
Each language client is in `clients/<lang>/` with its own README. They all consume the shared `.proto` files in `src/MxGateway.Contracts/Protos`:
|
Each language client is in `clients/<lang>/` with its own README. They all consume the shared `.proto` files in `src/MxGateway.Contracts/Protos`:
|
||||||
|
|
||||||
- `clients/dotnet`: `dotnet build clients/dotnet/MxGateway.Client.sln`
|
- `clients/dotnet`: `dotnet build clients/dotnet/ZB.MOM.WW.MxGateway.Client.slnx`
|
||||||
- `clients/python`: `python -m pip install -e ".[dev]"; python -m pytest`
|
- `clients/python`: `python -m pip install -e ".[dev]"; python -m pytest`
|
||||||
- `clients/rust`: `cargo test --workspace; cargo clippy --workspace --all-targets -- -D warnings`
|
- `clients/rust`: `cargo test --workspace; cargo clippy --workspace --all-targets -- -D warnings`
|
||||||
- `clients/java`: `gradle test` (Java 21)
|
- `clients/java`: `gradle test` (Java 21)
|
||||||
@@ -90,7 +90,7 @@ When source code changes, build and test the affected component before reporting
|
|||||||
| Contracts or `.proto` files | regenerate generated code, then build gateway, worker, and every generated client touched by the contract |
|
| Contracts or `.proto` files | regenerate generated code, then build gateway, worker, and every generated client touched by the contract |
|
||||||
| Gateway server, sessions, workers, gRPC, dashboard, metrics | `dotnet build src/MxGateway.Server` and run affected gateway / fake-worker tests |
|
| Gateway server, sessions, workers, gRPC, dashboard, metrics | `dotnet build src/MxGateway.Server` and run affected gateway / fake-worker tests |
|
||||||
| Worker IPC, STA, MXAccess, conversion | `dotnet build src/MxGateway.Worker -p:Platform=x86` and run worker tests |
|
| Worker IPC, STA, MXAccess, conversion | `dotnet build src/MxGateway.Worker -p:Platform=x86` and run worker tests |
|
||||||
| .NET client | `dotnet build clients/dotnet/MxGateway.Client.sln` and run its tests |
|
| .NET client | `dotnet build clients/dotnet/ZB.MOM.WW.MxGateway.Client.slnx` and run its tests |
|
||||||
| Go client | `gofmt`, `go build ./...`, `go test ./...` from `clients/go` |
|
| Go client | `gofmt`, `go build ./...`, `go test ./...` from `clients/go` |
|
||||||
| Rust client | `cargo fmt`, `cargo check --workspace`, `cargo test --workspace`, `cargo clippy --all-targets -- -D warnings` from `clients/rust` |
|
| Rust client | `cargo fmt`, `cargo check --workspace`, `cargo test --workspace`, `cargo clippy --all-targets -- -D warnings` from `clients/rust` |
|
||||||
| Python client | `python -m pytest` from `clients/python` |
|
| Python client | `python -m pytest` from `clients/python` |
|
||||||
@@ -116,7 +116,7 @@ External analysis sources referenced by design docs:
|
|||||||
|
|
||||||
Gateway gRPC clients authenticate with an API key in metadata: `authorization: Bearer mxgw_<key-id>_<secret>`. Keys are stored hashed (with a peppered SHA) in a gateway-owned SQLite DB (default `C:\ProgramData\MxGateway\gateway-auth.db`). Scopes (`session`, `invoke`, `event`, `metadata`, `admin`) gate specific RPCs; missing → `Unauthenticated`, insufficient → `PermissionDenied`. The `apikey` subcommand on the server exe manages keys; see `src/MxGateway.Server/Security/Authentication/`.
|
Gateway gRPC clients authenticate with an API key in metadata: `authorization: Bearer mxgw_<key-id>_<secret>`. Keys are stored hashed (with a peppered SHA) in a gateway-owned SQLite DB (default `C:\ProgramData\MxGateway\gateway-auth.db`). Scopes (`session`, `invoke`, `event`, `metadata`, `admin`) gate specific RPCs; missing → `Unauthenticated`, insufficient → `PermissionDenied`. The `apikey` subcommand on the server exe manages keys; see `src/MxGateway.Server/Security/Authentication/`.
|
||||||
|
|
||||||
Dashboard auth is LDAP-backed (separate from the gRPC API-key model). `/login` binds against `MxGateway:Ldap` and maps the user's LDAP groups to `Admin` or `Viewer` via `MxGateway:Dashboard:GroupToRole`, then issues an HTTP-only secure `__Host-MxGatewayDashboard` cookie. SignalR hubs at `/hubs/{snapshot,alarms,events}` accept either the cookie or a 30-minute bearer minted at `/hubs/token`. `Dashboard:AllowAnonymousLocalhost` bypasses auth on loopback when enabled.
|
Dashboard auth is LDAP-backed (separate from the gRPC API-key model). `/login` binds against `MxGateway:Ldap` and maps the user's LDAP groups to `Administrator` or `Viewer` via `MxGateway:Dashboard:GroupToRole`, then issues an HTTP-only secure `MxGatewayDashboard` cookie. SignalR hubs at `/hubs/{snapshot,alarms,events}` accept either the cookie or a 30-minute bearer minted at `/hubs/token`. `Dashboard:AllowAnonymousLocalhost` bypasses auth on loopback when enabled.
|
||||||
|
|
||||||
## Process / Platform Notes
|
## Process / Platform Notes
|
||||||
|
|
||||||
|
|||||||
@@ -212,7 +212,7 @@ Use bounded smoke flow and always attempt `close_session` in `finally`.
|
|||||||
Use `pyproject.toml`. Publishable package name should be stable, for example:
|
Use `pyproject.toml`. Publishable package name should be stable, for example:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
mxaccess-gateway-client
|
zb-mom-ww-mxaccess-gateway-client
|
||||||
```
|
```
|
||||||
|
|
||||||
Generated protobuf code should be regenerated through a documented command, not
|
Generated protobuf code should be regenerated through a documented command, not
|
||||||
|
|||||||
@@ -407,7 +407,7 @@ The stable client proto manifest defines the generated-code directories:
|
|||||||
clients/dotnet/generated
|
clients/dotnet/generated
|
||||||
clients/go/internal/generated
|
clients/go/internal/generated
|
||||||
clients/rust/src/generated
|
clients/rust/src/generated
|
||||||
clients/python/src/mxgateway/generated
|
clients/python/src/zb_mom_ww_mxgateway/generated
|
||||||
clients/java/src/main/generated
|
clients/java/src/main/generated
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ Pop-Location
|
|||||||
|
|
||||||
## Rust
|
## Rust
|
||||||
|
|
||||||
The Rust workspace builds the `mxgateway-client` library crate and the `mxgw`
|
The Rust workspace builds the `zb-mom-ww-mxgateway-client` library crate and the `mxgw`
|
||||||
CLI crate. `build.rs` generates `tonic` and `prost` modules into Cargo build
|
CLI crate. `build.rs` generates `tonic` and `prost` modules into Cargo build
|
||||||
output on each build that needs updated protobuf output.
|
output on each build that needs updated protobuf output.
|
||||||
|
|
||||||
@@ -156,8 +156,8 @@ Pop-Location
|
|||||||
|
|
||||||
## Python
|
## Python
|
||||||
|
|
||||||
The Python package is `mxaccess-gateway-client`. Generated modules live under
|
The Python package is `zb-mom-ww-mxaccess-gateway-client`. Generated modules live under
|
||||||
`clients/python/src/mxgateway/generated`.
|
`clients/python/src/zb_mom_ww_mxgateway/generated`.
|
||||||
|
|
||||||
Regenerate the Python bindings:
|
Regenerate the Python bindings:
|
||||||
|
|
||||||
@@ -184,7 +184,7 @@ Push-Location clients/python
|
|||||||
mxgw-py version --json
|
mxgw-py version --json
|
||||||
mxgw-py smoke --endpoint $env:MXGATEWAY_ENDPOINT --plaintext --api-key-env MXGATEWAY_API_KEY --item $env:MXGATEWAY_TEST_ITEM --json
|
mxgw-py smoke --endpoint $env:MXGATEWAY_ENDPOINT --plaintext --api-key-env MXGATEWAY_API_KEY --item $env:MXGATEWAY_TEST_ITEM --json
|
||||||
mxgw-py smoke --endpoint mxgateway.example.local:5001 --tls --ca-file C:\certs\mxgateway-ca.pem --server-name-override mxgateway.example.local --api-key-env MXGATEWAY_API_KEY --item $env:MXGATEWAY_TEST_ITEM --json
|
mxgw-py smoke --endpoint mxgateway.example.local:5001 --tls --ca-file C:\certs\mxgateway-ca.pem --server-name-override mxgateway.example.local --api-key-env MXGATEWAY_API_KEY --item $env:MXGATEWAY_TEST_ITEM --json
|
||||||
python -m mxgateway_cli version --json
|
python -m zb_mom_ww_mxgateway_cli version --json
|
||||||
Pop-Location
|
Pop-Location
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -198,7 +198,7 @@ Regenerate Java bindings:
|
|||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
Push-Location clients/java
|
Push-Location clients/java
|
||||||
gradle :mxgateway-client:generateProto
|
gradle :zb-mom-ww-mxgateway-client:generateProto
|
||||||
Pop-Location
|
Pop-Location
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -214,7 +214,7 @@ Create local library and CLI artifacts:
|
|||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
Push-Location clients/java
|
Push-Location clients/java
|
||||||
gradle :mxgateway-client:jar :mxgateway-cli:installDist
|
gradle :zb-mom-ww-mxgateway-client:jar :zb-mom-ww-mxgateway-cli:installDist
|
||||||
Pop-Location
|
Pop-Location
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -222,9 +222,9 @@ Run the CLI through Gradle:
|
|||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
Push-Location clients/java
|
Push-Location clients/java
|
||||||
gradle :mxgateway-cli:run --args="version --json"
|
gradle :zb-mom-ww-mxgateway-cli:run --args="version --json"
|
||||||
gradle :mxgateway-cli:run --args="smoke --endpoint $env:MXGATEWAY_ENDPOINT --plaintext --api-key-env MXGATEWAY_API_KEY --item $env:MXGATEWAY_TEST_ITEM --json"
|
gradle :zb-mom-ww-mxgateway-cli:run --args="smoke --endpoint $env:MXGATEWAY_ENDPOINT --plaintext --api-key-env MXGATEWAY_API_KEY --item $env:MXGATEWAY_TEST_ITEM --json"
|
||||||
gradle :mxgateway-cli:run --args="smoke --endpoint mxgateway.example.local:5001 --ca-file C:\certs\mxgateway-ca.pem --server-name-override mxgateway.example.local --api-key-env MXGATEWAY_API_KEY --item $env:MXGATEWAY_TEST_ITEM --json"
|
gradle :zb-mom-ww-mxgateway-cli:run --args="smoke --endpoint mxgateway.example.local:5001 --ca-file C:\certs\mxgateway-ca.pem --server-name-override mxgateway.example.local --api-key-env MXGATEWAY_API_KEY --item $env:MXGATEWAY_TEST_ITEM --json"
|
||||||
Pop-Location
|
Pop-Location
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ The manifest declares these generated-code directories:
|
|||||||
| .NET | `clients/dotnet/generated` |
|
| .NET | `clients/dotnet/generated` |
|
||||||
| Go | `clients/go/internal/generated` |
|
| Go | `clients/go/internal/generated` |
|
||||||
| Rust | `clients/rust/src/generated` |
|
| Rust | `clients/rust/src/generated` |
|
||||||
| Python | `clients/python/src/mxgateway/generated` |
|
| Python | `clients/python/src/zb_mom_ww_mxgateway/generated` |
|
||||||
| Java | `clients/java/src/main/generated` |
|
| Java | `clients/java/src/main/generated` |
|
||||||
|
|
||||||
Only generator output belongs in these directories. Handwritten client wrappers
|
Only generator output belongs in these directories. Handwritten client wrappers
|
||||||
@@ -142,7 +142,7 @@ cargo check --workspace
|
|||||||
```
|
```
|
||||||
|
|
||||||
Python clients should use `grpc_tools.protoc` and write generated modules under
|
Python clients should use `grpc_tools.protoc` and write generated modules under
|
||||||
`clients/python/src/mxgateway/generated` so imports stay separate from
|
`clients/python/src/zb_mom_ww_mxgateway/generated` so imports stay separate from
|
||||||
handwritten async wrappers.
|
handwritten async wrappers.
|
||||||
|
|
||||||
The Python scaffold provides a repo-local generation script:
|
The Python scaffold provides a repo-local generation script:
|
||||||
|
|||||||
@@ -288,7 +288,7 @@ Use this checklist when applying the design to another project:
|
|||||||
- Use top-bordered sections for page groups instead of nested cards.
|
- Use top-bordered sections for page groups instead of nested cards.
|
||||||
- Centralize formatting and redaction outside Razor markup.
|
- Centralize formatting and redaction outside Razor markup.
|
||||||
- Hide every destructive admin affordance from viewers; render it only for
|
- Hide every destructive admin affordance from viewers; render it only for
|
||||||
the `Admin` role and re-check the role server-side on every invocation.
|
the `Administrator` role and re-check the role server-side on every invocation.
|
||||||
- Route every destructive action (Close session, Kill worker, Rotate /
|
- Route every destructive action (Close session, Kill worker, Rotate /
|
||||||
Revoke / Delete API key) through the shared `ConfirmDialog` component so
|
Revoke / Delete API key) through the shared `ConfirmDialog` component so
|
||||||
the operator always gets one explicit confirmation step before the call
|
the operator always gets one explicit confirmation step before the call
|
||||||
|
|||||||
+2
-2
@@ -162,7 +162,7 @@ public static IApplicationBuilder UseGatewayRequestLoggingScope(this IApplicatio
|
|||||||
{
|
{
|
||||||
ILogger logger = context.RequestServices
|
ILogger logger = context.RequestServices
|
||||||
.GetRequiredService<ILoggerFactory>()
|
.GetRequiredService<ILoggerFactory>()
|
||||||
.CreateLogger("ZB.MOM.WW.MxGateway.Request");
|
.CreateLogger("MxGateway.Request");
|
||||||
|
|
||||||
using IDisposable? scope = logger.BeginGatewayScope(new GatewayLogScope(
|
using IDisposable? scope = logger.BeginGatewayScope(new GatewayLogScope(
|
||||||
SessionId: ReadHeader(context, SessionIdHeaderName),
|
SessionId: ReadHeader(context, SessionIdHeaderName),
|
||||||
@@ -188,7 +188,7 @@ The scope is keyed off four custom headers and the standard `authorization` head
|
|||||||
|
|
||||||
The numeric headers use `int.TryParse` and `ulong.TryParse`; missing or unparseable values become `null` and are dropped by `GatewayLogScope.ToDictionary`. This keeps the middleware tolerant of clients that do not yet emit every header, which matters because the earliest call in a session (`OpenSession`) has no `SessionId` to send.
|
The numeric headers use `int.TryParse` and `ulong.TryParse`; missing or unparseable values become `null` and are dropped by `GatewayLogScope.ToDictionary`. This keeps the middleware tolerant of clients that do not yet emit every header, which matters because the earliest call in a session (`OpenSession`) has no `SessionId` to send.
|
||||||
|
|
||||||
The logger category is `ZB.MOM.WW.MxGateway.Request`, which lets operators filter the request scope events independently from per-component categories.
|
The logger category is `MxGateway.Request`, which lets operators filter the request scope events independently from per-component categories.
|
||||||
|
|
||||||
### Pipeline ordering
|
### Pipeline ordering
|
||||||
|
|
||||||
|
|||||||
@@ -419,17 +419,17 @@ embedded in the status detail.
|
|||||||
|
|
||||||
The gateway's Blazor dashboard surfaces a Galaxy summary in two places:
|
The gateway's Blazor dashboard surfaces a Galaxy summary in two places:
|
||||||
|
|
||||||
- An overview card on `/dashboard` showing connectivity status, last deploy
|
- An overview card on `/` showing connectivity status, last deploy
|
||||||
timestamp, object count (with area count), attribute total, historized and
|
timestamp, object count (with area count), attribute total, historized and
|
||||||
alarm counts, and last successful refresh.
|
alarm counts, and last successful refresh.
|
||||||
- A dedicated `/dashboard/galaxy` page with object-category and top-template
|
- A dedicated `/galaxy` page with object-category and top-template
|
||||||
breakdowns plus a Sync Info table covering last successful refresh, last
|
breakdowns plus a Sync Info table covering last successful refresh, last
|
||||||
attempt, refresh interval, redacted connection string, and command timeout.
|
attempt, refresh interval, redacted connection string, and command timeout.
|
||||||
|
|
||||||
Both views are projected from the same `IGalaxyHierarchyCache` that backs the
|
Both views are projected from the same `IGalaxyHierarchyCache` that backs the
|
||||||
gRPC service. The dashboard does not run its own refresh — when the
|
gRPC service. The dashboard does not run its own refresh — when the
|
||||||
background `GalaxyHierarchyRefreshService` updates the cache, both the
|
background `GalaxyHierarchyRefreshService` updates the cache, both the
|
||||||
overview card and the `/dashboard/galaxy` page pick up the new state on the
|
overview card and the `/galaxy` page pick up the new state on the
|
||||||
next dashboard tick. When SQL is unreachable, the cache retains the previous
|
next dashboard tick. When SQL is unreachable, the cache retains the previous
|
||||||
data and flips `Status` to `Stale` or `Unavailable`; the dashboard surfaces
|
data and flips `Status` to `Stale` or `Unavailable`; the dashboard surfaces
|
||||||
that as a yellow or red status badge plus the truncated error.
|
that as a yellow or red status badge plus the truncated error.
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ paths, timeouts, queue sizes, enum values, or protocol values are invalid.
|
|||||||
"RecentSessionLimit": 200,
|
"RecentSessionLimit": 200,
|
||||||
"ShowTagValues": false,
|
"ShowTagValues": false,
|
||||||
"GroupToRole": {
|
"GroupToRole": {
|
||||||
"GwAdmin": "Admin",
|
"GwAdmin": "Administrator",
|
||||||
"GwReader": "Viewer"
|
"GwReader": "Viewer"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -153,7 +153,7 @@ the affected stream while the MXAccess session remains active.
|
|||||||
| `MxGateway:Dashboard:RecentFaultLimit` | `100` | Maximum number of fault summaries projected into each dashboard snapshot. |
|
| `MxGateway:Dashboard:RecentFaultLimit` | `100` | Maximum number of fault summaries projected into each dashboard snapshot. |
|
||||||
| `MxGateway:Dashboard:RecentSessionLimit` | `200` | Maximum number of session summaries projected into each dashboard snapshot. |
|
| `MxGateway:Dashboard:RecentSessionLimit` | `200` | Maximum number of session summaries projected into each dashboard snapshot. |
|
||||||
| `MxGateway:Dashboard:ShowTagValues` | `false` | Reserved display control for tag values. The dashboard does not show full tag values by default. |
|
| `MxGateway:Dashboard:ShowTagValues` | `false` | Reserved display control for tag values. The dashboard does not show full tag values by default. |
|
||||||
| `MxGateway:Dashboard:GroupToRole` | _(empty)_ | LDAP group → dashboard role mapping. Keys are LDAP group names (short CN or full DN — leading-RDN match). Values must be `Admin` (read/write, API-key CRUD) or `Viewer` (read-only). A user whose LDAP groups don't intersect this map cannot sign in; with no mapping at all, only the loopback bypass admits anyone. |
|
| `MxGateway:Dashboard:GroupToRole` | _(empty)_ | LDAP group → dashboard role mapping. Keys are LDAP group names (short CN or full DN — leading-RDN match). Values must be `Administrator` (read/write, API-key CRUD) or `Viewer` (read-only). A user whose LDAP groups don't intersect this map cannot sign in; with no mapping at all, only the loopback bypass admits anyone. |
|
||||||
|
|
||||||
`SnapshotIntervalMilliseconds` must be greater than zero. `RecentFaultLimit`
|
`SnapshotIntervalMilliseconds` must be greater than zero. `RecentFaultLimit`
|
||||||
and `RecentSessionLimit` must be greater than or equal to zero.
|
and `RecentSessionLimit` must be greater than or equal to zero.
|
||||||
@@ -166,10 +166,10 @@ users) but practical deployments populate at least one Admin group.
|
|||||||
Three authorization policies are registered out of these options:
|
Three authorization policies are registered out of these options:
|
||||||
|
|
||||||
- `MxGateway.Dashboard.Viewer` — gates the Razor component routes. Satisfied by
|
- `MxGateway.Dashboard.Viewer` — gates the Razor component routes. Satisfied by
|
||||||
either dashboard role (Admin or Viewer), by `AllowAnonymousLocalhost` on
|
either dashboard role (Administrator or Viewer), by `AllowAnonymousLocalhost` on
|
||||||
loopback, or by `Authentication.Mode = Disabled`.
|
loopback, or by `Authentication.Mode = Disabled`.
|
||||||
- `MxGateway.Dashboard.Admin` — gates write-capable surfaces (API-key CRUD).
|
- `MxGateway.Dashboard.Admin` — gates write-capable surfaces (API-key CRUD).
|
||||||
Satisfied only by the Admin role (same environmental bypasses).
|
Satisfied only by the Administrator role (same environmental bypasses).
|
||||||
- `MxGateway.Dashboard.HubClients` — attached to the SignalR hubs. Accepts
|
- `MxGateway.Dashboard.HubClients` — attached to the SignalR hubs. Accepts
|
||||||
either the dashboard cookie scheme or the `MxGateway.Dashboard.HubToken`
|
either the dashboard cookie scheme or the `MxGateway.Dashboard.HubToken`
|
||||||
bearer scheme (used by SignalR's WebSocket upgrade path where the HttpOnly
|
bearer scheme (used by SignalR's WebSocket upgrade path where the HttpOnly
|
||||||
|
|||||||
@@ -288,7 +288,7 @@ it opt-in and redacted.
|
|||||||
|
|
||||||
### Browse page
|
### Browse page
|
||||||
|
|
||||||
`/dashboard/browse` lets an operator explore the Galaxy tag hierarchy and watch
|
`/browse` lets an operator explore the Galaxy tag hierarchy and watch
|
||||||
live values. The tree is built in-process by `DashboardBrowseTreeBuilder` from
|
live values. The tree is built in-process by `DashboardBrowseTreeBuilder` from
|
||||||
`IGalaxyHierarchyCache.Current` — the same cache the Galaxy page reads — so a
|
`IGalaxyHierarchyCache.Current` — the same cache the Galaxy page reads — so a
|
||||||
render costs no gRPC call and no SQL round-trip. Each node shows its child
|
render costs no gRPC call and no SQL round-trip. Each node shows its child
|
||||||
@@ -306,7 +306,7 @@ diagnostic session/worker views.
|
|||||||
|
|
||||||
### Alarms page
|
### Alarms page
|
||||||
|
|
||||||
`/dashboard/alarms` lists the alarms the gateway's central alarm monitor
|
`/alarms` lists the alarms the gateway's central alarm monitor
|
||||||
currently holds as Active or ActiveAcked, refreshed every three seconds. It
|
currently holds as Active or ActiveAcked, refreshed every three seconds. It
|
||||||
defaults to showing unacknowledged `Active` alarms; filters add acknowledged
|
defaults to showing unacknowledged `Active` alarms; filters add acknowledged
|
||||||
alarms and narrow by area, severity range, and a reference/source/description
|
alarms and narrow by area, severity range, and a reference/source/description
|
||||||
@@ -335,7 +335,7 @@ the monitor never starts and the cache stays empty.
|
|||||||
|
|
||||||
### API keys page
|
### API keys page
|
||||||
|
|
||||||
`/dashboard/apikeys` lists the gateway's API keys and, for authorized
|
`/apikeys` lists the gateway's API keys and, for authorized
|
||||||
operators, manages them. It reads key metadata through the same
|
operators, manages them. It reads key metadata through the same
|
||||||
`IApiKeyAdminStore` the `apikey` CLI uses, so the dashboard and the CLI act
|
`IApiKeyAdminStore` the `apikey` CLI uses, so the dashboard and the CLI act
|
||||||
on one source of truth.
|
on one source of truth.
|
||||||
@@ -421,7 +421,7 @@ Implemented behavior:
|
|||||||
`ClaimTypes.Role` claims, alongside the per-group `mxgateway:ldap_group`
|
`ClaimTypes.Role` claims, alongside the per-group `mxgateway:ldap_group`
|
||||||
claims;
|
claims;
|
||||||
- a successful login signs in the `MxGateway.Dashboard` cookie scheme
|
- a successful login signs in the `MxGateway.Dashboard` cookie scheme
|
||||||
(`__Host-MxGatewayDashboard`, HttpOnly, SameSite=Strict, Secure);
|
(`MxGatewayDashboard`, HttpOnly, SameSite=Strict, Secure);
|
||||||
- a user with no matching group cannot sign in — the login screen returns the
|
- a user with no matching group cannot sign in — the login screen returns the
|
||||||
generic credential-rejected message;
|
generic credential-rejected message;
|
||||||
- antiforgery tokens guard the login and logout POSTs.
|
- antiforgery tokens guard the login and logout POSTs.
|
||||||
|
|||||||
@@ -247,12 +247,12 @@ Technology:
|
|||||||
Suggested routes:
|
Suggested routes:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
/dashboard
|
/
|
||||||
/dashboard/sessions
|
/sessions
|
||||||
/dashboard/sessions/{sessionId}
|
/sessions/{sessionId}
|
||||||
/dashboard/workers
|
/workers
|
||||||
/dashboard/events
|
/events
|
||||||
/dashboard/settings
|
/settings
|
||||||
```
|
```
|
||||||
|
|
||||||
Dashboard pages:
|
Dashboard pages:
|
||||||
|
|||||||
@@ -320,7 +320,7 @@ writes its `{"error":...}` envelope and the loop continues; the harness treats
|
|||||||
that envelope as the operation failure (used by the parity and auth phases).
|
that envelope as the operation failure (used by the parity and auth phases).
|
||||||
|
|
||||||
Before the per-client phases run, the script builds the .NET CLI
|
Before the per-client phases run, the script builds the .NET CLI
|
||||||
(`dotnet build`) and installs the Java CLI (`gradle :mxgateway-cli:installDist`)
|
(`dotnet build`) and installs the Java CLI (`gradle :zb-mom-ww-mxgateway-cli:installDist`)
|
||||||
once, so the `batch` process launches straight from the compiled exe / the
|
once, so the `batch` process launches straight from the compiled exe / the
|
||||||
installed launcher. The Go, Rust, and Python batch processes are launched via
|
installed launcher. The Go, Rust, and Python batch processes are launched via
|
||||||
`go run` / `cargo run` / `python -m`, which compile-or-start once when that
|
`go run` / `cargo run` / `python -m`, which compile-or-start once when that
|
||||||
|
|||||||
@@ -251,7 +251,7 @@ The loop should update a heartbeat timestamp after:
|
|||||||
- processing an MXAccess event.
|
- processing an MXAccess event.
|
||||||
|
|
||||||
`StaRuntime` implements this runtime boundary in the worker. It starts one
|
`StaRuntime` implements this runtime boundary in the worker. It starts one
|
||||||
background thread named `ZB.MOM.WW.MxGateway.Worker.STA`, sets it to `ApartmentState.STA`,
|
background thread named `MxGateway.Worker.STA`, sets it to `ApartmentState.STA`,
|
||||||
initializes COM through `StaComApartmentInitializer`, and runs
|
initializes COM through `StaComApartmentInitializer`, and runs
|
||||||
`StaMessagePump`. Commands are scheduled through `InvokeAsync`; the command
|
`StaMessagePump`. Commands are scheduled through `InvokeAsync`; the command
|
||||||
queue signals an `AutoResetEvent` so `MsgWaitForMultipleObjectsEx` can wake the
|
queue signals an `AutoResetEvent` so `MsgWaitForMultipleObjectsEx` can wake the
|
||||||
|
|||||||
+2
-2
@@ -20,13 +20,13 @@ The installed MXAccess interop assembly declares an `Apartment` threading model
|
|||||||
|
|
||||||
## STA Thread Initialization
|
## STA Thread Initialization
|
||||||
|
|
||||||
`StaRuntime`'s constructor configures a background `Thread` named `ZB.MOM.WW.MxGateway.Worker.STA` and forces it into `ApartmentState.STA` before the thread starts. `Start()` releases the thread and then blocks on `startedEvent` so callers observe a fully-initialized apartment (or a captured `startupException`) before the first `InvokeAsync` call:
|
`StaRuntime`'s constructor configures a background `Thread` named `MxGateway.Worker.STA` and forces it into `ApartmentState.STA` before the thread starts. `Start()` releases the thread and then blocks on `startedEvent` so callers observe a fully-initialized apartment (or a captured `startupException`) before the first `InvokeAsync` call:
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
staThread = new Thread(ThreadMain)
|
staThread = new Thread(ThreadMain)
|
||||||
{
|
{
|
||||||
IsBackground = true,
|
IsBackground = true,
|
||||||
Name = "ZB.MOM.WW.MxGateway.Worker.STA"
|
Name = "MxGateway.Worker.STA"
|
||||||
};
|
};
|
||||||
staThread.SetApartmentState(ApartmentState.STA);
|
staThread.SetApartmentState(ApartmentState.STA);
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ library, CLI, and tests.
|
|||||||
|
|
||||||
## Packages
|
## Packages
|
||||||
|
|
||||||
- Use lowercase package names under `com.dohertylan.mxgateway`.
|
- Use lowercase package names under `com.zb.mom.ww.mxgateway`.
|
||||||
- Keep client library code separate from CLI code.
|
- Keep client library code separate from CLI code.
|
||||||
- Keep generated protobuf classes in a generated package.
|
- Keep generated protobuf classes in a generated package.
|
||||||
- Do not expose implementation-only transport helpers as public API.
|
- Do not expose implementation-only transport helpers as public API.
|
||||||
|
|||||||
@@ -24,8 +24,8 @@ CLI, and tests.
|
|||||||
|
|
||||||
## Package Structure
|
## Package Structure
|
||||||
|
|
||||||
- Put library code under `src/mxgateway/`.
|
- Put library code under `src/zb_mom_ww_mxgateway/`.
|
||||||
- Put CLI entry points under `src/mxgateway_cli/`.
|
- Put CLI entry points under `src/zb_mom_ww_mxgateway_cli/`.
|
||||||
- Keep generated protobuf modules under a clearly named `generated` package.
|
- Keep generated protobuf modules under a clearly named `generated` package.
|
||||||
- Avoid import side effects that open channels, read environment variables, or
|
- Avoid import side effects that open channels, read environment variables, or
|
||||||
start background tasks.
|
start background tasks.
|
||||||
|
|||||||
Reference in New Issue
Block a user