mirror of
				https://github.com/karl0ss/homepage.git
				synced 2025-11-04 00:10:57 +00:00 
			
		
		
		
	Merge pull request #1084 from benphelps/feature-updated-resources
Feature: updated resources widget with cpu temp + uptime
This commit is contained in:
		
						commit
						c649c4ef17
					
				@ -23,7 +23,7 @@
 | 
			
		||||
    "minecraft-ping-js": "^1.0.2",
 | 
			
		||||
    "next": "^12.3.1",
 | 
			
		||||
    "next-i18next": "^12.0.1",
 | 
			
		||||
    "node-os-utils": "^1.3.7",
 | 
			
		||||
    "osx-temperature-sensor": "^1.0.8",
 | 
			
		||||
    "pretty-bytes": "^6.0.0",
 | 
			
		||||
    "raw-body": "^2.5.1",
 | 
			
		||||
    "react": "^18.2.0",
 | 
			
		||||
@ -32,6 +32,7 @@
 | 
			
		||||
    "react-icons": "^4.4.0",
 | 
			
		||||
    "shvl": "^3.0.0",
 | 
			
		||||
    "swr": "^1.3.0",
 | 
			
		||||
    "systeminformation": "^5.17.12",
 | 
			
		||||
    "tough-cookie": "^4.1.2",
 | 
			
		||||
    "winston": "^3.8.2",
 | 
			
		||||
    "xml-js": "^1.6.11"
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										24
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										24
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							@ -25,7 +25,7 @@ specifiers:
 | 
			
		||||
  minecraft-ping-js: ^1.0.2
 | 
			
		||||
  next: ^12.3.1
 | 
			
		||||
  next-i18next: ^12.0.1
 | 
			
		||||
  node-os-utils: ^1.3.7
 | 
			
		||||
  osx-temperature-sensor: ^1.0.8
 | 
			
		||||
  postcss: ^8.4.16
 | 
			
		||||
  prettier: ^2.7.1
 | 
			
		||||
  pretty-bytes: ^6.0.0
 | 
			
		||||
@ -36,6 +36,7 @@ specifiers:
 | 
			
		||||
  react-icons: ^4.4.0
 | 
			
		||||
  shvl: ^3.0.0
 | 
			
		||||
  swr: ^1.3.0
 | 
			
		||||
  systeminformation: ^5.17.12
 | 
			
		||||
  tailwind-scrollbar: ^2.0.1
 | 
			
		||||
  tailwindcss: ^3.1.8
 | 
			
		||||
  tough-cookie: ^4.1.2
 | 
			
		||||
@ -57,7 +58,7 @@ dependencies:
 | 
			
		||||
  minecraft-ping-js: 1.0.2
 | 
			
		||||
  next: 12.3.1_biqbaboplfbrettd7655fr4n2y
 | 
			
		||||
  next-i18next: 12.0.1_azq6kxkn3od7qdylwkyksrwopy
 | 
			
		||||
  node-os-utils: 1.3.7
 | 
			
		||||
  osx-temperature-sensor: 1.0.8
 | 
			
		||||
  pretty-bytes: 6.0.0
 | 
			
		||||
  raw-body: 2.5.1
 | 
			
		||||
  react: 18.2.0
 | 
			
		||||
@ -66,6 +67,7 @@ dependencies:
 | 
			
		||||
  react-icons: 4.4.0_react@18.2.0
 | 
			
		||||
  shvl: 3.0.0
 | 
			
		||||
  swr: 1.3.0_react@18.2.0
 | 
			
		||||
  systeminformation: 5.17.12
 | 
			
		||||
  tough-cookie: 4.1.2
 | 
			
		||||
  winston: 3.8.2
 | 
			
		||||
  xml-js: 1.6.11
 | 
			
		||||
