Overview
Orphelix follows Next.js 15 App Router conventions with a clear separation of concerns. The project is organized into distinct layers for frontend, backend, data, and utilities.app/
Next.js App Router pages and API routes
lib/
Reusable utilities, hooks, and business logic
types/
TypeScript type definitions
public/
Static assets (images, logos, fonts)
Directory Structure
Root Level
Copy
orphelix/
├── app/ # Main application directory
│ ├── app/ # Next.js App Router (pages + API)
│ ├── lib/ # Business logic & utilities
│ ├── types/ # TypeScript types
│ ├── public/ # Static assets
│ ├── tests/ # E2E tests (Playwright)
│ ├── __tests__/ # Unit tests (Vitest)
│ ├── package.json # Dependencies
│ ├── tsconfig.json # TypeScript config
│ ├── next.config.mjs # Next.js config
│ ├── vitest.config.ts # Vitest config
│ └── playwright.config.ts # Playwright config
├── docs/ # Mintlify documentation
├── .github/ # GitHub workflows & config
├── LICENSE # License file
└── README.md # Project README
App Directory (app/)
App Router (app/app/)
Next.js 15 App Router with file-based routing:
Copy
app/app/
├── page.tsx # Dashboard home page (/)
├── layout.tsx # Root layout with theme provider
├── globals.css # Global styles
├── favicon.ico # Favicon
│
├── deployments/ # Deployments pages
│ ├── page.tsx # List view (/deployments)
│ └── [name]/ # Dynamic route
│ └── page.tsx # Detail view (/deployments/nginx)
│
├── pods/ # Pods pages
│ ├── page.tsx # List view
│ └── [name]/
│ └── page.tsx # Pod detail + logs
│
├── nodes/ # Nodes pages
│ ├── page.tsx # Node list
│ └── [name]/
│ └── page.tsx # Node detail
│
├── services/ # Services pages
├── ingress/ # Ingress pages
├── configmaps/ # ConfigMaps pages
├── secrets/ # Secrets pages
├── namespaces/ # Namespaces pages
├── statefulsets/ # StatefulSets pages
├── daemonsets/ # DaemonSets pages
├── jobs/ # Jobs pages
├── cronjobs/ # CronJobs pages
├── events/ # Events page
├── labels/ # Labels explorer
├── topology/ # Topology graph
├── settings/ # Settings page
├── repo-browser/ # GitHub repo browser
│
├── demo/ # Demo mode routes (mirrors above)
│ ├── deployments/
│ ├── pods/
│ └── [...slug]/ # Catch-all for demo routes
│
├── api/ # API Routes (see below)
│
└── components/ # React components
├── layout/ # Layout components
├── dashboard/ # Dashboard components
├── common/ # Shared components
├── deployments/ # Deployment-specific
├── pods/ # Pod-specific
├── nodes/ # Node-specific
├── services/ # Service-specific
├── configmaps/ # ConfigMap-specific
├── secrets/ # Secret-specific
├── repo-browser/ # GitHub integration
├── topology/ # Topology graph
├── settings/ # Settings components
├── metrics/ # Charts & metrics
└── theme-provider.tsx # Theme context provider
API Routes (app/app/api/)
RESTful API endpoints following Next.js conventions:
Copy
app/app/api/
├── deployments/
│ ├── route.ts # GET /api/deployments
│ └── [name]/
│ ├── route.ts # GET /api/deployments/:name
│ ├── restart/
│ │ └── route.ts # POST /api/deployments/:name/restart
│ ├── pods/
│ │ └── route.ts # GET /api/deployments/:name/pods
│ └── events/
│ └── route.ts # GET /api/deployments/:name/events
│
├── pods/
│ ├── route.ts # GET /api/pods
│ └── [name]/
│ ├── route.ts # GET /api/pods/:name
│ ├── logs/
│ │ └── route.ts # GET /api/pods/:name/logs
│ ├── restart/
│ │ └── route.ts # POST /api/pods/:name/restart
│ └── events/
│ └── route.ts # GET /api/pods/:name/events
│
├── nodes/
│ ├── route.ts # GET /api/nodes
│ └── [name]/
│ ├── route.ts # GET /api/nodes/:name
│ ├── pods/
│ │ └── route.ts # GET /api/nodes/:name/pods
│ └── events/
│ └── route.ts # GET /api/nodes/:name/events
│
├── services/ # Service endpoints
├── ingress/ # Ingress endpoints
├── configmaps/ # ConfigMap endpoints
├── secrets/ # Secret endpoints
├── namespaces/ # Namespace endpoints
├── statefulsets/ # StatefulSet endpoints
├── daemonsets/ # DaemonSet endpoints
├── jobs/ # Job endpoints
├── cronjobs/ # CronJob endpoints
├── events/
│ └── route.ts # GET /api/events
├── hpa/
│ └── route.ts # GET /api/hpa
├── labels/
│ └── route.ts # GET /api/labels
│
├── dashboard/
│ └── summary/
│ └── route.ts # GET /api/dashboard/summary
│
├── metrics/
│ └── pods/
│ └── route.ts # GET /api/metrics/pods
│
├── stream/
│ └── route.ts # GET /api/stream (SSE)
│
├── contexts/
│ └── route.ts # GET /api/contexts
│
├── test-connection/
│ └── route.ts # GET /api/test-connection
│
├── cluster-health/
│ └── route.ts # GET /api/cluster-health
│
├── github/
│ ├── repos/
│ │ └── route.ts # GET /api/github/repos
│ ├── tree/
│ │ └── route.ts # GET /api/github/tree
│ ├── file/
│ │ └── route.ts # GET /api/github/file
│ ├── files/
│ │ └── route.ts # GET /api/github/files
│ ├── branches/
│ │ └── route.ts # GET /api/github/branches
│ ├── create-pr/
│ │ └── route.ts # POST /api/github/create-pr
│ ├── create-multi-file-pr/
│ │ └── route.ts # POST /api/github/create-multi-file-pr
│ ├── merge-pr/
│ │ └── route.ts # POST /api/github/merge-pr
│ ├── match-file/
│ │ └── route.ts # POST /api/github/match-file
│ ├── kustomize/
│ │ └── route.ts # GET /api/github/kustomize
│ ├── analyze-structure/
│ │ └── route.ts # POST /api/github/analyze-structure
│ └── auth-status/
│ └── route.ts # GET /api/github/auth-status
│
├── github-app/
│ ├── callback/
│ │ └── route.ts # GET /api/github-app/callback
│ ├── installations/
│ │ └── route.ts # GET /api/github-app/installations
│ ├── repositories/
│ │ └── route.ts # GET /api/github-app/repositories
│ └── logout/
│ └── route.ts # POST /api/github-app/logout
│
├── ai/
│ ├── match-file/
│ │ └── route.ts # POST /api/ai/match-file
│ └── troubleshoot/
│ └── route.ts # POST /api/ai/troubleshoot
│
├── settings/
│ ├── route.ts # GET/POST/DELETE /api/settings
│ ├── aliases/
│ │ └── route.ts # GET/POST /api/settings/aliases
│ ├── critical-issues/
│ │ └── route.ts # GET/POST /api/settings/critical-issues
│ ├── sidebar-pins/
│ │ └── route.ts # GET/POST /api/settings/sidebar-pins
│ ├── github/
│ │ ├── route.ts # GET/POST /api/settings/github
│ │ ├── basket/
│ │ │ └── route.ts # GET/POST/DELETE basket
│ │ └── prs/
│ │ └── route.ts # GET/POST/DELETE PRs
│ └── migrate/
│ └── route.ts # POST /api/settings/migrate
│
├── notifications/
│ └── route.ts # POST /api/notifications
│
├── auth/
│ └── [...nextauth]/
│ └── route.ts # NextAuth.js endpoints
│
└── resources/
└── [type]/
└── [name]/
└── yaml/
└── route.ts # GET /api/resources/:type/:name/yaml
Library Directory (app/lib/)
Reusable business logic, utilities, and shared code:
Copy
app/lib/
├── k8s/ # Kubernetes client layer
│ ├── client.ts # K8s client initialization
│ ├── deployments.ts # Deployment operations
│ ├── pods.ts # Pod operations
│ ├── nodes.ts # Node operations
│ ├── services.ts # Service operations
│ └── ... # Other resource operations
│
├── hooks/ # React hooks (TanStack Query)
│ ├── use-deployments.ts # useDeployments() hook
│ ├── use-pods.ts # usePods() hook
│ ├── use-nodes.ts # useNodes() hook
│ ├── use-services.ts # useServices() hook
│ ├── use-events.ts # useEvents() hook
│ ├── use-dashboard.ts # useDashboard() hook
│ ├── use-labels.ts # useLabels() hook
│ ├── use-jobs.ts # useJobs() hook
│ ├── use-cronjobs.ts # useCronJobs() hook
│ └── ... # Other resource hooks
│
├── core/ # Core utilities
│ ├── store.ts # Zustand stores
│ └── utils.ts # Helper functions
│
├── db/ # Database layer (SQLite)
│ ├── database.ts # Database connection
│ ├── services.ts # Database services (CRUD)
│ ├── schema.sql # Database schema
│ └── client-sync.ts # Client-server sync
│
├── github/ # GitHub integration
│ ├── client.ts # Octokit client
│ ├── auth.ts # GitHub App auth
│ ├── repos.ts # Repository operations
│ ├── files.ts # File operations
│ ├── prs.ts # Pull request operations
│ └── kustomize.ts # Kustomize detection
│
├── ai/ # AI features (OpenAI)
│ ├── match-file.ts # AI file matching
│ └── troubleshoot.ts # AI troubleshooting
│
├── mocks/ # Demo mode data
│ ├── data.ts # Mock K8s resources
│ └── github-data.ts # Mock GitHub data
│
├── ui/ # UI utilities
│ ├── theme/ # Theme system
│ │ ├── design-tokens.ts # Color tokens
│ │ └── visual-presets.ts # Visual presets
│ ├── components/ # Utility components
│ │ └── glass-panel.tsx # Glassmorphism panel
│ ├── providers/ # Context providers
│ │ └── react-query-provider.tsx
│ ├── use-glass-surface.ts # Glass effect hook
│ └── topology.ts # Topology graph utils
│
├── notifications/ # Desktop notifications
│ └── notifier.ts # node-notifier wrapper
│
├── auth/ # Authentication
│ └── config.ts # NextAuth config
│
├── config/ # App configuration
│ └── constants.ts # Constants
│
├── contexts/ # React contexts
│ └── theme-context.tsx # Theme context
│
└── cli/ # CLI utilities
├── commands.ts # CLI command handlers
└── pm2-config.ts # PM2 configuration
Types Directory (app/types/)
TypeScript type definitions:
Copy
app/types/
├── kubernetes.ts # K8s resource types
│ ├── Deployment
│ ├── Pod
│ ├── Node
│ ├── Service
│ ├── ConfigMap
│ ├── Secret
│ ├── Event
│ └── ...
│
├── app.ts # App-specific types
│ ├── AppMode ('demo' | 'real')
│ ├── KubernetesContext
│ ├── VisualPreset
│ └── ...
│
├── topology.ts # Topology graph types
│ ├── TopologyNode
│ ├── TopologyEdge
│ └── ...
│
└── next-auth.d.ts # NextAuth type extensions
Public Directory (app/public/)
Static assets served directly:
Copy
app/public/
├── logo/
│ ├── light-with-text.svg # Logo for light mode
│ ├── dark-with-text.svg # Logo for dark mode
│ ├── icon.svg # App icon
│ └── favicon.ico # Favicon
│
└── fonts/ # Custom fonts (if any)
Test Directory (app/tests/)
End-to-end tests with Playwright:
Copy
app/tests/
└── e2e/
├── app-launch.spec.ts # App startup tests
├── navigation.spec.ts # Navigation tests
├── dashboard.spec.ts # Dashboard tests
├── deployments.spec.ts # Deployment tests
└── pods.spec.ts # Pod tests
Unit Tests (app/__tests__/)
Unit tests with Vitest:
Copy
app/__tests__/
└── lib/
├── utils.test.ts # Utils tests
├── store.test.ts # Store tests
├── mock-data.test.ts # Mock data tests
└── hooks/
├── use-deployments.test.ts
├── use-pods.test.ts
├── use-dashboard.test.ts
├── use-jobs.test.ts
└── use-cronjobs.test.ts
Documentation (docs/)
Mintlify documentation:
Copy
docs/
├── mint.json # Mintlify config
├── introduction.mdx # Getting started
├── installation.mdx # Installation guide
│
├── user/ # User documentation
│ ├── dashboard.mdx
│ ├── deployments.mdx
│ ├── pods.mdx
│ ├── nodes.mdx
│ ├── ...
│ ├── github/
│ │ ├── yaml-editor.mdx
│ │ └── pull-requests.mdx
│ └── configuration/
│ ├── cluster-connection.mdx
│ ├── namespaces.mdx
│ ├── settings.mdx
│ └── demo-mode.mdx
│
└── developer/ # Developer documentation
├── overview.mdx
├── architecture.mdx
├── tech-stack.mdx
├── project-structure.mdx # This file
├── getting-started.mdx
├── testing.mdx
├── contributing.mdx
├── deployment.mdx
└── api/
├── introduction.mdx
├── deployments.mdx
├── pods.mdx
├── nodes.mdx
├── events.mdx
└── realtime.mdx
Configuration Files
Root Configuration
Copy
app/
├── package.json # Dependencies & scripts
├── tsconfig.json # TypeScript config
├── next.config.mjs # Next.js config
├── vitest.config.ts # Vitest config
├── playwright.config.ts # Playwright config
├── eslint.config.mjs # ESLint config
├── .prettierrc # Prettier config
├── .env.local # Environment variables
├── .gitignore # Git ignore rules
└── orphelix-cli.js # CLI entry point
Naming Conventions
Files
React Components
React Components
Convention:
kebab-case.tsxExamples:deployment-card.tsxpod-logs-viewer.tsxnamespace-selector.tsxcluster-health-score.tsx
PascalCaseCopy
// File: deployment-card.tsx
export function DeploymentCard({ deployment }: Props) {
// ...
}
TypeScript Files
TypeScript Files
Convention:
kebab-case.tsExamples:kubernetes-client.tsuse-deployments.tsgithub-auth.tsdesign-tokens.ts
camelCase for functions, PascalCase for types/classesCopy
// File: use-deployments.ts
export function useDeployments() { }
export type Deployment = { }
API Routes
API Routes
Convention:
route.ts in nested directoriesExamples:api/deployments/route.ts→/api/deploymentsapi/deployments/[name]/route.ts→/api/deployments/:nameapi/pods/[name]/logs/route.ts→/api/pods/:name/logs
GET, POST, PUT, DELETE)Copy
// File: api/deployments/route.ts
export async function GET(request: NextRequest) { }
export async function POST(request: NextRequest) { }
Page Routes
Page Routes
Convention:
page.tsx in route directoriesExamples:app/deployments/page.tsx→/deploymentsapp/deployments/[name]/page.tsx→/deployments/:nameapp/settings/page.tsx→/settings
Copy
// File: app/deployments/page.tsx
export default function DeploymentsPage() {
return <DeploymentList />
}
Layouts
Layouts
Convention:
layout.tsx in route directoriesExamples:app/layout.tsx→ Root layoutapp/deployments/layout.tsx→ Deployments layout
children propCopy
// File: app/layout.tsx
export default function RootLayout({ children }: Props) {
return (
<html>
<body>{children}</body>
</html>
)
}
Variables & Functions
Variables
Variables
Convention:
camelCaseExamples:Copy
const deploymentName = 'nginx'
const isLoading = true
const podStatusMap = new Map()
let retryCount = 0
Functions
Functions
Convention: React Components:
camelCaseExamples:Copy
function getDeployments() { }
async function fetchPodLogs() { }
const restartDeployment = () => { }
PascalCaseCopy
function DeploymentCard() { }
const PodStatusBadge = () => { }
Constants
Constants
Convention:
UPPER_SNAKE_CASEExamples:Copy
const API_BASE_URL = '/api'
const MAX_RETRY_COUNT = 3
const DEFAULT_NAMESPACE = 'default'
Types & Interfaces
Types & Interfaces
Convention:
PascalCaseExamples:Copy
type Deployment = { }
interface Pod { }
type DeploymentStatus = 'Available' | 'Progressing'
Enums
Enums
Convention:
PascalCase for enum, UPPER_SNAKE_CASE for valuesExamples:Copy
enum PodPhase {
PENDING = 'Pending',
RUNNING = 'Running',
SUCCEEDED = 'Succeeded',
FAILED = 'Failed',
}
Import Conventions
Path Aliases
Configured intsconfig.json:
Copy
// tsconfig.json
{
"compilerOptions": {
"paths": {
"@/*": ["./app/*"],
"@/lib/*": ["./app/lib/*"],
"@/components/*": ["./app/components/*"],
"@/types/*": ["./app/types/*"]
}
}
}
Import Order
Recommended order (enforced by ESLint):Copy
// 1. React & Next.js
import { useState, useEffect } from 'react'
import { useRouter } from 'next/navigation'
// 2. External libraries
import { useQuery } from '@tanstack/react-query'
import { Card, CardContent } from '@mui/material'
// 3. Internal utilities (@ aliases)
import { useDeployments } from '@/lib/hooks/use-deployments'
import { useModeStore } from '@/lib/core/store'
import type { Deployment } from '@/types/kubernetes'
// 4. Internal components
import { DeploymentCard } from '@/components/deployments/deployment-card'
import { StatusBadge } from '@/components/common/status-badge'
// 5. Relative imports (within same directory)
import { formatDeploymentStatus } from './utils'
import styles from './deployments.module.css'
Code Organization Best Practices
Component Structure
Copy
// app/components/deployments/deployment-card.tsx
// 1. Imports
import { Card, CardContent, Typography } from '@mui/material'
import { useDeployments } from '@/lib/hooks/use-deployments'
import type { Deployment } from '@/types/kubernetes'
// 2. Types
interface DeploymentCardProps {
deployment: Deployment
onRestart?: (name: string) => void
}
// 3. Constants
const DEFAULT_REPLICAS = 1
// 4. Component
export function DeploymentCard({ deployment, onRestart }: DeploymentCardProps) {
// 4.1. Hooks
const [isHovered, setIsHovered] = useState(false)
const { data, isLoading } = useDeployments()
// 4.2. Computed values
const statusColor = deployment.status === 'Available' ? 'success' : 'warning'
// 4.3. Handlers
const handleRestart = () => {
onRestart?.(deployment.name)
}
// 4.4. Effects
useEffect(() => {
// Side effects
}, [])
// 4.5. Render
return (
<Card onMouseEnter={() => setIsHovered(true)}>
<CardContent>
<Typography variant="h6">{deployment.name}</Typography>
{/* ... */}
</CardContent>
</Card>
)
}
API Route Structure
Copy
// app/api/deployments/route.ts
// 1. Imports
import { NextRequest } from 'next/server'
import { getAppsApi } from '@/lib/k8s/client'
// 2. Types
interface DeploymentParams {
namespace: string
context?: string
}
// 3. Helper functions
function validateNamespace(namespace: string): boolean {
return namespace.length > 0
}
// 4. Route handlers
export async function GET(request: NextRequest) {
// 4.1. Parse params
const searchParams = request.nextUrl.searchParams
const namespace = searchParams.get('namespace') || 'default'
try {
// 4.2. Validate
if (!validateNamespace(namespace)) {
return Response.json({ error: 'Invalid namespace' }, { status: 400 })
}
// 4.3. Fetch data
const appsApi = getAppsApi()
const response = await appsApi.listNamespacedDeployment(namespace)
// 4.4. Transform
const deployments = response.body.items.map(transformDeployment)
// 4.5. Return
return Response.json(deployments)
} catch (error) {
// 4.6. Error handling
console.error('[API] Error:', error)
return Response.json({ error: error.message }, { status: 500 })
}
}
Custom Hook Structure
Copy
// lib/hooks/use-deployments.ts
// 1. Imports
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
import { useModeStore } from '@/lib/core/store'
import type { Deployment } from '@/types/kubernetes'
// 2. Types
interface UseDeploymentsOptions {
namespace?: string
enabled?: boolean
}
// 3. Hook
export function useDeployments(options?: UseDeploymentsOptions) {
// 3.1. Get state
const { mode, selectedContext, selectedNamespace } = useModeStore()
const namespace = options?.namespace || selectedNamespace
// 3.2. Query
return useQuery({
queryKey: ['deployments', namespace, mode, selectedContext],
queryFn: async () => {
const params = new URLSearchParams({ namespace, context: selectedContext || '' })
const res = await fetch(`/api/deployments?${params}`)
if (!res.ok) throw new Error('Failed to fetch')
return res.json() as Promise<Deployment[]>
},
enabled: options?.enabled !== false && !!namespace,
refetchInterval: 30000,
})
}
// 4. Mutation hook
export function useRestartDeployment() {
const queryClient = useQueryClient()
return useMutation({
mutationFn: async ({ name, namespace }: { name: string; namespace: string }) => {
const res = await fetch(`/api/deployments/${name}/restart`, {
method: 'POST',
body: JSON.stringify({ namespace }),
})
if (!res.ok) throw new Error('Failed to restart')
return res.json()
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['deployments'] })
},
})
}
File Size Guidelines
Recommended Limits
- Components: < 300 lines
- Hooks: < 150 lines
- API Routes: < 200 lines
- Utilities: < 100 lines per function
When to Split
Split files when:- File exceeds recommended limit
- Multiple unrelated concerns in one file
- Code becomes hard to navigate
- Too many exports from single file
Copy
// Before: deployment-utils.ts (500 lines)
export function formatDeploymentStatus() { }
export function calculateReplicaHealth() { }
export function getDeploymentConditions() { }
export function transformDeploymentYaml() { }
export function validateDeploymentSpec() { }
// After: Split into multiple files
// lib/deployments/format.ts
export function formatDeploymentStatus() { }
// lib/deployments/health.ts
export function calculateReplicaHealth() { }
// lib/deployments/conditions.ts
export function getDeploymentConditions() { }
// lib/deployments/yaml.ts
export function transformDeploymentYaml() { }
export function validateDeploymentSpec() { }
// lib/deployments/index.ts (barrel export)
export * from './format'
export * from './health'
export * from './conditions'
export * from './yaml'