Major updates and improvements to BZZZ system
- Updated configuration and deployment files - Improved system architecture and components - Enhanced documentation and testing - Fixed various issues and added new features 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,403 @@
|
||||
'use client'
|
||||
|
||||
import { useState, useEffect } from 'react'
|
||||
import {
|
||||
CpuChipIcon,
|
||||
ServerIcon,
|
||||
CircleStackIcon,
|
||||
GlobeAltIcon,
|
||||
CheckCircleIcon,
|
||||
ExclamationTriangleIcon,
|
||||
ArrowPathIcon
|
||||
} from '@heroicons/react/24/outline'
|
||||
|
||||
interface SystemInfo {
|
||||
os: string
|
||||
architecture: string
|
||||
cpu_cores: number
|
||||
memory_mb: number
|
||||
gpus: Array<{
|
||||
name: string
|
||||
memory: string
|
||||
driver: string
|
||||
type: string
|
||||
}>
|
||||
network: {
|
||||
hostname: string
|
||||
interfaces: string[]
|
||||
public_ip?: string
|
||||
private_ips: string[]
|
||||
docker_bridge?: string
|
||||
}
|
||||
storage: {
|
||||
total_space_gb: number
|
||||
free_space_gb: number
|
||||
mount_path: string
|
||||
}
|
||||
docker: {
|
||||
available: boolean
|
||||
version?: string
|
||||
compose_available: boolean
|
||||
swarm_mode: boolean
|
||||
}
|
||||
}
|
||||
|
||||
interface SystemDetectionProps {
|
||||
systemInfo: SystemInfo | null
|
||||
configData: any
|
||||
onComplete: (data: any) => void
|
||||
onBack?: () => void
|
||||
isCompleted: boolean
|
||||
}
|
||||
|
||||
export default function SystemDetection({
|
||||
systemInfo,
|
||||
configData,
|
||||
onComplete,
|
||||
onBack,
|
||||
isCompleted
|
||||
}: SystemDetectionProps) {
|
||||
const [loading, setLoading] = useState(!systemInfo)
|
||||
const [refreshing, setRefreshing] = useState(false)
|
||||
const [detectedInfo, setDetectedInfo] = useState<SystemInfo | null>(systemInfo)
|
||||
|
||||
useEffect(() => {
|
||||
if (!detectedInfo) {
|
||||
refreshSystemInfo()
|
||||
}
|
||||
}, [])
|
||||
|
||||
const refreshSystemInfo = async () => {
|
||||
setRefreshing(true)
|
||||
try {
|
||||
const response = await fetch('/api/setup/system')
|
||||
if (response.ok) {
|
||||
const result = await response.json()
|
||||
setDetectedInfo(result.system_info)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to detect system info:', error)
|
||||
} finally {
|
||||
setLoading(false)
|
||||
setRefreshing(false)
|
||||
}
|
||||
}
|
||||
|
||||
const handleContinue = () => {
|
||||
if (detectedInfo) {
|
||||
onComplete({
|
||||
system: detectedInfo,
|
||||
validated: true
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const getStatusColor = (condition: boolean) => {
|
||||
return condition ? 'text-eucalyptus-600' : 'text-red-600'
|
||||
}
|
||||
|
||||
const getStatusIcon = (condition: boolean) => {
|
||||
return condition ? CheckCircleIcon : ExclamationTriangleIcon
|
||||
}
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="flex items-center justify-center py-12">
|
||||
<div className="text-center">
|
||||
<ArrowPathIcon className="h-8 w-8 text-bzzz-primary animate-spin mx-auto mb-4" />
|
||||
<p className="text-chorus-text-secondary">Detecting system configuration...</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
if (!detectedInfo) {
|
||||
return (
|
||||
<div className="text-center py-12">
|
||||
<ExclamationTriangleIcon className="h-12 w-12 text-red-500 mx-auto mb-4" />
|
||||
<h3 className="heading-subsection mb-2">
|
||||
System Detection Failed
|
||||
</h3>
|
||||
<p className="text-chorus-text-secondary mb-4">
|
||||
Unable to detect system configuration. Please try again.
|
||||
</p>
|
||||
<button
|
||||
onClick={refreshSystemInfo}
|
||||
disabled={refreshing}
|
||||
className="btn-primary"
|
||||
>
|
||||
{refreshing ? 'Retrying...' : 'Retry Detection'}
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
{/* System Overview */}
|
||||
<div className="card">
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<h3 className="heading-subsection">System Overview</h3>
|
||||
<button
|
||||
onClick={refreshSystemInfo}
|
||||
disabled={refreshing}
|
||||
className="text-bzzz-primary hover:text-bzzz-primary/80 transition-colors"
|
||||
>
|
||||
<ArrowPathIcon className={`h-5 w-5 ${refreshing ? 'animate-spin' : ''}`} />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<div className="text-sm font-medium text-chorus-text-secondary">Hostname</div>
|
||||
<div className="text-lg text-chorus-text-primary">{detectedInfo.network.hostname}</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-sm font-medium text-chorus-text-secondary">Operating System</div>
|
||||
<div className="text-lg text-chorus-text-primary">
|
||||
{detectedInfo.os} ({detectedInfo.architecture})
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Hardware Information */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
{/* CPU & Memory */}
|
||||
<div className="card">
|
||||
<div className="flex items-center mb-4">
|
||||
<CpuChipIcon className="h-6 w-6 text-bzzz-primary mr-2" />
|
||||
<h3 className="heading-subsection">CPU & Memory</h3>
|
||||
</div>
|
||||
|
||||
<div className="space-y-3">
|
||||
<div>
|
||||
<div className="text-sm font-medium text-chorus-text-secondary">CPU</div>
|
||||
<div className="text-chorus-text-primary">
|
||||
{detectedInfo.cpu_cores} cores
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-sm font-medium text-chorus-text-secondary">Memory</div>
|
||||
<div className="text-chorus-text-primary">
|
||||
{Math.round(detectedInfo.memory_mb / 1024)} GB total
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Storage */}
|
||||
<div className="card">
|
||||
<div className="flex items-center mb-4">
|
||||
<CircleStackIcon className="h-6 w-6 text-bzzz-primary mr-2" />
|
||||
<h3 className="heading-subsection">Storage</h3>
|
||||
</div>
|
||||
|
||||
<div className="space-y-3">
|
||||
<div>
|
||||
<div className="text-sm font-medium text-chorus-text-secondary">Disk Space</div>
|
||||
<div className="text-chorus-text-primary">
|
||||
{detectedInfo.storage.total_space_gb} GB total, {' '}
|
||||
{detectedInfo.storage.free_space_gb} GB available
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-full bg-chorus-border-invisible rounded-full h-2">
|
||||
<div
|
||||
className="bg-bzzz-primary h-2 rounded-full"
|
||||
style={{
|
||||
width: `${((detectedInfo.storage.total_space_gb - detectedInfo.storage.free_space_gb) / detectedInfo.storage.total_space_gb) * 100}%`
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* GPU Information */}
|
||||
{detectedInfo.gpus && detectedInfo.gpus.length > 0 && (
|
||||
<div className="card">
|
||||
<div className="flex items-center mb-4">
|
||||
<ServerIcon className="h-6 w-6 text-bzzz-primary mr-2" />
|
||||
<h3 className="heading-subsection">
|
||||
GPU Configuration ({detectedInfo.gpus.length} GPU{detectedInfo.gpus.length !== 1 ? 's' : ''})
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
{detectedInfo.gpus.map((gpu, index) => (
|
||||
<div key={index} className="bg-chorus-warm rounded-lg p-4">
|
||||
<div className="font-medium text-chorus-text-primary">{gpu.name}</div>
|
||||
<div className="text-sm text-chorus-text-secondary">
|
||||
{gpu.type.toUpperCase()} • {gpu.memory} • {gpu.driver}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Network Information */}
|
||||
<div className="card">
|
||||
<div className="flex items-center mb-4">
|
||||
<GlobeAltIcon className="h-6 w-6 text-bzzz-primary mr-2" />
|
||||
<h3 className="heading-subsection">Network Configuration</h3>
|
||||
</div>
|
||||
|
||||
<div className="space-y-3">
|
||||
<div>
|
||||
<div className="text-sm font-medium text-chorus-text-secondary">Hostname</div>
|
||||
<div className="text-chorus-text-primary">{detectedInfo.network.hostname}</div>
|
||||
</div>
|
||||
|
||||
{detectedInfo.network.private_ips && detectedInfo.network.private_ips.length > 0 && (
|
||||
<div>
|
||||
<div className="text-sm font-medium text-chorus-text-secondary mb-2">Private IP Addresses</div>
|
||||
<div className="space-y-2">
|
||||
{detectedInfo.network.private_ips.map((ip, index) => (
|
||||
<div key={index} className="flex justify-between items-center text-sm">
|
||||
<span>{ip}</span>
|
||||
<span className="status-indicator status-online">active</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{detectedInfo.network.public_ip && (
|
||||
<div>
|
||||
<div className="text-sm font-medium text-chorus-text-secondary">Public IP</div>
|
||||
<div className="text-chorus-text-primary">{detectedInfo.network.public_ip}</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Software Requirements */}
|
||||
<div className="card">
|
||||
<h3 className="heading-subsection mb-4">Software Requirements</h3>
|
||||
|
||||
<div className="space-y-4">
|
||||
{[
|
||||
{
|
||||
name: 'Docker',
|
||||
installed: detectedInfo.docker.available,
|
||||
version: detectedInfo.docker.version,
|
||||
required: true
|
||||
},
|
||||
{
|
||||
name: 'Docker Compose',
|
||||
installed: detectedInfo.docker.compose_available,
|
||||
version: undefined,
|
||||
required: false
|
||||
},
|
||||
{
|
||||
name: 'Docker Swarm',
|
||||
installed: detectedInfo.docker.swarm_mode,
|
||||
version: undefined,
|
||||
required: false
|
||||
}
|
||||
].map((software, index) => {
|
||||
const StatusIcon = getStatusIcon(software.installed)
|
||||
return (
|
||||
<div key={index} className="flex items-center justify-between">
|
||||
<div className="flex items-center">
|
||||
<StatusIcon className={`h-5 w-5 mr-3 ${getStatusColor(software.installed)}`} />
|
||||
<div>
|
||||
<div className="font-medium text-chorus-text-primary">{software.name}</div>
|
||||
{software.version && (
|
||||
<div className="text-sm text-chorus-text-secondary">Version: {software.version}</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center">
|
||||
{software.required && (
|
||||
<span className="text-xs bg-bzzz-primary text-white px-2 py-1 rounded mr-2">
|
||||
Required
|
||||
</span>
|
||||
)}
|
||||
<span className={`text-sm font-medium ${getStatusColor(software.installed)}`}>
|
||||
{software.installed ? 'Installed' : 'Missing'}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* System Validation */}
|
||||
<div className="panel panel-info">
|
||||
<h3 className="heading-subsection mb-4 panel-title">System Validation</h3>
|
||||
|
||||
<div className="space-y-2">
|
||||
{[
|
||||
{
|
||||
check: 'Minimum memory (2GB required)',
|
||||
passed: detectedInfo.memory_mb >= 2048,
|
||||
warning: detectedInfo.memory_mb < 4096
|
||||
},
|
||||
{
|
||||
check: 'Available disk space (10GB required)',
|
||||
passed: detectedInfo.storage.free_space_gb >= 10
|
||||
},
|
||||
{
|
||||
check: 'Docker installed and running',
|
||||
passed: detectedInfo.docker.available
|
||||
}
|
||||
].map((validation, index) => {
|
||||
const StatusIcon = getStatusIcon(validation.passed)
|
||||
return (
|
||||
<div key={index} className="flex items-center">
|
||||
<StatusIcon className={`h-4 w-4 mr-3 ${
|
||||
validation.passed
|
||||
? 'text-eucalyptus-600'
|
||||
: 'text-red-600'
|
||||
}`} />
|
||||
<span className={`text-sm ${
|
||||
validation.passed
|
||||
? 'text-eucalyptus-600'
|
||||
: 'text-red-600'
|
||||
}`}>
|
||||
{validation.check}
|
||||
{validation.warning && validation.passed && (
|
||||
<span className="text-yellow-600 ml-2">(Warning: Recommend 4GB+)</span>
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Action Buttons */}
|
||||
<div className="flex justify-between pt-6 border-t border-chorus-border-defined">
|
||||
<div>
|
||||
{onBack && (
|
||||
<button onClick={onBack} className="btn-outline">
|
||||
Back
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="flex space-x-3">
|
||||
<button
|
||||
onClick={refreshSystemInfo}
|
||||
disabled={refreshing}
|
||||
className="btn-outline"
|
||||
>
|
||||
{refreshing ? 'Refreshing...' : 'Refresh'}
|
||||
</button>
|
||||
|
||||
<button
|
||||
onClick={handleContinue}
|
||||
className="btn-primary"
|
||||
disabled={!detectedInfo.docker.available}
|
||||
>
|
||||
{isCompleted ? 'Continue' : 'Next: Repository Setup'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user