kopie van
				https://github.com/karl0ss/homepage.git
				synced 2025-10-27 04:23:57 +00:00 
			
		
		
		
	Enhanced glances widget (#1534)
* Enhanced glances widget (resource match) * Make widget clickable + cleanup helperrs * Prevent unused glances API calls --------- Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com>
This commit is contained in:
		
							bovenliggende
							
								
									3bc750bfe7
								
							
						
					
					
						commit
						cdd7b2d44b
					
				| @ -357,10 +357,14 @@ | |||||||
|     }, |     }, | ||||||
|     "glances": { |     "glances": { | ||||||
|         "cpu": "CPU", |         "cpu": "CPU", | ||||||
|         "mem": "MEM", |         "load": "Load", | ||||||
|         "wait": "Please wait", |         "wait": "Please wait", | ||||||
|         "temp": "TEMP", |         "temp": "TEMP", | ||||||
|  |         "warn": "Warn", | ||||||
|         "uptime": "UP", |         "uptime": "UP", | ||||||
|  |         "total": "Total", | ||||||
|  |         "free": "Free", | ||||||
|  |         "used": "Used", | ||||||
|         "days": "d", |         "days": "d", | ||||||
|         "hours": "h" |         "hours": "h" | ||||||
|     }, |     }, | ||||||
|  | |||||||
| @ -1,11 +1,14 @@ | |||||||
| import useSWR from "swr"; | import useSWR from "swr"; | ||||||
|  | import { useContext } from "react"; | ||||||
| import { BiError } from "react-icons/bi"; | import { BiError } from "react-icons/bi"; | ||||||
| import { FaMemory, FaRegClock, FaThermometerHalf } from "react-icons/fa"; | import { FaMemory, FaRegClock, FaThermometerHalf } from "react-icons/fa"; | ||||||
| import { FiCpu } from "react-icons/fi"; | import { FiCpu, FiHardDrive } from "react-icons/fi"; | ||||||
| import { useTranslation } from "next-i18next"; | import { useTranslation } from "next-i18next"; | ||||||
| 
 | 
 | ||||||
| import UsageBar from "../resources/usage-bar"; | import UsageBar from "../resources/usage-bar"; | ||||||
| 
 | 
 | ||||||
|  | import { SettingsContext } from "utils/contexts/settings"; | ||||||
|  | 
 | ||||||
| const cpuSensorLabels = ["cpu_thermal", "Core", "Tctl"]; | const cpuSensorLabels = ["cpu_thermal", "Core", "Tctl"]; | ||||||
| 
 | 
 | ||||||
