feat: add VPS Monitor backend and frontend services
Some checks failed
Build and Push Docker Images / docker (push) Failing after 5s
Some checks failed
Build and Push Docker Images / docker (push) Failing after 5s
- Create systemd service for VPS Monitor agent. - Add FastAPI backend with endpoints for managing VPS configurations and statuses. - Implement Dockerfile for backend service with required dependencies. - Create frontend using React with Vite and Tailwind CSS for styling. - Add API client for communicating with the backend. - Implement components for displaying VPS information and logs. - Set up Docker Compose for orchestrating backend and frontend services. - Add environment configuration files for backend and agent. - Implement CORS support in the backend for frontend communication.
This commit is contained in:
95
vps-monitor/frontend/src/components/VpsCard.jsx
Normal file
95
vps-monitor/frontend/src/components/VpsCard.jsx
Normal file
@@ -0,0 +1,95 @@
|
||||
import { Server, Wifi, WifiOff, Trash2, ChevronDown, ChevronUp } from 'lucide-react'
|
||||
import { useState } from 'react'
|
||||
import ContainerRow from './ContainerRow'
|
||||
|
||||
export default function VpsCard({ vps, onAction, onLogs, onDelete }) {
|
||||
const [collapsed, setCollapsed] = useState(false)
|
||||
|
||||
const running = vps.containers.filter(c => c.status === 'running').length
|
||||
const total = vps.containers.length
|
||||
|
||||
return (
|
||||
<div className="bg-gray-900 border border-gray-800 rounded-xl overflow-hidden flex flex-col">
|
||||
{/* Header */}
|
||||
<div className="flex items-center gap-3 px-4 py-3 border-b border-gray-800">
|
||||
<div className={`p-2 rounded-lg flex-shrink-0 ${vps.online ? 'bg-emerald-500/10' : 'bg-red-500/10'}`}>
|
||||
<Server size={15} className={vps.online ? 'text-emerald-400' : 'text-red-400'} />
|
||||
</div>
|
||||
|
||||
<div className="flex-1 min-w-0">
|
||||
<h3 className="font-semibold text-sm truncate">{vps.name}</h3>
|
||||
<p className="text-xs text-gray-500 truncate">{vps.host}</p>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-2 flex-shrink-0">
|
||||
{vps.online ? (
|
||||
<span className="flex items-center gap-1.5 text-xs text-emerald-400">
|
||||
<Wifi size={12} />
|
||||
<span className="hidden sm:inline">{running}/{total} actifs</span>
|
||||
</span>
|
||||
) : (
|
||||
<span className="flex items-center gap-1.5 text-xs text-red-400">
|
||||
<WifiOff size={12} />
|
||||
<span className="hidden sm:inline">Hors ligne</span>
|
||||
</span>
|
||||
)}
|
||||
|
||||
<button
|
||||
onClick={() => setCollapsed(c => !c)}
|
||||
className="p-1.5 rounded hover:bg-gray-800 text-gray-500 hover:text-gray-300 transition-colors"
|
||||
title={collapsed ? 'Déplier' : 'Replier'}
|
||||
>
|
||||
{collapsed ? <ChevronDown size={14} /> : <ChevronUp size={14} />}
|
||||
</button>
|
||||
|
||||
<button
|
||||
onClick={() => onDelete(vps.id)}
|
||||
className="p-1.5 rounded hover:bg-red-500/20 text-gray-500 hover:text-red-400 transition-colors"
|
||||
title="Supprimer ce VPS"
|
||||
>
|
||||
<Trash2 size={14} />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Erreur de connexion */}
|
||||
{!vps.online && vps.error && (
|
||||
<div className="px-4 py-2.5 bg-red-950/30 border-b border-red-900/30 text-xs text-red-400 font-mono">
|
||||
{vps.error}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Description */}
|
||||
{vps.description && !collapsed && (
|
||||
<p className="px-4 py-2 text-xs text-gray-500 border-b border-gray-800/60">{vps.description}</p>
|
||||
)}
|
||||
|
||||
{/* Conteneurs */}
|
||||
{!collapsed && vps.online && (
|
||||
<div className="divide-y divide-gray-800/50 flex-1">
|
||||
{total === 0 ? (
|
||||
<p className="px-4 py-6 text-sm text-gray-600 text-center">Aucun conteneur détecté.</p>
|
||||
) : (
|
||||
vps.containers.map(c => (
|
||||
<ContainerRow
|
||||
key={c.id}
|
||||
container={c}
|
||||
onAction={(action) => onAction(vps.id, c.id, action)}
|
||||
onLogs={() => onLogs(vps.id, c.id, `${vps.name} / ${c.name}`)}
|
||||
/>
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Footer stats */}
|
||||
{!collapsed && vps.online && total > 0 && (
|
||||
<div className="px-4 py-2 border-t border-gray-800/60 flex gap-4 text-xs text-gray-600">
|
||||
<span><span className="text-emerald-500">{running}</span> en cours</span>
|
||||
<span><span className="text-gray-400">{total - running}</span> arrêtés</span>
|
||||
<span><span className="text-gray-400">{total}</span> total</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user