diff --git a/app/api/stats/route.ts b/app/api/stats/route.ts index edf2afe..fbe1a20 100644 --- a/app/api/stats/route.ts +++ b/app/api/stats/route.ts @@ -1,205 +1,7 @@ -import { exec } from "child_process"; -import { promisify } from "util"; -import fs from "fs/promises"; -import { NextResponse } from "next/server"; - -const execAsync = promisify(exec); - -/** - * Helper to read files asynchronously - */ -async function readFile(path: string): Promise { - try { - const data = await fs.readFile(path, "utf8"); - return data.trim(); - } catch { - return null; - } -} - -/** - * Helper to run shell commands asynchronously - */ -async function runCommand(cmd: string): Promise { - try { - const { stdout } = await execAsync(cmd, { timeout: 3000 }); - return stdout.trim(); - } catch { - return null; - } -} - -async function getMemory() { - const raw = await readFile("/proc/meminfo"); - if (!raw) return null; - const lines: Record = {}; - for (const line of raw.split("\n")) { - const [key, val] = line.split(":"); - if (key && val) { - lines[key.trim()] = parseInt(val.trim()); - } - } - const total = lines["MemTotal"] ?? 0; - const available = lines["MemAvailable"] ?? 0; - const used = total - available; - return { - total: Math.round(total / 1024), - used: Math.round(used / 1024), - available: Math.round(available / 1024), - percent: Math.round((used / total) * 100), - }; -} - -async function getCpu() { - const sample = async () => { - const raw = await readFile("/proc/stat"); - if (!raw) return null; - const line = raw.split("\n")[0]; - const parts = line.split(/\s+/).slice(1).map(Number); - const idle = parts[3]; - const total = parts.reduce((a, b) => a + b, 0); - return { idle, total }; - }; - - const s1 = await sample(); - // Non-blocking delay (200ms) to calculate CPU delta - await new Promise((resolve) => setTimeout(resolve, 200)); - const s2 = await sample(); - - if (!s1 || !s2) return null; - - const idleDiff = s2.idle - s1.idle; - const totalDiff = s2.total - s1.total; - const percent = Math.round((1 - idleDiff / totalDiff) * 100); - - // Parse CPU Info without spawning extra shell processes - const cpuInfo = (await readFile("/proc/cpuinfo")) || ""; - const model = cpuInfo.match(/model name\s+:\s+(.*)/)?.[1] || "Unknown"; - const cores = cpuInfo.split("\n").filter((l) => l.startsWith("processor")).length; - - return { - percent, - model: model.trim(), - cores, - }; -} - -async function getTemperature() { - const paths = [ - "/sys/class/thermal/thermal_zone0/temp", - "/sys/class/thermal/thermal_zone1/temp", - "/sys/class/hwmon/hwmon0/temp1_input", - ]; - for (const path of paths) { - const raw = await readFile(path); - if (raw) return Math.round(parseInt(raw) / 1000); - } - - const sensors = await runCommand("sensors 2>/dev/null | grep 'Core 0' | head -1"); - if (sensors) { - const match = sensors.match(/\+(\d+\.\d+)/); - if (match) return parseFloat(match[1]); - } - return null; -} - -async function getDisk() { - const raw = await runCommand("df -B1 /"); - if (!raw) return null; - const lines = raw.split("\n"); - const parts = lines[1].split(/\s+/); - const total = parseInt(parts[1]); - const used = parseInt(parts[2]); - const available = parseInt(parts[3]); - return { - total: Math.round(total / 1024 / 1024 / 1024), - used: Math.round(used / 1024 / 1024 / 1024), - available: Math.round(available / 1024 / 1024 / 1024), - percent: Math.round((used / total) * 100), - }; -} - -async function getUptime() { - const raw = await readFile("/proc/uptime"); - if (!raw) return null; - const seconds = parseFloat(raw.split(" ")[0]); - const days = Math.floor(seconds / 86400); - const hours = Math.floor((seconds % 86400) / 3600); - const minutes = Math.floor((seconds % 3600) / 60); - return { seconds, days, hours, minutes }; -} - -async function getNetwork() { - const raw = await readFile("/proc/net/dev"); - if (!raw) return null; - const ifaces: Record = {}; - for (const line of raw.split("\n").slice(2)) { - const parts = line.trim().split(/\s+/); - if (parts.length < 10) continue; - const name = parts[0].replace(":", ""); - if (name === "lo") continue; - ifaces[name] = { - rx: parseInt(parts[1]), - tx: parseInt(parts[9]), - }; - } - return ifaces; -} - -async function getServices() { - const services = ["caddy", "syncthing", "sshd", "cloudflare-dyndns"]; - const result: Record = {}; - - // Run all status checks in parallel - await Promise.all( - services.map(async (svc) => { - const status = await runCommand(`systemctl is-active ${svc}`); - result[svc] = status ?? "unknown"; - }) - ); - - return result; -} - -async function getLoadAverage() { - const raw = await readFile("/proc/loadavg"); - if (!raw) return null; - const parts = raw.split(" "); - return { - "1m": parseFloat(parts[0]), - "5m": parseFloat(parts[1]), - "15m": parseFloat(parts[2]), - }; -} +import { NextResponse } from 'next/server'; export async function GET() { - try { - // Parallelize all data gathering - const [memory, cpu, disk, uptime, network, services, loadAvg, temperature] = - await Promise.all([ - getMemory(), - getCpu(), - getDisk(), - getUptime(), - getNetwork(), - getServices(), - getLoadAverage(), - getTemperature(), - ]); - - return NextResponse.json({ - timestamp: new Date().toISOString(), - memory, - cpu, - disk, - uptime, - network, - services, - loadAvg, - temperature, - }); - } catch (error) { - console.error("API Error:", error); - return NextResponse.json({ error: "Internal Server Error" }, { status: 500 }); - } + const res = await fetch('http://localhost:3001/stats'); + const data = await res.json(); + return NextResponse.json(data); } diff --git a/app/components/StatsGrid.tsx b/app/components/StatsGrid.tsx index 3b8be57..35fdf9b 100644 --- a/app/components/StatsGrid.tsx +++ b/app/components/StatsGrid.tsx @@ -10,22 +10,22 @@ export default function StatsGrid({ stats }: StatsGridProps) {
diff --git a/next.config.ts b/next.config.ts index c7b8d52..2a9fe71 100644 --- a/next.config.ts +++ b/next.config.ts @@ -1,7 +1,7 @@ import type { NextConfig } from "next"; const nextConfig: NextConfig = { - allowedDevOrigins: ['dashboard.jackmechem.dev'], + allowedDevOrigins: ['dashboard.jackmechem.dev', 'localhost'], output: 'standalone', };