mirror of
https://github.com/ajnart/homarr.git
synced 2026-01-31 03:39:21 +01:00
Co-authored-by: Crowdin Homarr <190541745+homarr-crowdin[bot]@users.noreply.github.com> Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com> Co-authored-by: homarr-crowdin[bot] <190541745+homarr-crowdin[bot]@users.noreply.github.com> Co-authored-by: Meier Lukas <meierschlumpf@gmail.com>
81 lines
2.8 KiB
TypeScript
81 lines
2.8 KiB
TypeScript
import dayjs from "dayjs";
|
|
import type { ContainerInfo, ContainerStats } from "dockerode";
|
|
|
|
import { db, like, or } from "@homarr/db";
|
|
import { icons } from "@homarr/db/schema";
|
|
|
|
import type { ContainerState } from "../../docker/src";
|
|
import { DockerSingleton } from "../../docker/src";
|
|
import { createCachedWidgetRequestHandler } from "./lib/cached-widget-request-handler";
|
|
|
|
export const dockerContainersRequestHandler = createCachedWidgetRequestHandler({
|
|
queryKey: "dockerContainersResult",
|
|
widgetKind: "dockerContainers",
|
|
async requestAsync() {
|
|
const containers = await getContainersWithStatsAsync();
|
|
|
|
return containers;
|
|
},
|
|
cacheDuration: dayjs.duration(20, "seconds"),
|
|
});
|
|
|
|
const dockerInstances = DockerSingleton.getInstances();
|
|
|
|
const extractImage = (container: ContainerInfo) => container.Image.split("/").at(-1)?.split(":").at(0) ?? "";
|
|
|
|
async function getContainersWithStatsAsync() {
|
|
const containers = await Promise.all(
|
|
dockerInstances.map(async ({ instance, host }) => {
|
|
const instanceContainers = await instance.listContainers({ all: true });
|
|
return instanceContainers.map((container) => ({
|
|
...container,
|
|
instance: host,
|
|
}));
|
|
}),
|
|
).then((res) => res.flat());
|
|
|
|
const likeQueries = containers.map((container) => like(icons.name, `%${extractImage(container)}%`));
|
|
|
|
const dbIcons =
|
|
likeQueries.length > 0
|
|
? await db.query.icons.findMany({
|
|
where: or(...likeQueries),
|
|
})
|
|
: [];
|
|
|
|
const containerStatsPromises = containers.map(async (container) => {
|
|
const instance = dockerInstances.find(({ host }) => host === container.instance)?.instance;
|
|
if (!instance) return null;
|
|
|
|
const stats = await instance.getContainer(container.Id).stats({ stream: false });
|
|
|
|
return {
|
|
id: container.Id,
|
|
name: container.Names[0]?.split("/")[1] ?? "Unknown",
|
|
state: container.State as ContainerState,
|
|
iconUrl:
|
|
dbIcons.find((icon) => {
|
|
const extractedImage = extractImage(container);
|
|
if (!extractedImage) return false;
|
|
return icon.name.toLowerCase().includes(extractedImage.toLowerCase());
|
|
})?.url ?? null,
|
|
cpuUsage: calculateCpuUsage(stats),
|
|
memoryUsage: stats.memory_stats.usage,
|
|
image: container.Image,
|
|
ports: container.Ports,
|
|
};
|
|
});
|
|
|
|
return (await Promise.all(containerStatsPromises)).filter((container) => container !== null);
|
|
}
|
|
|
|
function calculateCpuUsage(stats: ContainerStats): number {
|
|
const cpuDelta = stats.cpu_stats.cpu_usage.total_usage - stats.precpu_stats.cpu_usage.total_usage;
|
|
const systemDelta = stats.cpu_stats.system_cpu_usage - stats.precpu_stats.system_cpu_usage;
|
|
const numberOfCpus = stats.cpu_stats.online_cpus || 1;
|
|
|
|
if (systemDelta === 0) return 0;
|
|
|
|
return (cpuDelta / systemDelta) * numberOfCpus * 100;
|
|
}
|