| function convertToFahrenheit(t) { | function convertToFahrenheit(t) { | ||||||
| @ -14,6 +17,7 @@ function convertToFahrenheit(t) { | |||||||
| 
 | 
 | ||||||
| export default function Widget({ options }) { | export default function Widget({ options }) { | ||||||
|   const { t, i18n } = useTranslation(); |   const { t, i18n } = useTranslation(); | ||||||
|  |   const { settings } = useContext(SettingsContext); | ||||||
| 
 | 
 | ||||||
|   const { data, error } = useSWR( |   const { data, error } = useSWR( | ||||||
|     `/api/widgets/glances?${new URLSearchParams({ lang: i18n.language, ...options }).toString()}`, { |     `/api/widgets/glances?${new URLSearchParams({ lang: i18n.language, ...options }).toString()}`, { | ||||||
| @ -88,8 +92,16 @@ export default function Widget({ options }) { | |||||||
|   } |   } | ||||||
|   const tempPercent = Math.round((mainTemp / maxTemp) * 100); |   const tempPercent = Math.round((mainTemp / maxTemp) * 100); | ||||||
| 
 | 
 | ||||||
|  |   let disks = []; | ||||||
|  | 
 | ||||||
|  |   if (options.disk) { | ||||||
|  |     disks = Array.isArray(options.disk) | ||||||
|  |       ? options.disk.map((disk) => data.fs.find((d) => d.mnt_point === disk)).filter((d) => d) | ||||||
|  |       : [data.fs.find((d) => d.mnt_point === options.disk)].filter((d) => d); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   return ( |   return ( | ||||||
|     <div className="flex flex-col max-w:full sm:basis-auto self-center grow-0 flex-wrap ml-4"> |     <a href={options.url} target={settings.target ?? "_blank"} 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"> |       <div className="flex flex-row self-center flex-wrap justify-between"> | ||||||
|          <div className="flex-none flex flex-row items-center mr-3 py-1.5"> |          <div className="flex-none flex flex-row items-center mr-3 py-1.5"> | ||||||
|           <FiCpu className="text-theme-800 dark:text-theme-200 w-5 h-5" /> |           <FiCpu className="text-theme-800 dark:text-theme-200 w-5 h-5" /> | ||||||
| @ -97,7 +109,7 @@ export default function Widget({ options }) { | |||||||
|             <div className="text-theme-800 dark:text-theme-200 text-xs flex flex-row justify-between"> |             <div className="text-theme-800 dark:text-theme-200 text-xs flex flex-row justify-between"> | ||||||
|               <div className="pl-0.5"> |               <div className="pl-0.5"> | ||||||
|                 {t("common.number", { |                 {t("common.number", { | ||||||
|                   value: data.quicklook.cpu, |                   value: data.cpu.total, | ||||||
|                   style: "unit", |                   style: "unit", | ||||||
|                   unit: "percent", |                   unit: "percent", | ||||||
|                   maximumFractionDigits: 0, |                   maximumFractionDigits: 0, | ||||||
| @ -105,7 +117,20 @@ export default function Widget({ options }) { | |||||||
|               </div> |               </div> | ||||||
|               <div className="pr-1">{t("glances.cpu")}</div> |               <div className="pr-1">{t("glances.cpu")}</div> | ||||||
|             </div> |             </div> | ||||||
|             <UsageBar percent={data.quicklook.cpu} /> |             {options.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.number", { | ||||||
|  |                   value: data.load.min15, | ||||||
|  |                   style: "unit", | ||||||
|  |                   unit: "percent", | ||||||
|  |                   maximumFractionDigits: 0, | ||||||
|  |                 })} | ||||||
|  |                 </div> | ||||||
|  |                 <div className="pr-1">{t("glances.load")}</div> | ||||||
|  |               </span> | ||||||
|  |             )} | ||||||
|  |             <UsageBar percent={data.cpu.total} /> | ||||||
|           </div> |           </div> | ||||||
|         </div> |         </div> | ||||||
|         <div className="flex-none flex flex-row items-center mr-3 py-1.5"> |         <div className="flex-none flex flex-row items-center mr-3 py-1.5"> | ||||||
| @ -113,18 +138,46 @@ export default function Widget({ options }) { | |||||||
|           <div className="flex flex-col ml-3 text-left min-w-[85px]"> |           <div className="flex flex-col ml-3 text-left min-w-[85px]"> | ||||||
|             <div className="text-theme-800 dark:text-theme-200 text-xs flex flex-row justify-between"> |             <div className="text-theme-800 dark:text-theme-200 text-xs flex flex-row justify-between"> | ||||||
|               <div className="pl-0.5"> |               <div className="pl-0.5"> | ||||||
|                 {t("common.number", { |                 {t("common.bytes", { | ||||||
|                   value: data.quicklook.mem, |                   value: data.mem.free, | ||||||
|                   style: "unit", |                   maximumFractionDigits: 1, | ||||||
|                   unit: "percent", |                   binary: true, | ||||||
|                   maximumFractionDigits: 0, |  | ||||||
|                 })} |                 })} | ||||||
|               </div> |               </div> | ||||||
|               <div className="pr-1">{t("glances.mem")}</div> |               <div className="pr-1">{t("glances.free")}</div> | ||||||
|             </div> |             </div> | ||||||
|             <UsageBar percent={data.quicklook.mem} /> |             {options.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.mem.total, | ||||||
|  |                     maximumFractionDigits: 1, | ||||||
|  |                     binary: true, | ||||||
|  |                   })} | ||||||
|  |                 </div> | ||||||
|  |                 <div className="pr-1">{t("glances.total")}</div> | ||||||
|  |               </span> | ||||||
|  |             )} | ||||||
|  |             <UsageBar percent={data.mem.percent} /> | ||||||
|           </div> |           </div> | ||||||
|         </div> |         </div> | ||||||
|  |         {disks.map((disk) => ( | ||||||
|  |           <div key={disk.mnt_point} 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">{t("common.bytes", { value: disk.free })}</div> | ||||||
|  |                 <div className="pr-1">{t("glances.free")}</div> | ||||||
|  |               </span> | ||||||
|  |               {options.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: disk.size })}</div> | ||||||
|  |                   <div className="pr-1">{t("glances.total")}</div> | ||||||
|  |                 </span> | ||||||
|  |               )} | ||||||
|  |               <UsageBar percent={disk.percent} /> | ||||||
|  |             </div> | ||||||
|  |           </div>))} | ||||||
|         {options.cputemp && mainTemp > 0 && |         {options.cputemp && mainTemp > 0 && | ||||||
|             (<div className="flex-none flex flex-row items-center mr-3 py-1.5"> |             (<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" /> |             <FaThermometerHalf className="text-theme-800 dark:text-theme-200 w-5 h-5" /> | ||||||
| @ -140,6 +193,19 @@ export default function Widget({ options }) { | |||||||
|                 </div> |                 </div> | ||||||
|                 <div className="pr-1">{t("glances.temp")}</div> |                 <div className="pr-1">{t("glances.temp")}</div> | ||||||
|               </span> |               </span> | ||||||
|  |               {options.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.number", {  | ||||||
|  |                     value: maxTemp, | ||||||
|  |                     maximumFractionDigits: 1, | ||||||
|  |                     style: "unit", | ||||||
|  |                     unit | ||||||
|  |                   })} | ||||||
|  |                   </div> | ||||||
|  |                   <div className="pr-1">{t("glances.warn")}</div> | ||||||
|  |                 </span> | ||||||
|  |               )} | ||||||
|               <UsageBar percent={tempPercent} /> |               <UsageBar percent={tempPercent} /> | ||||||
|             </div> |             </div> | ||||||
|           </div>)} |           </div>)} | ||||||
| @ -160,6 +226,6 @@ export default function Widget({ options }) { | |||||||
|       {options.label && ( |       {options.label && ( | ||||||
|         <div className="pt-1 text-center text-theme-800 dark:text-theme-200 text-xs">{options.label}</div> |         <div className="pt-1 text-center text-theme-800 dark:text-theme-200 text-xs">{options.label}</div> | ||||||
|       )} |       )} | ||||||
|     </div> |     </a> | ||||||
|   ); |   ); | ||||||
| } | } | ||||||
|  | |||||||
| @ -40,20 +40,32 @@ async function retrieveFromGlancesAPI(privateWidgetOptions, endpoint) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export default async function handler(req, res) { | export default async function handler(req, res) { | ||||||
|   const { index } = req.query; |   const { index, cputemp: includeCpuTemp, uptime: includeUptime, disk: includeDisks } = req.query; | ||||||
| 
 | 
 | ||||||
|   const privateWidgetOptions = await getPrivateWidgetOptions("glances", index); |   const privateWidgetOptions = await getPrivateWidgetOptions("glances", index); | ||||||
| 
 | 
 | ||||||
|   try { |   try { | ||||||
|     const quicklookData = await retrieveFromGlancesAPI(privateWidgetOptions, "quicklook"); |     const cpuData = await retrieveFromGlancesAPI(privateWidgetOptions, "cpu"); | ||||||
| 
 |     const loadData = await retrieveFromGlancesAPI(privateWidgetOptions, "load"); | ||||||
|  |     const memoryData = await retrieveFromGlancesAPI(privateWidgetOptions, "mem"); | ||||||
|     const data = { |     const data = { | ||||||
|       quicklook: quicklookData |       cpu: cpuData, | ||||||
|  |       load: loadData, | ||||||
|  |       mem: memoryData, | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     // Disabled by default, dont call unless needed
 | ||||||
|  |     if (includeUptime) { | ||||||
|       data.uptime = await retrieveFromGlancesAPI(privateWidgetOptions, "uptime"); |       data.uptime = await retrieveFromGlancesAPI(privateWidgetOptions, "uptime"); | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|  |     if (includeCpuTemp) { | ||||||
|       data.sensors = await retrieveFromGlancesAPI(privateWidgetOptions, "sensors"); |       data.sensors = await retrieveFromGlancesAPI(privateWidgetOptions, "sensors"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (includeDisks) { | ||||||
|  |       data.fs = await retrieveFromGlancesAPI(privateWidgetOptions, "fs"); | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     return res.status(200).send(data); |     return res.status(200).send(data); | ||||||
|   } catch (e) { |   } catch (e) { | ||||||
|  | |||||||
| @ -5,8 +5,6 @@ import yaml from "js-yaml"; | |||||||
| 
 | 
 | ||||||
| import checkAndCopyConfig, { substituteEnvironmentVars } from "utils/config/config"; | import checkAndCopyConfig, { substituteEnvironmentVars } from "utils/config/config"; | ||||||
| 
 | 
 | ||||||
| const exemptWidgets = ["search"]; |  | ||||||
| 
 |  | ||||||
| export async function widgetsFromConfig() { | export async function widgetsFromConfig() { | ||||||
|     checkAndCopyConfig("widgets.yaml"); |     checkAndCopyConfig("widgets.yaml"); | ||||||
| 
 | 
 | ||||||
| @ -32,15 +30,17 @@ export async function cleanWidgetGroups(widgets) { | |||||||
|     return widgets.map((widget, index) => { |     return widgets.map((widget, index) => { | ||||||
|         const sanitizedOptions = widget.options; |         const sanitizedOptions = widget.options; | ||||||
|         const optionKeys = Object.keys(sanitizedOptions); |         const optionKeys = Object.keys(sanitizedOptions); | ||||||
|         if (!exemptWidgets.includes(widget.type)) { |          | ||||||
|             ["url", "username", "password", "key"].forEach((pO) => {  |         // delete private options from the sanitized options
 | ||||||
|  |         ["username", "password", "key"].forEach((pO) => {  | ||||||
|             if (optionKeys.includes(pO)) { |             if (optionKeys.includes(pO)) { | ||||||
|                     // allow URL in search
 |  | ||||||
|                     if (widget.type !== "search" && pO !== "key") { |  | ||||||
|                 delete sanitizedOptions[pO]; |                 delete sanitizedOptions[pO]; | ||||||
|             } |             } | ||||||
|                 } |  | ||||||
|         }); |         }); | ||||||
|  |          | ||||||
|  |         // delete url from the sanitized options if the widget is not a search or glances widgeth
 | ||||||
|  |         if (widget.type !== "search" && widget.type !== "glances" && optionKeys.includes("url")) { | ||||||
|  |             delete sanitizedOptions.url; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         return { |         return { | ||||||
|  | |||||||
		Laden…
	
	
			
			x
			
			
		
	
		Verwijs in nieuw issue
	
	Block a user
	 Georges-Antoine Assi
						Georges-Antoine Assi