mirror of
https://github.com/MacRimi/ProxMenux.git
synced 2025-12-14 16:16:21 +00:00
Update AppImage
This commit is contained in:
40
AppImage/app/api/gpu/nvidia/install/route.ts
Normal file
40
AppImage/app/api/gpu/nvidia/install/route.ts
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
import { NextResponse } from "next/server"
|
||||||
|
import { executeScript } from "@/lib/script-executor"
|
||||||
|
|
||||||
|
export async function POST() {
|
||||||
|
try {
|
||||||
|
// Execute the NVIDIA installer script
|
||||||
|
const result = await executeScript("/usr/local/share/proxmenux/scripts/gpu_tpu/nvidia_installer.sh", {
|
||||||
|
env: {
|
||||||
|
EXECUTION_MODE: "web",
|
||||||
|
WEB_LOG: "/tmp/nvidia_web_install.log",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
if (result.exitCode === 0) {
|
||||||
|
return NextResponse.json({
|
||||||
|
success: true,
|
||||||
|
message: "NVIDIA drivers installed successfully",
|
||||||
|
output: result.stdout,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
return NextResponse.json(
|
||||||
|
{
|
||||||
|
success: false,
|
||||||
|
error: "Installation failed",
|
||||||
|
output: result.stderr || result.stdout,
|
||||||
|
},
|
||||||
|
{ status: 500 },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("NVIDIA installation error:", error)
|
||||||
|
return NextResponse.json(
|
||||||
|
{
|
||||||
|
success: false,
|
||||||
|
error: error instanceof Error ? error.message : "Unknown error",
|
||||||
|
},
|
||||||
|
{ status: 500 },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,19 +5,21 @@ import { Badge } from "@/components/ui/badge"
|
|||||||
import { Progress } from "@/components/ui/progress"
|
import { Progress } from "@/components/ui/progress"
|
||||||
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "@/components/ui/dialog"
|
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "@/components/ui/dialog"
|
||||||
import {
|
import {
|
||||||
Thermometer,
|
|
||||||
CpuIcon,
|
|
||||||
Zap,
|
|
||||||
HardDrive,
|
|
||||||
Network,
|
|
||||||
FanIcon,
|
|
||||||
PowerIcon,
|
|
||||||
Battery,
|
|
||||||
Cpu,
|
Cpu,
|
||||||
MemoryStick,
|
HardDrive,
|
||||||
Cpu as Gpu,
|
Thermometer,
|
||||||
|
Zap,
|
||||||
Loader2,
|
Loader2,
|
||||||
|
CpuIcon,
|
||||||
|
Cpu as Gpu,
|
||||||
|
Network,
|
||||||
|
MemoryStick,
|
||||||
|
PowerIcon,
|
||||||
|
FanIcon,
|
||||||
|
Battery,
|
||||||
} from "lucide-react"
|
} from "lucide-react"
|
||||||
|
import { Download } from "lucide-react"
|
||||||
|
import { Button } from "@/components/ui/button"
|
||||||
import useSWR from "swr"
|
import useSWR from "swr"
|
||||||
import { useState, useEffect } from "react"
|
import { useState, useEffect } from "react"
|
||||||
import {
|
import {
|
||||||
@@ -236,6 +238,7 @@ export default function Hardware() {
|
|||||||
const [selectedDisk, setSelectedDisk] = useState<StorageDevice | null>(null)
|
const [selectedDisk, setSelectedDisk] = useState<StorageDevice | null>(null)
|
||||||
const [selectedNetwork, setSelectedNetwork] = useState<PCIDevice | null>(null)
|
const [selectedNetwork, setSelectedNetwork] = useState<PCIDevice | null>(null)
|
||||||
const [selectedUPS, setSelectedUPS] = useState<any>(null)
|
const [selectedUPS, setSelectedUPS] = useState<any>(null)
|
||||||
|
const [installingNvidiaDriver, setInstallingNvidiaDriver] = useState(false)
|
||||||
|
|
||||||
const fetcher = async (url: string) => {
|
const fetcher = async (url: string) => {
|
||||||
const data = await fetchApi(url)
|
const data = await fetchApi(url)
|
||||||
@@ -246,7 +249,7 @@ export default function Hardware() {
|
|||||||
data: hardwareDataSWR,
|
data: hardwareDataSWR,
|
||||||
error: swrError,
|
error: swrError,
|
||||||
isLoading: swrLoading,
|
isLoading: swrLoading,
|
||||||
mutate,
|
mutate: mutateHardware,
|
||||||
} = useSWR<HardwareData>("/api/hardware", fetcher, {
|
} = useSWR<HardwareData>("/api/hardware", fetcher, {
|
||||||
refreshInterval: 30000,
|
refreshInterval: 30000,
|
||||||
revalidateOnFocus: false,
|
revalidateOnFocus: false,
|
||||||
@@ -311,6 +314,34 @@ export default function Hardware() {
|
|||||||
return pciDevice || null
|
return pciDevice || null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleInstallNvidiaDriver = async () => {
|
||||||
|
setInstallingNvidiaDriver(true)
|
||||||
|
try {
|
||||||
|
const response = await fetch("/api/gpu/nvidia/install", {
|
||||||
|
method: "POST",
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error("Failed to start NVIDIA driver installation")
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await response.json()
|
||||||
|
|
||||||
|
// Show success message (you might want to add a toast notification here)
|
||||||
|
alert("NVIDIA driver installation started. Please check the terminal for progress.")
|
||||||
|
|
||||||
|
// Refresh GPU data after installation
|
||||||
|
setTimeout(() => {
|
||||||
|
mutateHardware()
|
||||||
|
}, 2000)
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error installing NVIDIA driver:", error)
|
||||||
|
alert("Failed to start NVIDIA driver installation. Please try manually.")
|
||||||
|
} finally {
|
||||||
|
setInstallingNvidiaDriver(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const hasRealtimeData = (): boolean => {
|
const hasRealtimeData = (): boolean => {
|
||||||
if (!realtimeGPUData) return false
|
if (!realtimeGPUData) return false
|
||||||
|
|
||||||
@@ -1090,11 +1121,31 @@ export default function Hardware() {
|
|||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div className="flex-1">
|
||||||
<h4 className="text-sm font-semibold text-blue-500 mb-1">Extended Monitoring Not Available</h4>
|
<h4 className="text-sm font-semibold text-blue-500 mb-1">Extended Monitoring Not Available</h4>
|
||||||
<p className="text-sm text-muted-foreground">
|
<p className="text-sm text-muted-foreground mb-3">
|
||||||
{getMonitoringToolRecommendation(selectedGPU.vendor)}
|
{getMonitoringToolRecommendation(selectedGPU.vendor)}
|
||||||
</p>
|
</p>
|
||||||
|
{selectedGPU.vendor.toLowerCase().includes("nvidia") && (
|
||||||
|
<Button
|
||||||
|
onClick={handleInstallNvidiaDriver}
|
||||||
|
disabled={installingNvidiaDriver}
|
||||||
|
size="sm"
|
||||||
|
className="bg-blue-500 hover:bg-blue-600 text-white"
|
||||||
|
>
|
||||||
|
{installingNvidiaDriver ? (
|
||||||
|
<>
|
||||||
|
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
||||||
|
Installing...
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<Download className="mr-2 h-4 w-4" />
|
||||||
|
Install NVIDIA Drivers
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -1105,7 +1156,6 @@ export default function Hardware() {
|
|||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|
||||||
|
|
||||||
{/* Power Consumption */}
|
{/* Power Consumption */}
|
||||||
{hardwareDataSWR?.power_meter && (
|
{hardwareDataSWR?.power_meter && (
|
||||||
<Card className="border-border/50 bg-card/50 p-6">
|
<Card className="border-border/50 bg-card/50 p-6">
|
||||||
@@ -1476,8 +1526,6 @@ export default function Hardware() {
|
|||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{/* PCI Devices - Changed to modal */}
|
{/* PCI Devices - Changed to modal */}
|
||||||
{hardwareDataSWR?.pci_devices && hardwareDataSWR.pci_devices.length > 0 && (
|
{hardwareDataSWR?.pci_devices && hardwareDataSWR.pci_devices.length > 0 && (
|
||||||
<Card className="border-border/50 bg-card/50 p-6">
|
<Card className="border-border/50 bg-card/50 p-6">
|
||||||
@@ -1564,8 +1612,6 @@ export default function Hardware() {
|
|||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{/* Network Summary - Clickable */}
|
{/* Network Summary - Clickable */}
|
||||||
{hardwareDataSWR?.pci_devices &&
|
{hardwareDataSWR?.pci_devices &&
|
||||||
hardwareDataSWR.pci_devices.filter((d) => d.type.toLowerCase().includes("network")).length > 0 && (
|
hardwareDataSWR.pci_devices.filter((d) => d.type.toLowerCase().includes("network")).length > 0 && (
|
||||||
|
|||||||
39
AppImage/lib/script-executor.ts
Normal file
39
AppImage/lib/script-executor.ts
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import { exec } from "child_process"
|
||||||
|
import { promisify } from "util"
|
||||||
|
|
||||||
|
const execAsync = promisify(exec)
|
||||||
|
|
||||||
|
interface ScriptExecutorOptions {
|
||||||
|
env?: Record<string, string>
|
||||||
|
timeout?: number
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ScriptResult {
|
||||||
|
stdout: string
|
||||||
|
stderr: string
|
||||||
|
exitCode: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function executeScript(scriptPath: string, options: ScriptExecutorOptions = {}): Promise<ScriptResult> {
|
||||||
|
const { env = {}, timeout = 300000 } = options // 5 minutes default timeout
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { stdout, stderr } = await execAsync(`bash ${scriptPath}`, {
|
||||||
|
env: { ...process.env, ...env },
|
||||||
|
timeout,
|
||||||
|
maxBuffer: 1024 * 1024 * 10, // 10MB buffer
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
stdout,
|
||||||
|
stderr,
|
||||||
|
exitCode: 0,
|
||||||
|
}
|
||||||
|
} catch (error: any) {
|
||||||
|
return {
|
||||||
|
stdout: error.stdout || "",
|
||||||
|
stderr: error.stderr || error.message || "Unknown error",
|
||||||
|
exitCode: error.code || 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user