Unit 1 of the slpsoftware-api feature (FR-1/FR-2/FR-3): a new Client project in the Clients solution folder, intended to eventually become the deployed API for test.slpsoftware.nl/slpsoftware.nl, hosting the same four modules as SlpModularCms.Api plus a future Offerings module. - Extracts SlpModularCms.Api/Program.cs's hosting-pipeline composition into SlpModularCms.Core.Hosting.CmsHost (ConfigureServices/ConfigurePipeline), shared by both Client projects so they cannot drift apart - Moves StaticContentExtensions.cs + WebsitePlaceholder.html from Api into Core, since CmsHost cannot live in Api but Core cannot depend on Api - Adds SlpModularCms.Api.SlpSoftware with its own isolated local dev database and dev ports (5286/7223, distinct from Api's and Api.Slave's) - Adds SlpModularCms.Api.Tests with WebApplicationFactory-based pipeline regression tests (security headers, health check, SPA fallback, rate limiting), scoped to Api per NFR Design - Adds a frontend dev:slpsoftware pnpm script mirroring dev:slave - Fixes GlobalExceptionHandler logging routine 401s (e.g. an expired/missing refresh token) as unhandled errors -- pre-existing, unrelated to this feature's own scope, found while testing the new instance Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FWyStNL2ZsjrS7FLd7xvvN
10 KiB
Code Generation Plan — Unit: SlpSoftware Client Setup
Unit Context
Stories implemented: None directly (unit-of-work-story-map.md — this unit is a purely technical enabling unit).
Functional requirements implemented: FR-1, FR-2, FR-3.
Dependencies: None on other units (this unit is the dependency Unit 2 "Offerings" needs).
Expected interfaces produced: SlpModularCms.Core.Hosting.CmsHost (ConfigureServices/ConfigurePipeline), consumed by both Client projects' Program.cs.
Database entities owned: None.
Workspace root: K:\Development\Projects\SlpModularCms (brownfield — modify/move existing files where noted, never duplicate).
Investigation Finding That Shapes This Plan
Reading SlpModularCms.Api/Program.cs and SlpModularCms.Api/Extensions/StaticContentExtensions.cs directly (not assumed) revealed that UseCmsStaticContent()/MapCmsSpaFallbacks() — needed by CmsHost.ConfigurePipeline — currently live in the Api project itself, not in Core. Since Core cannot depend on Api (wrong dependency direction — Api depends on Core, never the reverse), this file (and its embedded WebsitePlaceholder.html resource) must move into Core before CmsHost can call it. This is Step 1 below, not an afterthought.
Steps
Step 1 — Move Static Content Hosting into Core (prerequisite for CmsHost)
- Move
src/SlpModularCms.Api/Extensions/StaticContentExtensions.cs→src/SlpModularCms.Core/Hosting/StaticContentExtensions.cs; change namespaceSlpModularCms.Api.Extensions→SlpModularCms.Core.Hosting; updatePlaceholderResourceNamefrom"SlpModularCms.Api.Extensions.WebsitePlaceholder.html"to"SlpModularCms.Core.Hosting.WebsitePlaceholder.html" - Move
src/SlpModularCms.Api/Extensions/WebsitePlaceholder.html→src/SlpModularCms.Core/Hosting/WebsitePlaceholder.html SlpModularCms.Core.csproj: add<EmbeddedResource Include="Hosting\WebsitePlaceholder.html" />SlpModularCms.Api.csproj: remove the now-obsolete<EmbeddedResource Include="Extensions\WebsitePlaceholder.html" />item group and its explanatory comment (the file no longer lives there)- Delete the now-empty
src/SlpModularCms.Api/Extensions/directory if nothing else remains in it
Step 2 — CmsHostOptions (Business Logic Generation — Core)
- Create
src/SlpModularCms.Core/Hosting/CmsHostOptions.cs: an intentionally empty class (NFR Design Pattern 2 / Q2 = B) — a pure extension point, no properties yet
Step 3 — CmsHost (Business Logic Generation — Core)
- Create
src/SlpModularCms.Core/Hosting/CmsHost.cswith:public static ModuleOrchestrator ConfigureServices(WebApplicationBuilder builder, CmsHostOptions options)— reproducesApi/Program.cslines for:appsettings.local.jsonloading stays in each project's ownProgram.cs(NOT moved here — application-design.md component-methods.md is explicit that bootstrap lines stay per-project); logging (AddCmsLogging), Sentry (UseCmsSentry),ModuleOrchestratorconstruction +DiscoverModules(),AddCoreInfrastructure,AddCmsCors,AddCmsRateLimiting,AddCmsHealthChecks,AddCmsSecurityHeaders,AddCmsObservability,AddCmsDataProtection(before module services — order preserved exactly),RegisterModuleServices,AddSingleton(orchestrator),AddControllerswithApiPrefixConvention("api/v1")+JsonStringEnumConverter. Returns the orchestrator.public static void ConfigurePipeline(WebApplication app, ModuleOrchestrator orchestrator, CmsHostOptions options)— reproduces:MigrateCoreDatabase,UseExceptionHandler,UseCmsSecurityHeaders,UseRateLimiter, Development-onlyMapOpenApi/MapScalarApiReference,UseHttpsRedirection,UseCmsStaticContent,UseCors,orchestrator.UseModules(app),UseAuthentication/UseAuthorization,MapControllers,MapCmsHealthChecks,MapSentryTunnel,MapCmsSpaFallbacks— same order as today'sProgram.cs, since that order encodes real constraints documented in its comments
- Both methods accept
CmsHostOptionsper NFR-CS-02, even though it currently has no properties to read
Step 4 — Repoint SlpModularCms.Api/Program.cs (Modify In-Place)
- Rewrite
src/SlpModularCms.Api/Program.csto the thin form: create builder, loadappsettings.local.json,var orchestrator = CmsHost.ConfigureServices(builder, new CmsHostOptions());,var app = builder.Build();,CmsHost.ConfigurePipeline(app, orchestrator, new CmsHostOptions());,app.Run(); - No behavior change — verified by Step 7's regression tests
Step 5 — New Client Project: SlpModularCms.Api.SlpSoftware (Project Structure Setup)
- Create
src/SlpModularCms.Api.SlpSoftware/SlpModularCms.Api.SlpSoftware.csproj— mirrorsSlpModularCms.Api.csproj(SDK,TargetFramework,Nullable,ImplicitUsings, same package references:Asp.Versioning.Mvc,Microsoft.AspNetCore.Authentication.JwtBearer,Microsoft.AspNetCore.OpenApi,Microsoft.EntityFrameworkCore.Design,Scalar.AspNetCore), without theWebsitePlaceholder.htmlembedded resource (that now lives inCore, shared) and without aModules.Offeringsreference (added later by Unit 2, per unit-of-work.md)ProjectReference:SlpModularCms.Core,SlpModularCms.Modules.Availability,SlpModularCms.Modules.Identity,SlpModularCms.Modules.Master(FR-2)
- Create
src/SlpModularCms.Api.SlpSoftware/Program.cs— same thin shape as Step 4's rewrittenApi/Program.cs - Create
src/SlpModularCms.Api.SlpSoftware/appsettings.json— mirrorsApi's structure/keys (connection string placeholder, JWT settings, Availability, MasterModule, MasterPolling, Cors, RateLimiting, SecurityHeaders, Observability sections) - Create
src/SlpModularCms.Api.SlpSoftware/appsettings.Development.json— mirrorsApi's Development file, but with its own isolated local dev database name (Infrastructure Design Q1 = B), e.g.Database=SlpModularCmsSlpSoftwareDev - Not creating
appsettings.local.json— it's git-ignored (verified: listed in.gitignore, not tracked in git) and personal per-developer; the developer creates their own copy locally if needed, same as forApi - Not creating
Program.Coverage.csfor this project in this unit — Infrastructure/NFR Design (Q1 = C) scoped the new pipeline regression tests toApionly, so there is no test target requiringProgramto be a public partial class here yet; add it in a future unit/feature ifApi.SlpSoftware-specific pipeline tests are ever introduced
Step 6 — Solution File Updates (SlpModularCms.sln)
- Add
SlpModularCms.Api.SlpSoftwareproject entry, nested under the existing (currently empty)Clientssolution folder ({D72703E6-B021-4360-B1EE-0E99999B5899}) - Add
SlpModularCms.Api.Testsproject entry (Step 7), nested directly underTests({2F43D186-C7D5-4AB1-B821-4D595CA2ECB3}) — mirroring howSlpModularCms.Core.Testsis nested directly underTests, not underTests/Modules - Add both new projects' GUIDs to
ProjectConfigurationPlatforms(Debug/Release × Any CPU/x64/x86, matching the existing pattern for every other project) - Add both new projects' GUIDs to
NestedProjects
Step 7 — Pipeline Regression Tests (Business Logic Unit Testing, NFR-CS-01)
- Create
src/SlpModularCms.Api.Tests/SlpModularCms.Api.Tests.csproj— same SDK-style shape asSlpModularCms.Core.Tests.csproj(xunit, FluentAssertions,Microsoft.NET.Test.Sdk,coverlet.collector), plusMicrosoft.AspNetCore.Mvc.Testing(providesWebApplicationFactory<TEntryPoint>);ProjectReferencetoSlpModularCms.Api.csproj(itsProgram.Coverage.csalready makesPrograma public partial class, so no further change needed there) - Create
src/SlpModularCms.Api.Tests/PipelineTests.csusingWebApplicationFactory<Program>, asserting (NFR-CS-01 / NFR Design Pattern 1):- Required security headers (CSP, HSTS, X-Content-Type-Options, X-Frame-Options, Referrer-Policy) present on a representative response
/healthreturns a successful response- A non-file
/admin/{path}route resolves to the admin SPA'sindex.htmlfallback (or, absent a built SPA in the test environment, at minimum does not 404 as a missing-file/static-asset request would) - A burst of requests against a rate-limited route eventually receives
429 Too Many Requests
Step 8 — Business Logic Summary
- Create
aidlc-docs/features/slpsoftware-api/construction/slpsoftware-client-setup/code/summary.md(markdown only) summarizing: files moved (Step 1),CmsHost/CmsHostOptionsadded,Api/Program.csrewritten,Api.SlpSoftwarecreated, solution file changes, tests added
Step 9 — API Layer / Repository Layer Generation
- N/A — this unit introduces no new HTTP endpoints or persisted entities (FR-1/FR-2/FR-3 are pure composition/project-scaffolding); these categories apply to Unit 2 "Offerings" instead
Step 10 — Database Migration Scripts
- N/A — no new data model in this unit
Step 11 — Documentation Generation
- Update root
README.md: addSlpModularCms.Api.SlpSoftwareto the project-structure description alongsideSlpModularCms.Api/SlpModularCms.Api.Slave, and add a short note under the existing architecture/hosting section explainingCmsHostas the shared composition point both Client projects call, plus a one-line note thatApi.SlpSoftwareuses its own local dev database (Infrastructure Design Q1 = B)
Step 12 — Deployment Artifacts Generation
- N/A for this Construction stage — per Infrastructure Design, no
.gitea/workflows/*.yamlor Gitea Actions variable changes happen here; the CI/CD retarget is explicitly Operations-phase work (D-7/D-15)
Step 13 — Build and Test Verification (automatic, Step 13.5 of the workflow)
- Build the full solution (or at minimum
Api,Api.SlpSoftware,Core,Api.Tests) and confirm it compiles - Run
SlpModularCms.Api.Tests(new) and confirm all pipeline regression tests pass againstApi - Run
SlpModularCms.Core.Tests(existing) and confirm nothing regressed from the Step 1 file move - Fix and retry on any failure; only surface to the user if a fix requires a decision only they can make
Scope reminder: this plan implements Unit 1 only. Unit 2 "Offerings" (all 12 user stories) is a separate Code Generation pass, after this unit is approved and its own Build and Test step is green.