First swing.

This commit is contained in:
2025-10-12 17:44:54 +00:00
parent 33454bc4e1
commit e5bd3c36f5
106 changed files with 7592 additions and 1270 deletions

View File

@@ -0,0 +1,470 @@
# Wild Cloud Web App Routing Contract
## Front Matter
**Module Name**: `routing`
**Module Type**: Infrastructure
**Version**: 1.0.0
**Status**: Draft
**Last Updated**: 2025-10-12
**Owner**: Wild Cloud Development Team
### Dependencies
- `react`: ^19.1.0
- `react-router`: ^7.0.0 (to be added)
### Consumers
- All page components within Wild Cloud Web App
- Navigation components (AppSidebar)
- Instance context management
- External navigation links
## Purpose
This module defines the routing system for the Wild Cloud web application, providing declarative navigation between pages, URL-based state management, and integration with the existing instance context system.
## Public API
### Router Configuration
The routing system provides a centralized router configuration that manages all application routes.
#### Primary Routes
```typescript
interface RouteDefinition {
path: string;
element: React.ComponentType;
loader?: () => Promise<unknown>;
errorElement?: React.ComponentType;
}
```
**Root Route**:
- **Path**: `/`
- **Purpose**: Landing page and instance selector
- **Authentication**: None required
- **Data Loading**: None
**Instance Routes**:
- **Path Pattern**: `/instances/:instanceId/*`
- **Purpose**: All instance-specific pages
- **Authentication**: Instance must exist
- **Data Loading**: Instance configuration loaded at this level
#### Instance-Scoped Routes
All routes under `/instances/:instanceId/`:
| Path | Purpose | Data Dependencies |
|------|---------|-------------------|
| `dashboard` | Overview and quick status | Instance config, cluster status |
| `operations` | Operation monitoring and logs | Active operations list |
| `cluster/health` | Cluster health metrics | Node status, etcd health |
| `cluster/access` | Kubeconfig/Talosconfig download | Instance credentials |
| `secrets` | Secrets management interface | Instance secrets (redacted) |
| `services` | Base services management | Installed services list |
| `utilities` | Utilities panel | Available utilities |
| `cloud` | Cloud configuration | Cloud settings |
| `dns` | DNS management | DNS configuration |
| `dhcp` | DHCP management | DHCP configuration |
| `pxe` | PXE boot configuration | PXE assets and config |
| `infrastructure` | Cluster nodes | Node list and status |
| `cluster` | Cluster services | Kubernetes services |
| `apps` | Application management | Installed apps |
| `advanced` | Advanced settings | System configuration |
### Navigation Hooks
#### useNavigate
```typescript
function useNavigate(): NavigateFunction;
interface NavigateFunction {
(to: string | number, options?: NavigateOptions): void;
}
interface NavigateOptions {
replace?: boolean;
state?: unknown;
}
```
**Purpose**: Programmatic navigation within the application.
**Examples**:
```typescript
// Navigate to a specific instance dashboard
navigate('/instances/prod-cluster/dashboard');
// Navigate back
navigate(-1);
// Replace current history entry
navigate('/instances/prod-cluster/operations', { replace: true });
```
**Error Conditions**:
- Invalid paths are handled by error boundary
- Missing instance IDs redirect to root
#### useParams
```typescript
function useParams<T extends Record<string, string>>(): T;
```
**Purpose**: Access URL parameters, primarily for extracting instance ID.
**Example**:
```typescript
const { instanceId } = useParams<{ instanceId: string }>();
```
**Error Conditions**:
- Returns undefined for missing parameters
- Type safety through TypeScript generics
#### useLocation
```typescript
interface Location {
pathname: string;
search: string;
hash: string;
state: unknown;
key: string;
}
function useLocation(): Location;
```
**Purpose**: Access current location information for conditional rendering or analytics.
#### useSearchParams
```typescript
function useSearchParams(): [
URLSearchParams,
(nextInit: URLSearchParams | ((prev: URLSearchParams) => URLSearchParams)) => void
];
```
**Purpose**: Read and write URL query parameters for filters, sorting, and view state.
**Example**:
```typescript
const [searchParams, setSearchParams] = useSearchParams();
const view = searchParams.get('view') || 'grid';
setSearchParams({ view: 'list' });
```
### Link Component
```typescript
interface LinkProps {
to: string;
replace?: boolean;
state?: unknown;
children: React.ReactNode;
className?: string;
}
function Link(props: LinkProps): JSX.Element;
```
**Purpose**: Declarative navigation component for user-triggered navigation.
**Example**:
```typescript
<Link to="/instances/prod-cluster/dashboard">
Go to Dashboard
</Link>
```
**Behavior**:
- Prevents default browser navigation
- Supports keyboard navigation (Enter)
- Maintains browser history
- Supports Ctrl/Cmd+Click to open in new tab
### NavLink Component
```typescript
interface NavLinkProps extends LinkProps {
caseSensitive?: boolean;
end?: boolean;
className?: string | ((props: { isActive: boolean; isPending: boolean }) => string);
style?: React.CSSProperties | ((props: { isActive: boolean; isPending: boolean }) => React.CSSProperties);
}
function NavLink(props: NavLinkProps): JSX.Element;
```
**Purpose**: Navigation links that are aware of their active state.
**Example**:
```typescript
<NavLink
to="/instances/prod-cluster/dashboard"
className={({ isActive }) => isActive ? 'active-nav-link' : 'nav-link'}
>
Dashboard
</NavLink>
```
## Data Models
### Route Parameters
```typescript
interface InstanceRouteParams {
instanceId: string;
}
```
**Field Specifications**:
- `instanceId`: String identifier for the instance
- **Required**: Yes
- **Format**: Alphanumeric with hyphens, 1-64 characters
- **Validation**: Must correspond to an existing instance
- **Example**: `"prod-cluster"`, `"staging-env"`
### Navigation State
```typescript
interface NavigationState {
from?: string;
returnTo?: string;
[key: string]: unknown;
}
```
**Purpose**: Preserve state across navigation, such as return URLs or form data.
**Example**:
```typescript
navigate('/instances/prod-cluster/secrets', {
state: { returnTo: '/instances/prod-cluster/dashboard' }
});
```
## Error Model
### Route Errors
| Error Code | Condition | User Impact | Recovery Action |
|------------|-----------|-------------|-----------------|
| `ROUTE_NOT_FOUND` | Path does not match any route | 404 page displayed | Redirect to root or show available routes |
| `INSTANCE_NOT_FOUND` | Instance ID in URL does not exist | Error boundary with message | Redirect to instance selector at `/` |
| `INVALID_INSTANCE_ID` | Instance ID format invalid | Validation error displayed | Show error message, redirect to `/` |
| `NAVIGATION_CANCELLED` | User cancelled pending navigation | No visible change | Continue at current route |
| `LOADER_ERROR` | Route data loader failed | Error boundary with retry option | Show error, allow retry or navigate away |
### Error Response Format
```typescript
interface RouteError {
code: string;
message: string;
status?: number;
cause?: Error;
}
```
**Example**:
```typescript
{
code: "INSTANCE_NOT_FOUND",
message: "Instance 'unknown-instance' does not exist",
status: 404
}
```
## Performance Characteristics
### Route Transition Times
- **Static Routes**: < 50ms (no data loading)
- **Instance Routes**: < 200ms (with instance config loading)
- **Heavy Data Routes**: < 500ms (with large data sets)
### Bundle Size
- **Router Core**: ~45KB (minified)
- **Navigation Components**: ~5KB
- **Per-Route Code Splitting**: Enabled by default
### Memory Usage
- **History Stack**: O(n) where n is number of navigation entries
- **Route Cache**: Configurable, default 10 entries
- **Cleanup**: Automatic on unmount
## Configuration Requirements
### Environment Variables
None required. All routing is handled client-side.
### Route Configuration
```typescript
interface RouterConfig {
basename?: string;
future?: {
v7_startTransition?: boolean;
v7_relativeSplatPath?: boolean;
};
}
```
**basename**: Optional base path for deployment in subdirectories
- **Default**: `"/"`
- **Example**: `"/app"` if deployed to `example.com/app/`
## Conformance Criteria
### Functional Requirements
1. **F-ROUTE-01**: Router SHALL render correct component for each defined route
2. **F-ROUTE-02**: Router SHALL extract instance ID from URL parameters
3. **F-ROUTE-03**: Navigation hooks SHALL update browser history
4. **F-ROUTE-04**: Back/forward browser buttons SHALL work correctly
5. **F-ROUTE-05**: Invalid routes SHALL display error boundary
6. **F-ROUTE-06**: Instance routes SHALL validate instance existence
7. **F-ROUTE-07**: Link components SHALL support keyboard navigation
8. **F-ROUTE-08**: Routes SHALL support lazy loading of components
### Non-Functional Requirements
1. **NF-ROUTE-01**: Route transitions SHALL complete in < 200ms for cached routes
2. **NF-ROUTE-02**: Router SHALL support browser back/forward without page reload
3. **NF-ROUTE-03**: Navigation SHALL preserve scroll position when appropriate
4. **NF-ROUTE-04**: Router SHALL be compatible with React 19.1+
5. **NF-ROUTE-05**: Routes SHALL be defined declaratively in configuration
6. **NF-ROUTE-06**: Router SHALL integrate with existing ErrorBoundary
7. **NF-ROUTE-07**: Navigation SHALL work with InstanceContext
### Integration Requirements
1. **I-ROUTE-01**: Router SHALL integrate with InstanceContext
2. **I-ROUTE-02**: AppSidebar SHALL use routing for navigation
3. **I-ROUTE-03**: All page components SHALL be routed
4. **I-ROUTE-04**: Router SHALL integrate with React Query for data loading
5. **I-ROUTE-05**: Router SHALL support Vite code splitting
## API Stability
**Versioning Scheme**: Semantic Versioning (SemVer)
**Stability Level**: Stable (1.0.0+)
**Breaking Changes**:
- Route path changes require major version bump
- Hook signature changes require major version bump
- Added routes or optional parameters are minor version bumps
**Deprecation Policy**:
- Deprecated routes supported for 2 minor versions
- Console warnings for deprecated route usage
- Migration guide provided for breaking changes
## Security Considerations
### Route Protection
Instance routes SHALL verify:
1. Instance ID exists in available instances
2. User has permission to access instance (future)
### XSS Prevention
- All route parameters SHALL be sanitized
- User-provided navigation state SHALL be validated
- No executable code in route parameters
### CSRF Protection
Not applicable - all navigation is client-side without authentication tokens.
## Browser Compatibility
**Supported Browsers**:
- Chrome/Edge: Last 2 versions
- Firefox: Last 2 versions
- Safari: Last 2 versions
**Required Browser APIs**:
- History API
- URL API
- ES6+ JavaScript features
## Examples
### Basic Navigation
```typescript
// In a component
import { useNavigate } from 'react-router';
function InstanceCard({ instance }) {
const navigate = useNavigate();
const handleClick = () => {
navigate(`/instances/${instance.id}/dashboard`);
};
return <button onClick={handleClick}>Open {instance.name}</button>;
}
```
### Using Route Parameters
```typescript
// In an instance page
import { useParams } from 'react-router';
import { useInstanceContext } from '../hooks';
function DashboardPage() {
const { instanceId } = useParams<{ instanceId: string }>();
const { setCurrentInstance } = useInstanceContext();
useEffect(() => {
setCurrentInstance(instanceId);
}, [instanceId, setCurrentInstance]);
return <div>Dashboard for {instanceId}</div>;
}
```
### Sidebar Integration
```typescript
// AppSidebar using NavLink
import { NavLink } from 'react-router';
function AppSidebar() {
const { instanceId } = useParams();
return (
<nav>
<NavLink
to={`/instances/${instanceId}/dashboard`}
className={({ isActive }) => isActive ? 'active' : ''}
>
Dashboard
</NavLink>
</nav>
);
}
```
## Version History
| Version | Date | Changes |
|---------|------|---------|
| 1.0.0 | 2025-10-12 | Initial contract definition |