181 lines
8.5 KiB
Markdown
181 lines
8.5 KiB
Markdown
# TECH - Kickoff Blueprint
|
|
|
|
## 0) Current scaffold status
|
|
|
|
- Root solution: `RpgRoller.sln`
|
|
- Backend/full-stack project: `RpgRoller` (Minimal API + Blazor frontend host)
|
|
- Frontend source: `RpgRoller/Components/*` + `RpgRoller/wwwroot/*`
|
|
- Test project: `RpgRoller.Tests` (xUnit + `WebApplicationFactory` integration tests)
|
|
- Test file split: concern-based API tests (`RpgRoller.Tests/Api/*`), service tests (`RpgRoller.Tests/Services/*`), and shared helpers (`RpgRoller.Tests/Support/*`)
|
|
- Persistence: EF Core + SQLite (`RpgRoller/Data/RpgRollerDbContext.cs`) with in-memory runtime cache in `GameService`
|
|
- OpenAPI source: `openapi/RpgRoller.json`
|
|
- Local CI parity entrypoint: `scripts/ci-local.ps1`
|
|
- API endpoint modules: `RpgRoller/Api/*Endpoints.cs` + shared session/auth helpers
|
|
- Service boundary model: API request DTOs are mapped to explicit service method parameters before workflow execution
|
|
- Current backend features: auth/session, campaign/character/skill management (including d6 wild-dice/fumble skill options), ruleset-aware rolls, filtered campaign logs, and SSE state updates.
|
|
- Current frontend features: Blazor-based authenticated campaign workspace with live log updates, full roll workflow controls, and die-state visualization for roll outcomes.
|
|
|
|
## 1) Stack and baseline choices
|
|
|
|
- ASP.NET Core Minimal API on .NET 10.
|
|
- EF Core with SQLite file persistence in current project (single-node deployment).
|
|
- Game state is hydrated once on startup and then served from in-memory state; writes are persisted back to SQLite after successful mutations.
|
|
- Cookie authentication (`HttpOnly`, `SameSite=Strict`, secure in production).
|
|
- Blazor frontend host with Razor components and minimal JS interop for browser APIs.
|
|
- OpenAPI generated from backend as contract documentation.
|
|
- xUnit integration-heavy test suite with isolated SQLite test databases and coverage gates.
|
|
|
|
## 2) Architecture patterns to keep
|
|
|
|
### 2.1 API shape and layering
|
|
|
|
- Route mapping in thin endpoint modules (`MapXEndpoints` per feature area).
|
|
- Domain logic in workflow services (`*WorkflowService`) instead of endpoint lambdas.
|
|
- Service responses normalized via `ServiceResult<T>` + `ServiceError`, then mapped to HTTP at the edge.
|
|
- Consistent `ProblemDetails` payloads with `error` extension for machine-usable errors.
|
|
- Endpoint-level concerns handled by endpoint filters (`AdminOnlyFilter`, `PhaseRequirementFilter`, `PhaseOrJokerFilter`).
|
|
|
|
Keep this split:
|
|
- Endpoint adapters: auth, deserialization, HTTP mapping only.
|
|
- Workflow services: validation, query/update rules, transactions.
|
|
- Helpers: shared utility and security-sensitive routines.
|
|
|
|
### 2.2 Middleware pipeline discipline
|
|
|
|
- Security and behavior depend on middleware ordering; keep explicit ordering.
|
|
- Important current order:
|
|
1. Forwarded headers
|
|
2. Rate limiter
|
|
3. HSTS + HTTPS redirect (prod)
|
|
4. Security headers writer
|
|
5. Base path
|
|
6. Global exception handling
|
|
7. Authentication
|
|
8. Ensure player still exists
|
|
9. CSRF origin/referer checks
|
|
10. Authorization
|
|
11. State change notifier middleware
|
|
12. Static files
|
|
13. Endpoint mapping
|
|
|
|
### 2.3 State synchronization
|
|
|
|
- Event-driven invalidation with SSE (`/api/events/state`) plus heartbeats.
|
|
- Conditional reads for state (`ETag` + `If-None-Match`) to return `304`.
|
|
- In-process notifier (`StateChangeNotifier`) with monotonic version and etag stamp.
|
|
- Mutation middleware (`StateChangeNotificationMiddleware`) emits invalidation only for successful mutating API calls.
|
|
|
|
This pattern is a strong baseline for low to medium scale and should be the default in the new app.
|
|
|
|
### 2.4 Security baseline
|
|
|
|
- Cookie auth with short/medium session sliding expiration plus absolute lifetime cap.
|
|
- Explicit same-origin CSRF checks for authenticated mutating API calls.
|
|
- Rate limiting on auth-sensitive and admin-sensitive surfaces with custom `429` payload.
|
|
- Security headers on all responses (`CSP`, `X-Content-Type-Options`, `X-Frame-Options`, `Referrer-Policy`, `Permissions-Policy`).
|
|
- Forwarded headers restricted to configured trusted proxies/networks only.
|
|
- Owner/admin protection rules enforced in business logic and DB constraints.
|
|
- Destructive admin operations require password re-confirmation.
|
|
- Password hashing is versioned and supports transparent upgrade on successful auth.
|
|
- Current hash defaults to Argon2id, with forward compatibility retained via versioning.
|
|
|
|
### 2.5 Data and invariants
|
|
|
|
- Strong DB models backed by SQLite
|
|
- DB-level guardrails (trigger) to complement app-level checks
|
|
- EF patterns:
|
|
- `AsNoTracking()` for read-only queries
|
|
- `ExecuteUpdateAsync` / `ExecuteDeleteAsync` for efficient bulk operations
|
|
- Explicit transactions for multi-step destructive/admin operations
|
|
- Conflict handling around unique constraints
|
|
|
|
### 2.6 Frontend architecture
|
|
|
|
- Blazor component tree rooted in `Components/App.razor` and `Components/Pages/Home.razor`.
|
|
- Razor components are split into markup-first `.razor` files with behavior/state in paired `.razor.cs` code-behind classes.
|
|
- `Home.razor` + `Home.razor.cs` are intentionally minimal and only manage loading/auth/workspace view-mode switching.
|
|
- Authenticated workspace UI plus workspace state/behavior are centralized in `Components/Pages/Workspace.razor`.
|
|
- Form UX state uses reusable `FormState<TModel>` containers in leaf controls (`HomeControls/*`) rather than parallel form/error/message property sets in `Home`.
|
|
- Concern controls execute their own auth/campaign/character/skill mutation workflows and notify the workspace host only for shared-state refresh/orchestration.
|
|
- Skill management workflows are owned by `CharacterPanel` to keep character-skill behavior cohesive.
|
|
- Shared browser API interop is centralized in `RpgRollerApiClient` and reused by `Home`, `Workspace`, and concern controls.
|
|
- Browser API calls and SSE are handled via `wwwroot/js/rpgroller-api.js` interop.
|
|
- UI state is maintained server-side per circuit with session/tab persistence for campaign + screen selection.
|
|
- SSE-driven campaign refresh with reconnect backoff and explicit offline/manual-refresh fallback.
|
|
|
|
### 2.7 Testing strategy patterns
|
|
|
|
- Full-stack integration tests via `WebApplicationFactory`.
|
|
- Real migrations applied to in-memory Database during test host startup.
|
|
- HTTP side effects mocked deterministically (`StubHttpMessageHandler` and `IHttpClientFactory` replacement).
|
|
- Coverage-focused tests for:
|
|
- auth/security rules
|
|
- middleware behavior
|
|
- filter behavior
|
|
- link/vote/result edge cases
|
|
- OpenAPI operation id stability
|
|
- CI-local parity script (`scripts/ci-local.ps1`) mirrors pipeline flow.
|
|
|
|
### 2.8 Tooling and contract discipline
|
|
|
|
- OpenAPI generated during build (`openapi/RpgRoller.json`).
|
|
- Separate build + tests + coverage threshold checks.
|
|
- Build configured with warnings as errors in CI/local script.
|
|
|
|
## 3) Concrete feature set
|
|
|
|
Use this as a reusable "starter scope menu" for the new app:
|
|
|
|
- Auth:
|
|
- register/login/logout
|
|
- owner bootstrap via admin key
|
|
- auth options endpoint for registration UX
|
|
- Identity/session:
|
|
- cookie claim identity with admin claim
|
|
- stale/deleted-account cookie invalidation
|
|
- absolute session lifetime enforcement
|
|
- State:
|
|
- `/api/state`, `/api/me`
|
|
- SSE state invalidation
|
|
- etag conditional state reads
|
|
|
|
## 4) New-project starter checklist
|
|
|
|
- Security:
|
|
- cookie or token strategy finalized with CSRF model
|
|
- rate limiting partitions and thresholds defined
|
|
- strict CSP and security headers in first commit
|
|
- versioned password hashing with migration strategy
|
|
- trusted proxy/host settings explicit
|
|
- Contract:
|
|
- OpenAPI generation enabled in build
|
|
- operation-id stability tested
|
|
- Data integrity:
|
|
- enforce critical invariants both app-side and DB-side
|
|
- transaction boundaries for multi-entity admin actions
|
|
- Frontend:
|
|
- module boundaries and state refresh model defined
|
|
- escaping/url-safe helpers mandatory
|
|
- i18n structure and fallback behavior in place
|
|
- Testing:
|
|
- integration test host with real migrations
|
|
- deterministic stubs for network dependencies
|
|
- coverage gate enforced in local + CI scripts
|
|
|
|
## 7) Keep/avoid quick reference
|
|
|
|
Keep:
|
|
- Thin endpoints + workflow services.
|
|
- Shared service result abstraction.
|
|
- Explicit middleware order.
|
|
- SSE + ETag state sync.
|
|
- DB-enforced invariants.
|
|
- Regression tests for security-sensitive UI rendering.
|
|
|
|
Avoid:
|
|
- Hard-coded workflow transitions scattered in backend/frontend.
|
|
- Boolean-only role model for long-term products.
|
|
- Unbounded in-memory caches.
|
|
- Synchronous external network checks on hot write paths.
|
|
- Manual API contract duplication between docs/frontend/backend.
|