From 54c03a3139834ba642a9ad31b19cee307810600e Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Tue, 17 Mar 2026 13:35:54 -0400 Subject: [PATCH] Add implementation plan: deploy artifacts, remove config DB dependency --- ...-03-17-deploy-artifacts-remove-configdb.md | 185 ++++++++++++++++++ ...oy-artifacts-remove-configdb.md.tasks.json | 13 ++ 2 files changed, 198 insertions(+) create mode 100644 docs/plans/2026-03-17-deploy-artifacts-remove-configdb.md create mode 100644 docs/plans/2026-03-17-deploy-artifacts-remove-configdb.md.tasks.json diff --git a/docs/plans/2026-03-17-deploy-artifacts-remove-configdb.md b/docs/plans/2026-03-17-deploy-artifacts-remove-configdb.md new file mode 100644 index 0000000..ef6e80e --- /dev/null +++ b/docs/plans/2026-03-17-deploy-artifacts-remove-configdb.md @@ -0,0 +1,185 @@ +# Deploy Artifacts to Sites — Remove Config DB Dependency + +> **For Claude:** REQUIRED SUB-SKILL: Use superpowers-extended-cc:executing-plans to implement this plan task-by-task. + +**Goal:** Make site servers fully self-contained by reading external system, notification, and data connection configs from local SQLite instead of the central config DB. + +**Architecture:** The artifact deployment pipeline already exists (`DeployArtifactsCommand` → `DeploymentManagerActor` → `SiteStorageService`). The gap is that runtime services still query the config DB. We create site-local repository implementations backed by SQLite, wire them into DI, add a UI button to trigger deployment, and extend artifacts to include data connections. + +**Tech Stack:** C#, SQLite (Microsoft.Data.Sqlite), Blazor Server, Akka.NET messaging + +--- + +### Task 1: Create SiteExternalSystemRepository + +**Files:** +- Create: `src/ScadaLink.SiteRuntime/Repositories/SiteExternalSystemRepository.cs` + +**Step 1:** Create `SiteExternalSystemRepository` implementing `IExternalSystemRepository`. Read-only methods query `SiteStorageService` SQLite tables. Write methods throw `NotSupportedException` (site is read-only for these configs). + +```csharp +// Key methods to implement: +// GetAllExternalSystemsAsync → query external_systems table, deserialize method_definitions JSON +// GetMethodsByExternalSystemIdAsync → parse method_definitions from external_systems row +// GetAllDatabaseConnectionsAsync → query database_connections table +// All Add/Update/Delete methods → throw NotSupportedException("Managed via artifact deployment") +``` + +The `external_systems` table stores `method_definitions` as JSON. Parse it into `List` on read. The `ExternalSystemDefinition` entity needs Id, Name, EndpointUrl, AuthType, AuthConfiguration. Map the `name` column to a synthetic Id (hash or sequential). + +**Step 2:** Build and verify no compile errors. + +**Step 3:** Commit: `feat: add SiteExternalSystemRepository backed by SQLite` + +--- + +### Task 2: Create SiteNotificationRepository + +**Files:** +- Create: `src/ScadaLink.SiteRuntime/Repositories/SiteNotificationRepository.cs` + +**Step 1:** Create `SiteNotificationRepository` implementing `INotificationRepository`. Read-only methods query `SiteStorageService` SQLite tables. + +```csharp +// Key methods: +// GetListByNameAsync → query notification_lists by name +// GetRecipientsByListIdAsync → parse recipient_emails JSON from notification_lists row +// GetAllSmtpConfigurationsAsync → needs new smtp_configurations SQLite table +// All Add/Update/Delete → throw NotSupportedException +``` + +**Step 2:** Add `smtp_configurations` table to `SiteStorageService.InitializeAsync()`: + +```sql +CREATE TABLE IF NOT EXISTS smtp_configurations ( + name TEXT PRIMARY KEY, + server TEXT NOT NULL, + port INTEGER NOT NULL, + auth_mode TEXT NOT NULL, + from_address TEXT NOT NULL, + username TEXT, + password TEXT, + oauth_config TEXT, + updated_at TEXT NOT NULL +); +``` + +**Step 3:** Add `StoreSmtpConfigurationAsync` method to `SiteStorageService`. + +**Step 4:** Build and verify. + +**Step 5:** Commit: `feat: add SiteNotificationRepository and SMTP storage` + +--- + +### Task 3: Add data connections to DeployArtifactsCommand + +**Files:** +- Modify: `src/ScadaLink.Commons/Messages/Artifacts/DeployArtifactsCommand.cs` +- Modify: `src/ScadaLink.SiteRuntime/Actors/DeploymentManagerActor.cs` (HandleDeployArtifacts) +- Modify: `src/ScadaLink.SiteRuntime/Persistence/SiteStorageService.cs` + +**Step 1:** Add `DataConnectionArtifact` record and include it in `DeployArtifactsCommand`: + +```csharp +public record DataConnectionArtifact( + string Name, string Protocol, string? ConfigurationJson); + +// Add to DeployArtifactsCommand: +IReadOnlyList? DataConnections +``` + +**Step 2:** Add `data_connection_definitions` table to `SiteStorageService.InitializeAsync()`: + +```sql +CREATE TABLE IF NOT EXISTS data_connection_definitions ( + name TEXT PRIMARY KEY, + protocol TEXT NOT NULL, + configuration TEXT, + updated_at TEXT NOT NULL +); +``` + +**Step 3:** Add `StoreDataConnectionDefinitionAsync` and `GetAllDataConnectionDefinitionsAsync` to `SiteStorageService`. + +**Step 4:** Update `DeploymentManagerActor.HandleDeployArtifacts` to persist data connections. + +**Step 5:** Update `HandleDeployArtifacts` SMTP config storage (add to the existing handler alongside external systems and db connections). + +**Step 6:** Build and verify. + +**Step 7:** Commit: `feat: include data connections and SMTP in artifact deployment` + +--- + +### Task 4: Wire site-local repositories into DI + +**Files:** +- Modify: `src/ScadaLink.Host/Program.cs` (Site role, lines ~155-160) +- Modify: `src/ScadaLink.Host/appsettings.Site.json` +- Modify: `src/ScadaLink.SiteRuntime/ServiceCollectionExtensions.cs` + +**Step 1:** In `ServiceCollectionExtensions.AddSiteRuntime()`, register the site-local repositories: + +```csharp +services.AddScoped(); +services.AddScoped(); +``` + +**Step 2:** Remove `AddConfigurationDatabase` call from the Site role in `Program.cs`. Remove the `ConfigurationDb` connection string check. + +**Step 3:** Remove `ConfigurationDb` from `appsettings.Site.json`. + +**Step 4:** Build and verify — site should start without SQL Server. + +**Step 5:** Commit: `feat: wire site-local repos, remove config DB dependency from Site` + +--- + +### Task 5: Add Deploy Artifacts button to Sites admin page + +**Files:** +- Modify: `src/ScadaLink.CentralUI/Components/Pages/Admin/Sites.razor` + +**Step 1:** Inject `ArtifactDeploymentService` and add a "Deploy Artifacts" button in each site's action column (next to Edit/Delete). + +**Step 2:** Add handler method that: +- Collects all external systems, db connections, notification lists, SMTP configs, shared scripts, and data connections from config DB repositories +- Builds a `DeployArtifactsCommand` +- Calls `ArtifactDeploymentService.DeployToAllSitesAsync` (or per-site variant) +- Shows success/failure toast + +**Step 3:** Add a "Deploy Artifacts to All Sites" button in the page header for bulk deployment. + +**Step 4:** Build and verify UI renders. + +**Step 5:** Commit: `feat: add Deploy Artifacts button to Sites admin page` + +--- + +### Task 6: Update ArtifactDeploymentService to include all artifact types + +**Files:** +- Modify: `src/ScadaLink.DeploymentManager/ArtifactDeploymentService.cs` + +**Step 1:** Inject `ISiteRepository` (for data connections) and `INotificationRepository` (for SMTP configs). Update the command-building logic to include: +- Data connections from `ISiteRepository.GetAllDataConnectionsAsync()` +- SMTP configurations from `INotificationRepository.GetAllSmtpConfigurationsAsync()` + +**Step 2:** Build and verify. + +**Step 3:** Commit: `feat: include data connections and SMTP in artifact collection` + +--- + +### Task 7: End-to-end test + +**Step 1:** Start Central and Site servers. + +**Step 2:** Login as admin, navigate to Sites page, click "Deploy Artifacts" on Test Plant A. + +**Step 3:** Verify site logs show artifact receipt and SQLite storage. + +**Step 4:** Restart Site without config DB connection string — verify it starts and ExternalSystemClient/NotificationDeliveryService work from local data. + +**Step 5:** Commit any fixes. diff --git a/docs/plans/2026-03-17-deploy-artifacts-remove-configdb.md.tasks.json b/docs/plans/2026-03-17-deploy-artifacts-remove-configdb.md.tasks.json new file mode 100644 index 0000000..f5e19ab --- /dev/null +++ b/docs/plans/2026-03-17-deploy-artifacts-remove-configdb.md.tasks.json @@ -0,0 +1,13 @@ +{ + "planPath": "docs/plans/2026-03-17-deploy-artifacts-remove-configdb.md", + "tasks": [ + {"id": 4, "subject": "Task 1: Create SiteExternalSystemRepository", "status": "pending"}, + {"id": 5, "subject": "Task 2: Create SiteNotificationRepository", "status": "pending"}, + {"id": 6, "subject": "Task 3: Add data connections to DeployArtifactsCommand", "status": "pending"}, + {"id": 7, "subject": "Task 4: Wire site-local repositories into DI", "status": "pending", "blockedBy": [4, 5, 6]}, + {"id": 8, "subject": "Task 5: Add Deploy Artifacts button to Sites admin page", "status": "pending", "blockedBy": [9]}, + {"id": 9, "subject": "Task 6: Update ArtifactDeploymentService to include all artifact types", "status": "pending"}, + {"id": 10, "subject": "Task 7: End-to-end test", "status": "pending", "blockedBy": [7, 8, 9]} + ], + "lastUpdated": "2026-03-17T16:40:00Z" +}