Adds application design (awaiting approval)
This commit is contained in:
@@ -0,0 +1,112 @@
|
||||
# Services — CMS Frontend
|
||||
|
||||
## Service Architecture Overview
|
||||
|
||||
The frontend uses **TanStack Query** as the server-state service layer. All API interactions are encapsulated in typed query/mutation hooks. The `ApiClient` handles transport concerns (headers, 401 retry). `AuthContext` manages session state. There is no additional service abstraction layer needed.
|
||||
|
||||
---
|
||||
|
||||
## Backend Services (Unit 0 changes)
|
||||
|
||||
### CORS Service (new)
|
||||
- **Service**: `ServiceCollectionExtensions.AddCorsFrontendPolicy()`
|
||||
- **Purpose**: Register named CORS policy `"FrontendPolicy"` for frontend SPA
|
||||
- **Configuration source**: `IConfiguration` → `Cors:AllowedOrigins` array
|
||||
- **Key settings**:
|
||||
- `WithOrigins(allowedOrigins)` — from config
|
||||
- `AllowAnyHeader()` + `AllowAnyMethod()`
|
||||
- `AllowCredentials()` — required for httpOnly cookie
|
||||
- **dotnet-appsettings pattern**: `AllowedOrigins` stored in `appsettings.json` (production) and `appsettings.Development.json` (dev, e.g. `["http://localhost:5173"]`); `appsettings.local.json` for developer-specific overrides
|
||||
|
||||
---
|
||||
|
||||
## Frontend Services (TanStack Query hooks)
|
||||
|
||||
### Auth Service (`frontend/src/auth/`)
|
||||
|
||||
**AuthContext** functions as the auth service — it is the single source of truth for authentication state.
|
||||
|
||||
| Operation | Implementation | Notes |
|
||||
|-----------|----------------|-------|
|
||||
| Login | `POST /api/v1/auth/login` → stores `accessToken` in memory | httpOnly cookie set by server |
|
||||
| Session restore | `POST /api/v1/auth/refresh` on app mount → stores new `accessToken` | Reads cookie automatically |
|
||||
| Logout | `POST /api/v1/auth/revoke` → clears in-memory token | Server clears cookie |
|
||||
| Token refresh (interceptor) | `POST /api/v1/auth/refresh` on 401 → retry original request | Centralised in ApiClient |
|
||||
|
||||
### Availability Service (`frontend/src/api/useAvailability.ts`)
|
||||
|
||||
| Hook | Query key | Endpoint | Stale time |
|
||||
|------|-----------|----------|-----------|
|
||||
| `useAvailabilityStatus()` | `['availability', 'status']` | `GET /api/v1/availability/status` | 30s |
|
||||
|
||||
### Users Service (`frontend/src/api/useUsers.ts`)
|
||||
|
||||
| Hook | Query key | Endpoint | Notes |
|
||||
|------|-----------|----------|-------|
|
||||
| `useUsers()` | `['users']` | `GET /api/v1/users` | Owner/Admin only |
|
||||
| `useInviteUser()` | mutation | `POST /api/v1/users/invite` | Invalidates `['users']` on success |
|
||||
|
||||
### Invitation Service (`frontend/src/api/useInvitation.ts`)
|
||||
|
||||
| Hook | Query key | Endpoint | Notes |
|
||||
|------|-----------|----------|-------|
|
||||
| `useValidateInvitation(token)` | `['invitation', token]` | `GET /api/v1/users/validate-invitation?token=` | Public, no auth |
|
||||
| `useCompleteSetup()` | mutation | `POST /api/v1/users/complete-setup` | Public, no auth |
|
||||
|
||||
### Setup Service (`frontend/src/api/useSetup.ts`)
|
||||
|
||||
| Hook | Query key | Endpoint | Notes |
|
||||
|------|-----------|----------|-------|
|
||||
| `useSetupStatus()` | `['setup', 'status']` | `GET /api/v1/setup/status` | Used by `InitGuard` |
|
||||
| `useCreateOwner()` | mutation | `POST /api/v1/setup/owner` | Used on `/setup` page |
|
||||
|
||||
---
|
||||
|
||||
## Service Interaction Diagram
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
App["App Entry\n(main.tsx)"]
|
||||
InitGuard["InitGuard\n(useSetupStatus)"]
|
||||
AuthCtx["AuthContext\n(session state)"]
|
||||
ApiClient["ApiClient\n(fetch + 401 retry)"]
|
||||
Router["TanStack Router\n(ProtectedRoute + RoleGuard)"]
|
||||
|
||||
subgraph Pages["Page Components"]
|
||||
Login["LoginPage"]
|
||||
Dashboard["DashboardPage\n(useAvailabilityStatus)"]
|
||||
Users["UsersPage\n(useUsers, useInviteUser)"]
|
||||
Invite["InviteCompletePage\n(useValidateInvitation\nuseCompleteSetup)"]
|
||||
Settings["SettingsPage\n(useAvailabilityStatus)"]
|
||||
Profile["ProfilePage"]
|
||||
end
|
||||
|
||||
App --> InitGuard
|
||||
App --> AuthCtx
|
||||
InitGuard -->|initialized| Router
|
||||
Router --> Login
|
||||
Router --> Dashboard
|
||||
Router --> Users
|
||||
Router --> Invite
|
||||
Router --> Settings
|
||||
Router --> Profile
|
||||
|
||||
AuthCtx -->|getAccessToken| ApiClient
|
||||
AuthCtx -->|refresh| ApiClient
|
||||
Dashboard --> ApiClient
|
||||
Users --> ApiClient
|
||||
Invite --> ApiClient
|
||||
Settings --> ApiClient
|
||||
|
||||
style App fill:#CE93D8,stroke:#6A1B9A,color:#000
|
||||
style InitGuard fill:#FFC107,stroke:#F57F17,color:#000
|
||||
style AuthCtx fill:#FFC107,stroke:#F57F17,color:#000
|
||||
style ApiClient fill:#FFC107,stroke:#F57F17,color:#000
|
||||
style Router fill:#4CAF50,stroke:#2E7D32,color:#fff
|
||||
style Login fill:#2196F3,stroke:#0D47A1,color:#fff
|
||||
style Dashboard fill:#2196F3,stroke:#0D47A1,color:#fff
|
||||
style Users fill:#2196F3,stroke:#0D47A1,color:#fff
|
||||
style Invite fill:#2196F3,stroke:#0D47A1,color:#fff
|
||||
style Settings fill:#2196F3,stroke:#0D47A1,color:#fff
|
||||
style Profile fill:#2196F3,stroke:#0D47A1,color:#fff
|
||||
```
|
||||
Reference in New Issue
Block a user