Files
slp-modular-cms/README.md
T
Sluijsens 8072025e0b Completes master-cms-module: Build & Test, docs, and appsettings
Finishes the master-cms-module feature (Units 1-4): runs Build and
Test across master-backend, slave-availability-extension and
frontend-cms-page, fixes a missing Availability EF migration for
MasterRegistration and a TanStack Query v5 mutation-callback type
break, adds the missing MasterModule appsettings section, and
documents the module in README.md. Also seeds a tech-debt-backlog
feature to track dead config and pre-existing/introduced frontend
lint findings for later cleanup.
2026-07-01 23:09:15 +02:00

227 lines
11 KiB
Markdown

# SlpModularCms
Een modulaire monolith CMS gebouwd met .NET 10.
## Projectstructuur
- `src/SlpModularCms.Api`: De host applicatie en API shell.
- `src/SlpModularCms.Core`: Kern functionaliteiten, data modellen en interfaces.
- `src/SlpModularCms.Modules.*`: Onafhankelijke functionele modules.
- `frontend/`: De CMS admin web-UI (Vite + React + TypeScript). Bewust buiten `src/` gehouden om de .NET solution schoon te houden.
## Development Setup
### Vereisten
- .NET 10 SDK
- Podman of Docker (voor SQL Server)
### 1. Database opstarten
Start een SQL Server container met de volgende opdracht:
```powershell
podman run -e "ACCEPT_EULA=Y" -e "MSSQL_SA_PASSWORD=MSSQL_s3cr3t_pw!" -p 1433:1433 --name sql-server -d mcr.microsoft.com/mssql/server:2022-latest
```
### 2. Configuratie
De applicatie maakt gebruik van een drie-bestanden patroon voor configuratie:
- `appsettings.json`: Productie baseline.
- `appsettings.Development.json`: Ontwikkelinstellingen.
- `appsettings.local.json`: Lokale overrides (niet in Git).
Zorg dat er een `src/SlpModularCms.Api/appsettings.local.json` aanwezig is met de juiste connection string:
```json
{
"ConnectionStrings": {
"DefaultConnection": "Server=127.0.0.1,1433;User ID=sa;Password=MSSQL_s3cr3t_pw!;Database=SlpModularCms;TrustServerCertificate=True;MultipleActiveResultSets=true"
}
}
```
### 3. Applicatie starten
Voer de applicatie uit vanaf de root:
```powershell
dotnet run --project src/SlpModularCms.Api
```
De API is daarna bereikbaar op `https://localhost:7221` (of de geconfigureerde poort). De OpenAPI documentatie (Scalar) is beschikbaar op `/scalar`.
## Initiële Setup (Bootstrapping)
...
## Authenticatie & Security (Unit 0)
De API gebruikt een beveiligde flow voor authenticatie:
- **Login**: `POST /api/v1/auth/login`. Retourneert een `accessToken` in de body en een `refreshToken` in een beveiligde `httpOnly` cookie.
- **Refresh**: `POST /api/v1/auth/refresh`. Gebruikt de `refreshToken` cookie om een nieuwe `accessToken` en `refreshToken` (rotatie) te genereren.
- **Revoke**: `POST /api/v1/auth/revoke`. Trekt het token in en wist de cookie.
### Belangrijke NFR Details:
- **CORS**: Alleen toegestane origins uit `appsettings.json → Cors:AllowedOrigins` worden geaccepteerd. De frontend moet draaien op een van deze origins.
- **Cookies**: De `refreshToken` cookie is `httpOnly`, `SameSite=Strict` en heeft het pad `/api/v1/auth`.
- **Rate Limiting**: Login endpoints hebben rate limiting (Fixed window 5/min, Sliding window 20/min).
- **Error Handling**: Foutmeldingen volgen de RFC 9457 `ProblemDetails` standaard.
## Frontend Development (CMS Admin UI)
De frontend is een Vite + React 19 + TypeScript single-page application in de map `frontend/`. Hij gebruikt TanStack Router, Tailwind v4 (hoofdkleur `#ac0000`) met shadcn/ui-stijl componenten, react-i18next (NL/EN), en MSW voor mocking in tests.
### Vereisten
- Node.js 20+ (getest met v24)
- pnpm 9+ (getest met v11)
### Snel starten
```powershell
cd frontend
pnpm install
pnpm dev
```
De dev-server draait op `http://localhost:5173`.
### Configuratie
De frontend leest de API-basis-URL uit een environment-variabele (zie `frontend/.env.example`):
```
VITE_API_BASE_URL=http://localhost:5000
```
Kopieer `.env.example` naar `.env.local` en pas de waarde aan indien nodig. Optioneel kan met `VITE_ENABLE_MSW=true` de MSW-mockbackend in de browser worden ingeschakeld voor frontend-ontwikkeling zonder draaiende API.
### Vereiste backend
De app verwacht de .NET API (Unit 0) draaiend op de geconfigureerde origin met:
- **CORS** die de frontend-origin (`http://localhost:5173`) toestaat met `credentials`.
- De `httpOnly` `refreshToken` cookie op pad `/api/v1/auth` (silent refresh bij opstarten).
- RFC 9457 `ProblemDetails` foutmeldingen.
> Let op: de API-poort in `.env.example` (`5000`) moet overeenkomen met de werkelijke API-poort en de `Cors:AllowedOrigins` configuratie van de backend.
### Scripts
```powershell
pnpm dev # ontwikkelserver (HMR)
pnpm build # type-check (tsc) + productie-build
pnpm preview # productie-build lokaal bekijken
pnpm test # unit/integratietests (Vitest + Testing Library + MSW)
pnpm test:coverage # tests met coverage-rapport
pnpm lint # ESLint
pnpm format # Prettier (4-space indent)
```
## Database Migraties
Alle database commando's moeten worden uitgevoerd vanaf de **root** van de projectmap.
### Nieuwe migratie toevoegen
Wanneer je wijzigingen aanbrengt in de modellen (in `SlpModularCms.Core`):
```powershell
dotnet ef migrations add <NaamVanDeMigratie> --project src\SlpModularCms.Core --startup-project src\SlpModularCms.Api
```
### Database bijwerken
Om de migraties toe te passen op de database:
```powershell
dotnet ef database update --project src\SlpModularCms.Core --startup-project src\SlpModularCms.Api
```
### Per-module migraties
Sommige modules (`SlpModularCms.Modules.Master`, `SlpModularCms.Modules.Availability`) hebben een **eigen** `DbContext` met eigen migraties, los van `SlpModularCms.Core`. Deze worden automatisch toegepast bij het opstarten van de applicatie (via `Database.Migrate()` in de module's `UseModule`-methode), maar een nieuwe migratie genereren doe je expliciet per project:
```powershell
dotnet ef migrations add <NaamVanDeMigratie> --project src\SlpModularCms.Modules.Master --startup-project src\SlpModularCms.Api
dotnet ef migrations add <NaamVanDeMigratie> --project src\SlpModularCms.Modules.Availability --startup-project src\SlpModularCms.Api --context AvailabilityDbContext
```
> Let op: voor `SlpModularCms.Modules.Availability` is `--context AvailabilityDbContext` verplicht, omdat de API-startup-project meerdere `DbContext`-typen samenvoegt en de EF CLI anders niet kan bepalen welke bedoeld wordt.
## Nieuwe Module Toevoegen
Het systeem is ontworpen om eenvoudig uitgebreid te worden met nieuwe functionele modules. Volg deze stappen om een nieuwe module toe te voegen:
### 1. Project aanmaken
Maak een nieuw .NET 10 Class Library project aan in de `src/` map. Gebruik de naamconventie `SlpModularCms.Modules.<Naam>`.
```powershell
dotnet new classlib -n SlpModularCms.Modules.MijnNieuweModule -o src\SlpModularCms.Modules.MijnNieuweModule -f net10.0
```
### 2. Referenties toevoegen
Voeg de benodigde referentie naar `Core` toe en voeg het project toe aan de solution:
```powershell
dotnet add src\SlpModularCms.Modules.MijnNieuweModule reference src\SlpModularCms.Core
dotnet sln SlpModularCms.sln add src\SlpModularCms.Modules.MijnNieuweModule
```
### 3. IModule implementeren
Maak een class aan die de `IModule` interface implementeert:
```csharp
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using SlpModularCms.Core.Modules;
namespace SlpModularCms.Modules.MijnNieuweModule;
public class MijnNieuweModule : IModule
{
public string Name => "MijnNieuweModule";
public string Version => "1.0.0";
public void RegisterServices(IServiceCollection services)
{
// Registreer hier module-specifieke services
}
public void UseModule(IApplicationBuilder app)
{
// Configureer hier middleware of andere pipeline zaken
}
}
```
### 4. Registreren in de API
Om ervoor te zorgen dat de module tijdens ontwikkeling wordt meegenomen in de build-output van de API (zodat de `ModuleOrchestrator` de DLL kan vinden), voeg je een referentie toe aan het API project:
```powershell
dotnet add src\SlpModularCms.Api reference src\SlpModularCms.Modules.MijnNieuweModule
```
De `ModuleOrchestrator` zal de module nu automatisch ontdekken en laden bij het opstarten.
## Master CMS Module
De `SlpModularCms.Modules.Master` module laat een Owner op één "Master"-CMS de beschikbaarheid van andere ("slave") CMS-instanties centraal beheren. Elke slave die de `SlpModularCms.Modules.Availability`-module draait, respecteert een master-gecontroleerde aan/uit-status naast zijn eigen lokale beschikbaarheidsschakelaar.
### Architectuur
- **Master** (`SlpModularCms.Modules.Master`): eigen `MasterDbContext` met de `CmsInstance`-entiteit (URL, versleutelde API key, status). Bevat `CmsInstanceController` (`/CmsInstances`, Owner-only), `SlaveApiClient` (uitgaande HTTP-calls naar slaves) en `IntegrityCheckBackgroundService` (periodieke reconciliatie, standaard elk uur).
- **Slave-extensie** (`SlpModularCms.Modules.Availability`): eigen `MasterRegistration`-entiteit, `MasterController` (interne endpoints onder `/api/v1/master/*`, buiten de beschikbaarheids-gate om) en een uitgebreide `AvailabilityMiddleware` die zowel de lokale als de master-gate evalueert.
### Registratie- en statusflow
1. Owner voegt op de Master `/cms`-pagina een slave toe met diens URL.
2. De Master genereert een API key, versleutelt deze (Data Protection) en slaat hem op bij de `CmsInstance`.
3. De Master pusht de registratie naar de slave: `POST /api/v1/master/register` met header `X-Master-Api-Key`.
4. Zet de Owner de status van een slave om (Available / NotAvailable / Inactive), dan pusht de Master dit synchroon door naar de slave.
5. **Fail-open**: lukt de push niet, dan wordt de statuswijziging op de Master **niet** teruggedraaid — `IntegrityCheckBackgroundService` haalt de reconciliatie in tijdens de volgende cyclus (`MasterModuleOptions.IntegrityCheckIntervalMinutes`). Een slave die herstart voordat de Master opnieuw pusht, staat standaard weer open (`_masterIsAvailable = true` bij opstarten) — er is bewust geen TTL op de laatst bekende status.
### Configuratie
Nieuwe sectie `MasterModule` in `appsettings.json` (zie ook `appsettings.Development.json`):
```json
"MasterModule": {
"IntegrityCheckIntervalMinutes": 60,
"HttpTimeoutSeconds": 10,
"MasterUrl": "https://jouw-master-domein"
}
```
- `MasterUrl` is de publieke URL van déze master-instantie, gebruikt door `IntegrityCheckBackgroundService` (buiten een HTTP-requestcontext heeft de background service geen `HttpContext` om dit uit af te leiden).
## Productie Setup
### 1. Build & Publish
Compileer de applicatie voor productie:
```powershell
dotnet publish src/SlpModularCms.Api -c Release -o ./publish
```
### 2. Runtime Configuratie
In productie moeten gevoelige instellingen worden doorgegeven via Environment Variables:
- `ConnectionStrings__DefaultConnection`
- `JwtSettings__Secret`
- `JwtSettings__Issuer`
- `JwtSettings__Audience`
- `MasterModule__MasterUrl` — publieke URL van deze master-instantie (alleen relevant als de Master CMS Module actief is)
### 2a. Data Protection key ring (Master CMS Module)
De API keys van geregistreerde slaves worden versleuteld opgeslagen met ASP.NET Core Data Protection, standaard met een bestandssysteem-key-store. Voor gecontaineriseerde of multi-instance deployments **moet** een persistente key ring geconfigureerd worden (bijv. `PersistKeysToDbContext` of `PersistKeysToAzureBlobStorage`). Zonder dit worden alle opgeslagen API keys onleesbaar zodra de container herstart, waardoor master↔slave-communicatie stopt totdat instanties opnieuw worden toegevoegd.
### 3. Database
Zorg dat de doeltabel bestaat en de migraties zijn uitgevoerd. In productie kan dit via een CI/CD pipeline worden afgehandeld met `dotnet ef migrations script` of door de applicatie bij startup migraties te laten draaien (indien geconfigureerd).