feat: Phase 5 admin features - deployment management UI#23
Merged
Conversation
- Biome 2.5.3: TypeScript/JavaScript linting and formatting - biome.json: Configure Biome with recommended rules, single quotes, 100 char lines - npm scripts: lint, lint:check, format, format:check commands - package.json: Fixed all linting errors (import sorting, unused imports, literal keys) - Dockerfile: Add development stage with node --watch for live reload - docker-compose.yml: Use development target with source volume mounts - docker-compose.prod.yml: Production override with health checks - All 18 tests passing - All linting issues resolved (16 warnings only)
Use environment variables from docker-compose or shell instead of npm script
- README.md: Development quick start and commands - docs/DEVELOPMENT.md: Detailed development guide with: - Prerequisites and quick start - Docker setup and management - Biome linting workflow - TypeScript configuration and imports - Database development guide - Testing instructions - Architecture overview - Common tasks - Troubleshooting
- Update package.json scripts to use bun (bun test, bun run dev, etc.) - Update Dockerfile to use bun instead of node - Update docker-compose.yml comments (already uses correct targets) - Update README.md and DEVELOPMENT.md with bun commands - Fix remaining `as any` type casts in keycloak/index.test.ts - Add DeploymentId import to keycloak/index.test.ts - Auto-fix formatting issues with biome All 18 tests pass with bun All linting checks pass Dev server starts successfully with `bun run dev`
…, Playwright e2e Domain model: - ExternalApp: registers a domain/SaaS app, provisions a Keycloak client - AuthFlow: maps a domain to an app with configurable OAuth flow type - Status lifecycle: pending -> active -> suspended / revoked - Both added to shared/models.ts, core interfaces, InMemoryDeploymentRepository API (9 new endpoints): - POST/GET /deployments/:id/apps - GET/PUT /apps/:appId, PUT /apps/:appId/status - POST/GET /deployments/:id/flows - GET /flows/:flowId Unit tests (16 new, 34 total passing): - src/core/external-apps.test.ts - domain logic (8 tests) - src/admin-ui/external-apps.handler.test.ts - HTTP handler (8 tests) React frontend (src/web/): - Vite + React 18 + react-router-dom + CSS Modules - DeploymentsPage, DeploymentDetailPage, RegisterAppPage - Components: AppCard, StatusBadge, AuthFlowRow, RegisterAppForm - API client (src/web/src/api.ts) with proxy to backend Storybook: - AppCard.stories.tsx - 5 stories (active/pending/suspended/revoked/multi-redirect) - StatusBadge.stories.tsx - all 11 statuses + AllStatuses composite - RegisterAppForm.stories.tsx - 5 states (empty/prefilled/loading/error/multi-env) - AuthFlowRow.stories.tsx - 5 stories including panel composite Playwright e2e (e2e/): - deployments-list.spec.ts - list view and navigation - register-app.spec.ts - full register app workflow - app-lifecycle.spec.ts - activate/suspend/revoke transitions - auth-flows.spec.ts - flow display and multi-flow scenarios Scripts: bun run web, bun run storybook, bun run test:e2e
- Add deployment deletion with confirmation dialog and redirect - Add 3-step deployment creation wizard (Basic Info → Cloud Provider → Review) - Add deployment status transitions with state machine validation - States: draft → provisioning → healthy → drifted → repairing → failed → decommissioned - Enforces valid transitions at API layer with descriptive errors - Change-status dropdown shows only context-appropriate transitions - Confirmation dialogs, loading states, and dismissable error banners - Add frontend utilities: getAvailableTransitions(), canTransitionTo(), getStatusLabel() - Add API client methods: createDeployment(), deleteDeployment(), updateDeploymentStatus() - Add comprehensive test coverage (51 tests total, 0 failures) - 10 status transition tests covering all state paths and error cases - CRUD tests for deployment lifecycle - Fix status dropdown dark theme styling (dark surface background with light text)
There was a problem hiding this comment.
Pull request overview
This PR adds a React/Vite-based admin UI for managing Kloak deployments (create/delete, status transitions, and external app + auth-flow management), and expands the backend HTTP API + core domain model to support those workflows, along with new tooling (Bun/Biome) and Playwright e2e tests.
Changes:
- Introduces a new
src/webfrontend (Vite + React Router) with pages/components for deployments, deployment creation, deployment detail, and external app registration. - Expands backend API routes and core model to support external apps/auth flows and user auth/account linking, plus adds tests.
- Adds Playwright e2e coverage and updates repo tooling/scripts (Bun, Biome, Docker adjustments).
Reviewed changes
Copilot reviewed 80 out of 84 changed files in this pull request and generated 12 comments.
Show a summary per file
| File | Description |
|---|---|
| src/web/vite.config.ts | Adds Vite dev server + /api proxy configuration. |
| src/web/tsconfig.tsbuildinfo | Adds TS build info artifact (should not be committed). |
| src/web/tsconfig.json | Adds TypeScript config for the web package. |
| src/web/src/utils/statusTransitions.ts | Adds deployment status transition helpers for UI. |
| src/web/src/stories/StatusBadge.stories.tsx | Storybook stories for StatusBadge. |
| src/web/src/stories/RegisterAppForm.stories.tsx | Storybook stories for RegisterAppForm. |
| src/web/src/stories/AuthFlowRow.stories.tsx | Storybook stories for AuthFlowRow. |
| src/web/src/stories/AppCard.stories.tsx | Storybook stories for AppCard. |
| src/web/src/pages/SignupPage.tsx | Adds signup UI flow (currently mismatched with backend contract). |
| src/web/src/pages/SignupPage.module.css | Styles for signup page. |
| src/web/src/pages/RegisterAppPage.tsx | Adds “register external app” page wired to API. |
| src/web/src/pages/RegisterAppPage.module.css | Styles for register app page. |
| src/web/src/pages/LoginPage.tsx | Adds login UI flow (currently mismatched with backend/proxy expectations). |
| src/web/src/pages/LoginPage.module.css | Styles for login page. |
| src/web/src/pages/DocsPage.module.css | Styles for docs page. |
| src/web/src/pages/DeploymentsPage.tsx | Adds deployments list UI + “Create Deployment” CTA. |
| src/web/src/pages/DeploymentsPage.module.css | Styles for deployments list. |
| src/web/src/pages/DeploymentDetailPage.tsx | Adds deployment detail UI incl. delete + status transitions + apps/flows panels. |
| src/web/src/pages/DeploymentDetailPage.module.css | Styles for deployment detail page incl. status dropdown and error banner. |
| src/web/src/pages/DeploymentCreationPage.tsx | Adds 3-step deployment creation wizard. |
| src/web/src/pages/DeploymentCreationPage.module.css | Styles for deployment creation wizard. |
| src/web/src/pages/AccountPage.tsx | Adds account linking UI (GitHub/Gmail) + linked account management. |
| src/web/src/pages/AccountPage.module.css | Styles for account page. |
| src/web/src/main.tsx | Frontend bootstrap w/ React Router. |
| src/web/src/index.css | Adds global dark theme tokens and base styles. |
| src/web/src/components/StatusBadge.tsx | Adds shared status badge component for app/deployment statuses. |
| src/web/src/components/StatusBadge.module.css | Styles for status badge variants. |
| src/web/src/components/RegisterAppForm.tsx | Adds register-app form component + parsing helper. |
| src/web/src/components/RegisterAppForm.module.css | Styles for register-app form. |
| src/web/src/components/Layout.tsx | Adds app layout shell (header/nav + main). |
| src/web/src/components/Layout.module.css | Styles for layout shell. |
| src/web/src/components/AuthFlowRow.tsx | Adds auth flow row UI component. |
| src/web/src/components/AuthFlowRow.module.css | Styles for auth flow row. |
| src/web/src/components/AppCard.tsx | Adds external app card UI w/ status actions + copy client id. |
| src/web/src/components/AppCard.module.css | Styles for app card. |
| src/web/src/App.tsx | Defines web routes for the new UI. |
| src/web/src/api.ts | Adds web API client + shared types (currently has TS syntax + 204 handling issues). |
| src/web/package.json | Adds web workspace scripts/deps (vite, storybook, vitest). |
| src/web/index.html | Adds Vite HTML entrypoint. |
| src/web/.storybook/preview.ts | Storybook preview globals (dark backgrounds). |
| src/web/.storybook/main.ts | Storybook config for Vite/React. |
| src/sync/index.ts | Formatting/refactor updates. |
| src/sync/index.test.ts | Formatting updates for sync tests. |
| src/sync/fakes.ts | Formatting updates for sync fakes. |
| src/shared/models.ts | Extends domain model for external apps/auth flows/users (currently has TS union syntax errors). |
| src/orchestrator/index.ts | Formatting/refactor updates. |
| src/keycloak/keycloak.ts | Export ordering tweak. |
| src/keycloak/index.ts | Formatting/refactor updates. |
| src/keycloak/index.test.ts | Refactor test typing/formatting updates. |
| src/infrastructure/index.ts | Formatting updates. |
| src/infrastructure/index.test.ts | Formatting updates. |
| src/infrastructure/in-memory.ts | Formatting updates. |
| src/index.ts | Reorders exports. |
| src/database/sql-repository.ts | Import cleanup/formatting. |
| src/database/migrate.ts | Formatting updates. |
| src/database/index.ts | Export ordering tweak. |
| src/core/external-apps.test.ts | Adds core tests for external apps + auth flows. |
| src/core/auth.test.ts | Adds core auth tests (currently has broken import paths/extensions). |
| src/core/deployments.test.ts | Adds deployments CRUD/status tests (imports missing explicit .ts extensions). |
| src/aws/index.ts | AWS provisioner code formatting/cleanup. |
| src/aws/index.test.ts | Test refactor/cleanup. |
| src/aws/aws.ts | Export ordering tweak. |
| src/admin-ui/server.ts | Adds route pattern matching for new endpoints (currently missing auth/user/oauth patterns). |
| src/admin-ui/handler.ts | Adds handlers for apps/flows/auth/users/oauth endpoints. |
| src/admin-ui/handler.test.ts | Updates handler tests for new behavior/formatting. |
| src/admin-ui/external-apps.handler.test.ts | Adds handler tests for apps/flows endpoints. |
| src/admin-ui/cli.ts | Formatting updates. |
| README.md | Adds development/docs sections and commands. |
| playwright.config.ts | Adds Playwright e2e config with webServer setup. |
| package.json | Switches scripts/tooling to Bun/Biome/Playwright; adds deps. |
| e2e/register-app.spec.ts | Adds e2e tests for registering external apps. |
| e2e/helpers.ts | Adds shared e2e helpers that call the API server. |
| e2e/deployments-list.spec.ts | Adds e2e tests for deployments list navigation. |
| e2e/auth-flows.spec.ts | Adds e2e tests for auth flows display. |
| e2e/app-lifecycle.spec.ts | Adds e2e tests for app status transitions. |
| docs/DEVELOPMENT.md | Adds detailed development guide (includes import-extension convention). |
| Dockerfile | Moves to Bun-based images; adds dev/prod stages and Bun healthcheck. |
| docker-compose.yml | Uses development target + mounts src for live reload. |
| docker-compose.prod.yml | Adds production override compose (healthcheck currently uses node). |
| biome.json | Adds Biome config for formatting/linting. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+11
to
+12
| status:|'draft'|'provisioning'|'healthy'|'drifted'|'repairing'|'failed'| | ||
| 'decommissioned'; |
Comment on lines
+71
to
+76
| if (!res.ok) { | ||
| const err = await res.json().catch(() => ({error: res.statusText})); | ||
| throw new Error((err as {error: string}).error ?? res.statusText); | ||
| } | ||
| return res.json() as Promise<T>; | ||
| } |
Comment on lines
+33
to
44
| function findRoutePattern(path: string): string | null { | ||
| const patterns = [ | ||
| '/deployments/:id/versions', | ||
| '/deployments/:id/status', | ||
| '/deployments/:id/apps', | ||
| '/deployments/:id/flows', | ||
| '/deployments/:id', | ||
| '/deployments', | ||
| '/apps/:appId/status', | ||
| '/apps/:appId', | ||
| '/flows/:flowId', | ||
| ]; |
Comment on lines
+3
to
+6
| import type {CreateDeploymentInput, DeploymentId, DeploymentRepository} from '../shared'; | ||
|
|
||
| import {CoreService, InMemoryDeploymentRepository} from './index'; | ||
|
|
Comment on lines
+46
to
+64
| const response = await fetch('/auth/signup', { | ||
| method: 'POST', | ||
| headers: { 'Content-Type': 'application/json' }, | ||
| body: JSON.stringify({ | ||
| email: form.email, | ||
| password: form.password, | ||
| displayName: form.displayName || undefined, | ||
| }), | ||
| }); | ||
|
|
||
| if (!response.ok) { | ||
| const body = await response.text(); | ||
| throw new Error(body || 'Signup failed'); | ||
| } | ||
|
|
||
| const { user, token } = (await response.json()) as { | ||
| user: { id: string; email: string; deploymentId: string }; | ||
| token: string; | ||
| }; |
Comment on lines
+28
to
+42
| const response = await fetch('/auth/login', { | ||
| method: 'POST', | ||
| headers: { 'Content-Type': 'application/json' }, | ||
| body: JSON.stringify(form), | ||
| }); | ||
|
|
||
| if (!response.ok) { | ||
| const body = await response.text(); | ||
| throw new Error(body || 'Login failed'); | ||
| } | ||
|
|
||
| const { user, token } = (await response.json()) as { | ||
| user: { id: string; email: string; deploymentId: string }; | ||
| token: string; | ||
| }; |
| @@ -0,0 +1 @@ | |||
| {"root":["./src/app.tsx","./src/api.ts","./src/main.tsx","./src/components/appcard.tsx","./src/components/authflowrow.tsx","./src/components/layout.tsx","./src/components/registerappform.tsx","./src/components/statusbadge.tsx","./src/pages/accountpage.tsx","./src/pages/deploymentcreationpage.tsx","./src/pages/deploymentdetailpage.tsx","./src/pages/deploymentspage.tsx","./src/pages/docspage.tsx","./src/pages/loginpage.tsx","./src/pages/registerapppage.tsx","./src/pages/signuppage.tsx","./src/stories/appcard.stories.tsx","./src/stories/authflowrow.stories.tsx","./src/stories/registerappform.stories.tsx","./src/stories/statusbadge.stories.tsx","./src/utils/statustransitions.ts"],"errors":true,"version":"5.9.3"} No newline at end of file | |||
- Auto-fix Biome formatting across all modified files - Remove redeclared local DeploymentStatus type (conflicts with shared import) - Remove unused oauthProviders private field from InMemoryDeploymentRepository - Replace non-null assertion with safe null-check in OAuth callback handler - Fix 204 No Content handling in api.ts (avoid calling res.json() on empty body) - Add missing auth/user/oauth route patterns to server.ts findRoutePattern() - Add explicit .ts extensions to imports in deployments.test.ts and auth.test.ts - Fix broken import path in auth.test.ts (../../../shared -> ../shared/index.ts) - Update CI workflow to use Bun instead of npm (setup-bun, bun install, bun test) - Add lint check step to CI (biome check) - Fix docker-compose.prod.yml healthcheck to use bun instead of node - Add *.tsbuildinfo to .gitignore - Fix LoginPage and SignupPage fetch paths to use /api prefix for Vite proxy
- Fix invalid Deployment status union type syntax in web API model - Add CSS module declaration for web TypeScript builds - Remove tracked tsbuildinfo artifact from repository - Align login/signup forms with backend contract by including deploymentId - Keep signup response handling aligned with user-only response payload - Fix optimistic revoke rollback to restore previous app status - Expand node HTTP adapter route table to include auth/user/oauth/app/flow endpoints - Apply biome formatting updates in touched files
Comment on lines
+126
to
128
| const headers: Record<string, string> = { 'Content-Type': 'application/json' }; | ||
| res.writeHead(httpResponse.status, headers); | ||
| res.end(JSON.stringify(httpResponse.body)); |
Comment on lines
+41
to
+43
| const response = await fetch(`/users/${userId}`, { | ||
| headers: { Authorization: `Bearer ${token}` }, | ||
| }); |
Comment on lines
+66
to
+71
| const response = await fetch(`/users/${user.id}/link-account/${provider}`, { | ||
| method: 'DELETE', | ||
| headers: { | ||
| Authorization: `Bearer ${localStorage.getItem('authToken')}`, | ||
| }, | ||
| }); |
Comment on lines
+97
to
+107
| const response = await fetch(`/oauth/authorize/${provider}`, { | ||
| method: 'POST', | ||
| headers: { | ||
| 'Content-Type': 'application/json', | ||
| Authorization: `Bearer ${localStorage.getItem('authToken')}`, | ||
| }, | ||
| body: JSON.stringify({ | ||
| deploymentId, | ||
| redirectUri, | ||
| }), | ||
| }); |
Comment on lines
+117
to
+130
| ### Commands | ||
|
|
||
| ```bash | ||
| # Check for issues (don't fix) | ||
| npm run lint:check | ||
|
|
||
| # Fix issues automatically | ||
| npm run lint | ||
|
|
||
| # Format code | ||
| npm run format | ||
|
|
||
| # Check formatting | ||
| npm run format:check |
Comment on lines
+143
to
+145
| npm run lint # Fix issues | ||
| npm test # Verify tests pass | ||
| npm run lint:check # Double-check linting |
Comment on lines
+150
to
+153
| Kloak uses TypeScript with `--experimental-transform-types`: | ||
| - Direct TypeScript execution (no build step) | ||
| - `.ts` files run natively on Node.js 25+ | ||
| - Explicit file extensions required in imports (`./index.ts` not `./index`) |
Comment on lines
+344
to
+349
| if (!canTransitionTo(deployment.status, status)) { | ||
| throw new Error( | ||
| `Cannot transition from ${deployment.status} to ${status}. ` + | ||
| `Valid transitions: ${getAvailableTransitions(deployment.status).join(', ')}` | ||
| ); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds comprehensive deployment management features to the Kloak admin UI.
Features
Delete Deployment
Deployment Creation Wizard
/deployments/newDeployment Status Transitions
draft → provisioning → healthy → drifted → repairing → failed → decommissioneddecommissionedis a terminal state (no further transitions)Tests
51 tests, 0 failures:
Files
New:
src/core/deployments.test.ts— CRUD + status transition testssrc/core/auth.test.ts— User auth testssrc/web/src/utils/statusTransitions.ts— State machine utilitiessrc/web/src/pages/DeploymentCreationPage.tsx+ CSSModified:
src/core/index.ts— State machine +markDeploymentStatusvalidationsrc/web/src/api.ts— New API methodssrc/web/src/pages/DeploymentDetailPage.tsx— Status transition UI, error bannersrc/web/src/pages/DeploymentDetailPage.module.css— Dark-theme dropdown stylingsrc/web/src/pages/DeploymentsPage.tsx— Create Deployment button