113 lines
4.6 KiB
Markdown
113 lines
4.6 KiB
Markdown
# 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
|
|
```
|