# Code Summary — Unit 1: master-backend ## New Project: SlpModularCms.Modules.Master | File | Description | |------|-------------| | `SlpModularCms.Modules.Master.csproj` | Project file; references Core; adds Microsoft.Extensions.Http.Resilience 9.6.0 | | `Options/MasterModuleOptions.cs` | Configuration POCO: IntegrityCheckIntervalMinutes, HttpTimeoutSeconds, MasterUrl, CacheMinutes, ApiKey | | `Data/Entities/CmsInstanceStatus.cs` | Enum: Available=0, NotAvailable=1, Inactive=2 | | `Data/Entities/CmsInstance.cs` | EF Core entity with all domain fields including LastIntegrityCheckFailedAt | | `Data/MasterDbContext.cs` | Per-module DbContext; table MasterCmsInstances; configured via OnModelCreating | | `Models/CmsInstanceDto.cs` | Record DTO (excludes ApiKey); ExcludeFromCodeCoverage | | `Models/CreateCmsInstanceRequest.cs` | Record request for POST; ExcludeFromCodeCoverage | | `Models/UpdateStatusRequest.cs` | Record request for status update; ExcludeFromCodeCoverage | | `Models/UpdateStatusResult.cs` | Record result with Success + SlaveContactSuccess; ExcludeFromCodeCoverage | | `Repositories/ICmsInstanceRepository.cs` | Interface: GetAllAsync, GetActiveAsync, GetByIdAsync, AddAsync, Update, SaveChangesAsync | | `Repositories/CmsInstanceRepository.cs` | EF Core implementation; GetActiveAsync excludes Inactive | | `Services/IApiKeyProtector.cs` | Interface: Protect/Unprotect | | `Services/ApiKeyProtector.cs` | Data Protection wrapper; purpose string "SlpModularCms.Master.ApiKey" | | `Services/ISlaveApiClient.cs` | Interface: RegisterMasterAsync, PushStatusAsync, GetRegisteredMasterUrlAsync | | `Services/SlaveApiClient.cs` | Typed HTTP client; X-Master-Api-Key header on each call; fail-open (returns false on exception) | | `Services/MasterServiceDependencies.cs` | Record aggregating 6 CmsInstanceService dependencies; ExcludeFromCodeCoverage | | `Services/ICmsInstanceService.cs` | Interface: GetAllAsync, AddAsync, UpdateStatusAsync, VerifyIntegrityAsync | | `Services/CmsInstanceService.cs` | Business logic; HttpContext → config fallback for MasterUrl; never returns ApiKey in DTO | | `BackgroundServices/IntegrityCheckBackgroundService.cs` | PeriodicTimer; per-tick IServiceScope; catches all exceptions per tick | | `Controllers/CmsInstanceController.cs` | [Authorize(Policy="OwnerOnly")]; GET / POST / PUT /{id}/status | | `MasterModule.cs` | IModule implementation; DI registration; db.Database.Migrate() in UseModule; ExcludeFromCodeCoverage | | `Migrations/.gitkeep` | Placeholder; run CLI to generate migration (see below) | ## New Project: SlpModularCms.Modules.Master.Tests | File | Description | |------|-------------| | `SlpModularCms.Modules.Master.Tests.csproj` | xUnit + NSubstitute + FluentAssertions + EF InMemory | | `Repositories/CmsInstanceRepositoryTests.cs` | EF InMemory; covers all repository methods | | `Services/ApiKeyProtectorTests.cs` | Uses EphemeralDataProtectionProvider; round-trip + invalid ciphertext tests | | `Services/SlaveApiClientTests.cs` | FakeHttpMessageHandler; tests success/failure/exception paths + header assertion | | `Services/CmsInstanceServiceTests.cs` | NSubstitute; covers all business logic branches including HttpContext fallback | | `BackgroundServices/IntegrityCheckBackgroundServiceTests.cs` | PeriodicTimer integration; verifies exception isolation | | `Controllers/CmsInstanceControllerTests.cs` | NSubstitute ICmsInstanceService; verifies all HTTP response codes | ## Modified Files | File | Change | |------|--------| | `src/SlpModularCms.Api/SlpModularCms.Api.csproj` | Added ProjectReference to SlpModularCms.Modules.Master | | `SlpModularCms.sln` | Added both new projects with GUIDs and src folder nesting | ## EF Core Migration After building the solution, run: ```bash dotnet ef migrations add InitialCreate --project src/SlpModularCms.Modules.Master --startup-project src/SlpModularCms.Api ``` This generates the `Migrations/` folder contents. The migration is applied automatically on startup via `db.Database.Migrate()` in `MasterModule.UseModule`. ## Notes - `Microsoft.Extensions.Http.Resilience` version `9.6.0` — verify/update during `dotnet restore` if a newer version is available for .NET 10 - `IntegrityCheckIntervalMinutes = 0` in tests forces immediate PeriodicTimer ticks (valid for test scenarios only) - Slave-side endpoints (`/api/v1/master/register`, `/api/v1/master/status`, `/api/v1/master/registered-url`) are implemented in Unit 2 (slave-availability-extension) ## Addendum — 2026-07-04 (added outside this unit's original scope, in `local-dev-master-slave-setup` follow-up fixes) This unit predates the following; see `slave-availability-extension/functional-design/business-rules.md` Rule Set 5 and `application-design/application-design.md` for the full picture: | File | Description | |------|-------------| | `Controllers/SlaveStatusController.cs` | New. `[AllowAnonymous]` `GET /api/v1/SlaveStatus`; authenticates via `X-Master-Api-Key` header matched against each active `CmsInstance`'s decrypted key; lets a slave pull its own status instead of relying solely on the master's push | | `Services/ICmsInstanceService.cs` / `CmsInstanceService.cs` | `GetStatusForApiKeyAsync(plainApiKey)` added; `UpdateStatusAsync`'s `Inactive` branch now pushes `Available`/null to release the gate (previously a no-op); `VerifyIntegrityAsync` now also re-pushes persisted status to every reachable active slave each cycle | | `BackgroundServices/IntegrityCheckBackgroundService.cs` | Now runs one tick immediately on startup, in addition to the periodic timer | | `Models/SlaveStatusPollResponse` (in `ICmsInstanceService.cs`) | New record: `IsAvailable`, `DisableMessage` |