Files

12 KiB

Requirements — Master CMS Module

Intent Analysis

  • User Request: Add a new "Master" module that fills the /cms page, allows the owner to manage the availability of registered slave CMS instances, and integrates with the existing availability check mechanism — while exempting the Master CMS itself from external checks.
  • Request Type: New Feature
  • Scope Estimate: Multiple Components — new backend module, new frontend UI, modifications to the existing availability flow on slave CMS instances
  • Complexity Estimate: Complex — involves a Master/Slave network topology, bi-directional registration, background integrity checks, caching, fallback logic, and API key authentication

System Context

The existing system (SlpModularCms) is a modular ASP.NET Core CMS. The Availability module manages system status via IAvailabilityService and AvailabilityMiddleware. Slave CMSes are other deployed instances of the same SlpModularCms application. The Master Module adds a control plane layer on top of the existing availability system.


Functional Requirements

FR-MASTER-01 — New Module: SlpModularCms.Modules.Master

A new separate module SlpModularCms.Modules.Master is created within the existing solution. This module is installed only on the Master CMS. Slave CMS instances do not install this module. The module registers its own services and HTTP endpoints following the existing IModule pattern.

FR-MASTER-02 — CmsInstance Entity

A new CmsInstance database entity is added to SlpModularCms.Core with the following fields:

  • Id (Guid, PK)
  • Name (string, required) — friendly name for the slave CMS
  • Url (string, required) — base URL of the slave CMS API
  • ApiKey (string, required) — secret key used by the Master to authenticate against the slave
  • Status (enum: Available / NotAvailable / Inactive) — the master-controlled availability state
  • DisableMessage (string, nullable) — required when Status = NotAvailable; shown to end-users of the slave
  • LastContactedAt (DateTimeOffset, nullable) — timestamp of the last successful master → slave contact
  • LastStatusPushedAt (DateTimeOffset, nullable) — timestamp of the last status push to the slave

FR-MASTER-03 — Auto-Registration: Master Registers Itself with Slave

When a slave is added to the Master's CmsInstance registry, the Master automatically contacts the slave and registers itself (pushes its own URL). The slave stores the master's URL in its local database. No manual configuration is required on the slave side to know the Master URL.

The slave exposes a dedicated internal registration endpoint (POST /api/internal/master/register) that accepts the Master's URL and API key. After successful registration, the slave uses the stored Master URL for all future availability pulls.

FR-MASTER-04 — Integrity Check Service (Background Service)

A background service (periodic, configurable interval) runs on the Master CMS and periodically re-verifies that each registered slave still has the correct Master URL stored. If a slave's registered master does not match, the Master re-pushes its registration to that slave. This prevents clients from circumventing the master by removing or altering the stored master URL.

FR-MASTER-05 — Status Push: Master Sets Slave Availability

When the Master owner changes a slave's status (to Available or NotAvailable), the Master immediately calls the slave's existing PUT /api/availability/status endpoint (or a dedicated internal endpoint) to push the new status. Authentication uses the ApiKey from the CmsInstance record, sent as an X-Master-Api-Key header.

FR-MASTER-06 — Slave Pull Model: Periodic Master Check

The slave CMS periodically pulls its availability status from the Master. The pull interval is configurable in the slave's appsettings.json (MasterModule:CacheMinutes). If this value is not configured, a default of 60 minutes is used. The pulled status is cached locally in memory.

FR-MASTER-07 — Slave Fallback Behavior

If the Master CMS is unreachable when the slave attempts a pull:

  • The slave falls back to the value currently stored in its local database (GlobalAvailabilityState).
  • The local database value defaults to Available before any Master contact has occurred.
  • This ensures the feature is a fail-open safety measure — clients are not blocked if the Master is down.

FR-MASTER-08 — Slave Availability Middleware Integration

On a slave CMS, the availability check becomes a two-phase gate. The existing IAvailabilityService behavior is preserved — the Master adds an additional outer gate, not a replacement.

Check order:

  1. Master gate (outer): If a Master URL is registered in the slave's DB, check the Master-sourced cached status (refreshed per FR-MASTER-06).
    • If Master status = NotAvailable → block all requests. Only the frontend dashboard route is accessible so that users can see the availability widget with the DisableMessage.
    • If Master is unreachable → fall back to locally stored Master status (last known value; default Available).
    • If no Master registered → skip Master gate entirely.
  2. Local gate (inner): If the Master gate passes (status = Available or no Master registered), apply the existing IAvailabilityService check as it works today (local Available / Maintenance / NotAvailable logic, Owner/Admin bypass, etc.).

This means: when the Master sets a slave to Available, all existing local availability behavior continues unchanged. When the Master sets NotAvailable, the local gate is never reached.

The DisableMessage from the Master is included in the 503 Service Unavailable response body and delivered to the slave's frontend availability widget.

FR-MASTER-09 — Master CMS Exemption

When the Master Module is installed and active on a CMS instance, that CMS is automatically exempt from the external Master availability check. It does not pull status from any Master. The Master CMS retains and uses its own local IAvailabilityService check (existing behavior unchanged).

FR-MASTER-10 — Role Access Control

All Master Module management features (viewing and modifying slave CMS registrations) are restricted to the Owner role only. This applies to both the backend API endpoints and the frontend /cms page.

