diff --git a/docs/requirements/Component-Transport.md b/docs/requirements/Component-Transport.md index a2c8c48..b757641 100644 --- a/docs/requirements/Component-Transport.md +++ b/docs/requirements/Component-Transport.md @@ -155,7 +155,7 @@ Authorization: `RequireDesign` on both the Razor page and `IBundleExporter.Expor ## Import Flow -### UI — 5-Step Wizard (Design nav group) +### UI — 5-Step Wizard (Admin nav group) **Step 1 — Upload.** Drag-and-drop or browse. On selection, the manifest is parsed and displayed (source env, exporter, timestamp, content count, SHA-256, encrypted yes/no). The manifest hash is validated against the `content` blob. @@ -171,6 +171,8 @@ Bulk "Apply to all" at the top (Overwrite / Skip / Rename), overridable per row. Bundle references that cannot be satisfied in either the bundle or the target DB (e.g., a template references a shared script that is neither in the bundle nor pre-existing) appear as **blocker rows** — Apply is disabled until they are resolved (typically by skipping the dependent artifact or re-exporting with dependencies). +**Blocker-scan heuristic boundaries.** The scanner walks `TemplateScript.Code`, `TemplateAttribute.Value`, and `ApiMethod.Script` looking for top-level `Identifier(` or `Identifier.` tokens. To keep the heuristic usable on real script bodies it (a) skips identifiers preceded by `.` (member access — `obj.Method()` does not flag `Method`); (b) does NOT scan `TemplateAttribute.DataSourceReference` (an OPC UA address path, never script source); and (c) filters out a small `KnownNonReferenceNames` denylist of .NET stdlib types (`Convert`, `DateTimeOffset`, `ToString`, `Dispose`, `UtcNow`, …), ScadaLink runtime API roots (`Notify`, `Database`, `ExternalSystem`, `Scripts`, `Instance`, `Parameters`, `Attributes`, `Route`, …), and common SQL keywords that appear inside string literals (`COUNT`, `SELECT`, `FROM`, …). Both the diff-step `DetectBlockersAsync` and the Apply-time `RunSemanticValidationAsync` Pass 1 share this filter, so the diff preview and the Apply gate agree. + **Step 4 — Confirm.** Final summary plus a "N instances will become stale" warning enumerating affected instances. User types the source environment name to confirm (typo-resistant gate at the prod boundary). **Step 5 — Result.** Counts, link to the `BundleImported` audit row, link to the Deployments page filtered to the newly stale instances. @@ -264,13 +266,35 @@ Import flows through the same audited repository methods the UI and CLI use, so **Transactional guarantee:** the EF transaction wraps both the entity writes and the audit log writes (existing pattern). A rollback removes both. The `BundleImported` summary row is the final write inside the transaction, so partial audit trails are impossible. A failed import emits a single `BundleImportFailed` row outside the rolled-back transaction. +## CLI + +Three commands surface the same Transport operations as the Central UI wizards, designed for test automation. The bundle bytes travel as base64 inside the existing `/management` JSON envelope — no new HTTP endpoints — and the per-request body cap is raised to 200 MB to cover the 100 MB raw-bundle ceiling once base64-inflated. + +```bash +scadalink bundle export --output FILE --passphrase X [--all | --templates A,B ...] \ + [--shared-scripts ...] [--external-systems ...] [--db-connections ...] \ + [--notification-lists ...] [--smtp-configs ...] [--api-keys ...] \ + [--api-methods ...] [--include-dependencies] [--source-environment NAME] + +scadalink bundle preview --input FILE --passphrase X + # prints PreviewBundleResult JSON: per-row items + add/modified/identical/blocker counts + +scadalink bundle import --input FILE --passphrase X [--on-conflict skip|overwrite|rename] + # one-shot load + preview + apply with a single global policy for Modified rows + # Identical → Skip, New → Add, Blocker → abort +``` + +Selection uses entity **names** rather than IDs so scripts are portable across environments. The CLI per-command timeout is 5 minutes (vs the default 30 s for other commands) to comfortably cover large bundles. CLI commands route through `ManagementActor`'s `ExportBundleCommand` / `PreviewBundleCommand` / `ImportBundleCommand` handlers, which delegate to the same `IBundleExporter` / `IBundleImporter` services as the UI. + +Exit codes follow the project convention: `0` = success, `1` = command failure (validation, blockers, wrong passphrase), `2` = authorization failure. + ## Authorization | Operation | Required role | Enforced at | |---|---|---| -| Open Export page | `RequireDesign` | Razor page authorize attribute | +| Open Export page / `bundle export` CLI | `RequireDesign` | Razor page authorize attribute + `ManagementActor.GetRequiredRole` | | `IBundleExporter.ExportAsync` | `RequireDesign` | Service entrypoint | -| Open Import page | `RequireAdmin` | Razor page authorize attribute | +| Open Import page / `bundle preview` + `bundle import` CLI | `RequireAdmin` | Razor page authorize attribute + `ManagementActor.GetRequiredRole` | | `IBundleImporter.LoadAsync` / `PreviewAsync` / `ApplyAsync` | `RequireAdmin` | Service entrypoint | | Configuration Audit Log "Bundle Import" filter | `RequireAdmin` or `Audit` | Existing audit page logic | @@ -282,7 +306,8 @@ Import flows through the same audited repository methods the UI and CLI use, so ## Interactions -- **Central UI** — Hosts the Export Bundle (`/design/transport/export`) and Import Bundle (`/design/transport/import`) wizard pages under the Design nav group. The result page links to the Deployments page and to the filtered Configuration Audit Log Viewer. +- **Central UI** — Hosts the Export Bundle (`/design/transport/export`) page under the Design nav group and the Import Bundle (`/design/transport/import`) page under the Admin nav group. The import result page links to the Deployments page and to the filtered Configuration Audit Log Viewer. +- **Management Service / CLI** — `ManagementActor` registers three Transport command handlers (`ExportBundleCommand`, `PreviewBundleCommand`, `ImportBundleCommand`) and the CLI ships `bundle export` / `bundle preview` / `bundle import` subcommands. Bundle bytes ride the existing `/management` JSON envelope as base64. - **Deployment Manager** — Never directly invoked by Transport. Transport-driven template changes propagate to deployed instances through the existing revision-hash drift detection in `DeploymentService.CompareAsync`; the Deployments page surfaces affected instances as stale automatically. - **Security & Auth** — Provides `RequireDesign` and `RequireAdmin` policies from `ScadaLink.Security`, enforced at both the page and service layers. - **Audit Log (Configuration)** — Writes `BundleExported` / `BundleImported` / `BundleImportFailed` / `UnencryptedBundleExport` / `BundleImportUnlockFailed` rows via `IAuditService`, plus per-import name-resolution warnings `BundleImportAlarmScriptUnresolved` and `BundleImportCompositionUnresolved`; per-entity rows from audited repositories are correlated by `BundleImportId` via `IAuditCorrelationContext`.