# Code Generation Summary — Unit: SlpSoftware Client Setup ## Moved (brownfield relocation, Step 1) - `src/SlpModularCms.Api/Extensions/StaticContentExtensions.cs` → `src/SlpModularCms.Core/Hosting/StaticContentExtensions.cs` (namespace `SlpModularCms.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 call - `src/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 from `Api`'s 5284/7221 so both can run simultaneously) - `src/SlpModularCms.Api.Tests/` — new test project: `.csproj`, `PipelineTests.cs` (4 tests: `/health` reachable, required security headers present on an HTML response with HSTS's Development-only absence explicitly asserted, `/admin` fallback 404s correctly with no build present, the `sentry-tunnel` rate limiter engages past its configured permit limit) ## Modified - `src/SlpModularCms.Api/Program.cs` — rewritten to the thin form calling `CmsHost.ConfigureServices`/`ConfigurePipeline`; no behavior change - `src/SlpModularCms.Api/SlpModularCms.Api.csproj` — removed the `WebsitePlaceholder.html` embedded-resource item (moved to `Core`) - `src/SlpModularCms.Core/SlpModularCms.Core.csproj` — added the `WebsitePlaceholder.html` embedded-resource item - `SlpModularCms.sln` — added `SlpModularCms.Api.SlpSoftware` (nested under `Clients`) and `SlpModularCms.Api.Tests` (nested directly under `Tests`, mirroring `Core.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.cs` for `Api.SlpSoftware` — no pipeline tests target it in this unit (Q1 = C) - No `appsettings.local.json` for `Api.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/*.yaml` or 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:MasterUrl` updated 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:slpsoftware` script (`vite --mode slpsoftware --port 5175`), mirroring `dev:slave` - `frontend/.env.example` — documented the `.env.slpsoftware.local` pattern (`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). ## Post-Completion Fix: CI's backend-test Job Had No Database The Gitea Actions `backend-test` job (`.gitea/workflows/continuous_integration.yaml`) just ran `dotnet test` with no MariaDB service — every pre-existing test project mocks or uses EF Core InMemory, so none of them ever needed one. `SlpModularCms.Api.Tests` boots the real `Api` host via `WebApplicationFactory`, which unconditionally runs the startup migration, so it genuinely needs a reachable database — a design gap in this unit's own NFR Requirements/Design that should have been anticipated when committing to a real pipeline test (Q1 = C) rather than a mocked one. Fixed by adding a `mariadb` service container to `backend-test` plus a `ConnectionStrings__DefaultConnection` environment-variable override for that step only (overrides the placeholder in `Api/appsettings.Development.json` via standard ASP.NET Core config layering — env vars win over `appsettings.*.json`). No committed appsettings changed; local `dotnet test` runs are unaffected (developers' own `appsettings.local.json` still applies there). Verified the override mechanism directly against the local MariaDB container before trusting it in CI. **Scope note**: `continuous_integration.yaml` is nominally owned by the `gitea-deployment-workflow` feature. This specific change (making the test gate pass for a test project this feature introduced) was judged in-scope to fix directly as Build-and-Test correctness — distinct from the actual deploy-target retarget (D-15), which remains deferred to this feature's own Operations phase. **Round 2**: the `services:` block above did not actually work in CI — the self-hosted runner (`raspberry-pi-arm64`) runs job and service containers in Docker host-network mode, so the `ports:` mapping was silently ignored and the service ended up on the host's own port 3306, which something else on the runner already answers on. Replaced with an explicit `docker run` step publishing on host port 3307 instead, plus a readiness loop using `mariadb-admin ping` (not `mysqladmin`, which this image doesn't provide — confirmed locally). Diagnosed from the actual Gitea Actions job log (fetched via the API) rather than guessed, and the replacement was verified locally end-to-end before pushing again. ## 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): 1. `SlpModularCms.Core.csproj` was missing a `Scalar.AspNetCore` package reference — `CmsHost.ConfigurePipeline` calls `MapScalarApiReference()`, which moved into `Core` along with the rest of the pipeline composition but the package reference hadn't followed it. Added. 2. `CmsHost.cs` and the moved `StaticContentExtensions.cs` were missing explicit `using Microsoft.Extensions.Hosting;` / `using Microsoft.Extensions.DependencyInjection;` — `Core` is an `Microsoft.NET.Sdk` project (not `Sdk.Web`), so it does not get ASP.NET Core's implicit usings the way `Api` (`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 move - **`SlpModularCms.Api.Tests`** (new): ✅ 4 passed, 0 failed — `/health` reachable, required security headers present on an HTML response (with HSTS's Development-only absence correctly asserted, not overlooked), `/admin` fallback 404s correctly with no build present, the `sentry-tunnel` rate 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").