Procházet zdrojové kódy

resources monitoring

Denys Bashkatov před 1 týdnem
rodič
revize
859606d0f2

+ 1 - 0
Dockerfile

@@ -18,6 +18,7 @@ RUN apt-get update && apt-get install -y \
     unzip \
     ca-certificates \
     tzdata \
+    bc \
     && rm -rf /var/lib/apt/lists/*
 
 # Install skopeo for container image operations

+ 136 - 0
admin/app/components/shared/resource-monitor/resource-monitor.tsx

@@ -0,0 +1,136 @@
+import { useEffect, useState } from "react";
+
+interface ProcessInfo {
+  name: string;
+  status: "running" | "not_running" | "not_found" | "error";
+  ramMb: number;
+  cpuPercent: number;
+}
+
+interface SystemInfo {
+  totalRamMb: number;
+  cpuPercent: number;
+}
+
+interface ResourceData {
+  timestamp: string;
+  system: SystemInfo;
+  processes: ProcessInfo[];
+}
+
+export default function ResourceMonitor() {
+  const [resourceData, setResourceData] = useState<ResourceData | null>(null);
+  const [loading, setLoading] = useState(true);
+  const [error, setError] = useState<string | null>(null);
+
+  const fetchResourceData = async () => {
+    try {
+      const response = await fetch("/api/resources");
+      if (!response.ok) {
+        throw new Error(`HTTP error! status: ${response.status}`);
+      }
+      const data = await response.json();
+      setResourceData(data);
+      setError(null);
+    } catch (err) {
+      console.error("Failed to fetch resource data:", err);
+      setError("Failed to load resource data");
+    } finally {
+      setLoading(false);
+    }
+  };
+
+  useEffect(() => {
+    // Initial fetch
+    fetchResourceData();
+
+    // Set up interval for auto-refresh every 5 seconds
+    const interval = setInterval(fetchResourceData, 5000);
+
+    // Cleanup interval on unmount
+    return () => clearInterval(interval);
+  }, []);
+
+  if (loading) {
+    return (
+      <div className="bg-blue-50 border border-blue-200 rounded-md p-4 mb-4">
+        <div className="text-sm text-blue-600">Loading resource data...</div>
+      </div>
+    );
+  }
+
+  if (error) {
+    return (
+      <div className="bg-red-50 border border-red-200 rounded-md p-4 mb-4">
+        <div className="text-sm text-red-600">{error}</div>
+      </div>
+    );
+  }
+
+  if (!resourceData) {
+    return null;
+  }
+
+  const getStatusColor = (status: string) => {
+    switch (status) {
+      case "running":
+        return "text-green-600";
+      case "not_running":
+        return "text-yellow-600";
+      case "not_found":
+      case "error":
+        return "text-red-600";
+      default:
+        return "text-gray-600";
+    }
+  };
+
+  const getStatusText = (status: string) => {
+    switch (status) {
+      case "running":
+        return "Running";
+      case "not_running":
+        return "Stopped";
+      case "not_found":
+        return "Not Found";
+      case "error":
+        return "Error";
+      default:
+        return "Unknown";
+    }
+  };
+
+  return (
+    <div className="bg-gray-50 border border-gray-200 rounded-md p-[0px] py-[12px] mb-4">
+      <div className="grid grid-cols-1 md:grid-cols-3 gap-[12px] md:gap-[32px]">
+        {resourceData.processes.map((process) => (
+          <div key={process.name} className="px-[12px]">
+            <div className="flex items-center justify-between mb-1">
+              <span className="text-sm font-medium text-gray-700 capitalize">
+                {process.name.replace("-", " ")}
+              </span>
+              <span className={`text-xs font-medium ${getStatusColor(process.status)}`}>
+                {getStatusText(process.status)}
+              </span>
+            </div>
+            
+            <div className="space-y-0.5">
+              <div className="flex justify-between text-xs">
+                <span className="text-gray-500">RAM:</span>
+                <span className="font-medium">
+                  {process.ramMb} MB
+                </span>
+              </div>
+              <div className="flex justify-between text-xs">
+                <span className="text-gray-500">CPU:</span>
+                <span className="font-medium">
+                  {process.cpuPercent.toFixed(1)}%
+                </span>
+              </div>
+            </div>
+          </div>
+        ))}
+      </div>
+    </div>
+  );
+} 

+ 1 - 0
admin/app/config/config.build.json

@@ -3,6 +3,7 @@
   "nginxLogsDir": "/var/log/nginx",
   "filesDir": "/var/www/files",
   "mirroredPackagesDir": "/var/spool/apt-mirror",
+  "resourceMonitorScriptPath": "/usr/local/bin/resource-monitor.sh",
   "hosts": [
     { "id": "mirror", "address": "domain", "name": "APT Mirror", "description": "APT Mirror" },
     { "id": "admin", "address": "admin.domain", "name": "Admin", "description": "Admin UI for the mirror" },

+ 1 - 0
admin/app/config/config.dev.json

@@ -3,6 +3,7 @@
   "nginxLogsDir": "/var/log/nginx",
   "filesDir": "../data/data/files",
   "mirroredPackagesDir": "../data/data/apt-mirror",
+  "resourceMonitorScriptPath": "./scripts/resource-monitor.sh",
   "hosts": [
     { "id": "mirror", "address": "domain", "name": "APT Mirror", "description": "APT Mirror" },
     { "id": "admin", "address": "admin.domain", "name": "Admin", "description": "Admin UI for the mirror" },

+ 1 - 0
admin/app/config/config.json

@@ -3,6 +3,7 @@
   "nginxLogsDir": "/var/log/nginx",
   "filesDir": "../data/data/files",
   "mirroredPackagesDir": "../data/data/apt-mirror",
+  "resourceMonitorScriptPath": "../scripts/resource-monitor.sh",
   "hosts": [
     { "id": "mirror", "address": "domain", "name": "APT Mirror", "description": "APT Mirror" },
     { "id": "admin", "address": "admin.domain", "name": "Admin", "description": "Admin UI for the mirror" },

+ 2 - 1
admin/app/routes.ts

@@ -7,7 +7,8 @@ export default [
       index("routes/home/home.tsx"),
       route("logs/:log", "routes/logs/logs.tsx"),
       route("documentation/:section", "routes/documentation/documentation.tsx"),
-      route("file-manager", "routes/file-manager/file-manager.tsx")
+      route("file-manager", "routes/file-manager/file-manager.tsx"),
+      route("api/resources", "routes/api.resources.tsx")
     ]
   ),
 ] satisfies RouteConfig;

+ 28 - 0
admin/app/routes/api.resources.tsx

@@ -0,0 +1,28 @@
+import { exec } from "child_process";
+import { promisify } from "util";
+import appConfig from "~/config/config.json";
+
+const execAsync = promisify(exec);
+
+export async function loader() {
+  try {
+    const { stdout } = await execAsync(appConfig.resourceMonitorScriptPath);
+    
+    const resourceData = JSON.parse(stdout);
+    
+    return resourceData;
+  } catch (error) {
+    console.error("Error getting resource data:", error);
+    
+    return {
+      error: "Failed to get resource data",
+      timestamp: new Date().toISOString(),
+      system: { totalRamMb: 0, cpuPercent: 0 },
+      processes: [
+        { name: "apt-mirror", status: "error", ramMb: 0, cpuPercent: 0 },
+        { name: "nginx", status: "error", ramMb: 0, cpuPercent: 0 },
+        { name: "admin-app", status: "error", ramMb: 0, cpuPercent: 0 }
+      ]
+    };
+  }
+} 

+ 4 - 0
admin/app/routes/home/home.tsx

@@ -5,6 +5,7 @@ import appConfig from "~/config/config.json";
 import PageLayoutFull from "~/components/shared/layout/page-layout-full";
 import { useEffect, useMemo, useState } from "react";
 import { getHostAddress } from "~/utils/url";
+import ResourceMonitor from "~/components/shared/resource-monitor/resource-monitor";
 
 export function meta({}: Route.MetaArgs) {
   return [
@@ -90,6 +91,9 @@ export default function Home() {
       </div>
 
       <Title title="Services Status" />
+      <div className="px-[12px] md:px-0">
+        <ResourceMonitor />
+      </div>
       <div className="flex flex-row items-center md:gap-[32px] gap-[12px] flex-wrap px-[12px] md:px-0">
         {appConfig.hosts.map((page) => (
           <div key={page.address} className={classNames("h-[120px] md:w-[calc(50%-18px)] w-full lg:w-[calc(33%-17px)] relative bg-gray-100 border border-gray-200 shadow-md rounded-md flex flex-col gap-[12px] p-[12px]", {

+ 92 - 0
scripts/resource-monitor.sh

@@ -0,0 +1,92 @@
+#!/bin/bash
+
+# Resource monitoring script for apt-mirror, nginx, and admin app
+# Returns JSON with process resource usage
+
+# Function to get process info by name
+get_process_info() {
+    local process_name=$1
+    local display_name=$2
+    
+    # Find process by name
+    local pid=$(pgrep -f "$process_name" | head -1)
+    
+    if [ -z "$pid" ]; then
+        echo "{\"name\":\"$display_name\",\"status\":\"not_running\",\"ramMb\":0,\"cpuPercent\":0}"
+        return
+    fi
+    
+    # Get process stats using ps
+    local stats=$(ps -p "$pid" -o pid,ppid,pcpu,pmem,comm --no-headers 2>/dev/null)
+    
+    if [ -z "$stats" ]; then
+        echo "{\"name\":\"$display_name\",\"status\":\"not_found\",\"ramMb\":0,\"cpuPercent\":0}"
+        return
+    fi
+    
+    # Parse stats (format: PID PPID %CPU %MEM COMMAND)
+    local cpu_percent=$(echo "$stats" | awk '{print $3}')
+    local ram_percent=$(echo "$stats" | awk '{print $4}')
+    
+    # Convert RAM percentage to MB (assuming total RAM is available)
+    local total_ram_mb=$(free -m | awk 'NR==2{print $2}')
+    # Set locale to C to ensure decimal points instead of commas
+    export LC_NUMERIC=C
+    local ram_mb=$(echo "$ram_percent * $total_ram_mb / 100" | bc -l 2>/dev/null | cut -d. -f1)
+    
+    # Ensure we have valid numbers
+    if [ -z "$ram_mb" ] || [ "$ram_mb" = "0" ]; then
+        ram_mb=0
+    fi
+    
+    if [ -z "$cpu_percent" ]; then
+        cpu_percent=0
+    fi
+    
+    echo "{\"name\":\"$display_name\",\"status\":\"running\",\"ramMb\":$ram_mb,\"cpuPercent\":$cpu_percent}"
+}
+
+# Function to get system total RAM
+get_system_ram() {
+    local total_ram_mb=$(free -m | awk 'NR==2{print $2}')
+    echo "$total_ram_mb"
+}
+
+# Function to get system total CPU usage
+get_system_cpu() {
+    # Set locale to C to ensure decimal points instead of commas
+    export LC_NUMERIC=C
+    local cpu_usage=$(top -bn1 | grep "Cpu(s)" | awk '{print $2}' | cut -d'%' -f1)
+    echo "$cpu_usage"
+}
+
+# Main execution
+main() {
+    # Get system info
+    local total_ram=$(get_system_ram)
+    local system_cpu=$(get_system_cpu)
+    
+    # Get process info for each service
+    local apt_mirror_info=$(get_process_info "apt-mirror" "apt-mirror")
+    local nginx_info=$(get_process_info "nginx" "nginx")
+    local admin_info=$(get_process_info "react-router-serve" "admin-app")
+    
+    # Create JSON response
+    cat << EOF
+{
+  "timestamp": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
+  "system": {
+    "totalRamMb": $total_ram,
+    "cpuPercent": $system_cpu
+  },
+  "processes": [
+    $apt_mirror_info,
+    $nginx_info,
+    $admin_info
+  ]
+}
+EOF
+}
+
+# Run main function
+main