Files

4.6 KiB

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: IConfigurationCors: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

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