6.0 KiB
6.0 KiB
Component Methods — CMS Frontend
Note
: Detailed business rules and logic are defined per-unit in the Functional Design stage (CONSTRUCTION phase). This document captures method signatures, inputs, outputs, and high-level purpose only.
Backend — AuthController (updated)
// POST /api/v1/auth/login
// Body: LoginRequest { Email, Password }
// Sets: Set-Cookie: refreshToken=<token>; HttpOnly; Secure; SameSite=Strict; Path=/api/v1/auth
// Returns: { accessToken: string, expiresAt: datetime, user: { id, email, name, role, isActive } }
Task<IActionResult> Login(LoginRequest request)
// POST /api/v1/auth/refresh
// Reads: Cookie: refreshToken
// Sets: Set-Cookie: refreshToken=<newToken>; HttpOnly; Secure; SameSite=Strict; Path=/api/v1/auth
// Returns: { accessToken: string, expiresAt: datetime, user: { id, email, name, role, isActive } }
Task<IActionResult> Refresh() // no body parameter — reads from cookie
// POST /api/v1/auth/revoke
// Requires: Authorization: Bearer <accessToken>
// Reads: Cookie: refreshToken
// Clears: Set-Cookie: refreshToken=; Expires=epoch; Path=/api/v1/auth
// Returns: 204 No Content
Task<IActionResult> Revoke() // no body parameter — reads from cookie
Backend — ServiceCollectionExtensions (updated)
// Registers CORS policy "FrontendPolicy" using Cors:AllowedOrigins from configuration
// Called in AddCoreInfrastructure()
static void AddCorsFrontendPolicy(this IServiceCollection services, IConfiguration configuration)
// Adds app.UseCors("FrontendPolicy") to the pipeline
// Called in Program.cs after UseExceptionHandler, before UseAuthentication
static void UseFrontendCors(this WebApplication app)
Frontend — ApiClient (frontend/src/api/client.ts)
// Base configured fetch wrapper; sets Authorization header and credentials: 'include'
// Retries once on 401 after calling authContext.refresh()
function createApiClient(getAccessToken: () => string | null, refresh: () => Promise<void>): ApiClient
// Typed GET request
async function get<T>(path: string, options?: RequestInit): Promise<T>
// Typed POST request
async function post<T>(path: string, body: unknown, options?: RequestInit): Promise<T>
Frontend — AuthContext (frontend/src/auth/AuthContext.tsx)
interface AuthContextValue {
isAuthenticated: boolean
isLoading: boolean // true during initial session restore
user: User | null // { id, email, name, role, isActive }
accessToken: string | null // in-memory only, never in localStorage
login(email: string, password: string): Promise<void>
logout(): Promise<void>
refresh(): Promise<void> // called by ApiClient on 401; restores token from cookie
}
// Hook
function useAuth(): AuthContextValue
Frontend — Route Guards
// ProtectedRoute: renders children if authenticated, redirects to /login otherwise
// Used as a TanStack Router beforeLoad or as a wrapper component
function ProtectedRoute({ children }: { children: ReactNode }): JSX.Element
// RoleGuard: renders children if user.role is in allowedRoles, redirects to /403 otherwise
function RoleGuard({ allowedRoles, children }: { allowedRoles: string[], children: ReactNode }): JSX.Element
// InitGuard: checks GET /setup/status on app load; redirects to /setup if not initialized
function InitGuard({ children }: { children: ReactNode }): JSX.Element
Frontend — TanStack Query hooks (frontend/src/api/)
// Fetch current availability status; staleTime: 30s
function useAvailabilityStatus(): UseQueryResult<AvailabilityStatusResponse>
// Fetch user list; staleTime: 60s
function useUsers(): UseQueryResult<User[]>
// Invite a user; invalidates useUsers on success
function useInviteUser(): UseMutationResult<InviteResponse, Error, InviteUserRequest>
// Complete invitation setup (public, no auth required)
function useCompleteSetup(): UseMutationResult<void, Error, CompleteSetupRequest>
// Validate invitation token (public)
function useValidateInvitation(token: string): UseQueryResult<InvitationValidationResponse>
Frontend — Page component signatures
// All page components are default exports used as TanStack Router route components
export default function LoginPage(): JSX.Element
export default function SetupPage(): JSX.Element
export default function InviteCompletePage(): JSX.Element // reads token from search params
export default function DashboardPage(): JSX.Element
export default function UsersPage(): JSX.Element
export default function ProfilePage(): JSX.Element
export default function SettingsPage(): JSX.Element
export default function CmsPage(): JSX.Element
export default function NotFoundPage(): JSX.Element
export default function AccessDeniedPage(): JSX.Element
Frontend — Shared components
// Availability badge with colour-coding
function AvailabilityStatusBadge({ status, message }: {
status: 'Available' | 'Maintenance' | 'Unavailable'
message?: string
}): JSX.Element
// Two-step invite dialog
function InviteUserDialog({ open, onClose }: {
open: boolean
onClose: () => void
}): JSX.Element
// Error boundary — class component wrapping children
class ErrorBoundary extends React.Component<{
fallback?: ReactNode
children: ReactNode
}>
TypeScript Type Definitions (frontend/src/api/types.ts)
interface User {
id: string
email: string
name: string
role: 'Owner' | 'Admin' | 'User'
isActive: boolean
}
interface AuthResponse {
accessToken: string
expiresAt: string // ISO datetime
user: User
}
interface AvailabilityStatusResponse {
status: 'Available' | 'Maintenance' | 'Unavailable'
checkedAt: string // ISO datetime
message?: string
}
interface InviteUserRequest {
email: string
role: 'Admin' | 'User'
}
interface InviteResponse {
inviteLink: string
}
interface CompleteSetupRequest {
token: string
displayName: string
password: string
}
interface InvitationValidationResponse {
valid: boolean
email: string
role: string
}