FR-MASTER-11 — /cms Page: Slave CMS List

The frontend /cms page displays a list of all registered slave CMSes. For each slave, the following is shown:

  • Name
  • URL
  • Current status badge (Available / NotAvailable / Inactive)
  • Last contacted timestamp
  • Disable message (if status is NotAvailable)

Inactive entries are visually greyed out to indicate they are no longer in active use.

FR-MASTER-12 — /cms Page: Add Slave CMS

The Owner can add a new slave CMS registration by providing:

  • Name (required)
  • URL (required)
  • API key (required)

On save, the Master immediately attempts to register itself with the slave (FR-MASTER-03) and stores the result.

FR-MASTER-13 — /cms Page: Set Slave Status

The Owner can change the status of a registered slave CMS to:

  • Available — slave is enabled (normal operation)
  • NotAvailable — slave is disabled; a mandatory disable message must be provided
  • Inactive — slave is greyed out in the UI; no availability enforcement is applied (the Master does not contact the slave)

Slave registrations cannot be deleted; setting to Inactive is the "soft removal" mechanism.

FR-MASTER-14 — Mandatory Disable Message

When the Owner sets a slave's status to NotAvailable, a non-empty DisableMessage is required. This message is pushed to the slave together with the status change and included in the slave's 503 error response to end-users.

FR-MASTER-15 — Project Documentation

The root README.md already exists and covers project structure, dev setup, authentication, frontend development, database migrations, adding a module, and production setup. The following sections need to be updated or added to reflect this feature.

Updated: README.md — "Database Migraties" section The current section only covers migrations in SlpModularCms.Core. It must be updated to document the per-module migration pattern (NFR-MASTER-06):

  • How to add a migration for a specific module (using --project src\SlpModularCms.Modules.<Name>)
  • How to apply module-specific migrations
  • Note that each module owns its own tables and migrations

Updated: README.md — "Nieuwe Module Toevoegen" section Step 3 (IModule implementeren) must be extended to document the optional per-module DbContext pattern introduced by the Master Module:

  • How to add a module-specific DbContext
  • How to register it at startup via RegisterServices
  • How to apply its migrations in UseModule

Updated: README.md — "Productie Setup" section The environment variables list must be extended with the new MasterModule: keys:

  • MasterModule__CacheMinutes (slave instances only)
  • MasterModule__IntegrityCheckIntervalMinutes (master instance only)

Updated: frontend/README.md Currently contains Vite template boilerplate. Replace with project-specific frontend developer documentation (prerequisites, scripts, environment variables). The root README already covers most of this — the frontend README can be a brief pointer to the root README plus frontend-specific notes.


Non-Functional Requirements

NFR-MASTER-01 — Fail-Open Safety

The entire Master Module is designed as a safety measure, not a blocker. If any part of the Master → Slave communication fails (network error, timeout, misconfiguration), the slave must continue serving requests using its local fallback. End-users must never be blocked solely because the Master is unreachable.

NFR-MASTER-02 — Configurable Cache Interval

The slave pull interval is configurable via appsettings.json (MasterModule:CacheMinutes). Default: 60 minutes. This follows the existing dotnet-appsettings pattern used in this project.

NFR-MASTER-03 — API Key Security

The ApiKey stored in CmsInstance is a secret token used for Master → Slave authentication. It must not be exposed in API list responses. The slave validates the X-Master-Api-Key header on all Master-initiated requests.

NFR-MASTER-04 — Background Service Interval

The Master's integrity check background service interval is configurable in Master CMS appsettings.json (MasterModule:IntegrityCheckIntervalMinutes). Default: 60 minutes.

NFR-MASTER-05 — Test Coverage

New backend code must follow the existing test coverage standard (≥ 80%). New services and controllers in SlpModularCms.Modules.Master and any slave-side extensions require unit tests.

NFR-MASTER-06 — Per-Module Database Migrations

Each module manages its own database schema through a module-specific EF Core DbContext and a dedicated migrations assembly within that module's project. This ensures that tables belonging to a module are only created on CMS instances where that module is installed:

  • SlpModularCms.Modules.Master owns the CmsInstances table → migrations live in Modules.Master
  • Slave-side tables (e.g., stored Master registration URL) belong to the module or service that introduces them → migrations live in the corresponding project
  • SlpModularCms.Core retains only the shared/core entities (users, roles, tokens, GlobalAvailabilityState)
  • Each module's Configure(IApplicationBuilder) method applies its own pending EF Core migrations at startup
  • A migration is created for every discrete schema change (one migration per logical change, not batched)

Scope Boundaries

In Scope Out of Scope
New SlpModularCms.Modules.Master project Slave module as a separate installable package
Frontend /cms page with slave management UI Multi-level master hierarchy (master of masters)
Slave-side auto-registration endpoint Real-time push notifications to slave (WebSockets/SignalR)
Slave-side availability pull + cache + fallback Authentication delegation (SSO between master and slave)
Master integrity check background service Slave removal / permanent delete
CmsInstance entity in SlpModularCms.Modules.Master (own DbContext + migrations)
Frontend changes included in this feature
Updated README.md (migrations + module guide + prod env vars) Full README rewrite
Updated frontend/README.md (project-specific content)