Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
221 changes: 212 additions & 9 deletions src/class/Server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,17 @@ export default class Server {
configClass: ConfigServer;
console: ServerConsole;
docker: Docker;
systemMonitor: SystemMonitor;

readonly serversStore = useServerConfigStore();

constructor(id: string) {
this.id = id;
this.configClass = new ConfigServer(this.serversStore.getServer(id));
this.console = new ServerConsole(this);
this.docker = new Docker(this);
this.systemMonitor = new SystemMonitor(this);

}

config(): ConfigServer {
Expand Down Expand Up @@ -193,6 +197,7 @@ class Docker {
try {
const output = await this.server.console.execute("docker version");
const lines = output.split("\n");

let foundCommunity = false;
let version = null;

Expand Down Expand Up @@ -240,10 +245,11 @@ class Docker {
const command = `echo "===CONTAINERS===" && docker ps -a --format "{{.Status}}" && echo "===IMAGES===" && docker images --format "{{.Repository}}" && echo "===DANGLING===" && docker images -f dangling=true --format "{{.Repository}}" && echo "===SIZE===" && docker system df`;

const output = await this.server.console.execute(command);
console.log("Docker command output:", output); // Debug log
console.log("Docker command output:", output);

const lines = output.split('\n');
let currentSection = '';

const lines = output.split("\n");
let currentSection = "";
const containers: string[] = [];
const images: string[] = [];
const dangling: string[] = [];
Expand Down Expand Up @@ -277,8 +283,8 @@ class Docker {
case "dangling":
dangling.push(trimmedLine);
break;
case "size":
if (trimmedLine.includes("Images")) {
case 'size':
if (trimmedLine.includes('Images')) {
const sizeMatch = trimmedLine.match(/(\d+\.?\d*[GMK]?B)/);
if (sizeMatch) {
size = sizeMatch[1];
Expand All @@ -292,7 +298,7 @@ class Docker {
let running = 0;
let stopped = 0;
for (const container of containers) {
if (container.includes("Up")) {
if (container.includes('Up')) {
running++;
} else {
stopped++;
Expand All @@ -303,19 +309,216 @@ class Docker {
containers: {
running,
stopped,
total: running + stopped,
total: running + stopped
},
images: {
local: images.length,
size,
dangling: dangling.length,
},
dangling: dangling.length
}
};
} catch (error) {
console.error("Error retrieving Docker data:", error);
return {
containers: { running: 0, stopped: 0, total: 0 },
images: { local: 0, size: "0B", dangling: 0 },

};
}
}
}

class SystemMonitor {
readonly server: Server;

constructor(server: Server) {
this.server = server;
}

async getSystemInfo(): Promise<{
hostname: string;
uptime: string;
os: string;
kernel: string;
architecture: string;
}> {
try {
const commands = [
"hostname",
"uptime -p",
"uname -s",
"uname -r",
"uname -m",
];

const results = await Promise.all(
commands.map((cmd) => this.server.console.execute(cmd)),
);

return {
hostname: results[0].trim(),
uptime: results[1].trim(),
os: results[2].trim(),
kernel: results[3].trim(),
architecture: results[4].trim(),
};
} catch (error) {
console.error("Error getting system info:", error);
return {
hostname: "Unknown",
uptime: "Unknown",
os: "Unknown",
kernel: "Unknown",
architecture: "Unknown",
};
}
}

async getSystemStats(): Promise<{
cpu: {
usage: number;
cores: number;
load: number[];
};
memory: {
total: number;
used: number;
free: number;
cached: number;
percentage: number;
};
disk: {
total: number;
used: number;
free: number;
percentage: number;
};
network: {
rx: number;
tx: number;
rxRate: number;
txRate: number;
};
}> {
try {
const command = `
echo "===CPU==="
# CPU usage from /proc/stat
head -1 /proc/stat | awk '{idle=\$5+\$6; total=\$2+\$3+\$4+\$5+\$6+\$7+\$8; print (1-idle/total)*100}'
nproc
uptime | awk -F'load average:' '{print \$2}' | awk '{print \$1","\$2","\$3}' | sed 's/,/ /g'
echo "===MEMORY==="
free -m | grep Mem | awk '{print \$2","\$3","\$4","\$6}'
echo "===DISK==="
df / | tail -1 | awk '{total=\$2/1024/1024; used=\$3/1024/1024; free=\$4/1024/1024; print total","used","free}'
echo "===NETWORK==="
# Get network stats from first active interface
cat /proc/net/dev | grep -E "(eth|ens|enp|wlan|wlp)" | head -1 | awk '{print \$2","\$10}' || echo "0,0"
`;

const output = await this.server.console.execute(command);
console.log("System stats command output:", output); // Debug log

const lines = output.split("\n");
let currentSection = "";
const cpuData: string[] = [];
const memoryData: string[] = [];
const diskData: string[] = [];
const networkData: string[] = [];

for (const line of lines) {
const trimmedLine = line.trim();

if (trimmedLine === "===CPU===") {
currentSection = "cpu";
continue;
} else if (trimmedLine === "===MEMORY===") {
currentSection = "memory";
continue;
} else if (trimmedLine === "===DISK===") {
currentSection = "disk";
continue;
} else if (trimmedLine === "===NETWORK===") {
currentSection = "network";
continue;
}

if (trimmedLine && !trimmedLine.includes("===")) {
switch (currentSection) {
case "cpu":
cpuData.push(trimmedLine);
break;
case "memory":
memoryData.push(trimmedLine);
break;
case "disk":
diskData.push(trimmedLine);
break;
case "network":
networkData.push(trimmedLine);
break;
}
}
}

const cpuUsage = parseFloat(cpuData[0] || "0");
const cores = parseInt(cpuData[1] || "1");
const loadAvgStr = cpuData[2] || "0 0 0";
const loadAvg = loadAvgStr
.split(" ")
.map(parseFloat)
.filter((n) => !isNaN(n));

const memoryStr = memoryData[0] || "0,0,0,0";
const memoryValues = memoryStr.split(",").map(Number);
const [totalMB, usedMB, freeMB, cachedMB] = memoryValues;
const memoryPercentage = totalMB > 0 ? (usedMB / totalMB) * 100 : 0;

const diskStr = diskData[0] || "0,0,0";
const diskValues = diskStr.split(",").map(Number);
const [totalGB, usedGB, freeGB] = diskValues;
const diskPercentage = totalGB > 0 ? (usedGB / totalGB) * 100 : 0;

const networkStr = networkData[0] || "0,0";
const networkValues = networkStr.split(",").map((val) => {
const num = Number(val);
return isNaN(num) ? 0 : num;
});
const [rxBytes, txBytes] = networkValues;

return {
cpu: {
usage: cpuUsage,
cores: cores,
load: loadAvg.length >= 3 ? loadAvg : [0, 0, 0],
},
memory: {
total: totalMB,
used: usedMB,
free: freeMB,
cached: cachedMB,
percentage: memoryPercentage,
},
disk: {
total: totalGB,
used: usedGB,
free: freeGB,
percentage: diskPercentage,
},
network: {
rx: rxBytes,
tx: txBytes,
rxRate: 0,
txRate: 0,
},
};
} catch (error) {
console.error("Error getting system stats:", error);
return {
cpu: { usage: 0, cores: 1, load: [0, 0, 0] },
memory: { total: 0, used: 0, free: 0, cached: 0, percentage: 0 },
disk: { total: 0, used: 0, free: 0, percentage: 0 },
network: { rx: 0, tx: 0, rxRate: 0, txRate: 0 },
};
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/pages/server/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ const tools = [
name: "System Monitor",
desc: "Monitor system resources",
icon: "mdi:chart-line",
disabled: true,
click: () => {},
tag: "Beta",
click: () => router.push(`/server/${server.value?.id}/monitor`),
},
{
name: "Logs Viewer",
Expand Down
Loading