@ -2301,10 +2303,6 @@ packages:
 | 
			
		||||
    resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==}
 | 
			
		||||
    dev: false
 | 
			
		||||
 | 
			
		||||
  /node-os-utils/1.3.7:
 | 
			
		||||
    resolution: {integrity: sha512-fvnX9tZbR7WfCG5BAy3yO/nCLyjVWD6MghEq0z5FDfN+ZXpLWNITBdbifxQkQ25ebr16G0N7eRWJisOcMEHG3Q==}
 | 
			
		||||
    dev: false
 | 
			
		||||
 | 
			
		||||
  /node-releases/2.0.6:
 | 
			
		||||
    resolution: {integrity: sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==}
 | 
			
		||||
    dev: true
 | 
			
		||||
@ -2446,6 +2444,13 @@ packages:
 | 
			
		||||
      word-wrap: 1.2.3
 | 
			
		||||
    dev: true
 | 
			
		||||
 | 
			
		||||
  /osx-temperature-sensor/1.0.8:
 | 
			
		||||
    resolution: {integrity: sha512-Gl3b+bn7+oDDnqPa+4v/cg3yg9lnE8ppS7ivL3opBZh4i7h99JNmkm6zWmo0m2a83UUJu+C9D7lGP0OS8IlehA==}
 | 
			
		||||
    engines: {node: '>=4.0.0'}
 | 
			
		||||
    os: [darwin]
 | 
			
		||||
    requiresBuild: true
 | 
			
		||||
    dev: false
 | 
			
		||||
 | 
			
		||||
  /p-limit/3.1.0:
 | 
			
		||||
    resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==}
 | 
			
		||||
    engines: {node: '>=10'}
 | 
			
		||||
@ -3063,6 +3068,13 @@ packages:
 | 
			
		||||
      react: 18.2.0
 | 
			
		||||
    dev: false
 | 
			
		||||
 | 
			
		||||
  /systeminformation/5.17.12:
 | 
			
		||||
    resolution: {integrity: sha512-I3pfMW2vue53u+X08BNxaJieaHkRoMMKjWetY9lbYJeWFaeWPO6P4FkNc4XOCX8F9vbQ0HqQ25RJoz3U/B7liw==}
 | 
			
		||||
    engines: {node: '>=8.0.0'}
 | 
			
		||||
    os: [darwin, linux, win32, freebsd, openbsd, netbsd, sunos, android]
 | 
			
		||||
    hasBin: true
 | 
			
		||||
    dev: false
 | 
			
		||||
 | 
			
		||||
  /tailwind-scrollbar/2.0.1_tailwindcss@3.1.8:
 | 
			
		||||
    resolution: {integrity: sha512-OcR7qHBbux4k+k6bWqnEQFYFooLK/F4dhkBz6nvswIoaA9ancZ5h20e0tyV7ifSWLDCUBtpG+1NHRA8HMRH/wg==}
 | 
			
		||||
    engines: {node: '>=12.13.0'}
 | 
			
		||||
 | 
			
		||||
