mirror of
https://github.com/MacRimi/ProxMenux.git
synced 2025-11-18 03:26:17 +00:00
Update AppImage
This commit is contained in:
@@ -153,7 +153,6 @@ export function NetworkMetrics() {
|
|||||||
|
|
||||||
const [selectedInterface, setSelectedInterface] = useState<NetworkInterface | null>(null)
|
const [selectedInterface, setSelectedInterface] = useState<NetworkInterface | null>(null)
|
||||||
const [timeframe, setTimeframe] = useState<"hour" | "day" | "week" | "month" | "year">("day")
|
const [timeframe, setTimeframe] = useState<"hour" | "day" | "week" | "month" | "year">("day")
|
||||||
const [interfaceTimeframe, setInterfaceTimeframe] = useState<"hour" | "day" | "week" | "month" | "year">("day")
|
|
||||||
const [networkTotals, setNetworkTotals] = useState<{ received: number; sent: number }>({ received: 0, sent: 0 })
|
const [networkTotals, setNetworkTotals] = useState<{ received: number; sent: number }>({ received: 0, sent: 0 })
|
||||||
|
|
||||||
const { data: interfaceHistoricalData } = useSWR<any>(`/api/node/metrics?timeframe=${timeframe}`, fetcher, {
|
const { data: interfaceHistoricalData } = useSWR<any>(`/api/node/metrics?timeframe=${timeframe}`, fetcher, {
|
||||||
@@ -161,15 +160,6 @@ export function NetworkMetrics() {
|
|||||||
revalidateOnFocus: false,
|
revalidateOnFocus: false,
|
||||||
})
|
})
|
||||||
|
|
||||||
const { data: selectedInterfaceData, isLoading: interfaceDataLoading } = useSWR<any>(
|
|
||||||
selectedInterface ? `/api/network/${selectedInterface.name}/history?timeframe=${interfaceTimeframe}` : null,
|
|
||||||
fetcher,
|
|
||||||
{
|
|
||||||
refreshInterval: 30000,
|
|
||||||
revalidateOnFocus: false,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
return (
|
return (
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
@@ -652,22 +642,6 @@ export function NetworkMetrics() {
|
|||||||
|
|
||||||
{selectedInterface && (
|
{selectedInterface && (
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
{/* Timeframe Selector */}
|
|
||||||
<div className="flex justify-end">
|
|
||||||
<Select value={interfaceTimeframe} onValueChange={(value: any) => setInterfaceTimeframe(value)}>
|
|
||||||
<SelectTrigger className="w-[180px] bg-card border-border">
|
|
||||||
<SelectValue />
|
|
||||||
</SelectTrigger>
|
|
||||||
<SelectContent>
|
|
||||||
<SelectItem value="hour">1 Hour</SelectItem>
|
|
||||||
<SelectItem value="day">24 Hours</SelectItem>
|
|
||||||
<SelectItem value="week">7 Days</SelectItem>
|
|
||||||
<SelectItem value="month">30 Days</SelectItem>
|
|
||||||
<SelectItem value="year">1 Year</SelectItem>
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Basic Information */}
|
{/* Basic Information */}
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-sm font-semibold text-muted-foreground mb-3">Basic Information</h3>
|
<h3 className="text-sm font-semibold text-muted-foreground mb-3">Basic Information</h3>
|
||||||
@@ -799,39 +773,6 @@ export function NetworkMetrics() {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Network Traffic Chart */}
|
|
||||||
{selectedInterfaceData && (
|
|
||||||
<div>
|
|
||||||
<h3 className="text-sm font-semibold text-muted-foreground mb-3">
|
|
||||||
Network Traffic -{" "}
|
|
||||||
{interfaceTimeframe === "hour"
|
|
||||||
? "1 Hour"
|
|
||||||
: interfaceTimeframe === "day"
|
|
||||||
? "24 Hours"
|
|
||||||
: interfaceTimeframe === "week"
|
|
||||||
? "7 Days"
|
|
||||||
: interfaceTimeframe === "month"
|
|
||||||
? "30 Days"
|
|
||||||
: "1 Year"}
|
|
||||||
</h3>
|
|
||||||
{interfaceDataLoading ? (
|
|
||||||
<div className="text-center py-8 text-muted-foreground">
|
|
||||||
<div className="text-sm">Loading historical data...</div>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div className="rounded-lg border border-border/30 bg-background/50 p-4">
|
|
||||||
<NetworkTrafficChart
|
|
||||||
timeframe={interfaceTimeframe}
|
|
||||||
interfaceName={selectedInterface.name}
|
|
||||||
onTotalsCalculated={(totals) => {
|
|
||||||
// Optional: update totals if needed
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Traffic Statistics */}
|
{/* Traffic Statistics */}
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-sm font-semibold text-muted-foreground mb-3">Traffic since last boot</h3>
|
<h3 className="text-sm font-semibold text-muted-foreground mb-3">Traffic since last boot</h3>
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ interface NetworkMetricsData {
|
|||||||
|
|
||||||
interface NetworkTrafficChartProps {
|
interface NetworkTrafficChartProps {
|
||||||
timeframe: string
|
timeframe: string
|
||||||
interfaceName?: string // Added optional interfaceName prop for specific interface data
|
|
||||||
onTotalsCalculated?: (totals: { received: number; sent: number }) => void
|
onTotalsCalculated?: (totals: { received: number; sent: number }) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,7 +36,7 @@ const CustomNetworkTooltip = ({ active, payload, label }: any) => {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
export function NetworkTrafficChart({ timeframe, interfaceName, onTotalsCalculated }: NetworkTrafficChartProps) {
|
export function NetworkTrafficChart({ timeframe, onTotalsCalculated }: NetworkTrafficChartProps) {
|
||||||
const [data, setData] = useState<NetworkMetricsData[]>([])
|
const [data, setData] = useState<NetworkMetricsData[]>([])
|
||||||
const [loading, setLoading] = useState(true)
|
const [loading, setLoading] = useState(true)
|
||||||
const [error, setError] = useState<string | null>(null)
|
const [error, setError] = useState<string | null>(null)
|
||||||
@@ -48,7 +47,7 @@ export function NetworkTrafficChart({ timeframe, interfaceName, onTotalsCalculat
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchMetrics()
|
fetchMetrics()
|
||||||
}, [timeframe, interfaceName])
|
}, [timeframe])
|
||||||
|
|
||||||
const fetchMetrics = async () => {
|
const fetchMetrics = async () => {
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
@@ -57,15 +56,12 @@ export function NetworkTrafficChart({ timeframe, interfaceName, onTotalsCalculat
|
|||||||
try {
|
try {
|
||||||
const baseUrl =
|
const baseUrl =
|
||||||
typeof window !== "undefined" ? `${window.location.protocol}//${window.location.hostname}:8008` : ""
|
typeof window !== "undefined" ? `${window.location.protocol}//${window.location.hostname}:8008` : ""
|
||||||
|
const apiUrl = `${baseUrl}/api/node/metrics?timeframe=${timeframe}`
|
||||||
const apiUrl = interfaceName
|
|
||||||
? `${baseUrl}/api/network/${interfaceName}/history?timeframe=${timeframe}`
|
|
||||||
: `${baseUrl}/api/node/metrics?timeframe=${timeframe}`
|
|
||||||
|
|
||||||
const response = await fetch(apiUrl)
|
const response = await fetch(apiUrl)
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error(`Failed to fetch network metrics: ${response.status}`)
|
throw new Error(`Failed to fetch node metrics: ${response.status}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await response.json()
|
const result = await response.json()
|
||||||
|
|||||||
@@ -2653,7 +2653,7 @@ def get_detailed_gpu_info(gpu):
|
|||||||
mem_clock = clocks['GFX_MCLK']
|
mem_clock = clocks['GFX_MCLK']
|
||||||
if 'value' in mem_clock:
|
if 'value' in mem_clock:
|
||||||
detailed_info['clock_memory'] = f"{mem_clock['value']} MHz"
|
detailed_info['clock_memory'] = f"{mem_clock['value']} MHz"
|
||||||
print(f"[v0] Memory Clock: {detailed_info['clock_memory']} MHz", flush=True)
|
print(f"[v0] Memory Clock: {detailed_info['clock_memory']}", flush=True)
|
||||||
data_retrieved = True
|
data_retrieved = True
|
||||||
|
|
||||||
# Parse GPU activity (gpu_activity.GFX)
|
# Parse GPU activity (gpu_activity.GFX)
|
||||||
@@ -3711,6 +3711,92 @@ def api_network():
|
|||||||
"""Get network information"""
|
"""Get network information"""
|
||||||
return jsonify(get_network_info())
|
return jsonify(get_network_info())
|
||||||
|
|
||||||
|
@app.route('/api/network/<interface_name>/metrics', methods=['GET'])
|
||||||
|
def api_network_interface_metrics(interface_name):
|
||||||
|
"""Get historical metrics (RRD data) for a specific network interface"""
|
||||||
|
try:
|
||||||
|
timeframe = request.args.get('timeframe', 'day') # hour, day, week, month, year
|
||||||
|
|
||||||
|
print(f"[v0] ===== NETWORK INTERFACE METRICS REQUEST =====")
|
||||||
|
print(f"[v0] Interface: {interface_name}")
|
||||||
|
print(f"[v0] Timeframe: {timeframe}")
|
||||||
|
|
||||||
|
# Validate timeframe
|
||||||
|
valid_timeframes = ['hour', 'day', 'week', 'month', 'year']
|
||||||
|
if timeframe not in valid_timeframes:
|
||||||
|
print(f"[v0] ERROR: Invalid timeframe: {timeframe}")
|
||||||
|
return jsonify({'error': f'Invalid timeframe. Must be one of: {", ".join(valid_timeframes)}'}), 400
|
||||||
|
|
||||||
|
# Get local node name
|
||||||
|
local_node = socket.gethostname()
|
||||||
|
print(f"[v0] Local node: {local_node}")
|
||||||
|
|
||||||
|
# Determine interface type and get appropriate RRD data
|
||||||
|
interface_type = get_interface_type(interface_name)
|
||||||
|
print(f"[v0] Interface type: {interface_type}")
|
||||||
|
|
||||||
|
rrd_data = []
|
||||||
|
|
||||||
|
if interface_type == 'vm_lxc':
|
||||||
|
# For VM/LXC interfaces, get data from the VM/LXC RRD
|
||||||
|
vmid, vm_type = extract_vmid_from_interface(interface_name)
|
||||||
|
if vmid:
|
||||||
|
print(f"[v0] Fetching RRD data for {vm_type} {vmid}...")
|
||||||
|
rrd_result = subprocess.run(['pvesh', 'get', f'/nodes/{local_node}/{vm_type}/{vmid}/rrddata',
|
||||||
|
'--timeframe', timeframe, '--output-format', 'json'],
|
||||||
|
capture_output=True, text=True, timeout=10)
|
||||||
|
|
||||||
|
if rrd_result.returncode == 0:
|
||||||
|
all_data = json.loads(rrd_result.stdout)
|
||||||
|
# Filter to only network-related fields
|
||||||
|
for point in all_data:
|
||||||
|
filtered_point = {'time': point.get('time')}
|
||||||
|
# Add network fields if they exist
|
||||||
|
for key in ['netin', 'netout', 'diskread', 'diskwrite']:
|
||||||
|
if key in point:
|
||||||
|
filtered_point[key] = point[key]
|
||||||
|
rrd_data.append(filtered_point)
|
||||||
|
print(f"[v0] RRD data points: {len(rrd_data)}")
|
||||||
|
else:
|
||||||
|
print(f"[v0] ERROR: Failed to get RRD data for VM/LXC")
|
||||||
|
print(f"[v0] stderr: {rrd_result.stderr}")
|
||||||
|
else:
|
||||||
|
# For physical/bridge interfaces, get data from node RRD
|
||||||
|
print(f"[v0] Fetching RRD data for node {local_node}...")
|
||||||
|
rrd_result = subprocess.run(['pvesh', 'get', f'/nodes/{local_node}/rrddata',
|
||||||
|
'--timeframe', timeframe, '--output-format', 'json'],
|
||||||
|
capture_output=True, text=True, timeout=10)
|
||||||
|
|
||||||
|
if rrd_result.returncode == 0:
|
||||||
|
all_data = json.loads(rrd_result.stdout)
|
||||||
|
# Filter to only network-related fields for this interface
|
||||||
|
for point in all_data:
|
||||||
|
filtered_point = {'time': point.get('time')}
|
||||||
|
# Add network fields if they exist
|
||||||
|
for key in ['netin', 'netout']:
|
||||||
|
if key in point:
|
||||||
|
filtered_point[key] = point[key]
|
||||||
|
rrd_data.append(filtered_point)
|
||||||
|
print(f"[v0] RRD data points: {len(rrd_data)}")
|
||||||
|
else:
|
||||||
|
print(f"[v0] ERROR: Failed to get RRD data for node")
|
||||||
|
print(f"[v0] stderr: {rrd_result.stderr}")
|
||||||
|
|
||||||
|
print(f"[v0] ===== NETWORK INTERFACE METRICS REQUEST SUCCESS =====")
|
||||||
|
return jsonify({
|
||||||
|
'interface': interface_name,
|
||||||
|
'type': interface_type,
|
||||||
|
'timeframe': timeframe,
|
||||||
|
'data': rrd_data
|
||||||
|
})
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[v0] EXCEPTION in api_network_interface_metrics: {e}")
|
||||||
|
print(f"[v0] ===== NETWORK INTERFACE METRICS REQUEST EXCEPTION =====")
|
||||||
|
return jsonify({'error': str(e)}), 500
|
||||||
|
|
||||||
|
# ... existing code ...
|
||||||
|
|
||||||
@app.route('/api/vms', methods=['GET'])
|
@app.route('/api/vms', methods=['GET'])
|
||||||
def api_vms():
|
def api_vms():
|
||||||
"""Get virtual machine information"""
|
"""Get virtual machine information"""
|
||||||
|
|||||||
Reference in New Issue
Block a user