- 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.
132 lines
7.5 KiB
JavaScript
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>
|
|
);
|
|
}
|