168 lines
8.9 KiB
Markdown
168 lines
8.9 KiB
Markdown
# Components — Master CMS Module
|
|
|
|
## Unit 1 — master-backend (`SlpModularCms.Modules.Master`)
|
|
|
|
### MasterModule
|
|
- **Type**: Module registration (`IModule`)
|
|
- **Responsibilities**: Registers all master-side services (repository, service, typed HTTP client, background service, options); applies `MasterDbContext` migrations at startup via `UseModule`; does NOT register middleware (master instance has no availability gate)
|
|
- **Interface**: `IModule` (`RegisterServices`, `UseModule`)
|
|
|
|
### MasterDbContext
|
|
- **Type**: EF Core `DbContext`
|
|
- **Responsibilities**: Per-module DbContext; owns the `CmsInstances` table and its migrations; migrations live in `SlpModularCms.Modules.Master` (NFR-MASTER-06)
|
|
- **Entities owned**: `CmsInstance`
|
|
|
|
### CmsInstance
|
|
- **Type**: Domain Entity
|
|
- **Responsibilities**: Represents a registered slave CMS instance
|
|
- **Fields**:
|
|
- `Id` — `Guid`, primary key
|
|
- `Name` — `string`, friendly display name
|
|
- `Url` — `string`, base URL of slave CMS API
|
|
- `ApiKey` — `string`, secret used by Master to authenticate against slave; never returned in API responses (NFR-MASTER-03)
|
|
- `Status` — `CmsInstanceStatus` enum (`Available` / `NotAvailable` / `Inactive`)
|
|
- `DisableMessage` — `string?`, required when `Status = NotAvailable`
|
|
- `LastContactedAt` — `DateTimeOffset?`
|
|
- `LastStatusPushedAt` — `DateTimeOffset?`
|
|
|
|
### CmsInstanceStatus
|
|
- **Type**: Enum
|
|
- **Values**: `Available`, `NotAvailable`, `Inactive`
|
|
|
|
### ICmsInstanceRepository / CmsInstanceRepository
|
|
- **Type**: Repository (data access only)
|
|
- **Responsibilities**: CRUD operations on `CmsInstance` via `MasterDbContext`; no business logic
|
|
- **Lifetime**: Scoped
|
|
|
|
### ICmsInstanceService / CmsInstanceService
|
|
- **Type**: Service (orchestration)
|
|
- **Responsibilities**: Business orchestration — calls repository for data access; calls `ISlaveApiClient` for HTTP side-effects (auto-registration, status push, integrity verification); enforces business rules (e.g., `DisableMessage` required when `NotAvailable`)
|
|
- **Lifetime**: Scoped
|
|
|
|
### ISlaveApiClient / SlaveApiClient
|
|
- **Type**: Typed HTTP client
|
|
- **Responsibilities**: All Master → Slave HTTP communication (registration, status push, integrity check); adds `X-Master-Api-Key` header; handles HTTP errors and returns success flags
|
|
- **Registration**: `services.AddHttpClient<ISlaveApiClient, SlaveApiClient>()`
|
|
- **Lifetime**: Transient (managed by `IHttpClientFactory`)
|
|
|
|
### CmsInstanceController
|
|
- **Type**: ASP.NET Core `ControllerBase`
|
|
- **Responsibilities**: REST API for slave CMS management; `[Authorize(Policy = "OwnerOnly")]`; delegates to `ICmsInstanceService`
|
|
- **Route**: `/api/v1/CmsInstances`
|
|
- **Actions**: GET list, POST add, PUT update status
|
|
|
|
### IntegrityCheckBackgroundService
|
|
- **Type**: `BackgroundService`
|
|
- **Responsibilities**: Periodic background loop; verifies each non-Inactive slave still has the correct master URL registered; re-registers if mismatch found; interval configurable via `MasterModuleOptions.IntegrityCheckIntervalMinutes` (default 60)
|
|
- **Pattern**: Uses `PeriodicTimer`; injects `IServiceScopeFactory` to resolve scoped `ICmsInstanceService` per tick
|
|
- **Lifetime**: Singleton (as required by `BackgroundService`)
|
|
|
|
### MasterModuleOptions
|
|
- **Type**: Configuration POCO
|
|
- **Fields**:
|
|
- `IntegrityCheckIntervalMinutes` — `int`, default 60 (master-side)
|
|
- `CacheMinutes` — `int`, default 60 (slave-side)
|
|
- `ApiKey` — `string` (slave-side; key the slave uses to validate incoming master requests)
|
|
- **Registration**: `services.Configure<MasterModuleOptions>(configuration.GetSection("MasterModule"))`
|
|
|
|
### DTOs and Request Models
|
|
|
|
| Type | Fields | Notes |
|
|
|------|--------|-------|
|
|
| `CmsInstanceDto` | `Id`, `Name`, `Url`, `Status`, `DisableMessage`, `LastContactedAt`, `LastStatusPushedAt` | No `ApiKey` (NFR-MASTER-03) |
|
|
| `CreateCmsInstanceRequest` | `Name`, `Url`, `ApiKey` | API key stored securely, never returned |
|
|
| `UpdateStatusRequest` | `Status`, `DisableMessage?` | `DisableMessage` required when `Status = NotAvailable` |
|
|
|
|
---
|
|
|
|
## Unit 2 — slave-availability-extension (`SlpModularCms.Modules.Availability`)
|
|
|
|
### MasterRegistration
|
|
- **Type**: Domain Entity
|
|
- **Responsibilities**: Stores the registered Master CMS URL on the slave side; zero or one records per slave (the slave knows at most one master)
|
|
- **Fields**:
|
|
- `Id` — `Guid`, primary key
|
|
- `MasterUrl` — `string`, base URL of the Master CMS
|
|
- `RegisteredAt` — `DateTimeOffset`
|
|
|
|
### AvailabilityDbContext
|
|
- **Type**: EF Core `DbContext` (new, per-module)
|
|
- **Responsibilities**: Per-module DbContext introduced in the Availability module for the slave-side entity; owns the `MasterRegistrations` table; migrations live in `SlpModularCms.Modules.Availability`
|
|
- **Entities owned**: `MasterRegistration`
|
|
|
|
### IMasterAvailabilityService / MasterAvailabilityService
|
|
- **Type**: Service
|
|
- **Responsibilities**: Checks whether a master URL is registered (DB lookup); pulls master-controlled availability status via HTTP GET; caches last known status using static fields + timestamp (same pattern as `PersistentAvailabilityService`); implements fail-open fallback when master is unreachable; respects `MasterModuleOptions.CacheMinutes`
|
|
- **Cache pattern**: Static fields `_cachedStatus` (default `Available`) + `_lastFetchedAt`; stale check based on `CacheMinutes`
|
|
- **Exemption (FR-MASTER-09)**: No special exemption logic needed — if no `MasterRegistration` record exists in DB (which is the case on a Master instance that never registered itself), the gate is skipped automatically
|
|
- **Lifetime**: Scoped (static fields provide cross-request caching)
|
|
|
|
### AvailabilityMiddleware (extended)
|
|
- **Type**: ASP.NET Core Middleware
|
|
- **Responsibilities**: Extended with Master gate logic at the **top** of `InvokeAsync`; two-phase check:
|
|
1. **Master gate** — calls `IMasterAvailabilityService.GetMasterStatusAsync()`; if no master registered → skip to local gate; if master says `NotAvailable` → 503 with `DisableMessage`; if unreachable → use cached/fallback value (fail-open)
|
|
2. **Local gate** — existing `IAvailabilityService` check, unchanged
|
|
- **Bypass prefixes**: Extended to also bypass internal master endpoints (`/api/internal/master/`) so registration calls are never blocked
|
|
|
|
### AvailabilityController (extended)
|
|
- **Type**: ASP.NET Core `ControllerBase` (existing class extended)
|
|
- **Responsibilities**: New action `RegisterMaster` added; validates `X-Master-Api-Key` header against configured `MasterModuleOptions.ApiKey`; upserts `MasterRegistration` in `AvailabilityDbContext`
|
|
- **New route**: `POST /api/internal/master/register`
|
|
- **Authentication**: API key validation (no JWT; the registration endpoint is called machine-to-machine)
|
|
|
|
### RegisterMasterRequest
|
|
- **Type**: Request model
|
|
- **Fields**: `MasterUrl` — `string`
|
|
|
|
### MasterGateResult
|
|
- **Type**: Result record
|
|
- **Fields**: `HasMaster` (`bool`), `Status` (`CmsInstanceStatus?`), `DisableMessage` (`string?`)
|
|
|
|
---
|
|
|
|
## Unit 3 — frontend-cms-page (`frontend/`)
|
|
|
|
### CmsPage
|
|
- **Type**: React page component
|
|
- **Route**: `/cms`
|
|
- **Responsibilities**: Owner-only route guard; fetches slave list via `useCmsInstances`; renders `CmsInstanceList`; manages dialog open state for Add and Set Status actions
|
|
|
|
### CmsInstanceList
|
|
- **Type**: React component
|
|
- **Responsibilities**: Renders a table of `CmsInstance` items; shows Name, URL, Status badge, LastContactedAt, DisableMessage; `Inactive` rows are visually greyed out; provides action triggers (Add button, Set Status button per row)
|
|
|
|
### AddCmsInstanceDialog
|
|
- **Type**: React component (modal dialog using shadcn/ui `Dialog`)
|
|
- **Responsibilities**: Form with fields Name, URL, ApiKey (all required); validates before submission; calls `useAddCmsInstance` mutation; closes on success
|
|
|
|
### SetStatusDialog
|
|
- **Type**: React component (modal dialog using shadcn/ui `Dialog`)
|
|
- **Responsibilities**: Status dropdown (`Available`, `NotAvailable`, `Inactive`); `DisableMessage` text field rendered and required when status is `NotAvailable`; calls `useUpdateCmsInstanceStatus` mutation; closes on success
|
|
|
|
### CmsInstanceStatus (TypeScript enum)
|
|
- **Values**: `Available`, `NotAvailable`, `Inactive`
|
|
|
|
### CmsInstance (TypeScript type)
|
|
- **Fields**: `id`, `name`, `url`, `status`, `disableMessage`, `lastContactedAt`, `lastStatusPushedAt`
|
|
|
|
### useCmsInstances
|
|
- **Type**: TanStack Query `useQuery` hook
|
|
- **File**: `hooks/useCmsInstances.ts`
|
|
- **Responsibilities**: GET `/api/v1/CmsInstances`; returns list of `CmsInstance`
|
|
|
|
### useAddCmsInstance
|
|
- **Type**: TanStack Query `useMutation` hook
|
|
- **File**: `hooks/useAddCmsInstance.ts`
|
|
- **Responsibilities**: POST `/api/v1/CmsInstances`; invalidates `useCmsInstances` query on success
|
|
|
|
### useUpdateCmsInstanceStatus
|
|
- **Type**: TanStack Query `useMutation` hook
|
|
- **File**: `hooks/useUpdateCmsInstanceStatus.ts`
|
|
- **Responsibilities**: PUT `/api/v1/CmsInstances/{id}/status`; invalidates `useCmsInstances` query on success
|
|
|
|
---
|
|
|
|
## Unit 4 — documentation
|
|
|
|
No new components. Covers README updates only (FR-MASTER-15). See requirements for scope.
|