mirror of
https://github.com/MacRimi/ProxMenux.git
synced 2025-11-18 03:26:17 +00:00
aupdate AppImage
This commit is contained in:
@@ -4,7 +4,7 @@ import { useState, useEffect } from "react"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
|
||||
import { ArrowLeft, Loader2 } from "lucide-react"
|
||||
import { AreaChart, Area, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, Legend } from "recharts"
|
||||
import { AreaChart, Area, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer } from "recharts"
|
||||
|
||||
interface MetricsViewProps {
|
||||
vmid: number
|
||||
@@ -90,8 +90,8 @@ export function MetricsView({ vmid, vmName, vmType, onBack }: MetricsViewProps)
|
||||
timestamp: item.time,
|
||||
cpu: item.cpu ? Number((item.cpu * 100).toFixed(2)) : 0,
|
||||
memory: item.mem ? Number(((item.mem / item.maxmem) * 100).toFixed(2)) : 0,
|
||||
memoryMB: item.mem ? Number((item.mem / 1024 / 1024).toFixed(0)) : 0,
|
||||
maxMemoryMB: item.maxmem ? Number((item.maxmem / 1024 / 1024).toFixed(0)) : 0,
|
||||
memoryGB: item.mem ? Number((item.mem / 1024 / 1024 / 1024).toFixed(2)) : 0,
|
||||
maxMemoryGB: item.maxmem ? Number((item.maxmem / 1024 / 1024 / 1024).toFixed(2)) : 0,
|
||||
netin: item.netin ? Number((item.netin / 1024 / 1024).toFixed(2)) : 0,
|
||||
netout: item.netout ? Number((item.netout / 1024 / 1024).toFixed(2)) : 0,
|
||||
diskread: item.diskread ? Number((item.diskread / 1024 / 1024).toFixed(2)) : 0,
|
||||
@@ -162,7 +162,7 @@ export function MetricsView({ vmid, vmName, vmType, onBack }: MetricsViewProps)
|
||||
className="text-foreground"
|
||||
tick={{ fill: "currentColor" }}
|
||||
label={{ value: "%", angle: -90, position: "insideLeft", fill: "currentColor" }}
|
||||
domain={[0, (dataMax: number) => Math.ceil(dataMax * 1.15)]}
|
||||
domain={[0, "dataMax"]}
|
||||
/>
|
||||
<Tooltip
|
||||
contentStyle={{
|
||||
@@ -171,7 +171,6 @@ export function MetricsView({ vmid, vmName, vmType, onBack }: MetricsViewProps)
|
||||
borderRadius: "6px",
|
||||
}}
|
||||
/>
|
||||
<Legend wrapperStyle={{ paddingTop: "10px" }} />
|
||||
<Area
|
||||
type="monotone"
|
||||
dataKey="cpu"
|
||||
@@ -206,8 +205,8 @@ export function MetricsView({ vmid, vmName, vmType, onBack }: MetricsViewProps)
|
||||
stroke="currentColor"
|
||||
className="text-foreground"
|
||||
tick={{ fill: "currentColor" }}
|
||||
label={{ value: "%", angle: -90, position: "insideLeft", fill: "currentColor" }}
|
||||
domain={[0, (dataMax: number) => Math.ceil(dataMax * 1.15)]}
|
||||
label={{ value: "GB", angle: -90, position: "insideLeft", fill: "currentColor" }}
|
||||
domain={[0, "dataMax"]}
|
||||
/>
|
||||
<Tooltip
|
||||
contentStyle={{
|
||||
@@ -216,15 +215,14 @@ export function MetricsView({ vmid, vmName, vmType, onBack }: MetricsViewProps)
|
||||
borderRadius: "6px",
|
||||
}}
|
||||
/>
|
||||
<Legend wrapperStyle={{ paddingTop: "10px" }} />
|
||||
<Area
|
||||
type="monotone"
|
||||
dataKey="memory"
|
||||
dataKey="memoryGB"
|
||||
stroke="#10b981"
|
||||
fill="#10b981"
|
||||
fillOpacity={0.3}
|
||||
strokeWidth={2}
|
||||
name="Memory %"
|
||||
name="Memory GB"
|
||||
/>
|
||||
</AreaChart>
|
||||
</ResponsiveContainer>
|
||||
@@ -252,7 +250,7 @@ export function MetricsView({ vmid, vmName, vmType, onBack }: MetricsViewProps)
|
||||
className="text-foreground"
|
||||
tick={{ fill: "currentColor" }}
|
||||
label={{ value: "MB", angle: -90, position: "insideLeft", fill: "currentColor" }}
|
||||
domain={[0, (dataMax: number) => Math.ceil(dataMax * 1.15)]}
|
||||
domain={[0, "dataMax"]}
|
||||
/>
|
||||
<Tooltip
|
||||
contentStyle={{
|
||||
@@ -261,7 +259,6 @@ export function MetricsView({ vmid, vmName, vmType, onBack }: MetricsViewProps)
|
||||
borderRadius: "6px",
|
||||
}}
|
||||
/>
|
||||
<Legend wrapperStyle={{ paddingTop: "10px" }} />
|
||||
<Area
|
||||
type="monotone"
|
||||
dataKey="diskread"
|
||||
@@ -306,7 +303,7 @@ export function MetricsView({ vmid, vmName, vmType, onBack }: MetricsViewProps)
|
||||
className="text-foreground"
|
||||
tick={{ fill: "currentColor" }}
|
||||
label={{ value: "MB", angle: -90, position: "insideLeft", fill: "currentColor" }}
|
||||
domain={[0, (dataMax: number) => Math.ceil(dataMax * 1.15)]}
|
||||
domain={[0, "dataMax"]}
|
||||
/>
|
||||
<Tooltip
|
||||
contentStyle={{
|
||||
@@ -315,7 +312,6 @@ export function MetricsView({ vmid, vmName, vmType, onBack }: MetricsViewProps)
|
||||
borderRadius: "6px",
|
||||
}}
|
||||
/>
|
||||
<Legend wrapperStyle={{ paddingTop: "10px" }} />
|
||||
<Area
|
||||
type="monotone"
|
||||
dataKey="netin"
|
||||
|
||||
@@ -809,9 +809,6 @@ export function VirtualMachines() {
|
||||
{selectedVM && (
|
||||
<>
|
||||
<div>
|
||||
<h3 className="text-sm font-semibold text-muted-foreground mb-3 uppercase tracking-wide">
|
||||
Basic Information
|
||||
</h3>
|
||||
<Card
|
||||
className="border border-border bg-card/50 cursor-pointer hover:bg-card/70 transition-colors"
|
||||
onClick={handleMetricsClick}
|
||||
@@ -1102,9 +1099,6 @@ export function VirtualMachines() {
|
||||
</div>
|
||||
|
||||
<div className="border-t border-border bg-background px-6 py-4 mt-auto">
|
||||
<h3 className="text-sm font-semibold text-muted-foreground mb-3 uppercase tracking-wide">
|
||||
Control Actions
|
||||
</h3>
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
<Button
|
||||
className="w-full bg-green-600 hover:bg-green-700 text-white"
|
||||
|
||||
@@ -4831,12 +4831,45 @@ def api_vm_details(vmid):
|
||||
if config_result.returncode == 0:
|
||||
config = json.loads(config_result.stdout)
|
||||
|
||||
return jsonify({
|
||||
os_info = {}
|
||||
if vm_type == 'lxc' and resource.get('status') == 'running':
|
||||
try:
|
||||
print(f"[v0] Reading /etc/os-release for LXC {vmid}...")
|
||||
os_release_result = subprocess.run(
|
||||
['pct', 'exec', str(vmid), '--', 'cat', '/etc/os-release'],
|
||||
capture_output=True, text=True, timeout=5)
|
||||
|
||||
if os_release_result.returncode == 0:
|
||||
# Parse /etc/os-release content
|
||||
for line in os_release_result.stdout.split('\n'):
|
||||
line = line.strip()
|
||||
if line.startswith('ID='):
|
||||
os_info['id'] = line.split('=', 1)[1].strip('"').strip("'")
|
||||
elif line.startswith('VERSION_ID='):
|
||||
os_info['version_id'] = line.split('=', 1)[1].strip('"').strip("'")
|
||||
elif line.startswith('NAME='):
|
||||
os_info['name'] = line.split('=', 1)[1].strip('"').strip("'")
|
||||
elif line.startswith('PRETTY_NAME='):
|
||||
os_info['pretty_name'] = line.split('=', 1)[1].strip('"').strip("'")
|
||||
|
||||
print(f"[v0] OS Info for LXC {vmid}: {os_info}")
|
||||
else:
|
||||
print(f"[v0] Failed to read /etc/os-release for LXC {vmid}: {os_release_result.stderr}")
|
||||
except Exception as e:
|
||||
print(f"[v0] Error reading OS info for LXC {vmid}: {e}")
|
||||
|
||||
response_data = {
|
||||
**resource,
|
||||
'config': config,
|
||||
'node': node,
|
||||
'vm_type': vm_type
|
||||
})
|
||||
}
|
||||
|
||||
# Add OS info if available
|
||||
if os_info:
|
||||
response_data['os_info'] = os_info
|
||||
|
||||
return jsonify(response_data)
|
||||
|
||||
return jsonify({'error': f'VM/LXC {vmid} not found'}), 404
|
||||
else:
|
||||
@@ -4904,7 +4937,7 @@ def api_vm_control(vmid):
|
||||
return jsonify({'error': 'Invalid action'}), 400
|
||||
|
||||
# Get VM type and node
|
||||
result = subprocess.run(['pvesh', 'get', f'/cluster/resources', '--type', 'vm', '--output-format', 'json'],
|
||||
result = subprocess.run(['pvesh', 'get', '/cluster/resources', '--type', 'vm', '--output-format', 'json'],
|
||||
capture_output=True, text=True, timeout=10)
|
||||
|
||||
if result.returncode == 0:
|
||||
|
||||
Reference in New Issue
Block a user