Plans the Gitea deployment feature and refreshes the codebase analysis
Adds the AI-DLC inception record for deploying the CMS as a single .NET application on hosting where no server configuration is possible. The reverse-engineering artifacts were regenerated: the previous set predated the Master module, the Slave host, the solution reorganisation and single-host serving, all of which matter for deployment. Findings were verified by running the build, both test suites and the linter rather than inferred, which surfaced two facts the plan depends on: the frontend lint gate currently fails (5 errors), and two transitive packages carry high-severity advisories. Records 24 functional requirements, 32 traced decisions and a seven-unit decomposition whose ordering is load-bearing: durability work must land before the first automated deploy, or the very first deploy is the one that silently breaks master/slave trust. Two conflicts found while designing and carried into the units: - Both modules call AddDataProtection(), which runs after the host and would override a persistent key store while still passing any registration test. - The availability gate runs before authentication, so its admin bypass cannot read HttpContext.User. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HHoJpxYXzHACSQguHrC5fw
This commit is contained in:
@@ -1,119 +1,253 @@
|
||||
# Code Structure
|
||||
# Code Structure
|
||||
|
||||
## Build System
|
||||
- **Type**: .NET SDK (MSBuild / dotnet CLI)
|
||||
- **Configuration**: `SlpModularCms.sln` — solution file referencing all projects
|
||||
- **Target Framework**: `net10.0`
|
||||
|
||||
- **Type**: .NET SDK (MSBuild / `dotnet` CLI) for the backend; pnpm + Vite for the admin SPA.
|
||||
- **Solution**: `SlpModularCms.sln` — 10 projects, organised into three top-level Solution Folders (see `CLAUDE.md` / `AGENTS.md`):
|
||||
- **Application** — `SlpModularCms.Core` plus a nested **Modules** folder (`Modules.Master`, `Modules.Identity`, `Modules.Availability`)
|
||||
- **Tests** — mirrors Application, with its own nested **Modules** folder
|
||||
- **Clients** — the deployable hosts: `SlpModularCms.Api`, `SlpModularCms.Api.Slave`
|
||||
- **Target framework**: `net10.0` for every project. SDK in use: 10.0.301.
|
||||
- **Key build settings**: `Nullable` and `ImplicitUsings` enabled everywhere. `SlpModularCms.Core` uses `<FrameworkReference Include="Microsoft.AspNetCore.App" />` so a class library can depend on ASP.NET Core types.
|
||||
- **Coverage**: `coverlet.runsettings` at the repository root excludes migrations, `obj/`, generated OpenAPI interceptors, and anything marked `[ExcludeFromCodeCoverage]`.
|
||||
- **Frontend build**: `frontend/package.json` — `build` runs `tsc -b && vite build`; `vite.config.ts` sets `base: '/admin/'` for `command === 'build'` only, so the dev server still serves from `/`.
|
||||
- **Publish coupling**: `SlpModularCms.Api.csproj` defines the `BuildAndCopyAdminFrontend` target with `BeforeTargets="Publish"`, which runs `pnpm install --frozen-lockfile` and `pnpm build` in `frontend/` and copies `frontend/dist/**` into `wwwroot/admin/`. **`dotnet publish` therefore requires Node and pnpm on the build machine.** `wwwroot/` is gitignored.
|
||||
|
||||
## Project Structure
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
Root["SlpModularCms/"]
|
||||
Src["src/"]
|
||||
Api["SlpModularCms.Api\n(API host)"]
|
||||
ApiExt["Extensions/\nServiceCollectionExtensions.cs"]
|
||||
ApiInfra["Infrastructure/\nGlobal exception handler"]
|
||||
ApiProg["Program.cs\nApp startup + module loading"]
|
||||
root["SlpModularCms (repo root)"]
|
||||
sln["SlpModularCms.sln"]
|
||||
src["src/"]
|
||||
fe["frontend/ (admin SPA)"]
|
||||
docs["aidlc-docs/"]
|
||||
|
||||
Core["SlpModularCms.Core\n(Shared core)"]
|
||||
CoreAvail["Availability/\nAvailabilityOptions.cs\nAvailabilityStatus.cs"]
|
||||
CoreData["Data/\nApplicationDbContext.cs"]
|
||||
CoreIdentity["Identity/\nEntities, Models, Services\nAuthorization/"]
|
||||
CoreMigrations["Migrations/\nEF Core migrations"]
|
||||
CoreModules["Modules/\nIModule.cs, ModuleInfo.cs"]
|
||||
api["SlpModularCms.Api<br/>Client / host"]
|
||||
slave["SlpModularCms.Api.Slave<br/>Client / host"]
|
||||
core["SlpModularCms.Core<br/>shared library"]
|
||||
mid["Modules.Identity"]
|
||||
mav["Modules.Availability"]
|
||||
mma["Modules.Master"]
|
||||
tests["4 test projects<br/>Core, Identity, Availability, Master"]
|
||||
|
||||
ModIdentity["SlpModularCms.Modules.Identity\n(Identity module)"]
|
||||
ModIdentityCtrl["Controllers/\nAuthController\nSetupController\nUsersController"]
|
||||
root --> sln
|
||||
root --> src
|
||||
root --> fe
|
||||
root --> docs
|
||||
src --> api
|
||||
src --> slave
|
||||
src --> core
|
||||
src --> mid
|
||||
src --> mav
|
||||
src --> mma
|
||||
src --> tests
|
||||
|
||||
ModAvail["SlpModularCms.Modules.Availability\n(Availability module)"]
|
||||
ModAvailCtrl["Controllers/\nAvailabilityController"]
|
||||
ModAvailSvc["Services/\nPersistentAvailabilityService"]
|
||||
|
||||
Tests1["SlpModularCms.Core.Tests"]
|
||||
Tests2["SlpModularCms.Modules.Availability.Tests"]
|
||||
Docs["aidlc-docs/\nAI-DLC workflow documentation"]
|
||||
|
||||
Root --> Src
|
||||
Root --> Docs
|
||||
Src --> Api
|
||||
Src --> Core
|
||||
Src --> ModIdentity
|
||||
Src --> ModAvail
|
||||
Src --> Tests1
|
||||
Src --> Tests2
|
||||
Api --> ApiExt
|
||||
Api --> ApiInfra
|
||||
Api --> ApiProg
|
||||
Core --> CoreAvail
|
||||
Core --> CoreData
|
||||
Core --> CoreIdentity
|
||||
Core --> CoreMigrations
|
||||
Core --> CoreModules
|
||||
ModIdentity --> ModIdentityCtrl
|
||||
ModAvail --> ModAvailCtrl
|
||||
ModAvail --> ModAvailSvc
|
||||
|
||||
style Api fill:#4CAF50,stroke:#2E7D32,color:#fff
|
||||
style ApiExt fill:#4CAF50,stroke:#2E7D32,color:#fff
|
||||
style ApiInfra fill:#4CAF50,stroke:#2E7D32,color:#fff
|
||||
style ApiProg fill:#4CAF50,stroke:#2E7D32,color:#fff
|
||||
style Core fill:#FFC107,stroke:#F57F17,color:#000
|
||||
style CoreAvail fill:#FFC107,stroke:#F57F17,color:#000
|
||||
style CoreData fill:#FFC107,stroke:#F57F17,color:#000
|
||||
style CoreIdentity fill:#FFC107,stroke:#F57F17,color:#000
|
||||
style CoreMigrations fill:#FFC107,stroke:#F57F17,color:#000
|
||||
style CoreModules fill:#FFC107,stroke:#F57F17,color:#000
|
||||
style ModIdentity fill:#4CAF50,stroke:#2E7D32,color:#fff
|
||||
style ModIdentityCtrl fill:#4CAF50,stroke:#2E7D32,color:#fff
|
||||
style ModAvail fill:#4CAF50,stroke:#2E7D32,color:#fff
|
||||
style ModAvailCtrl fill:#4CAF50,stroke:#2E7D32,color:#fff
|
||||
style ModAvailSvc fill:#4CAF50,stroke:#2E7D32,color:#fff
|
||||
style Tests1 fill:#9E9E9E,stroke:#424242,color:#fff
|
||||
style Tests2 fill:#9E9E9E,stroke:#424242,color:#fff
|
||||
style Docs fill:#CE93D8,stroke:#6A1B9A,color:#000
|
||||
classDef client fill:#90cdf4,stroke:#2b6cb0,stroke-width:1px,color:#000;
|
||||
classDef corelayer fill:#fbd38d,stroke:#c05621,stroke-width:1px,color:#000;
|
||||
classDef module fill:#9ae6b4,stroke:#2f855a,stroke-width:1px,color:#000;
|
||||
classDef test fill:#e2e8f0,stroke:#4a5568,stroke-width:1px,color:#000;
|
||||
classDef meta fill:#d6bcfa,stroke:#6b46c1,stroke-width:1px,color:#000;
|
||||
class api,slave,fe client;
|
||||
class core corelayer;
|
||||
class mid,mav,mma module;
|
||||
class tests test;
|
||||
class root,sln,src,docs meta;
|
||||
```
|
||||
|
||||
Text alternative: The repository root holds the solution file, a `src/` folder with two host projects, Core, three modules and four test projects, plus a separate `frontend/` admin SPA and the `aidlc-docs/` documentation tree.
|
||||
|
||||
## Key Classes/Modules
|
||||
|
||||
### Core Domain Entities
|
||||
- `ApplicationUser` — extends `IdentityUser<Guid>` with `IsActive`, `CreatedAt`, `Naam`
|
||||
- `ApplicationRole` — extends `IdentityRole<Guid>`
|
||||
- `RefreshToken` — linked to user; has `Token`, `ExpiryDate`, `IsRevoked`, `IsActive`
|
||||
- `Invitation` — linked to user (invitee); has `Token`, `ExpiryDate`, `IsUsed`, `Role`
|
||||
- `GlobalAvailabilityState` — singleton-ish entity storing `Status`, `Message`, `LastUpdatedAt`, `UpdatedBy`
|
||||
```mermaid
|
||||
classDiagram
|
||||
class IModule {
|
||||
+string Name
|
||||
+string Version
|
||||
+RegisterServices(IServiceCollection)
|
||||
+UseModule(IApplicationBuilder)
|
||||
}
|
||||
class ModuleOrchestrator {
|
||||
+IReadOnlyList~string~ ModuleNames
|
||||
+DiscoverModules()
|
||||
+RegisterModuleServices(IServiceCollection)
|
||||
+UseModules(IApplicationBuilder)
|
||||
}
|
||||
class IdentityModule
|
||||
class AvailabilityModule
|
||||
class MasterModule
|
||||
class IAvailabilityService {
|
||||
+IsAvailableAsync()
|
||||
+UpdateStatusAsync()
|
||||
}
|
||||
class PersistentAvailabilityService
|
||||
class IMasterAvailabilityService {
|
||||
+GetMasterStatus()
|
||||
}
|
||||
class MasterAvailabilityService
|
||||
|
||||
### Core Services
|
||||
- `IAuthService` / `AuthService` — `AuthenticateAsync`, `RefreshTokenAsync`, `RevokeTokenAsync`
|
||||
- `IInvitationService` / `InvitationService` — `CreateInvitationAsync`, `CompleteInvitationAsync`, `ValidateInvitationAsync`
|
||||
- `ISetupService` / `SetupService` — `IsSystemInitializedAsync`, `CreateInitialOwnerAsync`
|
||||
- `IAvailabilityService` / `PersistentAvailabilityService` — `IsAvailableAsync`, `UpdateStatusAsync`
|
||||
IModule <|.. IdentityModule
|
||||
IModule <|.. AvailabilityModule
|
||||
IModule <|.. MasterModule
|
||||
ModuleOrchestrator --> IModule
|
||||
IAvailabilityService <|.. PersistentAvailabilityService
|
||||
IMasterAvailabilityService <|.. MasterAvailabilityService
|
||||
```
|
||||
|
||||
### Module System
|
||||
- `IModule` — interface: `RegisterServices(IServiceCollection)`, `UseModule(IApplicationBuilder)`
|
||||
- Modules discovered at startup and invoked in sequence
|
||||
Text alternative: `ModuleOrchestrator` works against the `IModule` contract implemented by the three modules; the Availability module supplies the concrete availability and master-gate services behind Core's interfaces.
|
||||
|
||||
### Existing Files Inventory
|
||||
|
||||
#### Clients (deployable hosts)
|
||||
|
||||
- `src/SlpModularCms.Api/Program.cs` — Host composition: local settings overlay, module discovery, core infrastructure/CORS/rate limiting, `/api/v1` convention, enum-as-string JSON, exception handler, HTTPS redirect, static files, CORS, module middleware, auth, controllers, and the two SPA fallbacks (`/admin/{*path:nonfile}` → `admin/index.html`, `{*path:nonfile}` → `index.html`).
|
||||
- `src/SlpModularCms.Api/Program.Coverage.cs` — Coverage-support partial.
|
||||
- `src/SlpModularCms.Api/SlpModularCms.Api.csproj` — Package/project references plus the `BuildAndCopyAdminFrontend` publish target.
|
||||
- `src/SlpModularCms.Api/appsettings.json` — Production baseline with placeholder secrets; `Logging` default `Warning`; `AllowedHosts: "*"`; empty `Cors:AllowedOrigins`; `JwtSettings`, `Availability`, `MasterModule`, `MasterPolling`, `RateLimiting` sections.
|
||||
- `src/SlpModularCms.Api/appsettings.Development.json` — LocalDB connection, dev JWT secret, `CookieSameSite: None`, CORS for `localhost:5173`, relaxed rate limits, `MasterUrl: https://localhost:7221`.
|
||||
- `src/SlpModularCms.Api/appsettings.local.json` — Gitignored developer overrides.
|
||||
- `src/SlpModularCms.Api/Properties/launchSettings.json` — `http` (5284) and `https` (7221) profiles, both `Development`, launching `/scalar`.
|
||||
- `src/SlpModularCms.Api.Slave/Program.cs`, `…/appsettings*.json`, `…/Properties/launchSettings.json` — Second host on 7222; no Master module reference; expects its own database.
|
||||
|
||||
#### Core
|
||||
|
||||
- `src/SlpModularCms.Core/Hosting/ModuleOrchestrator.cs` — Globs `SlpModularCms.Modules.*.dll` from `AppDomain.CurrentDomain.BaseDirectory`, loads assemblies, instantiates every non-abstract `IModule`, and exposes `ModuleNames`. Failures are logged, not thrown.
|
||||
- `src/SlpModularCms.Core/Hosting/ServiceCollectionExtensions.cs` — `AddCoreInfrastructure` (DbContext, Identity with password policy, JWT bearer with `ClockSkew.Zero`, the three hierarchical policies, exception handler + ProblemDetails, API versioning, OpenAPI), `AddCmsCors`, `AddCmsRateLimiting` (fixed-window `login`, sliding-window `refresh`).
|
||||
- `src/SlpModularCms.Core/Hosting/ApiPrefixConvention.cs` — Applies the single `api/v1` prefix to every controller.
|
||||
- `src/SlpModularCms.Core/Hosting/SystemController.cs` — `GET /api/v1/System/capabilities`, returns loaded module names.
|
||||
- `src/SlpModularCms.Core/Data/ApplicationDbContext.cs` — Identity + `RefreshToken`, `Invitation`, `ModulePermission`, `GlobalAvailabilityState`.
|
||||
- `src/SlpModularCms.Core/Identity/Entities/*.cs` — `ApplicationUser`, `ApplicationRole`, `RefreshToken`, `Invitation`, `ModulePermission`, `GlobalAvailabilityState`.
|
||||
- `src/SlpModularCms.Core/Identity/Models/*.cs` — `IdentityRequests`, `JwtSettings`, `TokenResponse`.
|
||||
- `src/SlpModularCms.Core/Identity/Services/{AuthService,IAuthService,InvitationService,IInvitationService,SetupService}.cs` — Login/refresh/revoke with token rotation, invitation lifecycle, first-Owner bootstrap.
|
||||
- `src/SlpModularCms.Core/Identity/Authorization/{HierarchicalRoleHandler,HierarchicalRoleRequirement}.cs` — Owner > Administrator > User satisfaction.
|
||||
- `src/SlpModularCms.Core/Availability/{AvailabilityOptions,AvailabilityStatus,AvailabilityStatusDetails,IAvailabilityService,MasterControlledAvailabilityException}.cs` — Availability contract; the exception is what turns a local override attempt into `409 Conflict` while master-controlled.
|
||||
- `src/SlpModularCms.Core/Exceptions/{GlobalExceptionHandler,ValidationException,UnauthorizedException,InvitationOrUserAlreadyExistsException}.cs` — RFC 9457 mapping.
|
||||
- `src/SlpModularCms.Core/Modules/{IModule,ModuleInfo}.cs` — Module contract.
|
||||
- `src/SlpModularCms.Core/Migrations/` — 5 files; `ApplicationDbContext` migrations, applied **manually only**.
|
||||
|
||||
#### Modules.Identity
|
||||
|
||||
- `Controllers/AuthController.cs` — `login` (rate-limited `login`), `refresh` (rate-limited `refresh`), `revoke`, `change-password`.
|
||||
- `Controllers/SetupController.cs` — `status`, `owner`.
|
||||
- `Controllers/InvitationController.cs` — `validate`, `complete`; anonymous.
|
||||
- `Controllers/UsersController.cs` — list, `me` update, `invite`, role/active updates, delete; `AdminOnly` by default.
|
||||
- `IdentityModule.cs` — Module registration.
|
||||
|
||||
#### Modules.Availability
|
||||
|
||||
- `AvailabilityModule.cs` — Registers services, `AvailabilityDbContext`, Data Protection, polling `HttpClient` and hosted service; on `UseModule` runs `Database.Migrate()` and installs `AvailabilityMiddleware`.
|
||||
- `Middleware/AvailabilityMiddleware.cs` — Bypass prefixes, admin-token bypass, master gate then local status, 503 `ProblemDetails` otherwise.
|
||||
- `Services/PersistentAvailabilityService.cs` — Persisted local status with caching/circuit breaker; throws `MasterControlledAvailabilityException` on local override while master-controlled.
|
||||
- `Services/{MasterAvailabilityService,MasterGateStatus,MasterStatusPollClient,MasterApiKeyProtector,…}.cs` — Master gate state, poll client, encrypted key handling, DI dependency bundle.
|
||||
- `BackgroundServices/MasterStatusPollingBackgroundService.cs` — Periodic pull with fail-open.
|
||||
- `Controllers/{AvailabilityController,MasterController}.cs` — Public status + Owner-only update; inbound master register/status/registered-url.
|
||||
- `Data/AvailabilityDbContext.cs`, `Data/Entities/MasterRegistration.cs`, `Config/MasterPollingOptions.cs`, `Repositories/*`, `Models/MasterModels.cs`; `Migrations/` — 5 files, auto-applied.
|
||||
|
||||
#### Modules.Master
|
||||
|
||||
- `MasterModule.cs` — Data Protection, `MasterModuleOptions`, `MasterDbContext`, repositories/services, `SlaveApiClient` with a `slave-resilience` handler (2 retries, exponential backoff with jitter, configurable timeout), `IntegrityCheckBackgroundService`, `HttpContextAccessor`; migrates on `UseModule`.
|
||||
- `Controllers/CmsInstanceController.cs` — Owner-only list/create/update-status.
|
||||
- `Controllers/SlaveStatusController.cs` — Anonymous pull endpoint for slaves.
|
||||
- `Services/{CmsInstanceService,SlaveApiClient,ApiKeyProtector,MasterServiceDependencies,…}.cs`
|
||||
- `BackgroundServices/IntegrityCheckBackgroundService.cs` — Periodic reconciliation and status re-push.
|
||||
- `Data/MasterDbContext.cs`, `Data/Entities/{CmsInstance,CmsInstanceStatus}.cs`, `Models/*`, `Options/MasterModuleOptions.cs`, `Repositories/*`; `Migrations/` — 3 files, auto-applied.
|
||||
|
||||
#### Tests
|
||||
|
||||
- `src/SlpModularCms.Core.Tests/` — 7 files (Exceptions, Hosting, Identity).
|
||||
- `src/SlpModularCms.Modules.Identity.Tests/` — 4 files (Controllers).
|
||||
- `src/SlpModularCms.Modules.Availability.Tests/` — 10 files (Controllers, Services, Repositories, BackgroundServices).
|
||||
- `src/SlpModularCms.Modules.Master.Tests/` — 7 files (Controllers, Services, Repositories, BackgroundServices).
|
||||
|
||||
#### frontend (admin SPA)
|
||||
|
||||
- `frontend/vite.config.ts` — `base: '/admin/'` on build, `@` alias, dev port 5173, Vitest config with v8 coverage.
|
||||
- `frontend/package.json` — Scripts `dev`, `dev:slave` (mode `slave`, port 5174), `dev:all` (concurrently), `build`, `lint`, `format`, `format:check`, `test`, `test:watch`, `test:coverage`, `preview`.
|
||||
- `frontend/.env.example` — `VITE_API_BASE_URL`, `VITE_APP_TITLE`, plus notes for the slave setup. `.env.local` / `.env.slave.local` are local-only.
|
||||
- `frontend/src/lib/config.ts` — Reads `VITE_API_BASE_URL` and `VITE_APP_TITLE`; Zod-validates `apiBaseUrl` as a URL, warning only in dev. **Makes the production bundle environment-specific.**
|
||||
- `frontend/src/lib/api-client.ts` — `fetch` wrapper: credentials always sent, in-memory access token, 401 refresh-and-retry interceptor, `ProblemDetailsError` and `NetworkError`.
|
||||
- `frontend/src/router.tsx` — TanStack Router with `basepath: import.meta.env.BASE_URL`, so routing follows the `/admin/` base automatically.
|
||||
- `frontend/src/main.tsx` — Sets document title from config, React Query client, optional MSW via `VITE_ENABLE_MSW`, renders only after the initial silent refresh settles.
|
||||
- `frontend/src/api/use*.ts` — Typed hooks per resource (availability, users, profile, invitation, setup, CMS instances, system capabilities), each with a colocated test.
|
||||
- `frontend/src/pages/` — 10 pages, each with a test.
|
||||
- `frontend/src/components/{auth,cms,layout,shared,ui,users}/` — Guards (`ModuleGuard`, `RoleGuard`), CMS instance dialogs/list, layout shell, shadcn-style primitives.
|
||||
- `frontend/src/{contexts,hooks,i18n,mocks,test}/` — Auth provider, hooks, NL/EN translations, MSW handlers per domain, test setup.
|
||||
|
||||
## Design Patterns
|
||||
### Module Pattern
|
||||
- **Location**: `SlpModularCms.Core/Modules/`, `SlpModularCms.Api/Program.cs`
|
||||
- **Purpose**: Allows features to be developed, tested, and deployed independently
|
||||
- **Implementation**: Each module class implements `IModule` and is registered in the API host
|
||||
|
||||
### Repository Pattern via EF Core
|
||||
- **Location**: `ApplicationDbContext` used directly in services
|
||||
- **Purpose**: Centralized persistence with Entity Framework
|
||||
### Module / plugin pattern
|
||||
- **Location**: `Core/Modules/IModule.cs`, `Core/Hosting/ModuleOrchestrator.cs`, each `*Module.cs`.
|
||||
- **Purpose**: Let one codebase deploy with different capability sets.
|
||||
- **Implementation**: Reflection-based discovery of `SlpModularCms.Modules.*.dll` in the app base directory; each module registers services and middleware itself. Deployment content, not configuration, decides capability.
|
||||
|
||||
### JWT with Refresh Token Rotation
|
||||
- **Location**: `AuthService.cs`, `AuthController.cs`
|
||||
- **Purpose**: Stateless auth with token refresh capability
|
||||
### Repository pattern
|
||||
- **Location**: `Modules.Master/Repositories/`, `Modules.Availability/Repositories/`.
|
||||
- **Purpose**: Keep EF Core access behind an interface so services stay unit-testable.
|
||||
- **Implementation**: Interface plus EF-backed implementation per aggregate. Core's identity services use `ApplicationDbContext`/Identity managers directly rather than repositories — an intentional inconsistency between old and new code.
|
||||
|
||||
### Options pattern
|
||||
- **Location**: `JwtSettings`, `AvailabilityOptions`, `MasterModuleOptions`, `MasterPollingOptions`.
|
||||
- **Purpose**: Bind configuration sections to typed objects.
|
||||
- **Implementation**: `services.Configure<T>` / `AddOptions<T>().BindConfiguration(...)`.
|
||||
|
||||
### Dependency-bundle (parameter object)
|
||||
- **Location**: `MasterServiceDependencies`, `MasterAvailabilityServiceDependencies`.
|
||||
- **Purpose**: Keep constructors manageable where a service needs many collaborators.
|
||||
|
||||
### Middleware gate
|
||||
- **Location**: `AvailabilityMiddleware`.
|
||||
- **Purpose**: Enforce availability centrally rather than per controller, with explicit bypasses.
|
||||
|
||||
### Background reconciliation (push + pull)
|
||||
- **Location**: `IntegrityCheckBackgroundService` (master push), `MasterStatusPollingBackgroundService` (slave pull, fail-open).
|
||||
- **Purpose**: Make distributed status self-healing without a message broker.
|
||||
|
||||
### JWT with refresh-token rotation
|
||||
- **Location**: `AuthService`, `AuthController`, `frontend/src/lib/api-client.ts`.
|
||||
- **Purpose**: Short-lived access tokens held in memory; rotating refresh token in an httpOnly cookie scoped to `/api/v1/auth`.
|
||||
|
||||
### Global exception handling to RFC 9457
|
||||
- **Location**: `GlobalExceptionHandler` + typed exceptions.
|
||||
- **Purpose**: One error contract for all clients.
|
||||
|
||||
### Resilience pipeline
|
||||
- **Location**: `MasterModule` `slave-resilience` handler.
|
||||
- **Purpose**: Tolerate slow or briefly unreachable slaves without failing the Owner's action outright.
|
||||
|
||||
## Critical Dependencies
|
||||
### ASP.NET Core Identity
|
||||
- **Version**: .NET 10 built-in
|
||||
- **Usage**: User/Role management, password hashing
|
||||
- **Purpose**: Provides authentication primitives
|
||||
|
||||
### Entity Framework Core
|
||||
- **Version**: .NET 10 built-in
|
||||
- **Usage**: Data persistence with SQL Server provider
|
||||
- **Purpose**: ORM for all domain entities
|
||||
### Microsoft.EntityFrameworkCore.SqlServer — 10.0.9
|
||||
- **Usage**: All three `DbContext` types, one shared connection string.
|
||||
- **Purpose**: Persistence. Module contexts self-migrate; the Core context does not.
|
||||
|
||||
### Microsoft.AspNetCore.Identity.EntityFrameworkCore — 10.0.9
|
||||
- **Usage**: `ApplicationUser`/`ApplicationRole` stores, password hashing, policy.
|
||||
- **Purpose**: Account primitives.
|
||||
|
||||
### Microsoft.AspNetCore.Authentication.JwtBearer — 10.0.9
|
||||
- **Usage**: Token validation in `AddCoreInfrastructure` with `ClockSkew.Zero`.
|
||||
- **Purpose**: Stateless authentication. Requires `JwtSettings:Secret` to be present or startup throws.
|
||||
|
||||
### ASP.NET Core Data Protection (shared framework)
|
||||
- **Usage**: `ApiKeyProtector`, `MasterApiKeyProtector`.
|
||||
- **Purpose**: Encrypt slave API keys at rest. **Default file-system key ring with no persistent store configured** — a redeploy that loses the key folder makes stored keys unreadable.
|
||||
|
||||
### Microsoft.Extensions.Http.Resilience — 9.6.0
|
||||
- **Usage**: `SlaveApiClient`.
|
||||
- **Purpose**: Retry and timeout for master→slave calls. Note: 9.x package on a `net10.0` target.
|
||||
|
||||
### Asp.Versioning.Mvc — 10.0.0
|
||||
- **Usage**: `AddApiVersioning` with `ReportApiVersions`.
|
||||
- **Purpose**: Version reporting alongside the static `/api/v1` prefix convention.
|
||||
|
||||
### Scalar.AspNetCore — 2.16.3
|
||||
- **Usage**: `MapScalarApiReference()`, Development only.
|
||||
- **Purpose**: API reference UI at `/scalar`. Not exposed in production.
|
||||
|
||||
### Vite 8 + React 19 + TanStack Router/Query (frontend)
|
||||
- **Usage**: Admin SPA build and runtime.
|
||||
- **Purpose**: `base: '/admin/'` and `basepath: import.meta.env.BASE_URL` are what make the `/admin` mount work.
|
||||
|
||||
### pnpm (build-time, backend publish)
|
||||
- **Usage**: Invoked from `SlpModularCms.Api.csproj` during publish.
|
||||
- **Purpose**: Builds the admin SPA. Makes Node + pnpm a hard prerequisite of `dotnet publish`.
|
||||
|
||||
Reference in New Issue
Block a user