Skip to content

Commit 671a683

Browse files
committed
modules ui support added
1 parent 85fed29 commit 671a683

File tree

12 files changed

+1532
-3
lines changed

12 files changed

+1532
-3
lines changed

CLAUDE.md

Lines changed: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## Project Overview
6+
7+
This is the **Forkspacer Operator UI** - a React-based web interface for managing Forkspacer Kubernetes operator resources. It's part of a three-component ecosystem:
8+
9+
1. **Forkspacer Operator** - Core Kubernetes operator managing custom resources
10+
2. **API Server** - Backend REST API (default: http://localhost:8421)
11+
3. **Operator UI** (this repo) - Frontend dashboard served via Nginx
12+
13+
The UI communicates with the API Server to manage **Workspaces** and **Modules** (Helm charts, databases, services).
14+
15+
## Development Commands
16+
17+
### Local Development
18+
```bash
19+
# Install dependencies
20+
npm install
21+
22+
# Start dev server (connects to localhost:8421 by default)
23+
make dev
24+
25+
# Or manually with custom API URL
26+
REACT_APP_API_BASE_URL=http://localhost:8421 npm start
27+
```
28+
29+
### Testing
30+
```bash
31+
# Run tests in watch mode
32+
npm test
33+
34+
# Run tests once (for CI)
35+
npm test -- --watchAll=false
36+
37+
# Run tests with coverage
38+
npm test -- --coverage --watchAll=false
39+
```
40+
41+
### Building
42+
```bash
43+
# Production build
44+
npm run build
45+
46+
# Docker build (production - uses relative /api/v1 paths)
47+
docker build -t operator-ui:local .
48+
49+
# Docker build with custom API URL (for standalone deployments)
50+
docker build --build-arg REACT_APP_API_BASE_URL=http://your-api:8421 -t operator-ui:local .
51+
```
52+
53+
### Releases
54+
```bash
55+
# Create and push a version tag to trigger automated release
56+
git tag v0.1.0
57+
git push origin v0.1.0
58+
# This triggers .github/workflows/release.yml which:
59+
# 1. Automatically updates helm/Chart.yaml and helm/values.yaml versions (via make update-version)
60+
# 2. Builds and pushes Docker image to ghcr.io with version tag
61+
# 3. Packages and publishes Helm chart to GitHub Pages
62+
# 4. Notifies parent forkspacer/forkspacer repo of new version
63+
```
64+
65+
## Architecture
66+
67+
### Component Structure
68+
```
69+
src/
70+
├── components/ # React components
71+
│ ├── WorkspaceList.tsx # Main workspace listing & management
72+
│ ├── WorkspaceCard.tsx # Individual workspace card UI
73+
│ ├── WorkspaceDetailsModal.tsx # Workspace detail view
74+
│ ├── CreateWorkspaceModal.tsx # Workspace creation form
75+
│ ├── ModuleCatalog.tsx # Module catalog browser with grid/search
76+
│ ├── ModuleCatalogCard.tsx # Individual module card in catalog
77+
│ ├── RepositoryManager.tsx # Repository add/remove/enable UI
78+
│ ├── SearchAndFilter.tsx # Search/filter controls
79+
│ ├── CustomCheckbox.tsx # Reusable checkbox component
80+
│ └── Toast.tsx # Toast notification system
81+
├── services/
82+
│ ├── api.ts # API client (backend communication)
83+
│ └── repositoryService.ts # Module repository management
84+
├── types/
85+
│ ├── workspace.ts # Workspace-related TypeScript types
86+
│ ├── module.ts # Module-related TypeScript types
87+
│ └── catalog.ts # Module catalog types
88+
└── App.tsx # Root component with tab navigation
89+
```
90+
91+
### API Communication
92+
93+
**API Base URL Configuration:**
94+
- Build-time: Set `REACT_APP_API_BASE_URL` env var or Docker build arg
95+
- Default (production): Uses relative path `/api/v1` (assumes ingress/nginx proxy)
96+
- Development: `make dev` sets to `http://localhost:8421`
97+
98+
**API Service** (`src/services/api.ts`):
99+
- Singleton class `ApiService` exported as `apiService`
100+
- All backend communication goes through this service
101+
- Handles error parsing and API response format (`{success: {...}, error: {...}}`)
102+
103+
**Key API Methods:**
104+
- Workspaces: `listWorkspaces()`, `createWorkspace()`, `deleteWorkspace()`, `updateWorkspace()`, `hibernateWorkspace()`, `wakeWorkspace()`
105+
- Modules: `listModules()`, `createModule()`, `updateModule()`, `deleteModule()`
106+
- Module Catalog: `fetchModuleCatalog(catalogUrl)` - Fetches module catalog from GitHub (default: https://raw.githubusercontent.com/forkspacer/modules/main/index.json)
107+
108+
### Deployment Architecture
109+
110+
**Production Setup (with Nginx proxy):**
111+
```
112+
User Request → Nginx (port 80)
113+
├─> /api/v1/* → API Server (proxied via nginx.conf.template)
114+
└─> /* → React static files
115+
```
116+
117+
The nginx configuration (`nginx.conf.template`) uses envsubst to inject `API_SERVER_URL` at runtime via `docker-entrypoint.sh`.
118+
119+
**Build Arguments:**
120+
- `REACT_APP_API_BASE_URL`: API endpoint for React app
121+
- Empty/unset: Uses relative `/api/v1` (expects Nginx proxy)
122+
- Set: Uses absolute URL (e.g., `http://api-server:8421`)
123+
124+
## Key Concepts
125+
126+
### Workspaces
127+
Represents a logical environment/namespace in the operator. Fields:
128+
- `name`, `namespace`: Kubernetes identifiers
129+
- `phase`: Current state (e.g., "Ready", "Pending")
130+
- `type`: Connection type ("in-cluster" or "kubeconfig")
131+
- `hibernated`: Whether workspace is hibernated
132+
- `message`: Status message
133+
134+
### Modules
135+
Deployable components (typically Helm charts) managed by the operator. Fields:
136+
- `name`, `namespace`: Kubernetes identifiers
137+
- `phase`: ModulePhase enum ("ready" | "installing" | "uninstalling" | "sleeping" | "sleeped" | "resuming" | "failed")
138+
- `workspace`: Reference to parent workspace
139+
- `source`: Module definition (raw YAML or HTTP URL)
140+
- `config`: Key-value configuration parameters
141+
142+
Modules define:
143+
- Dynamic form fields for configuration
144+
- Helm chart repository, name, version
145+
- Outputs (connection strings, credentials)
146+
- Resource usage (CPU/memory)
147+
148+
### Module Catalog
149+
The UI includes a **Module Catalog** browser for discovering and installing pre-built modules:
150+
- Features: Grid layout, search, category filtering
151+
- Categories: Cache, Database, Messaging, Storage, Monitoring
152+
- Each catalog entry includes: displayName, description, icon, tags, versions, maintainers
153+
- Tab-based navigation: "Workspaces" tab shows deployed resources, "Module Catalog" tab shows available modules to install
154+
155+
### Repository Management (like `helm repo add`)
156+
The UI supports adding multiple module repositories:
157+
- **Add/Remove Repos**: Click "Repositories" button to manage sources
158+
- **localStorage Persistence**: Added repos are saved in browser localStorage
159+
- **Environment Variable**: Set `MODULES_REPO` to pre-configure repos for container deployments
160+
```bash
161+
# Docker runtime env
162+
docker run -e MODULES_REPO='[{"name":"official","url":"https://raw.githubusercontent.com/forkspacer/modules/main/index.json","enabled":true}]' operator-ui
163+
164+
# Note: Create React App requires REACT_APP_ prefix at build time, so use MODULES_REPO at runtime or inject it via nginx/entrypoint
165+
```
166+
- **Multi-Repo Support**: Fetches and merges catalogs from all enabled repositories
167+
- **Default Repo**: If no repos configured, defaults to https://github.com/forkspacer/modules
168+
- Service: `src/services/repositoryService.ts` handles all repo operations
169+
170+
## Testing Strategy
171+
172+
- Uses `@testing-library/react` for component testing
173+
- Tests configured with `react-scripts test` (Jest)
174+
- Setup file: `src/setupTests.ts`
175+
- Test files: `*.test.tsx` pattern
176+
177+
## CI/CD Workflows
178+
179+
### CI Workflow (`.github/workflows/ci.yml`)
180+
- Triggers: PRs and pushes to `main`
181+
- Jobs: Lint, Test, Build, Docker Build (no push)
182+
183+
### Release Workflow (`.github/workflows/release.yml`)
184+
- Trigger: Git tags matching `v*` (e.g., `v0.1.0`)
185+
- Automatically updates versions via `make update-version`:
186+
- `helm/Chart.yaml`: Sets `version` (e.g., `0.1.0`) and `appVersion` (e.g., `"v0.1.0"`)
187+
- `helm/values.yaml`: Sets `image.tag` to match version
188+
- Note: These updates happen in CI environment only, not committed back to repo
189+
- Builds and publishes Docker image to `ghcr.io/forkspacer/operator-ui` with two tags:
190+
- Versioned: `v0.1.0`
191+
- Latest: `latest`
192+
- Packages Helm chart and deploys to GitHub Pages (https://forkspacer.github.io/operator-ui)
193+
- Triggers repository dispatch event to notify parent `forkspacer/forkspacer` repo
194+
195+
### CLA Workflow (`.github/workflows/cla.yml`)
196+
- Ensures contributors sign the Contributor License Agreement
197+
- Auto-comments on PRs if not signed
198+
199+
## Helm Chart
200+
201+
Located in `helm/` directory:
202+
- Deployable as standalone chart or part of unified Forkspacer chart
203+
- Configured via `helm/values.yaml`
204+
- Makefile targets:
205+
- `make helm-package`: Package chart into .tgz file
206+
- `make build-charts-site`: Create GitHub Pages site with all chart versions
207+
- `make update-version VERSION=v0.1.0`: Update Chart.yaml and values.yaml versions (used by release workflow)
208+
209+
## TypeScript Configuration
210+
211+
- Target: ES5
212+
- Strict mode enabled
213+
- JSX: react-jsx (React 18+)
214+
- Module: ESNext with node resolution
215+
216+
## Common Patterns
217+
218+
### Adding a New API Endpoint
219+
1. Update TypeScript types in `src/types/`
220+
2. Add method to `ApiService` class in `src/services/api.ts`
221+
3. Use the `apiService` singleton in components
222+
223+
### Creating a New Component
224+
1. Add to `src/components/`
225+
2. Use functional components with hooks
226+
3. Import types from `src/types/`
227+
4. Use `apiService` for backend calls
228+
229+
### Error Handling
230+
API errors are parsed in `api.ts` and thrown as Error objects. Components should catch and display errors using the Toast component pattern.
231+
232+
### State Management
233+
Currently uses React built-in state (useState, useEffect). No external state management library.

src/App.css

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,46 @@ body {
1919
flex-direction: column;
2020
width: 100%;
2121
min-height: 100vh;
22-
padding: 32px;
2322
background: #0a0a0a;
2423
}
2524

25+
.app-tabs {
26+
display: flex;
27+
gap: 4px;
28+
padding: 16px 32px 0 32px;
29+
background: #0a0a0a;
30+
border-bottom: 1px solid #2a2a2a;
31+
}
32+
33+
.app-tab {
34+
padding: 12px 24px;
35+
background: none;
36+
border: none;
37+
color: #9ca3af;
38+
font-size: 15px;
39+
font-weight: 500;
40+
cursor: pointer;
41+
border-bottom: 2px solid transparent;
42+
transition: all 0.2s ease;
43+
position: relative;
44+
top: 1px;
45+
}
46+
47+
.app-tab:hover {
48+
color: #e5e7eb;
49+
background: rgba(255, 255, 255, 0.02);
50+
}
51+
52+
.app-tab.active {
53+
color: #667eea;
54+
border-bottom-color: #667eea;
55+
}
56+
57+
.app-content {
58+
flex: 1;
59+
padding: 32px;
60+
}
61+
2662
h1 {
2763
font-size: 32px;
2864
font-weight: 700;

src/App.tsx

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,34 @@
1-
import React from 'react';
1+
import React, { useState } from 'react';
22
import './App.css';
33
import { WorkspaceList } from './components/WorkspaceList';
4+
import { ModuleCatalog } from './components/ModuleCatalog';
5+
6+
type Tab = 'workspaces' | 'catalog';
47

58
function App() {
9+
const [activeTab, setActiveTab] = useState<Tab>('workspaces');
10+
611
return (
712
<div className="app-container">
8-
<WorkspaceList />
13+
<div className="app-tabs">
14+
<button
15+
className={`app-tab ${activeTab === 'workspaces' ? 'active' : ''}`}
16+
onClick={() => setActiveTab('workspaces')}
17+
>
18+
Workspaces
19+
</button>
20+
<button
21+
className={`app-tab ${activeTab === 'catalog' ? 'active' : ''}`}
22+
onClick={() => setActiveTab('catalog')}
23+
>
24+
Module Catalog
25+
</button>
26+
</div>
27+
28+
<div className="app-content">
29+
{activeTab === 'workspaces' && <WorkspaceList />}
30+
{activeTab === 'catalog' && <ModuleCatalog />}
31+
</div>
932
</div>
1033
);
1134
}

0 commit comments

Comments
 (0)