File transfer
This commit is contained in:
125
resources/scripts/addons/advanced-admin/network/NetworkTab.tsx
Normal file
125
resources/scripts/addons/advanced-admin/network/NetworkTab.tsx
Normal file
@@ -0,0 +1,125 @@
|
||||
/**
|
||||
* Network Traffic Dashboard — Main Tab
|
||||
* Ball Studios <https://git.balls.studio>
|
||||
*/
|
||||
|
||||
import React, { useEffect, useState, useCallback } from 'react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import http from '@/api/http';
|
||||
import LiveTrafficCards from './LiveTrafficCards';
|
||||
import TrafficChart from './TrafficChart';
|
||||
import PortTable from './PortTable';
|
||||
import FlowGraph from './FlowGraph';
|
||||
|
||||
interface LiveStats {
|
||||
rx_bytes: number;
|
||||
tx_bytes: number;
|
||||
recorded_at: string;
|
||||
}
|
||||
|
||||
interface HistoryPoint {
|
||||
rx_bytes: number;
|
||||
tx_bytes: number;
|
||||
recorded_at: string;
|
||||
}
|
||||
|
||||
type Resolution = '5m' | '1h' | '1d';
|
||||
|
||||
export default function NetworkTab() {
|
||||
const { id: serverUuid } = useParams<{ id: string }>();
|
||||
|
||||
const [live, setLive] = useState<LiveStats | null>(null);
|
||||
const [history, setHistory] = useState<HistoryPoint[]>([]);
|
||||
const [ports, setPorts] = useState<any[]>([]);
|
||||
const [flows, setFlows] = useState<any[]>([]);
|
||||
const [resolution, setRes] = useState<Resolution>('5m');
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const fetchLive = useCallback(async () => {
|
||||
if (!serverUuid) return;
|
||||
try {
|
||||
const { data } = await http.get(`/api/client/servers/${serverUuid}/addons/network/live`);
|
||||
setLive(data.data);
|
||||
} catch {
|
||||
// silently fail on live poll errors
|
||||
}
|
||||
}, [serverUuid]);
|
||||
|
||||
const fetchHistory = useCallback(async () => {
|
||||
if (!serverUuid) return;
|
||||
const { data } = await http.get(`/api/client/servers/${serverUuid}/addons/network/history`, {
|
||||
params: { resolution },
|
||||
});
|
||||
setHistory(data.data ?? []);
|
||||
}, [serverUuid, resolution]);
|
||||
|
||||
const fetchPorts = useCallback(async () => {
|
||||
if (!serverUuid) return;
|
||||
const { data } = await http.get(`/api/client/servers/${serverUuid}/addons/network/ports`);
|
||||
setPorts(data.data ?? []);
|
||||
}, [serverUuid]);
|
||||
|
||||
const fetchFlows = useCallback(async () => {
|
||||
if (!serverUuid) return;
|
||||
const { data } = await http.get(`/api/client/servers/${serverUuid}/addons/network/flows`);
|
||||
setFlows(data.data ?? []);
|
||||
}, [serverUuid]);
|
||||
|
||||
// Initial load
|
||||
useEffect(() => {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
Promise.all([fetchHistory(), fetchPorts(), fetchFlows()])
|
||||
.catch(() => setError('Failed to load network data.'))
|
||||
.finally(() => setLoading(false));
|
||||
}, [fetchHistory, fetchPorts, fetchFlows]);
|
||||
|
||||
// Live polling — every 10s
|
||||
useEffect(() => {
|
||||
fetchLive();
|
||||
const id = setInterval(fetchLive, 10_000);
|
||||
return () => clearInterval(id);
|
||||
}, [fetchLive]);
|
||||
|
||||
if (loading) return <div className='addon-loading'>Loading network data…</div>;
|
||||
if (error) return <div className='addon-error'>{error}</div>;
|
||||
|
||||
return (
|
||||
<div className='addon-network'>
|
||||
<h2 className='addon-section-title'>🌐 Network Traffic</h2>
|
||||
|
||||
{/* Live cards */}
|
||||
<LiveTrafficCards live={live} />
|
||||
|
||||
{/* Historical chart */}
|
||||
<div className='addon-card'>
|
||||
<div className='addon-card-header'>
|
||||
<span>Bandwidth Over Time</span>
|
||||
<div className='addon-resolution-tabs'>
|
||||
{(['5m', '1h', '1d'] as Resolution[]).map(r => (
|
||||
<button
|
||||
key={r}
|
||||
className={`addon-tab ${resolution === r ? 'active' : ''}`}
|
||||
onClick={() => setRes(r)}
|
||||
>{r}</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<TrafficChart data={history} />
|
||||
</div>
|
||||
|
||||
{/* Port breakdown */}
|
||||
<div className='addon-card'>
|
||||
<div className='addon-card-header'>Top Ports</div>
|
||||
<PortTable ports={ports} />
|
||||
</div>
|
||||
|
||||
{/* Flow graph */}
|
||||
<div className='addon-card'>
|
||||
<div className='addon-card-header'>Container Traffic Flow</div>
|
||||
<FlowGraph flows={flows} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user