@ -36,7 +36,14 @@
 | 
			
		||||
        "total": "Total",
 | 
			
		||||
        "free": "Free",
 | 
			
		||||
        "used": "Used",
 | 
			
		||||
        "load": "Load"
 | 
			
		||||
        "load": "Load",
 | 
			
		||||
        "temp": "TEMP",
 | 
			
		||||
        "max": "Max",
 | 
			
		||||
        "uptime": "UP",
 | 
			
		||||
        "months": "mo",
 | 
			
		||||
        "days": "d",
 | 
			
		||||
        "hours": "h",
 | 
			
		||||
        "minutes": "m"
 | 
			
		||||
    },
 | 
			
		||||
    "unifi": {
 | 
			
		||||
        "users": "Users",
 | 
			
		||||
@ -298,7 +305,11 @@
 | 
			
		||||
    "glances": {
 | 
			
		||||
        "cpu": "CPU",
 | 
			
		||||
        "mem": "MEM",
 | 
			
		||||
        "wait": "Please wait"
 | 
			
		||||
        "wait": "Please wait",
 | 
			
		||||
        "temp": "TEMP",
 | 
			
		||||
        "uptime": "UP",
 | 
			
		||||
        "days": "d",
 | 
			
		||||
        "hours": "h"
 | 
			
		||||
    },
 | 
			
		||||
    "quicklaunch": {
 | 
			
		||||
        "bookmark": "Bookmark",
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,6 @@
 | 
			
		||||
import useSWR from "swr";
 | 
			
		||||
import { BiError } from "react-icons/bi";
 | 
			
		||||
import { FaMemory } from "react-icons/fa";
 | 
			
		||||
import { FaMemory, FaRegClock, FaThermometerHalf } from "react-icons/fa";
 | 
			
		||||
import { FiCpu } from "react-icons/fi";
 | 
			
		||||
import { useTranslation } from "next-i18next";
 | 
			
		||||
 | 
			
		||||
@ -64,6 +64,9 @@ export default function Widget({ options }) {
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const unit = options.units === "imperial" ? "fahrenheit" : "celsius";
 | 
			
		||||
  const mainTemp = (options.cputemp && data.sensors && unit === "celsius") ? data.sensors.find(s => s.label.includes("cpu_thermal")).value : data.sensors.find(s => s.label.includes("cpu_thermal")).value * 5/9 + 32;
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <div className="flex flex-col max-w:full sm:basis-auto self-center grow-0 flex-wrap ml-4">
 | 
			
		||||
      <div className="flex flex-row self-center flex-wrap justify-between">
 | 
			
		||||
@ -73,7 +76,7 @@ export default function Widget({ options }) {
 | 
			
		||||
            <div className="text-theme-800 dark:text-theme-200 text-xs flex flex-row justify-between">
 | 
			
		||||
              <div className="pl-0.5">
 | 
			
		||||
                {t("common.number", {
 | 
			
		||||
                  value: data.cpu,
 | 
			
		||||
                  value: data.quicklook.cpu,
 | 
			
		||||
                  style: "unit",
 | 
			
		||||
                  unit: "percent",
 | 
			
		||||
                  maximumFractionDigits: 0,
 | 
			
		||||
@ -81,7 +84,7 @@ export default function Widget({ options }) {
 | 
			
		||||
              </div>
 | 
			
		||||
              <div className="pr-1">{t("glances.cpu")}</div>
 | 
			
		||||
            </div>
 | 
			
		||||
            <UsageBar percent={data.cpu} />
 | 
			
		||||
            <UsageBar percent={data.quicklook.cpu} />
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div className="flex-none flex flex-row items-center mr-3 py-1.5">
 | 
			
		||||
@ -90,7 +93,7 @@ export default function Widget({ options }) {
 | 
			
		||||
            <div className="text-theme-800 dark:text-theme-200 text-xs flex flex-row justify-between">
 | 
			
		||||
              <div className="pl-0.5">
 | 
			
		||||
                {t("common.number", {
 | 
			
		||||
                  value: data.mem,
 | 
			
		||||
                  value: data.quicklook.mem,
 | 
			
		||||
                  style: "unit",
 | 
			
		||||
                  unit: "percent",
 | 
			
		||||
                  maximumFractionDigits: 0,
 | 
			
		||||
@ -98,9 +101,38 @@ export default function Widget({ options }) {
 | 
			
		||||
              </div>
 | 
			
		||||
              <div className="pr-1">{t("glances.mem")}</div>
 | 
			
		||||
            </div>
 | 
			
		||||
            <UsageBar percent={data.mem} />
 | 
			
		||||
            <UsageBar percent={data.quicklook.mem} />
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
        {options.cputemp &&
 | 
			
		||||
            (<div className="flex-none flex flex-row items-center mr-3 py-1.5">
 | 
			
		||||
            <FaThermometerHalf className="text-theme-800 dark:text-theme-200 w-5 h-5" />
 | 
			
		||||
            <div className="flex flex-col ml-3 text-left min-w-[85px]">
 | 
			
		||||
              <span className="text-theme-800 dark:text-theme-200 text-xs flex flex-row justify-between">
 | 
			
		||||
                <div className="pl-0.5">
 | 
			
		||||
                  {t("common.number", { 
 | 
			
		||||
                    value: mainTemp,
 | 
			
		||||
                    maximumFractionDigits: 1,
 | 
			
		||||
                    style: "unit",
 | 
			
		||||
                    unit
 | 
			
		||||
                  })}
 | 
			
		||||
                </div>
 | 
			
		||||
                <div className="pr-1">{t("glances.temp")}</div>
 | 
			
		||||
              </span>
 | 
			
		||||
            </div>
 | 
			
		||||
          </div>)}
 | 
			
		||||
        {options.uptime && data.uptime &&
 | 
			
		||||
            (<div className="flex-none flex flex-row items-center mr-3 py-1.5">
 | 
			
		||||
            <FaRegClock className="text-theme-800 dark:text-theme-200 w-5 h-5" />
 | 
			
		||||
            <div className="flex flex-col ml-3 text-left min-w-[85px]">
 | 
			
		||||
              <span className="text-theme-800 dark:text-theme-200 text-xs flex flex-row justify-between">
 | 
			
		||||
                <div className="pl-0.5">
 | 
			
		||||
                  {data.uptime.replace(" days,", t("glances.days")).replace(/:\d\d:\d\d$/g, t("glances.hours"))}
 | 
			
		||||
                </div>
 | 
			
		||||
                <div className="pr-1">{t("glances.uptime")}</div>
 | 
			
		||||
              </span>
 | 
			
		||||
            </div>
 | 
			
		||||
          </div>)}
 | 
			
		||||
      </div>
 | 
			
		||||
      {options.label && (
 | 
			
		||||
        <div className="pt-1 text-center text-theme-800 dark:text-theme-200 text-xs">{options.label}</div>
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										79
									
								
								src/components/widgets/resources/cputemp.jsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								src/components/widgets/resources/cputemp.jsx
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,79 @@
 | 
			
		||||
import useSWR from "swr";
 | 
			
		||||
import { FaThermometerHalf } from "react-icons/fa";
 | 
			
		||||
import { BiError } from "react-icons/bi";
 | 
			
		||||
import { useTranslation } from "next-i18next";
 | 
			
		||||
 | 
			
		||||
export default function CpuTemp({ expanded, units }) {
 | 
			
		||||
  const { t } = useTranslation();
 | 
			
		||||
 | 
			
		||||
  const { data, error } = useSWR(`/api/widgets/resources?type=cputemp`, {
 | 
			
		||||
    refreshInterval: 1500,
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  if (error || data?.error) {
 | 
			
		||||
    return (
 | 
			
		||||
      <div className="flex-none flex flex-row items-center mr-3 py-1.5">
 | 
			
		||||
        <BiError className="text-theme-800 dark:text-theme-200 w-5 h-5" />
 | 
			
		||||
        <div className="flex flex-col ml-3 text-left">
 | 
			
		||||
          <span className="text-theme-800 dark:text-theme-200 text-xs">{t("widget.api_error")}</span>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (!data) {
 | 
			
		||||
    return (
 | 
			
		||||
      <div className="flex-none flex flex-row items-center mr-3 py-1.5 animate-pulse">
 | 
			
		||||
        <FaThermometerHalf className="text-theme-800 dark:text-theme-200 w-5 h-5" />
 | 
			
		||||
        <div className="flex flex-col ml-3 text-left min-w-[85px]">
 | 
			
		||||
          <span className="text-theme-800 dark:text-theme-200 text-xs flex flex-row justify-between">
 | 
			
		||||
            <div className="pl-0.5">-</div>
 | 
			
		||||
            <div className="pr-1">{t("resources.temp")}</div>
 | 
			
		||||
          </span>
 | 
			
		||||
          {expanded && (
 | 
			
		||||
            <span className="text-theme-800 dark:text-theme-200 text-xs flex flex-row justify-between">
 | 
			
		||||
              <div className="pl-0.5">-</div>
 | 
			
		||||
              <div className="pr-1">{t("resources.max")}</div>
 | 
			
		||||
            </span>
 | 
			
		||||
          )}
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const unit = units === "imperial" ? "fahrenheit" : "celsius";
 | 
			
		||||
  const mainTemp = (unit === "celsius") ? data.cputemp.main : data.cputemp.main * 5/9 + 32;
 | 
			
		||||
  const maxTemp = (unit === "celsius") ? data.cputemp.max : data.cputemp.max * 5/9 + 32;
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <div className="flex-none flex flex-row items-center mr-3 py-1.5">
 | 
			
		||||
      <FaThermometerHalf className="text-theme-800 dark:text-theme-200 w-5 h-5" />
 | 
			
		||||
      <div className="flex flex-col ml-3 text-left min-w-[85px]">
 | 
			
		||||
        <span className="text-theme-800 dark:text-theme-200 text-xs flex flex-row justify-between">
 | 
			
		||||
          <div className="pl-0.5">
 | 
			
		||||
            {t("common.number", { 
 | 
			
		||||
              value: mainTemp,
 | 
			
		||||
              maximumFractionDigits: 1,
 | 
			
		||||
              style: "unit",
 | 
			
		||||
              unit
 | 
			
		||||
            })}
 | 
			
		||||
          </div>
 | 
			
		||||
          <div className="pr-1">{t("resources.temp")}</div>
 | 
			
		||||
        </span>
 | 
			
		||||
        {expanded && (
 | 
			
		||||
          <span className="text-theme-800 dark:text-theme-200 text-xs flex flex-row justify-between">
 | 
			
		||||
            <div className="pl-0.5">
 | 
			
		||||
              {t("common.number", {
 | 
			
		||||
                value: maxTemp,
 | 
			
		||||
                maximumFractionDigits: 1,
 | 
			
		||||
                style: "unit",
 | 
			
		||||
                unit
 | 
			
		||||
              })}
 | 
			
		||||
            </div>
 | 
			
		||||
            <div className="pr-1">{t("resources.max")}</div>
 | 
			
		||||
          </span>
 | 
			
		||||
        )}
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
@ -44,19 +44,19 @@ export default function Disk({ options, expanded }) {
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const percent = Math.round((data.drive.usedGb / data.drive.totalGb) * 100);
 | 
			
		||||
  const percent = Math.round((data.drive.used / data.drive.size) * 100);
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <div className="flex-none flex flex-row items-center mr-3 py-1.5">
 | 
			
		||||
      <FiHardDrive className="text-theme-800 dark:text-theme-200 w-5 h-5" />
 | 
			
		||||
      <div className="flex flex-col ml-3 text-left min-w-[85px]">
 | 
			
		||||
        <span className="text-theme-800 dark:text-theme-200 text-xs flex flex-row justify-between">
 | 
			
		||||
          <div className="pl-0.5 pr-1">{t("common.bytes", { value: data.drive.freeGb * 1024 * 1024 * 1024 })}</div>
 | 
			
		||||
          <div className="pl-0.5 pr-1">{t("common.bytes", { value: data.drive.available })}</div>
 | 
			
		||||
          <div className="pr-1">{t("resources.free")}</div>
 | 
			
		||||
        </span>
 | 
			
		||||
        {expanded && (
 | 
			
		||||
          <span className="text-theme-800 dark:text-theme-200 text-xs flex flex-row justify-between">
 | 
			
		||||
            <div className="pl-0.5 pr-1">{t("common.bytes", { value: data.drive.totalGb * 1024 * 1024 * 1024 })}</div>
 | 
			
		||||
            <div className="pl-0.5 pr-1">{t("common.bytes", { value: data.drive.size })}</div>
 | 
			
		||||
            <div className="pr-1">{t("resources.total")}</div>
 | 
			
		||||
          </span>
 | 
			
		||||
        )}
 | 
			
		||||
 | 
			
		||||
@ -44,7 +44,7 @@ export default function Memory({ expanded }) {
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const percent = Math.round((data.memory.usedMemMb / data.memory.totalMemMb) * 100);
 | 
			
		||||
  const percent = Math.round((data.memory.used / data.memory.total) * 100);
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <div className="flex-none flex flex-row items-center mr-3 py-1.5">
 | 
			
		||||
@ -52,7 +52,7 @@ export default function Memory({ expanded }) {
 | 
			
		||||
      <div className="flex flex-col ml-3 text-left min-w-[85px]">
 | 
			
		||||
        <span className="text-theme-800 dark:text-theme-200 text-xs flex flex-row justify-between">
 | 
			
		||||
          <div className="pl-0.5 pr-1">
 | 
			
		||||
            {t("common.bytes", { value: data.memory.freeMemMb * 1024 * 1024, maximumFractionDigits: 1, binary: true })}
 | 
			
		||||
            {t("common.bytes", { value: data.memory.free, maximumFractionDigits: 1, binary: true })}
 | 
			
		||||
          </div>
 | 
			
		||||
          <div className="pr-1">{t("resources.free")}</div>
 | 
			
		||||
        </span>
 | 
			
		||||
@ -60,7 +60,7 @@ export default function Memory({ expanded }) {
 | 
			
		||||
          <span className="text-theme-800 dark:text-theme-200 text-xs flex flex-row justify-between">
 | 
			
		||||
            <div className="pl-0.5 pr-1">
 | 
			
		||||
              {t("common.bytes", {
 | 
			
		||||
                value: data.memory.totalMemMb * 1024 * 1024,
 | 
			
		||||
                value: data.memory.total,
 | 
			
		||||
                maximumFractionDigits: 1,
 | 
			
		||||
                binary: true,
 | 
			
		||||
              })}
 | 
			
		||||
 | 
			
		||||
@ -1,9 +1,11 @@
 | 
			
		||||
import Disk from "./disk";
 | 
			
		||||
import Cpu from "./cpu";
 | 
			
		||||
import Memory from "./memory";
 | 
			
		||||
import CpuTemp from "./cputemp";
 | 
			
		||||
import Uptime from "./uptime";
 | 
			
		||||
 | 
			
		||||
export default function Resources({ options }) {
 | 
			
		||||
  const { expanded } = options;
 | 
			
		||||
  const { expanded, units } = options;
 | 
			
		||||
  return (
 | 
			
		||||
    <div className="flex flex-col max-w:full sm:basis-auto self-center grow-0 flex-wrap">
 | 
			
		||||
      <div className="flex flex-row self-center flex-wrap justify-between">
 | 
			
		||||
@ -12,6 +14,8 @@ export default function Resources({ options }) {
 | 
			
		||||
        {Array.isArray(options.disk)
 | 
			
		||||
          ? options.disk.map((disk) => <Disk key={disk} options={{ disk }} expanded={expanded} />)
 | 
			
		||||
          : options.disk && <Disk options={options} expanded={expanded} />}
 | 
			
		||||
        {options.cputemp && <CpuTemp expanded={expanded} units={units} />}
 | 
			
		||||
        {options.uptime && <Uptime />}
 | 
			
		||||
      </div>
 | 
			
		||||
      {options.label && (
 | 
			
		||||
        <div className="ml-6 pt-1 text-center text-theme-800 dark:text-theme-200 text-xs">{options.label}</div>
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										61
									
								
								src/components/widgets/resources/uptime.jsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								src/components/widgets/resources/uptime.jsx
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,61 @@
 | 
			
		||||
import useSWR from "swr";
 | 
			
		||||
import { FaRegClock } from "react-icons/fa";
 | 
			
		||||
import { BiError } from "react-icons/bi";
 | 
			
		||||
import { useTranslation } from "next-i18next";
 | 
			
		||||
 | 
			
		||||
export default function Uptime() {
 | 
			
		||||
  const { t } = useTranslation();
 | 
			
		||||
 | 
			
		||||
  const { data, error } = useSWR(`/api/widgets/resources?type=uptime`, {
 | 
			
		||||
    refreshInterval: 1500,
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  if (error || data?.error) {
 | 
			
		||||
    return (
 | 
			
		||||
      <div className="flex-none flex flex-row items-center mr-3 py-1.5">
 | 
			
		||||
        <BiError className="text-theme-800 dark:text-theme-200 w-5 h-5" />
 | 
			
		||||
        <div className="flex flex-col ml-3 text-left">
 | 
			
		||||
          <span className="text-theme-800 dark:text-theme-200 text-xs">{t("widget.api_error")}</span>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (!data) {
 | 
			
		||||
    return (
 | 
			
		||||
      <div className="flex-none flex flex-row items-center mr-3 py-1.5 animate-pulse">
 | 
			
		||||
        <FaRegClock className="text-theme-800 dark:text-theme-200 w-5 h-5" />
 | 
			
		||||
        <div className="flex flex-col ml-3 text-left min-w-[85px]">
 | 
			
		||||
          <span className="text-theme-800 dark:text-theme-200 text-xs flex flex-row justify-between">
 | 
			
		||||
            <div className="pl-0.5">-</div>
 | 
			
		||||
            <div className="pr-1">{t("resources.temp")}</div>
 | 
			
		||||
          </span>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const mo = Math.floor(data.uptime / (3600 * 24 * 31));
 | 
			
		||||
  const d = Math.floor(data.uptime % (3600 * 24 * 31) / (3600 * 24));
 | 
			
		||||
  const h = Math.floor(data.uptime % (3600 * 24) / 3600);
 | 
			
		||||
  const m = Math.floor(data.uptime % 3600 / 60);
 | 
			
		||||
  
 | 
			
		||||
  let uptime;
 | 
			
		||||
  if (mo > 0) uptime = `${mo}${t("resources.months")} ${d}${t("resources.days")}`;
 | 
			
		||||
  else if (d > 0) uptime = `${d}${t("resources.days")} ${h}${t("resources.hours")}`;
 | 
			
		||||
  else uptime = `${h}${t("resources.hours")} ${m}${t("resources.minutes")}`;
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <div className="flex-none flex flex-row items-center mr-3 py-1.5">
 | 
			
		||||
      <FaRegClock className="text-theme-800 dark:text-theme-200 w-5 h-5" />
 | 
			
		||||
      <div className="flex flex-col ml-3 text-left min-w-[85px]">
 | 
			
		||||
        <span className="text-theme-800 dark:text-theme-200 text-xs flex flex-row justify-between">
 | 
			
		||||
          <div className="pl-0.5">
 | 
			
		||||
            {uptime}
 | 
			
		||||
          </div>
 | 
			
		||||
          <div className="pr-1">{t("resources.uptime")}</div>
 | 
			
		||||
        </span>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
@ -4,19 +4,16 @@ import { getPrivateWidgetOptions } from "utils/config/widget-helpers";
 | 
			
		||||
 | 
			
		||||
const logger = createLogger("glances");
 | 
			
		||||
 | 
			
		||||
export default async function handler(req, res) {
 | 
			
		||||
  const { index } = req.query;
 | 
			
		||||
 | 
			
		||||
  const privateWidgetOptions = await getPrivateWidgetOptions("glances", index);
 | 
			
		||||
  
 | 
			
		||||
async function retrieveFromGlancesAPI(privateWidgetOptions, endpoint) {
 | 
			
		||||
  let errorMessage;
 | 
			
		||||
  const url = privateWidgetOptions?.url;
 | 
			
		||||
  if (!url) {
 | 
			
		||||
    const errorMessage = "Missing Glances URL";
 | 
			
		||||
    errorMessage = "Missing Glances URL";
 | 
			
		||||
    logger.error(errorMessage);
 | 
			
		||||
    return res.status(400).json({ error: errorMessage });
 | 
			
		||||
    throw new Error(errorMessage);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const apiUrl = `${url}/api/3/quicklook`;
 | 
			
		||||
  const apiUrl = `${url}/api/3/${endpoint}`;
 | 
			
		||||
  const headers = {
 | 
			
		||||
    "Accept-Encoding": "application/json"
 | 
			
		||||
  };
 | 
			
		||||
@ -25,16 +22,41 @@ export default async function handler(req, res) {
 | 
			
		||||
  }
 | 
			
		||||
  const params = { method: "GET", headers };
 | 
			
		||||
 | 
			
		||||
  const [status, contentType, data] = await httpProxy(apiUrl, params);
 | 
			
		||||
  const [status, , data] = await httpProxy(apiUrl, params);
 | 
			
		||||
 | 
			
		||||
  if (status === 401) {
 | 
			
		||||
    logger.error("Authorization failure getting data from glances API. Data: %s", data);
 | 
			
		||||
    errorMessage = `Authorization failure getting data from glances API. Data: ${data.toString()}`
 | 
			
		||||
    logger.error(errorMessage);
 | 
			
		||||
    throw new Error(errorMessage);
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  if (status !== 200) {
 | 
			
		||||
    logger.error("HTTP %d getting data from glances API. Data: %s", status, data);
 | 
			
		||||
    errorMessage = `HTTP ${status} getting data from glances API. Data: ${data.toString()}`
 | 
			
		||||
    logger.error(errorMessage);
 | 
			
		||||
    throw new Error(errorMessage);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (contentType) res.setHeader("Content-Type", contentType);
 | 
			
		||||
  return res.status(status).send(data);
 | 
			
		||||
  return JSON.parse(Buffer.from(data).toString());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default async function handler(req, res) {
 | 
			
		||||
  const { index } = req.query;
 | 
			
		||||
 | 
			
		||||
  const privateWidgetOptions = await getPrivateWidgetOptions("glances", index);
 | 
			
		||||
 | 
			
		||||
  try {
 | 
			
		||||
    const quicklookData = await retrieveFromGlancesAPI(privateWidgetOptions, "quicklook");
 | 
			
		||||
 | 
			
		||||
    const data = {
 | 
			
		||||
      quicklook: quicklookData
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    data.uptime = await retrieveFromGlancesAPI(privateWidgetOptions, "uptime");
 | 
			
		||||
    
 | 
			
		||||
    data.sensors = await retrieveFromGlancesAPI(privateWidgetOptions, "sensors");
 | 
			
		||||
 | 
			
		||||
    return res.status(200).send(data);
 | 
			
		||||
  } catch (e) {
 | 
			
		||||
    return res.status(400).json({ error: e.message });
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,15 +1,16 @@
 | 
			
		||||
import { existsSync } from "fs";
 | 
			
		||||
 | 
			
		||||
import { cpu, drive, mem } from "node-os-utils";
 | 
			
		||||
const si = require('systeminformation');
 | 
			
		||||
 | 
			
		||||
export default async function handler(req, res) {
 | 
			
		||||
  const { type, target } = req.query;
 | 
			
		||||
 | 
			
		||||
  if (type === "cpu") {
 | 
			
		||||
    const load = await si.currentLoad();
 | 
			
		||||
    return res.status(200).json({
 | 
			
		||||
      cpu: {
 | 
			
		||||
        usage: await cpu.usage(1000),
 | 
			
		||||
        load: cpu.loadavgTime(5),
 | 
			
		||||
        usage: load.currentLoad,
 | 
			
		||||
        load: load.avgLoad,
 | 
			
		||||
      },
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
@ -21,14 +22,29 @@ export default async function handler(req, res) {
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const fsSize = await si.fsSize();
 | 
			
		||||
 | 
			
		||||
    return res.status(200).json({
 | 
			
		||||
      drive: await drive.info(target || "/"),
 | 
			
		||||
      drive: fsSize.find(fs => fs.mount === target) ?? fsSize.find(fs => fs.mount === "/")
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (type === "memory") {
 | 
			
		||||
    return res.status(200).json({
 | 
			
		||||
      memory: await mem.info(),
 | 
			
		||||
      memory: await si.mem(),
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (type === "cputemp") {
 | 
			
		||||
    return res.status(200).json({
 | 
			
		||||
      cputemp: await si.cpuTemperature(),
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (type === "uptime") {
 | 
			
		||||
    const timeData = await si.time();
 | 
			
		||||
    return res.status(200).json({
 | 
			
		||||
      uptime: timeData.uptime
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user