Files
Unraid-AppOps-Gateway-Public/src/lib/containerFocusView.ts
T

101 lines
3.8 KiB
TypeScript

import type { Container } from "../types/appops.ts";
export type ContainerFocusFilter =
| "all"
| "attention"
| "running"
| "unhealthy"
| "unmapped"
| "unknown"
| "protected"
| "managed"
| "connector_available"
| "no_connector"
| "missing_metrics";
export interface ContainerFocusSummary {
total: number;
attention: number;
unhealthy: number;
unmapped: number;
unknown: number;
protected: number;
missingMetrics: number;
measuredMetrics: number;
noConnector: number;
}
const statusValue = (value: unknown): string => String(value || "").toLowerCase();
export const hasMissingContainerMetrics = (container: Container): boolean => (
container.state === "running"
&& statusValue(container.metricsStatus) !== "measured"
&& (!container.cpu
|| !container.ram
|| statusValue(container.cpu) === "n/a"
|| statusValue(container.ram) === "n/a")
);
export const hasMeasuredContainerMetrics = (container: Container): boolean => (
statusValue(container.metricsStatus) === "measured"
|| (
Boolean(container.cpu)
&& Boolean(container.ram)
&& statusValue(container.cpu) !== "n/a"
&& statusValue(container.ram) !== "n/a"
)
);
export const isUnmappedContainer = (container: Container): boolean => {
const importStatus = statusValue(container.importStatus);
return ["unmapped", "changed", "missing"].includes(importStatus);
};
export const containerNeedsOperatorAttention = (container: Container): boolean => (
container.health === "unhealthy"
|| container.health === "critical"
|| isUnmappedContainer(container)
);
export const buildContainerFocusSummary = (containers: Container[]): ContainerFocusSummary => {
const summary: ContainerFocusSummary = {
total: containers.length,
attention: 0,
unhealthy: 0,
unmapped: 0,
unknown: 0,
protected: 0,
missingMetrics: 0,
measuredMetrics: 0,
noConnector: 0,
};
for (const container of containers) {
if (containerNeedsOperatorAttention(container)) summary.attention += 1;
if (container.health === "unhealthy" || container.health === "critical") summary.unhealthy += 1;
if (isUnmappedContainer(container)) summary.unmapped += 1;
if (statusValue(container.appType) === "unknown") summary.unknown += 1;
if (container.protected) summary.protected += 1;
if (hasMissingContainerMetrics(container)) summary.missingMetrics += 1;
if (hasMeasuredContainerMetrics(container)) summary.measuredMetrics += 1;
if (!container.connectorType || statusValue(container.connectorType) === "none") summary.noConnector += 1;
}
return summary;
};
export const filterContainersByFocus = (containers: Container[], focus: ContainerFocusFilter): Container[] => {
if (focus === "all") return containers;
if (focus === "attention") return containers.filter(containerNeedsOperatorAttention);
if (focus === "running") return containers.filter(container => container.state === "running");
if (focus === "unhealthy") return containers.filter(container => container.health === "unhealthy" || container.health === "critical");
if (focus === "unmapped") return containers.filter(isUnmappedContainer);
if (focus === "unknown") return containers.filter(container => statusValue(container.appType) === "unknown");
if (focus === "protected") return containers.filter(container => container.protected === true);
if (focus === "managed") return containers.filter(container => container.isManaged === true || container.importStatus === "managed");
if (focus === "connector_available") return containers.filter(container => Boolean(container.connectorType) && statusValue(container.connectorType) !== "none");
if (focus === "no_connector") return containers.filter(container => !container.connectorType || statusValue(container.connectorType) === "none");
if (focus === "missing_metrics") return containers.filter(hasMissingContainerMetrics);
return containers;
};