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
6.9 KiB
Code Generation Summary — Unit: SlpSoftware Client Setup
Moved (brownfield relocation, Step 1)
src/SlpModularCms.Api/Extensions/StaticContentExtensions.cs→src/SlpModularCms.Core/Hosting/StaticContentExtensions.cs(namespaceSlpModularCms.Api.Extensions→SlpModularCms.Core.Hosting; embedded-resource name updated to match)src/SlpModularCms.Api/Extensions/WebsitePlaceholder.html→src/SlpModularCms.Core/Hosting/WebsitePlaceholder.html- Deleted the now-empty
src/SlpModularCms.Api/Extensions/directory
Created
src/SlpModularCms.Core/Hosting/CmsHostOptions.cs— empty extension-point class (NFR Design Pattern 2)src/SlpModularCms.Core/Hosting/CmsHost.cs—ConfigureServices/ConfigurePipeline, the shared composition both Client projects callsrc/SlpModularCms.Api.SlpSoftware/— new Client project:.csproj,Program.cs,appsettings.json,appsettings.Development.json(own isolated local dev database, Infrastructure Design Q1 = B),Properties/launchSettings.json(ports 5286/7222, distinct fromApi's 5284/7221 so both can run simultaneously)src/SlpModularCms.Api.Tests/— new test project:.csproj,PipelineTests.cs(4 tests:/healthreachable, required security headers present on an HTML response with HSTS's Development-only absence explicitly asserted,/adminfallback 404s correctly with no build present, thesentry-tunnelrate limiter engages past its configured permit limit)
Modified
src/SlpModularCms.Api/Program.cs— rewritten to the thin form callingCmsHost.ConfigureServices/ConfigurePipeline; no behavior changesrc/SlpModularCms.Api/SlpModularCms.Api.csproj— removed theWebsitePlaceholder.htmlembedded-resource item (moved toCore)src/SlpModularCms.Core/SlpModularCms.Core.csproj— added theWebsitePlaceholder.htmlembedded-resource itemSlpModularCms.sln— addedSlpModularCms.Api.SlpSoftware(nested underClients) andSlpModularCms.Api.Tests(nested directly underTests, mirroringCore.Tests's placement)README.md— documented the new project,CmsHost, and the local dev database isolation
Not Created (explicit N/A, per the plan)
- No
Program.Coverage.csforApi.SlpSoftware— no pipeline tests target it in this unit (Q1 = C) - No
appsettings.local.jsonforApi.SlpSoftware— git-ignored, personal-per-developer, created locally when needed - No API/Repository layer code, no database migrations — this unit has neither (Unit 2 "Offerings" does)
- No
.gitea/workflows/*.yamlor Gitea Actions variable changes — Operations-phase scope (D-7/D-15)
Post-Completion Fix: Dev Port Collision + Frontend Dev Script
Prompted by a user question about a frontend pnpm dev:slpsoftware script, investigation surfaced a real bug: Api.SlpSoftware's launchSettings.json had been assigned HTTPS port 7222, which is already SlpModularCms.Api.Slave's port (confirmed via frontend/.env.example and Api.Slave/Properties/launchSettings.json). Fixed:
src/SlpModularCms.Api.SlpSoftware/Properties/launchSettings.json— HTTPS port 7222 → 7223 (HTTP 5286 unchanged, already free)src/SlpModularCms.Api.SlpSoftware/appsettings.Development.json—MasterModule:MasterUrlupdated to match (https://localhost:7223)README.md— corrected port note, with an explicit call-out of the 7222 collision reason
Added (frontend, not part of the original per-unit plan, but a direct, symmetrical follow-up to the port fix):
frontend/package.json—dev:slpsoftwarescript (vite --mode slpsoftware --port 5175), mirroringdev:slavefrontend/.env.example— documented the.env.slpsoftware.localpattern (VITE_API_BASE_URL=https://localhost:7223,VITE_APP_TITLE=SlpModularCms (SlpSoftware))
Post-Completion Fix: CORS Origin Mismatch Blocked the Setup Page
Api.SlpSoftware/appsettings.Development.json's Cors:AllowedOrigins had been copied from Api's own file verbatim, listing localhost:5173 — but pnpm dev:slpsoftware serves the frontend on port 5175. The mismatch caused the frontend's /api/v1/Setup/status check to fail on CORS, and router.tsx's InitGuard fails open toward the login page (not setup) on any fetch error — so the setup page never appeared, even against a genuinely empty database (verified directly via the local MariaDB, not assumed). Fixed: Cors:AllowedOrigins now lists localhost:5175.
Post-Completion Fix: GlobalExceptionHandler Logged Expected 401s as Errors
Pre-existing bug, unrelated to this feature's own scope (not introduced by the CmsHost extraction) but surfaced by testing the new instance: src/SlpModularCms.Core/Exceptions/GlobalExceptionHandler.cs logged every exception at LogError before mapping it to a status code, so a routine UnauthorizedException (e.g. an invalid/missing refresh token — the normal state for a fresh, never-logged-in session) was indistinguishable from a genuine unhandled 500 in the console and in Sentry. Fixed: exceptions that map to a status below 500 now log at LogWarning; only genuinely unmapped exceptions (500) log at LogError. Affects every Client project equally (Api, Api.Slave, Api.SlpSoftware), not just this feature. No existing test asserted log level; full suite reran green (196 + 4).
Build and Test Verification (Step 13.5)
Two real build fixes were needed and applied during this step (not deviations from the plan — the plan didn't anticipate these, since they only surface once the code actually compiles):
SlpModularCms.Core.csprojwas missing aScalar.AspNetCorepackage reference —CmsHost.ConfigurePipelinecallsMapScalarApiReference(), which moved intoCorealong with the rest of the pipeline composition but the package reference hadn't followed it. Added.CmsHost.csand the movedStaticContentExtensions.cswere missing explicitusing Microsoft.Extensions.Hosting;/using Microsoft.Extensions.DependencyInjection;—Coreis anMicrosoft.NET.Sdkproject (notSdk.Web), so it does not get ASP.NET Core's implicit usings the wayApi(Sdk.Web) did. Added both.
After both fixes:
- Build: ✅ Success (full solution, all 13 projects)
SlpModularCms.Core.Tests: ✅ 196 passed, 0 failed — no regression from the Step 1 file moveSlpModularCms.Api.Tests(new): ✅ 4 passed, 0 failed —/healthreachable, required security headers present on an HTML response (with HSTS's Development-only absence correctly asserted, not overlooked),/adminfallback 404s correctly with no build present, thesentry-tunnelrate limiter engages past its configured permit limit
Local environment note: running the new tests required a local MariaDB (the same one Api itself needs to run, per the README). It was stopped; started it via podman machine start + podman start mariadb for this verification, and left both running afterward for continued local development (including Unit 2 "Offerings").