Files
I-Fun c649f055d8 feat: initialize Infra Manager with React, Tailwind CSS, and Inertia.js
- Added main application entry point (app.jsx) and bootstrap file for React setup.
- Created Wiki index page (Index.jsx) for documentation and file management.
- Implemented utility functions for formatting and status handling in utils.js.
- Set up Blade template (app.blade.php) for Inertia.js integration.
- Defined API routes for metrics and terminal access in api.php.
- Established console commands for scheduled tasks in console.php.
- Created web routes for various application features including authentication, hypervisors, containers, cloud accounts, security, backups, and wiki management in web.php.
- Developed setup scripts for Windows (setup.ps1) and Linux (setup.sh) environments.
- Configured Tailwind CSS for styling with custom themes and animations in tailwind.config.js.
- Set up Vite for asset bundling and development server in vite.config.js.
2026-04-07 20:29:32 +07:00

132 lines
7.5 KiB
JavaScript

import AppLayout from '@/Layouts/AppLayout';
import { Head } from '@inertiajs/react';
import { motion } from 'framer-motion';
import { Activity, Bell, CheckCircle, AlertTriangle, XCircle, Plus } from 'lucide-react';
import { GlassCard, StatCard } from '@/Components/ui/GlassCard';
import { StatusBadge } from '@/Components/ui/StatusBadge';
import { Button } from '@/Components/ui/Button';
import { DataTable } from '@/Components/ui/DataTable';
import { ProgressBar } from '@/Components/ui/ProgressBar';
import { timeAgo } from '@/lib/utils';
import {
LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, Legend,
ResponsiveContainer, AreaChart, Area
} from 'recharts';
// Demo metric data
const metricStream = Array.from({ length: 30 }, (_, i) => ({
t: `-${30 - i}m`,
cpu: 30 + Math.sin(i * 0.3) * 20 + Math.random() * 10,
mem: 55 + Math.cos(i * 0.2) * 15 + Math.random() * 8,
net: Math.abs(Math.sin(i * 0.5) * 400 + Math.random() * 100),
}));
export default function MonitoringIndex({ alerts, alert_stats, dashboards }) {
const firing = alerts?.filter(a => a.status === 'firing') ?? [];
const acked = alerts?.filter(a => a.status === 'acknowledged') ?? [];
const alertColumns = [
{ header: 'Alert', accessor: 'title', render: row => (
<div>
<p className="text-sm text-white">{row.title}</p>
<p className="text-xs text-white/30">{row.target_name}</p>
</div>
)},
{ header: 'Severity', accessor: 'severity', render: row => (
<StatusBadge status={row.severity} />
)},
{ header: 'Status', accessor: 'status', render: row => (
<StatusBadge status={row.status} />
)},
{ header: 'Fired', accessor: 'created_at', render: row => (
<span className="text-xs text-white/40">{timeAgo(row.created_at)}</span>
)},
{ header: '', key: 'actions', render: row => (
row.status === 'firing' && (
<Button size="sm" variant="secondary" onClick={() => {}}>Acknowledge</Button>
)
)},
];
return (
<AppLayout title="Monitoring">
<Head title="Monitoring" />
<div className="space-y-5">
<div className="flex items-center justify-between">
<div>
<h2 className="text-xl font-bold text-white">Unified Monitoring</h2>
<p className="text-sm text-white/40">Real-time metrics, alerts, and dashboards</p>
</div>
<div className="flex gap-2">
<a href="/monitoring/dashboards"><Button variant="secondary">Dashboards</Button></a>
<a href="/monitoring/alerts"><Button variant="secondary" icon={Bell}>Alert Rules</Button></a>
</div>
</div>
{/* Alert Stats */}
<div className="grid grid-cols-2 lg:grid-cols-4 gap-3">
<StatCard title="Firing" value={firing.length} color="red" icon={XCircle} />
<StatCard title="Acknowledged" value={acked.length} color="yellow" icon={Bell} />
<StatCard title="Resolved 24h" value={alert_stats?.resolved_24h ?? 0} color="green" icon={CheckCircle} />
<StatCard title="Total Rules" value={alert_stats?.total_rules ?? 0} color="blue" icon={Activity} />
</div>
{/* Charts */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
<GlassCard className="p-5">
<h3 className="text-sm font-semibold text-white mb-1">CPU & Memory</h3>
<p className="text-xs text-white/30 mb-4">Average across all VMs — last 30 min</p>
<ResponsiveContainer width="100%" height={200}>
<AreaChart data={metricStream} margin={{ top: 0, right: 0, left: -20, bottom: 0 }}>
<defs>
<linearGradient id="cpu2" x1="0" y1="0" x2="0" y2="1">
<stop offset="5%" stopColor="#00d4ff" stopOpacity={0.3} />
<stop offset="95%" stopColor="#00d4ff" stopOpacity={0} />
</linearGradient>
<linearGradient id="mem2" x1="0" y1="0" x2="0" y2="1">
<stop offset="5%" stopColor="#9d4edd" stopOpacity={0.3} />
<stop offset="95%" stopColor="#9d4edd" stopOpacity={0} />
</linearGradient>
</defs>
<CartesianGrid strokeDasharray="3 3" stroke="rgba(255,255,255,0.05)" />
<XAxis dataKey="t" tick={{ fill: 'rgba(255,255,255,0.3)', fontSize: 10 }} axisLine={false} tickLine={false} interval={5} />
<YAxis tick={{ fill: 'rgba(255,255,255,0.3)', fontSize: 10 }} axisLine={false} tickLine={false} domain={[0, 100]} />
<Tooltip contentStyle={{ backgroundColor: '#0f1f35', border: '1px solid rgba(255,255,255,0.1)', borderRadius: '8px', fontSize: '12px' }} />
<Area type="monotone" dataKey="cpu" stroke="#00d4ff" fill="url(#cpu2)" strokeWidth={1.5} name="CPU %" />
<Area type="monotone" dataKey="mem" stroke="#9d4edd" fill="url(#mem2)" strokeWidth={1.5} name="Mem %" />
</AreaChart>
</ResponsiveContainer>
</GlassCard>
<GlassCard className="p-5">
<h3 className="text-sm font-semibold text-white mb-1">Network I/O</h3>
<p className="text-xs text-white/30 mb-4">Mbps across all endpoints</p>
<ResponsiveContainer width="100%" height={200}>
<LineChart data={metricStream} margin={{ top: 0, right: 0, left: -20, bottom: 0 }}>
<CartesianGrid strokeDasharray="3 3" stroke="rgba(255,255,255,0.05)" />
<XAxis dataKey="t" tick={{ fill: 'rgba(255,255,255,0.3)', fontSize: 10 }} axisLine={false} tickLine={false} interval={5} />
<YAxis tick={{ fill: 'rgba(255,255,255,0.3)', fontSize: 10 }} axisLine={false} tickLine={false} />
<Tooltip contentStyle={{ backgroundColor: '#0f1f35', border: '1px solid rgba(255,255,255,0.1)', borderRadius: '8px', fontSize: '12px' }} />
<Line type="monotone" dataKey="net" stroke="#00ff88" strokeWidth={1.5} dot={false} name="Net Mbps" />
</LineChart>
</ResponsiveContainer>
</GlassCard>
</div>
{/* Active Alerts Table */}
<GlassCard className="p-5">
<div className="flex items-center justify-between mb-4">
<h3 className="text-sm font-semibold text-white">Active Alerts</h3>
<Button icon={Plus} size="sm">Create Rule</Button>
</div>
<DataTable
columns={alertColumns}
data={alerts ?? []}
emptyMessage="No active alerts — all systems operational"
/>
</GlassCard>
</div>
</AppLayout>
);
}