diff --git a/README.md b/README.md index 3717823..4bfa563 100644 --- a/README.md +++ b/README.md @@ -118,6 +118,14 @@ Om de migraties toe te passen op de database: 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 --project src\SlpModularCms.Modules.Master --startup-project src\SlpModularCms.Api +dotnet ef migrations add --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: @@ -169,6 +177,32 @@ dotnet add src\SlpModularCms.Api reference src\SlpModularCms.Modules.MijnNieuweM 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 @@ -183,6 +217,10 @@ In productie moeten gevoelige instellingen worden doorgegeven via Environment Va - `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). diff --git a/aidlc-docs/active-features.md b/aidlc-docs/active-features.md index b02454d..ba866a1 100644 --- a/aidlc-docs/active-features.md +++ b/aidlc-docs/active-features.md @@ -4,4 +4,5 @@ |---------|--------|--------|---------------------|---------------| | SlpModularCms.Api Implementation (slp-modular-cms-api) | ✅ Complete | unknown | Core, Identity, Availability, Shell | 2026-06-07 | | CMS Frontend (cms-frontend) | ✅ Complete | unknown | Frontend, Identity (Auth), Availability | 2026-06-16 | -| Master CMS Module (master-cms-module) | 🟢 Construction | unknown | Modules, Availability | 2026-06-26 | +| Master CMS Module (master-cms-module) | ✅ Complete | unknown | Modules, Availability | 2026-06-26 | +| Tech Debt Backlog (tech-debt-backlog) | 🔵 Inception | unknown | Modules.Master, Frontend | 2026-07-01 | diff --git a/aidlc-docs/features/master-cms-module/aidlc-state.md b/aidlc-docs/features/master-cms-module/aidlc-state.md index 72d1b03..71e02da 100644 --- a/aidlc-docs/features/master-cms-module/aidlc-state.md +++ b/aidlc-docs/features/master-cms-module/aidlc-state.md @@ -5,7 +5,7 @@ - **Feature Slug**: master-cms-module - **Project Type**: Brownfield - **Start Date**: 2026-06-26T00:00:00Z -- **Current Stage**: CONSTRUCTION - Unit 3 (frontend-cms-page) Code Generation Complete +- **Current Stage**: COMPLETE - All 4 units done, Build and Test passed, Unit 4 approved - **Branch**: unknown ## Workspace State @@ -55,9 +55,11 @@ - [x] NFR Requirements — Complete (Unit 3) - [x] NFR Design — Complete (Unit 3) - [x] Code Generation — Complete (Unit 3) -- [ ] Infrastructure Design — Skipped -- [ ] Code Generation — Execute (per unit) -- [ ] Build and Test — Execute +- [x] Infrastructure Design — Skipped (Units 1-3) +- [x] Code Generation — Complete (Units 1-3) +- [x] Build and Test — Complete (Units 1-3: build success, 402 automated tests pass) +- [x] Functional Design — Skipped (Unit 4 - documentation only, no business logic) +- [x] Code Generation — Complete (Unit 4 - documentation: README.md, frontend/README.md, appsettings.json/Development.json) ### 🟡 OPERATIONS PHASE -- [ ] Operations — Placeholder +- [x] Operations — Placeholder acknowledged; workflow complete diff --git a/aidlc-docs/features/master-cms-module/audit.md b/aidlc-docs/features/master-cms-module/audit.md index 43cc60f..8b64128 100644 --- a/aidlc-docs/features/master-cms-module/audit.md +++ b/aidlc-docs/features/master-cms-module/audit.md @@ -1,5 +1,108 @@ # Audit Log — Master CMS Module +## Build and Test Stage — Approved + +**Timestamp**: 2026-07-01T00:10:00Z + +**User Input**: "Goedkeuren & doorgaan naar Unit 4 documentation" + +**AI Response**: "Build and Test resultaten goedgekeurd. Doorgaan naar Unit 4 (documentation): Functional Design en NFR stages worden overgeslagen (geen business logic / geen nieuwe NFR's voor documentatiewerk), rechtstreeks naar Code Generation voor deze unit." + +**Context**: Build and Test stage afgesloten voor Units 1-3; Unit 4 (documentation) gestart + +--- + +## Gap Found & Fixed — Missing Availability Migration + +**Timestamp**: 2026-07-01T00:15:00Z + +**User Input**: (discovered during Unit 4 documentation prep, no direct user input) + +**AI Response**: "Ontdekt dat `SlpModularCms.Modules.Availability` geen EF Core migratie had voor de nieuwe `MasterRegistration`-entiteit (Unit 2), terwijl `AvailabilityModule.UseModule` wel `Database.Migrate()` aanroept. Zonder migratie zou de `AvailabilityMasterRegistrations`-tabel nooit worden aangemaakt op een echte database, wat de master-registratie-flow zou breken. Gegenereerd: `dotnet ef migrations add InitialCreate --project src/SlpModularCms.Modules.Availability --startup-project src/SlpModularCms.Api --context AvailabilityDbContext`. Solution build geverifieerd na toevoeging — nog steeds succesvol." + +**Context**: Build and Test — gap gevonden na goedkeuring; gefixt vóór Unit 4 start + +--- + +## Code Generation Unit 4 — Plan Created + +**Timestamp**: 2026-07-01T00:20:00Z + +**AI Response**: "Code generation plan aangemaakt voor Unit 4 (documentation): 5 stappen — README.md Master CMS Module sectie, migraties-sectie uitbreiden, productie-setup uitbreiden, frontend/README.md vervangen, state bijwerken. Wachten op goedkeuring." + +**Context**: Code Generation Unit 4 Part 1 (Planning) afgerond; wachten op plan-goedkeuring + +--- + +## Correction — Lint Baseline Was Contaminated + +**Timestamp**: 2026-07-01T00:25:00Z + +**AI Response**: "Tijdens appsettings-mapping (dotnet-appsettings skill) opnieuw geverifieerd of de 6 lint-problemen echt pre-existing waren. De eerdere check gebruikte `git checkout c156107 -- .` in de werkende frontend-map, wat bestanden die pas bij HEAD zijn toegevoegd (o.a. AddCmsInstanceDialog.tsx, SetStatusDialog.tsx) niet verwijdert — de baseline-meting was dus besmet. Opnieuw gemeten via een schone `git worktree` op c156107 + verse `npm install`: echte pre-existing baseline = 4 problemen (niet 6). De overige 2 (AddCmsInstanceDialog.tsx error, SetStatusDialog.tsx warning) zijn wel degelijk door Unit 3 van deze feature geïntroduceerd. build-and-test-summary.md gecorrigeerd; alle 6 items vastgelegd in de nieuwe tech-debt-backlog feature (TD-002/TD-003) per gebruikersbeslissing, i.p.v. direct gefixt." + +**Context**: Zelfcorrectie tijdens Unit 4 voorbereiding; geen impact op Build & Test goedkeuring (gebruiker had al ingestemd om afwijkingen in een tech-debt feature te verzamelen) + +--- + +## MasterModuleOptions Dead Config — Captured as Tech Debt + +**Timestamp**: 2026-07-01T00:20:00Z + +**User Input**: "Laat ze staan en documenteer ze als unused. Maak ook een nieuwe feature aan waar je dit in verwerkt om te fixen dat ze niet worden gebruikt. Als je meer issues vindt gooi die dan ook in deze feature. Maak het dus eigenlijk een soort tech debt feature. Net als de gefaalde tests bij de Build & Test check die je zojuist gedaan hebt. Die tech debt feature kan ik dan later oppakken. Voor nu doorgaan met unit 4" + +**AI Response**: "Nieuwe feature 'tech-debt-backlog' aangemaakt met TD-001 (dode CacheMinutes/ApiKey properties op MasterModuleOptions). Doorgaan met Unit 4 appsettings-mapping: alleen de 3 daadwerkelijk gebruikte properties (IntegrityCheckIntervalMinutes, HttpTimeoutSeconds, MasterUrl) worden in de appsettings-bestanden gedocumenteerd." + +**Context**: Beslissing genomen tijdens Unit 4 voorbereiding (dotnet-appsettings skill); zie aidlc-docs/features/tech-debt-backlog/ voor details + +--- + +## Code Generation Unit 4 — Complete + +**Timestamp**: 2026-07-01T00:35:00Z + +**AI Response**: "Unit 4 (documentation) code generation afgerond: README.md uitgebreid met 'Master CMS Module' sectie, migraties-sectie en productie-sectie; frontend/README.md vervangen (stond nog op Vite-boilerplate); MasterModule config-sectie toegevoegd aan appsettings.json + appsettings.Development.json (ontbrak volledig, ontdekt via dotnet-appsettings skill). Solution build geverifieerd — nog steeds succesvol. aidlc-state.md bijgewerkt: alle 4 units compleet." + +**Context**: Code Generation Unit 4 Part 2 (Generation) afgerond; wachten op goedkeuring + +--- + +## Code Generation Unit 4 — Approved / Workflow Complete + +**Timestamp**: 2026-07-01T00:40:00Z + +**User Input**: "Doorgaan naar de volgende fase" + +**AI Response**: "Unit 4 (documentation) goedgekeurd — dit was de laatste unit. Operations-fase is een placeholder voor deze feature, dus aidlc-state.md en active-features.md bijgewerkt naar Complete. Sluitsamenvatting gepresenteerd." + +**Context**: Alle 4 units (master-backend, slave-availability-extension, frontend-cms-page, documentation) compleet; Build and Test compleet; workflow voor master-cms-module afgerond + +--- + +## Build and Test Stage (Units 1-3) + +**Timestamp**: 2026-07-01T00:00:00Z + +**User Input**: "hervat de master-cms module feature. Waar zijn we gebleven. moet ik nog vragen beantwoorden voor we verder gaan of iets goedkeuren?" — followed by choosing "Build & Test eerst" when asked how to proceed. + +**AI Response**: "Resumed feature; confirmed no outstanding unanswered questions. Ran `dotnet build`/`dotnet test` (193 tests pass) and `npm run build`/`npm run lint`/`npm run test` for the frontend. Found and fixed a TypeScript build break in `useUpdateCmsInstanceStatus.ts` caused by an `@tanstack/react-query` v5.101 signature change (forwarded onSuccess args with a rest parameter instead of a fixed 3-arg call). Frontend tests: 209/209 pass. Frontend lint: 6 pre-existing issues (5 errors, 1 warning), confirmed identical in count to the pre-feature baseline commit `c156107` — none introduced by this feature, left unfixed as out of scope. Generated build-instructions.md, unit-test-instructions.md, integration-test-instructions.md, performance-test-instructions.md, build-and-test-summary.md." + +**Context**: Build and Test stage executed for Units 1-3 (master-backend, slave-availability-extension, frontend-cms-page); Unit 4 (documentation) remains outstanding + +--- + +**Build Status**: Success + +**Test Status**: Pass (193 backend + 209 frontend = 402 automated tests) + +**Files Generated**: +- build-instructions.md +- unit-test-instructions.md +- integration-test-instructions.md +- performance-test-instructions.md +- build-and-test-summary.md + +--- + ## Initial Request — Workspace Detection **Timestamp**: 2026-06-26T00:00:00Z diff --git a/aidlc-docs/features/master-cms-module/construction/build-and-test/build-and-test-summary.md b/aidlc-docs/features/master-cms-module/construction/build-and-test/build-and-test-summary.md new file mode 100644 index 0000000..d515cf2 --- /dev/null +++ b/aidlc-docs/features/master-cms-module/construction/build-and-test/build-and-test-summary.md @@ -0,0 +1,52 @@ +# Build and Test Summary + +## Build Status +- **Build Tool**: .NET SDK 10 (backend), Vite + tsc (frontend) +- **Build Status**: Success (after one fix — see below) +- **Build Artifacts**: `src/**/bin/Debug/net10.0/*.dll`, `frontend/dist/` +- **Build Time**: ~15s backend, ~5s frontend + +### Fixes Applied During Build Verification +1. `frontend/src/api/useUpdateCmsInstanceStatus.ts` failed `tsc` because the installed `@tanstack/react-query@5.101.0` mutation `onSuccess` callback signature gained a 4th `context` parameter, and the code was manually forwarding only 3 args to the consumer callback. Fixed by forwarding all trailing args with a rest parameter (`...rest`). No behavior change — this is a type-signature compatibility fix, not a logic change. +2. **Missing EF Core migration** — `SlpModularCms.Modules.Availability` had no migration for the `MasterRegistration` entity added in Unit 2, even though `AvailabilityModule.UseModule` calls `Database.Migrate()`. On a real database this meant the `AvailabilityMasterRegistrations` table would never be created, breaking the master registration flow end-to-end. Generated via `dotnet ef migrations add InitialCreate --project src/SlpModularCms.Modules.Availability --startup-project src/SlpModularCms.Api --context AvailabilityDbContext`. + +## Test Execution Summary + +### Unit Tests — Backend (.NET) +- **Total Tests**: 193 +- **Passed**: 193 +- **Failed**: 0 +- **Status**: Pass +- Breakdown: Core.Tests (45), Modules.Identity.Tests (46), Modules.Availability.Tests (60), Modules.Master.Tests (42) + +### Unit Tests — Frontend (Vitest) +- **Total Tests**: 209 (34 test files) +- **Passed**: 209 +- **Failed**: 0 +- **Status**: Pass + +### Integration Tests +- **Test Scenarios**: 4 documented in `integration-test-instructions.md` (registration handshake, status push + middleware enforcement, fail-open on push failure/slave restart, frontend CMS page end-to-end) +- **Status**: Manual — no automated cross-process harness exists in this codebase; scenarios are documented for manual verification. Underlying logic for each scenario is covered by the unit test suites above. + +### Performance Tests +- **Status**: N/A — no explicit performance SLA defined in NFR Requirements. Architectural review confirms the master-gate check is a zero-I/O in-memory field read (documented in `performance-test-instructions.md`). + +### Additional Tests +- **Contract Tests**: N/A — no separate service contract test suite; API shape covered by controller/integration unit tests +- **Security Tests**: N/A for this stage — API key handling, Data Protection, and auth bypass-prefix design were validated during NFR Design/Requirements review, not re-tested here +- **E2E Tests**: N/A — no browser-automation E2E suite in this codebase; frontend interactions covered by Vitest component tests + React Testing Library + +### Lint (Frontend) +- `npm run lint`: 6 problems (5 errors, 1 warning). Re-verified with a clean `git worktree` checkout of `c156107` (the commit before this feature's frontend work) plus a fresh `npm install` — an earlier same-tree `git checkout -- .` comparison had been contaminated by files that only exist at HEAD, giving a false "all pre-existing" reading. + - **4 pre-existing** (confirmed via the clean baseline): `badge.tsx:32`, `InviteUserDialog.tsx` (×2), `SettingsPage.tsx:39`. + - **2 newly introduced by this feature's Unit 3**: `AddCmsInstanceDialog.tsx:55` (error), `SetStatusDialog.tsx:72` (warning) — same underlying pattern as the pre-existing findings. + - All 6 items logged to the new `tech-debt-backlog` feature (`aidlc-docs/features/tech-debt-backlog/inception/requirements/backlog.md`, TD-002/TD-003) per user decision (2026-07-01) rather than fixed in-flight, to avoid blocking Unit 4. + +## Overall Status +- **Build**: Success +- **All Tests**: Pass (402 automated tests: 193 backend + 209 frontend) +- **Ready for Operations**: Yes + +## Next Steps +Proceed to Operations phase (placeholder) — or, per the execution plan, Unit 4 (documentation) is still outstanding before the feature is fully complete. diff --git a/aidlc-docs/features/master-cms-module/construction/build-and-test/build-instructions.md b/aidlc-docs/features/master-cms-module/construction/build-and-test/build-instructions.md new file mode 100644 index 0000000..7a553f6 --- /dev/null +++ b/aidlc-docs/features/master-cms-module/construction/build-and-test/build-instructions.md @@ -0,0 +1,32 @@ +# Build Instructions + +## Prerequisites +- **Build Tool**: .NET SDK 10 (backend), Node.js + npm/pnpm (frontend) +- **Dependencies**: NuGet packages (restored automatically), npm packages in `frontend/` +- **Environment Variables**: None required for build +- **System Requirements**: Windows/Linux/macOS, .NET 10 runtime + +## Build Steps + +### 1. Backend — Install Dependencies & Build +```bash +dotnet build SlpModularCms.sln +``` + +### 2. Frontend — Install Dependencies & Build +```bash +cd frontend +npm install +npm run build # tsc -b && vite build +``` + +## Verify Build Success +- **Backend**: `Build succeeded. 0 Error(s)` — all 9 projects compile (Core, Modules.Identity, Modules.Availability, Modules.Master, Api, plus 4 test projects) +- **Frontend**: Vite produces a `dist/` bundle with no TypeScript errors +- **Build Artifacts**: `src/**/bin/Debug/net10.0/*.dll`, `frontend/dist/` + +## Troubleshooting + +### TypeScript build fails with mutation callback arity errors +- **Cause**: `@tanstack/react-query` v5.101 changed the `onSuccess` mutation callback signature to include a 4th `context` parameter. Manually calling `options?.onSuccess?.(data, variables, context)` with only 3 args now under-supplies the callback's expected arity. +- **Solution**: Forward all trailing callback args with a rest parameter, e.g. `onSuccess: (data, variables, ...rest) => { ...; options?.onSuccess?.(data, variables, ...rest); }`. Fixed in `frontend/src/api/useUpdateCmsInstanceStatus.ts`. diff --git a/aidlc-docs/features/master-cms-module/construction/build-and-test/integration-test-instructions.md b/aidlc-docs/features/master-cms-module/construction/build-and-test/integration-test-instructions.md new file mode 100644 index 0000000..188fec7 --- /dev/null +++ b/aidlc-docs/features/master-cms-module/construction/build-and-test/integration-test-instructions.md @@ -0,0 +1,82 @@ +# Integration Test Instructions + +## Purpose +Verify that the three units of the Master CMS Module work together: the Master module (Unit 1), the slave-side Availability extension (Unit 2), and the `/cms` frontend page (Unit 3). + +## Test Scenarios + +### Scenario 1: Owner Adds a Slave — Master Pushes Registration (Unit 1 → Unit 2) +- **Description**: The Owner registers a slave CMS from the Master `/cms` page; the Master generates an API key and pushes registration to the slave. +- **Setup**: Run two instances of the API — one configured as Master (`SlpModularCms.Modules.Master` enabled), one as Slave (`SlpModularCms.Modules.Availability` running, reachable at a known URL). +- **Test Steps**: + 1. On the Master, call `POST /api/v1/CmsInstances` with the slave's URL. + 2. `CmsInstanceService` generates an API key, stores it (Data-Protection-encrypted) in `CmsInstance`, and calls `ISlaveApiClient.RegisterMasterAsync` which `POST`s to the slave's `/api/v1/master/register` with the `X-Master-Api-Key` header. + 3. Query the Master's `GET /api/v1/CmsInstances` list endpoint. + 4. Query the slave's `GET /api/v1/master/registered-url` (with the same header) to confirm it stored the registration. +- **Expected Results**: The new instance appears in the Master's `CmsInstance` list (API key never included in the DTO); the slave's `MasterRegistration` table stores the Master's URL and the Data-Protection-encrypted API key. +- **Cleanup**: Stop both instances; clear both DBs if reused. + +### Scenario 2: Master Sets Slave to Inactive → Slave Enforces via Two-Phase Middleware (Unit 1 → Unit 2) +- **Description**: Owner disables a slave from the Master `/cms` page; the Master pushes the new status to the slave, whose `AvailabilityMiddleware` blocks CMS requests. +- **Setup**: Both instances running and registered (per Scenario 1). +- **Test Steps**: + 1. On the Master, call `PUT /api/v1/CmsInstances/{id}/status` with `Inactive`. + 2. Confirm the Master's `SlaveApiClient` pushes the status to the slave synchronously as part of the same request (`SlaveContactSuccess` in the response). + 3. Send a request to the slave's CMS route. +- **Expected Results**: Slave's static `_masterIsAvailable` flag flips immediately on push; slave responds with `503` and the extended response body signaling master-controlled unavailability; frontend on the slave redirects to the disable message. +- **Cleanup**: Set status back to `Active` on the Master. + +### Scenario 3: Fail-Open on Master Unreachable or Slave Restart (Unit 1 & Unit 2) +- **Description**: The slave gate must never hard-fail when it cannot reach the Master. +- **Setup A (push failure)**: Registered slave; stop the Slave instance, then call the status-update endpoint on the Master. +- **Test Steps A**: + 1. Stop the Slave process. + 2. On the Master, call `PUT /api/v1/CmsInstances/{id}/status` with `Inactive`. + 3. Inspect the Master's response body and `CmsInstance.LastIntegrityCheckFailedAt`. +- **Expected Results A**: The DB status change is NOT rolled back; response reports `SlaveContactSuccess = false`; `IntegrityCheckBackgroundService` will retry and update `LastIntegrityCheckFailedAt` on its next interval (`IntegrityCheckIntervalMinutes`, default 60). +- **Setup B (slave restart)**: Restart the Slave process while the Master is unreachable. +- **Test Steps B**: + 1. Stop both Master and Slave. + 2. Start only the Slave. + 3. Send a request to the slave's CMS route immediately after startup. +- **Expected Results B**: `_masterIsAvailable` defaults to `true` on process start (fail-open), so the slave is reachable even before the Master pushes a fresh status. + +### Scenario 4: Frontend CMS Page End-to-End (Unit 3 → Unit 1) +- **Description**: Owner manages slave CMS instances through the `/cms` page. +- **Setup**: Master API running; frontend dev server pointed at it (`npm run dev`); logged in as Administrator. +- **Test Steps**: + 1. Navigate to `/cms`. + 2. Add a new CMS instance via `AddCmsInstanceDialog`. + 3. Confirm it appears in `CmsInstanceList`. + 4. Open `SetStatusDialog` and toggle its status. +- **Expected Results**: List refreshes via TanStack Query invalidation after each mutation; API key is never shown in the list response (per NFR). + +## Setup Integration Test Environment + +### 1. Start Required Services +```bash +# Terminal 1 — Master instance +dotnet run --project src/SlpModularCms.Api --urls http://localhost:5001 + +# Terminal 2 — Slave instance (separate DB, MasterUrl pointing at Terminal 1) +dotnet run --project src/SlpModularCms.Api --urls http://localhost:5002 +``` + +### 2. Configure Frontend +```bash +cd frontend +npm run dev # defaults to Master instance per vite proxy config +``` + +## Run Integration Tests +These scenarios are currently **manual** (no automated cross-process integration test harness exists in this codebase). Automated coverage for the underlying units (registration logic, middleware two-phase check, cache/fallback, frontend mutations) is provided by the unit test suites listed in `unit-test-instructions.md`. + +### Verify Service Interactions +- Check Master logs for registration and status-update requests +- Check Slave logs for `MasterAvailabilityService` pull attempts and cache hits/misses +- Check browser dev tools network tab for `/api/v1/CmsInstances` calls from the frontend + +### Cleanup +```bash +# Stop both dotnet run processes (Ctrl+C) +``` diff --git a/aidlc-docs/features/master-cms-module/construction/build-and-test/performance-test-instructions.md b/aidlc-docs/features/master-cms-module/construction/build-and-test/performance-test-instructions.md new file mode 100644 index 0000000..03533e8 --- /dev/null +++ b/aidlc-docs/features/master-cms-module/construction/build-and-test/performance-test-instructions.md @@ -0,0 +1,40 @@ +# Performance Test Instructions + +## Purpose +Validate that the master-controlled availability gate does not introduce meaningful request latency on the slave, per NFR-PERF-01/PERF-02 (`aidlc-docs/features/master-cms-module/construction/slave-availability-extension/nfr-requirements/nfr-requirements.md`). + +## Performance Requirements +No explicit throughput/latency SLA was defined during NFR Requirements for this feature. The design goal instead is architectural: +- **PERF-01**: The slave's master-gate check reads a `volatile` static in-memory field — zero async overhead, zero DB call per request. +- **PERF-02**: Only the local availability gate touches the DB, unchanged from pre-existing behavior (1s cache per `AvailabilityOptions.StatusCacheSeconds`). + +## Setup Performance Test Environment + +### 1. Prepare Test Environment +```bash +dotnet run --project src/SlpModularCms.Api --urls http://localhost:5002 --environment Production +``` + +### 2. Configure Test Parameters +- **Test Duration**: 60 seconds +- **Virtual Users**: 50 concurrent +- **Target endpoint**: Any slave CMS route protected by `AvailabilityMiddleware` + +## Run Performance Tests + +### 1. Baseline (master-gate disabled / no `MasterRegistration` configured) +```bash +k6 run --vus 50 --duration 60s baseline-script.js +``` + +### 2. With Master-Gate Enabled (registered slave, `_masterIsAvailable = true`) +```bash +k6 run --vus 50 --duration 60s gated-script.js +``` + +### 3. Analyze Performance Results +- **Expected**: p95 latency delta between baseline and gated runs should be negligible (sub-millisecond), since the gate check is a single `volatile` field read with no I/O. +- **Bottlenecks**: If a measurable delta appears, verify no accidental DB or network call was introduced into `AvailabilityMiddleware`'s master-gate branch. + +## Performance Optimization +Not applicable at this time — no bottleneck identified in code review or NFR design. Revisit only if production monitoring shows gate-related latency. diff --git a/aidlc-docs/features/master-cms-module/construction/build-and-test/unit-test-instructions.md b/aidlc-docs/features/master-cms-module/construction/build-and-test/unit-test-instructions.md new file mode 100644 index 0000000..0f13f72 --- /dev/null +++ b/aidlc-docs/features/master-cms-module/construction/build-and-test/unit-test-instructions.md @@ -0,0 +1,33 @@ +# Unit Test Execution + +## Run Backend Unit Tests +```bash +dotnet test SlpModularCms.sln --no-build +``` + +### Expected Results +| Test Project | Tests | Result | +|---|---|---| +| SlpModularCms.Core.Tests | 45 | Pass | +| SlpModularCms.Modules.Identity.Tests | 46 | Pass | +| SlpModularCms.Modules.Availability.Tests | 60 | Pass | +| SlpModularCms.Modules.Master.Tests | 42 | Pass | +| **Total** | **193** | **All Pass** | + +## Run Frontend Unit Tests +```bash +cd frontend +npm run test -- --run +``` + +### Expected Results +- **Test Files**: 34 passed (34) +- **Tests**: 209 passed (209) +- `Not implemented: Window's scrollTo()` messages are jsdom environment noise, not failures + +## Fix Failing Tests +If tests fail: +1. Review console output for the failing test name and assertion +2. For backend: check the relevant module's test project under `src/` +3. For frontend: check `frontend/src/**/*.test.ts(x)` +4. Rerun the specific test project/file until green diff --git a/aidlc-docs/features/master-cms-module/construction/plans/documentation-code-generation-plan.md b/aidlc-docs/features/master-cms-module/construction/plans/documentation-code-generation-plan.md new file mode 100644 index 0000000..b94a6df --- /dev/null +++ b/aidlc-docs/features/master-cms-module/construction/plans/documentation-code-generation-plan.md @@ -0,0 +1,41 @@ +# Code Generation Plan — Unit 4: documentation + +## Unit Context +- **Scope**: Update `README.md` (root) with a Master CMS Module section covering the new module, migrations, and production env vars; replace the default Vite template `frontend/README.md` was never actually replaced by Unit 3 and still contains boilerplate — bring it in line with the rest of the documented feature set. +- **Depends on**: Units 1-3 (master-backend, slave-availability-extension, frontend-cms-page) — documents their final, built-and-tested patterns. +- **No new code**: Documentation only; no business logic, no NFRs, no tests. Functional Design / NFR Requirements / NFR Design were correctly skipped for this unit. + +## Steps + +- [x] **Step 1 — Root README: Add "Master CMS Module" section** + Add a new `## Master CMS Module` section to `README.md` after the existing "Nieuwe Module Toevoegen" section, covering: + - What the module does (Owner registers/manages slave CMS instances; slaves enforce master-controlled availability) + - Architecture summary: `SlpModularCms.Modules.Master` (per-module `MasterDbContext`), extended `SlpModularCms.Modules.Availability` (slave-side `MasterRegistration` + two-phase `AvailabilityMiddleware`) + - Registration flow: Owner adds a slave URL via `/cms` → Master generates an API key → Master pushes registration to the slave's `/api/v1/master/register` (`X-Master-Api-Key` header) + - Status flow: Owner toggles status on Master → Master pushes to slave synchronously → fail-open on push failure (DB change not rolled back) → `IntegrityCheckBackgroundService` retries/reconciles on `IntegrityCheckIntervalMinutes` (default 60) + - Fail-open behavior: slave defaults `_masterIsAvailable = true` at startup; no TTL on cached status + +- [x] **Step 2 — Root README: Extend "Database Migraties" section** + Add a subsection noting that `SlpModularCms.Modules.Master` and `SlpModularCms.Modules.Availability` are per-module `DbContext`s with their own migrations, applied automatically at startup via `Database.Migrate()`. Document the exact commands: + ```powershell + dotnet ef migrations add --project src\SlpModularCms.Modules.Master --startup-project src\SlpModularCms.Api + dotnet ef migrations add --project src\SlpModularCms.Modules.Availability --startup-project src\SlpModularCms.Api --context AvailabilityDbContext + ``` + Note the `--context` requirement for the Availability project (it now hosts two logical concerns sharing one `AvailabilityDbContext`, but `dotnet ef` needs disambiguation because the API composes multiple `DbContext` types across modules). + +- [x] **Step 3 — Root README: Extend "Productie Setup" section** + Add the new production-relevant configuration: + - `MasterModule` options actually consumed by the code (bound from config section `MasterModule`): `IntegrityCheckIntervalMinutes` (default 60), `HttpTimeoutSeconds` (default 10), `MasterUrl`. **Revised during execution**: `CacheMinutes` and `ApiKey` are declared on `MasterModuleOptions` but never read anywhere in the codebase (verified via `dotnet-appsettings` skill pass) — they were superseded by Unit 2's actual design (per-instance encrypted keys, no-TTL cache). Left undocumented here per user decision; tracked as TD-001 in the new `tech-debt-backlog` feature instead of documenting dead config as if live. + - **Data Protection key ring warning** (per NFR tech-stack-decisions.md): the API key encryption uses ASP.NET Core Data Protection with the default file-system key store. For containerized/multi-instance deployments, configure a persistent key ring (`PersistKeysToDbContext`, `PersistKeysToAzureBlobStorage`, etc.) — otherwise a container restart makes all stored `ApiKey` values undecryptable, breaking master↔slave communication until instances are re-added. + - **Added during execution**: actual `MasterModule` config section added to `appsettings.json` and `appsettings.Development.json` (was completely missing before this unit, discovered via the `dotnet-appsettings` skill pass). + +- [x] **Step 4 — Replace `frontend/README.md`** + Replace the default Vite/React template content with a short pointer document: describe the frontend briefly and redirect to the root `README.md`'s "Frontend Development (CMS Admin UI)" section, which already documents setup, scripts, and configuration in full. Avoids duplicating content that already exists and stays in sync. + +- [x] **Step 5 — Update feature state** + Mark Unit 4 (documentation) Code Generation complete in `aidlc-docs/features/master-cms-module/aidlc-state.md`; update `aidlc-docs/active-features.md` status. + +## Deviations From Plan (logged during execution) +- **Missing EF migration found & fixed**: `SlpModularCms.Modules.Availability` had no migration for `MasterRegistration` (Unit 2 gap). Generated during Build & Test, before this unit started — see `aidlc-docs/features/master-cms-module/construction/build-and-test/build-and-test-summary.md`. +- **Lint baseline correction**: the "all pre-existing" claim for frontend lint findings in the Build & Test summary was based on a contaminated comparison; corrected via a clean `git worktree` checkout. 2 of the 6 lint findings are new (Unit 3); all 6 moved to `tech-debt-backlog` (TD-002/TD-003) rather than fixed in-flight, per user decision. +- **appsettings gap found & fixed**: `MasterModule` config section was entirely absent from `appsettings.json`/`appsettings.Development.json` despite `MasterModuleOptions.BindConfiguration("MasterModule")` in code. Added both files during this unit's execution (see Step 3). diff --git a/aidlc-docs/features/tech-debt-backlog/aidlc-state.md b/aidlc-docs/features/tech-debt-backlog/aidlc-state.md new file mode 100644 index 0000000..a9d969e --- /dev/null +++ b/aidlc-docs/features/tech-debt-backlog/aidlc-state.md @@ -0,0 +1,34 @@ +# AI-DLC State Tracking + +## Project Information +- **Feature Name**: Tech Debt Backlog +- **Feature Slug**: tech-debt-backlog +- **Project Type**: Brownfield +- **Start Date**: 2026-07-01T00:00:00Z +- **Current Stage**: INCEPTION - Backlog captured, not yet triaged/prioritized +- **Branch**: unknown + +## Workspace State +- **Existing Code**: Yes +- **Reverse Engineering Needed**: No (artifacts exist in `aidlc-docs/_shared/reverse-engineering/`) +- **Workspace Root**: K:\Development\Projects\SlpModularCms + +## Code Location Rules +- **Application Code**: Workspace root (NEVER in aidlc-docs/) +- **Feature Documentation**: aidlc-docs/features/tech-debt-backlog/ only +- **Shared Artifacts**: aidlc-docs/_shared/ + +## Language Configuration +- **Documentation Language**: English +- **Conversation Language**: Dutch (User Language) + +## Purpose + +A running backlog of small, non-blocking issues discovered incidentally while working on other features (dead config, pre-existing lint findings, etc.). Items are captured here instead of fixed in-flight so they don't block the feature where they were found. Pick up and triage when convenient. + +## Stage Progress + +### 🔵 INCEPTION PHASE +- [x] Workspace Detection — Complete (feature created ad-hoc during master-cms-module Build & Test) +- [ ] Requirements Analysis — Backlog list captured in `inception/requirements/backlog.md`; not yet prioritized or scoped into units +- [ ] Everything else — Not started; pick up when the user chooses to work this feature diff --git a/aidlc-docs/features/tech-debt-backlog/audit.md b/aidlc-docs/features/tech-debt-backlog/audit.md new file mode 100644 index 0000000..32ed3ca --- /dev/null +++ b/aidlc-docs/features/tech-debt-backlog/audit.md @@ -0,0 +1,13 @@ +# Audit Log — Tech Debt Backlog + +## Initial Request — Feature Created + +**Timestamp**: 2026-07-01T00:00:00Z + +**User Input**: "Laat ze staan en documenteer ze als unused. Maak ook een nieuwe feature aan waar je dit in verwerkt om te fixen dat ze niet worden gebruikt. Als je meer issues vindt gooi die dan ook in deze feature. Maak het dus eigenlijk een soort tech debt feature. Net als de gefaalde tests bij de Build & Test check die je zojuist gedaan hebt. Die tech debt feature kan ik dan later oppakken. Voor nu doorgaan met unit 4" + +**AI Response**: "Nieuwe feature 'tech-debt-backlog' aangemaakt als lichtgewicht backlog (geen volledige inception-cyclus doorlopen, gezien het doel puur registratie is voor latere oppak). Backlog vastgelegd in inception/requirements/backlog.md met 3 items: TD-001 (dode MasterModuleOptions properties), TD-002 (4 pre-existing frontend lint findings, geverifieerd via git worktree checkout van commit c156107 met schone npm install), TD-003 (2 nieuwe lint findings geïntroduceerd door master-cms-module Unit 3, bewust hierheen verplaatst i.p.v. direct gefixt). Toegevoegd aan active-features.md." + +**Context**: Feature aangemaakt tijdens master-cms-module sessie; geen actieve executie gestart, puur registratie voor later + +--- diff --git a/aidlc-docs/features/tech-debt-backlog/inception/requirements/backlog.md b/aidlc-docs/features/tech-debt-backlog/inception/requirements/backlog.md new file mode 100644 index 0000000..ba9e2b5 --- /dev/null +++ b/aidlc-docs/features/tech-debt-backlog/inception/requirements/backlog.md @@ -0,0 +1,49 @@ +# Tech Debt Backlog + +Items discovered incidentally while working on other features. Not yet prioritized. Each item lists where it was found and what "done" looks like. + +--- + +## TD-001: Dead config properties on `MasterModuleOptions` + +**Found during**: `master-cms-module` — Build & Test / appsettings mapping (2026-07-01) + +**Location**: `src/SlpModularCms.Modules.Master/Options/MasterModuleOptions.cs` + +**Issue**: `CacheMinutes` and `ApiKey` are declared on the options POCO and were originally designed (per `aidlc-docs/features/master-cms-module/construction/master-backend/functional-design/domain-entities.md`) as slave-side settings for Unit 2 (slave-availability-extension) to consume. When Unit 2 was actually implemented, it used a different pattern instead — per-instance Data-Protection-encrypted API keys pushed from the Master, and a no-TTL in-memory cache (`REL-02` in the Availability NFR requirements) rather than a configurable `CacheMinutes`. As a result, neither property is read anywhere in the codebase. + +**Done looks like**: Either remove both properties from `MasterModuleOptions` (and any corresponding appsettings documentation), or — if a future feature revives the pull/TTL model — wire them up for real. Confirm via `grep -rn "CacheMinutes\|Options.Value.ApiKey" src/` that nothing reads them before removing. + +--- + +## TD-002: Pre-existing frontend lint errors (react-hooks plugin) + +**Found during**: `master-cms-module` — Build & Test (2026-07-01), confirmed pre-existing via `git worktree` checkout of commit `c156107` (the commit immediately before this feature's frontend work) with a clean `npm install` + +**Issues** (4 total — 4 errors, 0 warnings at that baseline): +1. `frontend/src/components/ui/badge.tsx:32` — `react-refresh/only-export-components`: the file exports both a component and a non-component value (likely `badgeVariants`), breaking Fast Refresh. Fix: move the shared constant/function to a separate file. +2. `frontend/src/components/users/InviteUserDialog.tsx` — two related findings in the same `useEffect`: + - `react-hooks/set-state-in-effect`: `setStep(1)` (and sibling `setState` calls) run synchronously inside a `useEffect` that resets dialog state on close. + - `react-hooks/immutability`: `reset()` (from `useForm`) is referenced in the effect body before its `const { reset } = useForm(...)` declaration further down the component — works today due to hoisting/closure timing but is flagged as fragile. +3. `frontend/src/pages/SettingsPage.tsx:39` — `react-hooks/set-state-in-effect`: `setSelectedMode`/`setReason` run synchronously inside a `useEffect` that syncs local form state from a query result (`availability`). + +**Done looks like**: Refactor each flagged effect per the React docs' "you might not need an effect" guidance (react.dev/learn/you-might-not-need-an-effect) — e.g. compute derived state during render, or move the reset logic into the `onOpenChange`/event handler instead of an effect. For `badge.tsx`, split the non-component export into its own module. Re-run `npm run lint` in `frontend/` until clean. + +--- + +## TD-003: New lint findings introduced by `master-cms-module` Unit 3 (same pattern as TD-002) + +**Found during**: `master-cms-module` — Build & Test (2026-07-01). These were introduced by this feature's own Unit 3 (frontend-cms-page) code, but deferred here per explicit user decision (2026-07-01: "Laat ze staan... gooi die dan ook in deze feature") rather than fixed in-flight, to avoid blocking Unit 4. + +**Issues** (2 total — 1 error, 1 warning): +1. `frontend/src/components/cms/AddCmsInstanceDialog.tsx:55` — `react-hooks/set-state-in-effect`: `setServerError(null)` (and sibling calls) run synchronously inside the dialog's close-reset `useEffect`. Same pattern as TD-002 item 2. +2. `frontend/src/components/cms/SetStatusDialog.tsx:72` — `react-hooks/incompatible-library` (warning): `watch()` from `react-hook-form`'s `useForm()` is used directly; React Compiler can't safely memoize it, so compilation is skipped for this component/hook. + +**Done looks like**: Same remediation approach as TD-002 item 2 for the `AddCmsInstanceDialog.tsx` finding. For `SetStatusDialog.tsx`, either accept the compiler skipping memoization for this component (likely fine, low-traffic dialog) or replace `watch('status')` with a subscription-based pattern (`useWatch`) if it becomes a proven perf issue. + +--- + +## Notes + +- None of these block functionality — `npm run build` and all test suites pass regardless. +- TD-002 and TD-003 share the same underlying pattern (`setState` in a mount/close-reset effect) — worth fixing as one pass across all four files rather than one-by-one, once picked up. diff --git a/frontend/README.md b/frontend/README.md index 7dbf7eb..4885ea7 100644 --- a/frontend/README.md +++ b/frontend/README.md @@ -1,73 +1,5 @@ -# React + TypeScript + Vite +# SlpModularCms Frontend -This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. +De CMS admin web-UI: Vite + React 19 + TypeScript, TanStack Router/Query, Tailwind v4 met shadcn/ui-stijl componenten, react-i18next (NL/EN), en MSW voor mocking in tests. -Currently, two official plugins are available: - -- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Oxc](https://oxc.rs) -- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) - -## React Compiler - -The React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see [this documentation](https://react.dev/learn/react-compiler/installation). - -## Expanding the ESLint configuration - -If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules: - -```js -export default defineConfig([ - globalIgnores(['dist']), - { - files: ['**/*.{ts,tsx}'], - extends: [ - // Other configs... - - // Remove tseslint.configs.recommended and replace with this - tseslint.configs.recommendedTypeChecked, - // Alternatively, use this for stricter rules - tseslint.configs.strictTypeChecked, - // Optionally, add this for stylistic rules - tseslint.configs.stylisticTypeChecked, - - // Other configs... - ], - languageOptions: { - parserOptions: { - project: ['./tsconfig.node.json', './tsconfig.app.json'], - tsconfigRootDir: import.meta.dirname, - }, - // other options... - }, - }, -]) -``` - -You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules: - -```js -// eslint.config.js -import reactX from 'eslint-plugin-react-x' -import reactDom from 'eslint-plugin-react-dom' - -export default defineConfig([ - globalIgnores(['dist']), - { - files: ['**/*.{ts,tsx}'], - extends: [ - // Other configs... - // Enable lint rules for React - reactX.configs['recommended-typescript'], - // Enable lint rules for React DOM - reactDom.configs.recommended, - ], - languageOptions: { - parserOptions: { - project: ['./tsconfig.node.json', './tsconfig.app.json'], - tsconfigRootDir: import.meta.dirname, - }, - // other options... - }, - }, -]) -``` +Volledige setup-instructies, scripts, configuratie en de vereiste backend staan in de root `README.md` van deze repository, sectie **"Frontend Development (CMS Admin UI)"**. diff --git a/frontend/src/api/useUpdateCmsInstanceStatus.ts b/frontend/src/api/useUpdateCmsInstanceStatus.ts index b307762..f5dd18d 100644 --- a/frontend/src/api/useUpdateCmsInstanceStatus.ts +++ b/frontend/src/api/useUpdateCmsInstanceStatus.ts @@ -11,9 +11,9 @@ export function useUpdateCmsInstanceStatus( return useMutation({ mutationFn: ({ id, ...body }) => api.put(`/api/v1/CmsInstances/${id}/status`, body), - onSuccess: (data, variables, context) => { + onSuccess: (data, variables, ...rest) => { queryClient.invalidateQueries({ queryKey: ['cmsInstances'] }); - options?.onSuccess?.(data, variables, context); + options?.onSuccess?.(data, variables, ...rest); }, onError: options?.onError, }); diff --git a/src/SlpModularCms.Api/appsettings.Development.json b/src/SlpModularCms.Api/appsettings.Development.json index b7c1215..09bd322 100644 --- a/src/SlpModularCms.Api/appsettings.Development.json +++ b/src/SlpModularCms.Api/appsettings.Development.json @@ -20,6 +20,11 @@ "CircuitBreakerSeconds": 30, "StatusCacheSeconds": 1 }, + "MasterModule": { + "IntegrityCheckIntervalMinutes": 60, + "HttpTimeoutSeconds": 10, + "MasterUrl": "https://localhost:7221" + }, "Cors": { "AllowedOrigins": [ "http://localhost:5173", diff --git a/src/SlpModularCms.Api/appsettings.json b/src/SlpModularCms.Api/appsettings.json index dde960d..64c0797 100644 --- a/src/SlpModularCms.Api/appsettings.json +++ b/src/SlpModularCms.Api/appsettings.json @@ -20,6 +20,11 @@ "CircuitBreakerSeconds": 30, "StatusCacheSeconds": 1 }, + "MasterModule": { + "IntegrityCheckIntervalMinutes": 60, + "HttpTimeoutSeconds": 10, + "MasterUrl": "" + }, "Cors": { "AllowedOrigins": [] }, diff --git a/src/SlpModularCms.Modules.Availability/Migrations/20260701200414_InitialCreate.Designer.cs b/src/SlpModularCms.Modules.Availability/Migrations/20260701200414_InitialCreate.Designer.cs new file mode 100644 index 0000000..e12bb53 --- /dev/null +++ b/src/SlpModularCms.Modules.Availability/Migrations/20260701200414_InitialCreate.Designer.cs @@ -0,0 +1,57 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using SlpModularCms.Modules.Availability.Data; + +#nullable disable + +namespace SlpModularCms.Modules.Availability.Migrations +{ + [DbContext(typeof(AvailabilityDbContext))] + [Migration("20260701200414_InitialCreate")] + partial class InitialCreate + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.9") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("SlpModularCms.Modules.Availability.Data.Entities.MasterRegistration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ApiKey") + .IsRequired() + .HasMaxLength(2000) + .HasColumnType("nvarchar(2000)"); + + b.Property("LastContactedAt") + .HasColumnType("datetimeoffset"); + + b.Property("MasterUrl") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("RegisteredAt") + .HasColumnType("datetimeoffset"); + + b.HasKey("Id"); + + b.ToTable("AvailabilityMasterRegistrations", (string)null); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/SlpModularCms.Modules.Availability/Migrations/20260701200414_InitialCreate.cs b/src/SlpModularCms.Modules.Availability/Migrations/20260701200414_InitialCreate.cs new file mode 100644 index 0000000..99c134b --- /dev/null +++ b/src/SlpModularCms.Modules.Availability/Migrations/20260701200414_InitialCreate.cs @@ -0,0 +1,37 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace SlpModularCms.Modules.Availability.Migrations +{ + /// + public partial class InitialCreate : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "AvailabilityMasterRegistrations", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + MasterUrl = table.Column(type: "nvarchar(500)", maxLength: 500, nullable: false), + ApiKey = table.Column(type: "nvarchar(2000)", maxLength: 2000, nullable: false), + RegisteredAt = table.Column(type: "datetimeoffset", nullable: false), + LastContactedAt = table.Column(type: "datetimeoffset", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AvailabilityMasterRegistrations", x => x.Id); + }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "AvailabilityMasterRegistrations"); + } + } +} diff --git a/src/SlpModularCms.Modules.Availability/Migrations/AvailabilityDbContextModelSnapshot.cs b/src/SlpModularCms.Modules.Availability/Migrations/AvailabilityDbContextModelSnapshot.cs new file mode 100644 index 0000000..1c9354c --- /dev/null +++ b/src/SlpModularCms.Modules.Availability/Migrations/AvailabilityDbContextModelSnapshot.cs @@ -0,0 +1,54 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using SlpModularCms.Modules.Availability.Data; + +#nullable disable + +namespace SlpModularCms.Modules.Availability.Migrations +{ + [DbContext(typeof(AvailabilityDbContext))] + partial class AvailabilityDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.9") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("SlpModularCms.Modules.Availability.Data.Entities.MasterRegistration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ApiKey") + .IsRequired() + .HasMaxLength(2000) + .HasColumnType("nvarchar(2000)"); + + b.Property("LastContactedAt") + .HasColumnType("datetimeoffset"); + + b.Property("MasterUrl") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("RegisteredAt") + .HasColumnType("datetimeoffset"); + + b.HasKey("Id"); + + b.ToTable("AvailabilityMasterRegistrations", (string)null); + }); +#pragma warning restore 612, 618 + } + } +}