Commit Graph

967 Commits

Author SHA1 Message Date
Joseph Doherty
0ee914e36c docs(docker-env2): add env2 README 2026-05-24 07:17:38 -04:00
Joseph Doherty
9d5b814f9b chore(gitignore): add explicit docker-env2 runtime data patterns 2026-05-24 07:17:36 -04:00
Joseph Doherty
4316aacd44 feat(docker-env2): add docker-compose for env2 cluster 2026-05-24 07:17:34 -04:00
Joseph Doherty
c252a80f9d feat(docker-env2): add site-x appsettings 2026-05-24 07:17:32 -04:00
Joseph Doherty
63d1a96557 feat(docker-env2): add central node appsettings 2026-05-24 07:17:29 -04:00
Joseph Doherty
9c6abc6517 feat(docker-env2): add Traefik load-balancer config 2026-05-24 07:17:26 -04:00
Joseph Doherty
4b797c9f69 feat(infra): add env2 database setup script + mount 2026-05-24 07:17:23 -04:00
Joseph Doherty
e66fee0d26 docs(plans): add second environment (env2) implementation plan
11-task plan (T0-T10) covering the sibling docker-env2/ directory:
SQL setup script + mount, Traefik config, central/site appsettings,
docker-compose, lifecycle scripts, .gitignore, READMEs and cross-refs,
verification checklist, and a manual smoke test. No application code
changes -- pure deploy tooling. Most tasks (T0-T9) are independent
and parallel-ready; T5 is gated on T0 + T4; T10 gates on all of T0-T9.
2026-05-24 07:08:46 -04:00
Joseph Doherty
2fd3426fc2 docs(plans): add second environment (env2) design
Brainstorming output for a sibling docker-env2/ tree that brings up a
minimal second cluster (2 central + 1 site x 2 nodes + Traefik) on the
same machine alongside the primary docker/ stack. Shares the existing
scadalink-net network and scadalink-mssql container but uses separate
logical databases (ScadaLinkConfig2 / ScadaLinkMachineData2) so the
Transport (#24) feature can be exercised end-to-end with real
cross-environment exports and imports.
2026-05-24 07:03:02 -04:00
Joseph Doherty
a7141c704f test(centralui): remove stale LoginPage_RendersLdapCredentialHint test
The asserted 'LDAP credentials' tagline was deliberately removed from
Login.razor in f973f49 but the test was not updated alongside. Drop
the test — it asserts on UI text that no longer exists by design.
2026-05-24 06:51:37 -04:00
Joseph Doherty
624cf255a4 feat(transport): wire full SemanticValidator at bundle import time 2026-05-24 06:32:42 -04:00
Joseph Doherty
8e73e60f4a feat(transport): restore composition + alarm-script edges on bundle import 2026-05-24 06:16:24 -04:00
Joseph Doherty
cef77e1378 fix(transport): carry TemplateAlarm.OnTriggerScript by name in bundle DTO 2026-05-24 06:10:59 -04:00
Joseph Doherty
79d74ee59c fix(centralui): hint that notification list export does not include SMTP config
Add an unconditional alert-info banner in the Notification Lists fieldset
(Step 1) explaining that SMTP configurations are not auto-included as
dependencies and must be selected separately.
2026-05-24 06:05:53 -04:00
Joseph Doherty
e6706c26e6 fix(transport): preserve MinTimeBetweenRuns + ExternalSystem retry fields in bundle DTOs
Add TimeSpan? MinTimeBetweenRuns to TemplateScriptDto and int MaxRetries /
TimeSpan RetryDelay to ExternalSystemDto; wire both directions in
EntitySerializer. Extends the existing script round-trip assertion and adds
Roundtrip_external_system_preserves_retry_config.
2026-05-24 06:05:26 -04:00
Joseph Doherty
a2b8b69281 fix(transport): NavMenu Admin-only visibility + BundleImportUnlockFailed audit + docker appsettings
- NavMenu: move Import Bundle out of the nested RequireDesign/RequireAdmin
  double-gate into the top-level Admin section so an Admin-only user sees it
  without needing the Design role; Export Bundle stays in the Design section.
- TransportImport: inject IAuditService + ScadaLinkDbContext; emit a
  BundleImportUnlockFailed audit row (best-effort, swallowed on failure) on
  every wrong-passphrase attempt in SubmitPassphraseAsync, with attempt
  number and error reason in afterState.
- docker central-node-a/b appsettings: add ScadaLink:Transport section with
  SourceEnvironment = "docker-cluster" so the importer picks up a non-null
  environment name in the audit trail.
- CentralUI.Tests: register IAuditService mock + SQLite in-memory
  ScadaLinkDbContext in TransportImportPageTests to satisfy the two new injects.
2026-05-24 05:59:04 -04:00
Joseph Doherty
9f1bb81993 test(transport): integration conflict resolution + rollback 2026-05-24 05:50:11 -04:00
Joseph Doherty
623aa8d061 test(transport): integration round-trip export → wipe → import 2026-05-24 05:48:24 -04:00
Joseph Doherty
ef025a325d feat(centralui): Bundle Import filter on ConfigurationAuditLog page 2026-05-24 05:44:21 -04:00
Joseph Doherty
39f994f9bc feat(centralui): add Export/Import Bundle nav entries 2026-05-24 05:38:48 -04:00
Joseph Doherty
acadb83712 feat(centralui): TransportImport wizard under Design nav group 2026-05-24 05:38:09 -04:00
Joseph Doherty
0dbc0c02f9 feat(centralui): TransportExport wizard under Design nav group
Implements Task T21 of the Transport feature. A four-step Blazor wizard
(Select → Review → Encrypt → Download) under /design/transport/export,
gated on AuthorizationPolicies.RequireDesign:

  1. Select  — TemplateFolderTree (checkbox-mode) plus flat checkbox
               lists for shared scripts, external systems, DB connections,
               notification lists, SMTP configs, API keys, API methods.
  2. Review  — runs DependencyResolver, surfaces seed vs auto-included.
               "Include all dependencies" toggle re-resolves on flip.
  3. Encrypt — passphrase + confirm with strength meter, secret-count
               warning over the resolved closure, explicit unencrypted
               opt-out path (calls BundleExporter with passphrase=null
               so the audit row tags UnencryptedBundleExport).
  4. Download— calls IBundleExporter.ExportAsync, streams bytes to the
               browser via JS interop (wwwroot/js/transport.js), displays
               filename + size + SHA-256 + encryption status.

Source environment is sourced from new TransportOptions.SourceEnvironment
(bound from ScadaLink:Transport:SourceEnvironment, defaults "scadalink"),
filename pattern scadabundle-{env}-{yyyy-MM-dd-HHmmss}.scadabundle.

Tests (bUnit + policy): step 1 group rendering, step 2 dependency
expansion (Pump composes Motor), step 4 full walkthrough verifying
ExportAsync receives the selected ids + authenticated identity, and a
RequireDesign policy-deny test for users without the Design role. Also
unit-pins the filename-sanitisation contract.
2026-05-24 05:30:16 -04:00
Joseph Doherty
01f4eeaef5 refactor(centralui): extract TemplateFolderTree as shared component 2026-05-24 05:18:12 -04:00
Joseph Doherty
e099ed2038 feat(centralui): TreeView checkbox-selection mode with tri-state 2026-05-24 05:13:04 -04:00
Joseph Doherty
9a3f5231db feat(transport): register AddTransport() on central nodes 2026-05-24 05:09:51 -04:00
Joseph Doherty
cda80cf821 fix(transport): robust failure-audit when rollback throws + doc clarifications
Address one Blocker and three Important findings from code review of
2c34f12 (BundleImporter.ApplyAsync):

- BLOCKER: wrap RollbackAsync in nested try/catch so a rollback fault
  does not swallow the BundleImportFailed audit row. Dispose the
  failed transaction before the audit-write so the new SaveChangesAsync
  uses a fresh implicit transaction instead of enlisting in the broken
  one. Surface the rollback exception's message on the failure row
  alongside the original cause, and swallow audit-write faults per the
  design's best-effort-audit invariant. Add regression integration
  test using a SQLite transaction interceptor that throws on rollback.

- Document re-entrancy assumption on IAuditCorrelationContext: scoped
  lifetime, single circuit, concurrent imports within a shared scope
  must serialize externally.

- Document repository audit responsibility on BundleImporter: repos
  are thin EF wrappers; ApplyAsync writes audit rows explicitly. If
  repos ever start emitting audit rows, the explicit calls here must
  be removed to avoid double-logging.

- Document BundleSessionStore thread-safety: ConcurrentDictionary
  primitives are safe under concurrent callers; BundleSession itself
  is not thread-safe.
2026-05-24 05:06:04 -04:00
Joseph Doherty
2c34f12a6f feat(transport): BundleImporter.ApplyAsync transactional with audit correlation 2026-05-24 04:55:43 -04:00
Joseph Doherty
90baa4d6d5 docs(transport): manual cluster verification checklist 2026-05-24 04:52:59 -04:00
Joseph Doherty
b1daf9abb8 docs: README + component cross-references for Transport (#24) 2026-05-24 04:52:55 -04:00
Joseph Doherty
268a847ef3 docs: Component-Transport.md (component #24) 2026-05-24 04:52:51 -04:00
Joseph Doherty
2400249453 feat(transport): BundleImporter.PreviewAsync diff engine 2026-05-24 04:41:24 -04:00
Joseph Doherty
5fc6790c36 feat(transport): BundleImporter.LoadAsync with manifest validation 2026-05-24 04:37:02 -04:00
Joseph Doherty
7c70ce0dbf feat(transport): BundleExporter with audit logging 2026-05-24 04:30:18 -04:00
Joseph Doherty
901d9affdf feat(transport): in-memory BundleSessionStore with TTL + lockout 2026-05-24 04:20:55 -04:00
Joseph Doherty
06c2b20178 feat(transport): DependencyResolver with topological closure 2026-05-24 04:19:23 -04:00
Joseph Doherty
550ab0e034 feat(transport): BundleSerializer ZIP packer/reader 2026-05-24 04:11:11 -04:00
Joseph Doherty
ee76b84b0f feat(transport): bundle entity DTOs + secret carving in EntitySerializer 2026-05-24 04:08:43 -04:00
Joseph Doherty
447bf84b13 feat(transport): ManifestBuilder + ManifestValidator with schema-version gating 2026-05-24 04:04:58 -04:00
Joseph Doherty
dc669a119b feat(transport): AES-256-GCM + PBKDF2 BundleSecretEncryptor 2026-05-24 04:03:44 -04:00
Joseph Doherty
c5bd5418ad feat(transport): add TransportOptions 2026-05-24 03:58:07 -04:00
Joseph Doherty
7e51274812 feat(transport): scaffold ScadaLink.Transport project + test projects 2026-05-24 03:57:07 -04:00
Joseph Doherty
f32b59a557 feat(transport): AuditService stamps BundleImportId from correlation context 2026-05-24 03:55:17 -04:00
Joseph Doherty
233e0f996e feat(transport): EF migration AddBundleImportIdToAuditLog 2026-05-24 03:50:30 -04:00
Joseph Doherty
33f7b3979d feat(transport): add IAuditCorrelationContext scoped service 2026-05-24 03:49:26 -04:00
Joseph Doherty
ee10eba04c feat(transport): add BundleImportId column on AuditLogEntry 2026-05-24 03:48:22 -04:00
Joseph Doherty
9442c9a92c feat(transport): add IBundleExporter / IBundleImporter interfaces 2026-05-24 03:47:27 -04:00
Joseph Doherty
7e89f2092f feat(transport): add bundle manifest DTOs in Commons 2026-05-24 03:46:09 -04:00
Joseph Doherty
1bc98e10a1 docs(plans): add Transport (Component #24) implementation plan
30-task plan covering ScadaLink.Transport project, AES-256-GCM bundle
encryption, IAuditCorrelationContext for BundleImportId threading,
TreeView checkbox-selection mode + TemplateFolderTree wrapper, two
Central UI wizard pages, EF migration, integration tests, README +
cross-reference updates. Single shipping slice, no feature flag.
2026-05-24 03:43:18 -04:00
Joseph Doherty
1b02f33829 docs(plans): add Transport (Component #24) brainstorming design
File-based, encrypted bundle export/import via the Central UI for
promoting templates, system artifacts, and central-only configuration
across environments. Site-scoped artifacts excluded. Per-artifact
conflict resolution; config-only import (user redeploys via existing
Deployments page). Per-entity audit rows correlated by BundleImportId.
2026-05-24 03:32:21 -04:00
Joseph Doherty
d630e2646b feat(ui): show SourceNode under SourceSiteId in audit log detail popup
The audit log drilldown drawer (and the execution-tree node-detail modal,
which shares this component) now renders the SourceNode field directly
under SourceSiteId so provenance reads 'site → node → instance → script'
in declared order. Two focused tests pin the field's presence in both
populated and null cases plus the inter-field ordering.
2026-05-23 19:01:48 -04:00