mirror of
https://github.com/OneUptime/oneuptime.git
synced 2026-04-06 00:32:12 +02:00
Refactor and clean up code across multiple components
- Simplified error handling in KubernetesEventsTab and KubernetesLogsTab by removing unnecessary line breaks. - Consolidated import statements in KubernetesMetricsTab for better readability. - Improved JSX formatting in KubernetesOverviewTab, KubernetesClusterContainerDetail, and other components for consistency. - Enhanced code clarity in KubernetesObjectFetcher and KubernetesObjectParser by removing redundant line breaks and comments. - Streamlined API response handling in IPWhitelistAPI for better readability. - Updated PageSEO configuration for improved formatting.
This commit is contained in:
@@ -2,6 +2,7 @@ import React, { FunctionComponent, ReactElement, useState } from "react";
|
||||
import Card from "Common/UI/Components/Card/Card";
|
||||
import DictionaryOfStringsViewer from "Common/UI/Components/Dictionary/DictionaryOfStingsViewer";
|
||||
import {
|
||||
KubernetesContainerPort,
|
||||
KubernetesContainerSpec,
|
||||
KubernetesContainerStatus,
|
||||
} from "../../Pages/Kubernetes/Utils/KubernetesObjectParser";
|
||||
@@ -104,15 +105,19 @@ const ContainerCard: FunctionComponent<ContainerCardProps> = (
|
||||
{props.container.ports.length > 0 && (
|
||||
<div className="text-sm">
|
||||
<span className="text-gray-500 font-medium">Ports:</span>{" "}
|
||||
{props.container.ports.map((port, idx) => (
|
||||
<span
|
||||
key={idx}
|
||||
className="inline-flex px-2 py-0.5 text-xs font-medium rounded bg-blue-50 text-blue-700 mr-1"
|
||||
>
|
||||
{port.name ? `${port.name}: ` : ""}
|
||||
{port.containerPort}/{port.protocol}
|
||||
</span>
|
||||
))}
|
||||
{props.container.ports.map(
|
||||
(port: KubernetesContainerPort, idx: number) => {
|
||||
return (
|
||||
<span
|
||||
key={idx}
|
||||
className="inline-flex px-2 py-0.5 text-xs font-medium rounded bg-blue-50 text-blue-700 mr-1"
|
||||
>
|
||||
{port.name ? `${port.name}: ` : ""}
|
||||
{port.containerPort}/{port.protocol}
|
||||
</span>
|
||||
);
|
||||
},
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -176,20 +181,33 @@ const ContainerCard: FunctionComponent<ContainerCardProps> = (
|
||||
</button>
|
||||
{showMounts && (
|
||||
<div className="mt-2 space-y-1">
|
||||
{props.container.volumeMounts.map((mount, idx) => (
|
||||
<div key={idx} className="text-sm flex gap-2">
|
||||
<span className="font-medium text-gray-700">
|
||||
{mount.name}
|
||||
</span>
|
||||
<span className="text-gray-500">→</span>
|
||||
<code className="text-xs bg-gray-100 px-1 py-0.5 rounded">
|
||||
{mount.mountPath}
|
||||
</code>
|
||||
{mount.readOnly && (
|
||||
<span className="text-xs text-gray-400">(read-only)</span>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
{props.container.volumeMounts.map(
|
||||
(
|
||||
mount: {
|
||||
name: string;
|
||||
mountPath: string;
|
||||
readOnly: boolean;
|
||||
},
|
||||
idx: number,
|
||||
) => {
|
||||
return (
|
||||
<div key={idx} className="text-sm flex gap-2">
|
||||
<span className="font-medium text-gray-700">
|
||||
{mount.name}
|
||||
</span>
|
||||
<span className="text-gray-500">→</span>
|
||||
<code className="text-xs bg-gray-100 px-1 py-0.5 rounded">
|
||||
{mount.mountPath}
|
||||
</code>
|
||||
{mount.readOnly && (
|
||||
<span className="text-xs text-gray-400">
|
||||
(read-only)
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
@@ -202,10 +220,7 @@ const ContainerCard: FunctionComponent<ContainerCardProps> = (
|
||||
const KubernetesContainersTab: FunctionComponent<ComponentProps> = (
|
||||
props: ComponentProps,
|
||||
): ReactElement => {
|
||||
if (
|
||||
props.containers.length === 0 &&
|
||||
props.initContainers.length === 0
|
||||
) {
|
||||
if (props.containers.length === 0 && props.initContainers.length === 0) {
|
||||
return (
|
||||
<div className="text-gray-500 text-sm p-4">
|
||||
No container information available.
|
||||
@@ -223,32 +238,36 @@ const KubernetesContainersTab: FunctionComponent<ComponentProps> = (
|
||||
const statuses: Array<KubernetesContainerStatus> | undefined = isInit
|
||||
? props.initContainerStatuses
|
||||
: props.containerStatuses;
|
||||
return statuses?.find(
|
||||
(s: KubernetesContainerStatus) => s.name === name,
|
||||
);
|
||||
return statuses?.find((s: KubernetesContainerStatus) => {
|
||||
return s.name === name;
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
{props.initContainers.map(
|
||||
(container: KubernetesContainerSpec, index: number) => (
|
||||
<ContainerCard
|
||||
key={`init-${index}`}
|
||||
container={container}
|
||||
status={getStatus(container.name, true)}
|
||||
isInit={true}
|
||||
/>
|
||||
),
|
||||
(container: KubernetesContainerSpec, index: number) => {
|
||||
return (
|
||||
<ContainerCard
|
||||
key={`init-${index}`}
|
||||
container={container}
|
||||
status={getStatus(container.name, true)}
|
||||
isInit={true}
|
||||
/>
|
||||
);
|
||||
},
|
||||
)}
|
||||
{props.containers.map(
|
||||
(container: KubernetesContainerSpec, index: number) => (
|
||||
<ContainerCard
|
||||
key={`container-${index}`}
|
||||
container={container}
|
||||
status={getStatus(container.name, false)}
|
||||
isInit={false}
|
||||
/>
|
||||
),
|
||||
(container: KubernetesContainerSpec, index: number) => {
|
||||
return (
|
||||
<ContainerCard
|
||||
key={`container-${index}`}
|
||||
container={container}
|
||||
status={getStatus(container.name, false)}
|
||||
isInit={false}
|
||||
/>
|
||||
);
|
||||
},
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -29,18 +29,15 @@ const KubernetesEventsTab: FunctionComponent<ComponentProps> = (
|
||||
const fetchEvents: () => Promise<void> = async (): Promise<void> => {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
const result: Array<KubernetesEvent> =
|
||||
await fetchK8sEventsForResource({
|
||||
clusterIdentifier: props.clusterIdentifier,
|
||||
resourceKind: props.resourceKind,
|
||||
resourceName: props.resourceName,
|
||||
namespace: props.namespace,
|
||||
});
|
||||
const result: Array<KubernetesEvent> = await fetchK8sEventsForResource({
|
||||
clusterIdentifier: props.clusterIdentifier,
|
||||
resourceKind: props.resourceKind,
|
||||
resourceName: props.resourceName,
|
||||
namespace: props.namespace,
|
||||
});
|
||||
setEvents(result);
|
||||
} catch (err) {
|
||||
setError(
|
||||
err instanceof Error ? err.message : "Failed to fetch events",
|
||||
);
|
||||
setError(err instanceof Error ? err.message : "Failed to fetch events");
|
||||
}
|
||||
setIsLoading(false);
|
||||
};
|
||||
@@ -91,8 +88,7 @@ const KubernetesEventsTab: FunctionComponent<ComponentProps> = (
|
||||
</thead>
|
||||
<tbody className="bg-white divide-y divide-gray-200">
|
||||
{events.map((event: KubernetesEvent, index: number) => {
|
||||
const isWarning: boolean =
|
||||
event.type.toLowerCase() === "warning";
|
||||
const isWarning: boolean = event.type.toLowerCase() === "warning";
|
||||
return (
|
||||
<tr key={index} className={isWarning ? "bg-yellow-50" : ""}>
|
||||
<td className="px-4 py-3 whitespace-nowrap text-sm text-gray-500">
|
||||
|
||||
@@ -38,9 +38,7 @@ const KubernetesLogsTab: FunctionComponent<ComponentProps> = (
|
||||
});
|
||||
setLogs(result);
|
||||
} catch (err) {
|
||||
setError(
|
||||
err instanceof Error ? err.message : "Failed to fetch logs",
|
||||
);
|
||||
setError(err instanceof Error ? err.message : "Failed to fetch logs");
|
||||
}
|
||||
setIsLoading(false);
|
||||
};
|
||||
@@ -65,8 +63,8 @@ const KubernetesLogsTab: FunctionComponent<ComponentProps> = (
|
||||
return (
|
||||
<div className="text-gray-500 text-sm p-4">
|
||||
No application logs found for this pod in the last 6 hours. Logs will
|
||||
appear here once the kubernetes-agent's filelog receiver is collecting
|
||||
data.
|
||||
appear here once the kubernetes-agent's filelog receiver is
|
||||
collecting data.
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
import React, {
|
||||
FunctionComponent,
|
||||
ReactElement,
|
||||
useState,
|
||||
} from "react";
|
||||
import React, { FunctionComponent, ReactElement, useState } from "react";
|
||||
import MetricView from "../../Components/Metrics/MetricView";
|
||||
import MetricViewData from "Common/Types/Metrics/MetricViewData";
|
||||
import MetricQueryConfigData from "Common/Types/Metrics/MetricQueryConfigData";
|
||||
|
||||
@@ -44,23 +44,20 @@ const KubernetesOverviewTab: FunctionComponent<ComponentProps> = (
|
||||
{/* Summary Info Cards */}
|
||||
{props.summaryFields.length > 0 && (
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
{props.summaryFields.map(
|
||||
(field: SummaryField, index: number) => {
|
||||
return (
|
||||
<InfoCard
|
||||
key={index}
|
||||
title={field.title}
|
||||
value={field.value}
|
||||
/>
|
||||
);
|
||||
},
|
||||
)}
|
||||
{props.summaryFields.map((field: SummaryField, index: number) => {
|
||||
return (
|
||||
<InfoCard key={index} title={field.title} value={field.value} />
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Owner References */}
|
||||
{props.ownerReferences && props.ownerReferences.length > 0 && (
|
||||
<Card title="Owner References" description="Resources that own this object.">
|
||||
<Card
|
||||
title="Owner References"
|
||||
description="Resources that own this object."
|
||||
>
|
||||
<div className="space-y-1">
|
||||
{props.ownerReferences.map(
|
||||
(ref: { kind: string; name: string }, index: number) => {
|
||||
@@ -147,7 +144,10 @@ const KubernetesOverviewTab: FunctionComponent<ComponentProps> = (
|
||||
|
||||
{/* Labels */}
|
||||
{Object.keys(props.labels).length > 0 && (
|
||||
<Card title="Labels" description="Key-value labels attached to this resource.">
|
||||
<Card
|
||||
title="Labels"
|
||||
description="Key-value labels attached to this resource."
|
||||
>
|
||||
<DictionaryOfStringsViewer value={props.labels} />
|
||||
</Card>
|
||||
)}
|
||||
|
||||
@@ -413,11 +413,17 @@ const DashboardLogsViewer: FunctionComponent<ComponentProps> = (
|
||||
}
|
||||
|
||||
try {
|
||||
// When live polling, recompute the time range so the query window
|
||||
// slides forward to "now" and new logs become visible.
|
||||
/*
|
||||
* When live polling, recompute the time range so the query window
|
||||
* slides forward to "now" and new logs become visible.
|
||||
*/
|
||||
let query: Query<Log> = filterOptions;
|
||||
|
||||
if (skipLoadingState && isLiveEnabled && timeRange.range !== TimeRange.CUSTOM) {
|
||||
if (
|
||||
skipLoadingState &&
|
||||
isLiveEnabled &&
|
||||
timeRange.range !== TimeRange.CUSTOM
|
||||
) {
|
||||
const freshRange: InBetween<Date> =
|
||||
RangeStartAndEndDateTimeUtil.getStartAndEndDate(timeRange);
|
||||
query = {
|
||||
@@ -468,7 +474,16 @@ const DashboardLogsViewer: FunctionComponent<ComponentProps> = (
|
||||
}
|
||||
}
|
||||
},
|
||||
[filterOptions, isLiveEnabled, page, pageSize, select, sortField, sortOrder, timeRange],
|
||||
[
|
||||
filterOptions,
|
||||
isLiveEnabled,
|
||||
page,
|
||||
pageSize,
|
||||
select,
|
||||
sortField,
|
||||
sortOrder,
|
||||
timeRange,
|
||||
],
|
||||
);
|
||||
|
||||
// --- Fetch histogram ---
|
||||
|
||||
@@ -263,8 +263,7 @@ export async function fetchK8sEventsForResource(options: {
|
||||
const objectKvList: JSONObject = objectVal;
|
||||
|
||||
// Get event details
|
||||
const eventType: string =
|
||||
getKvStringValue(objectKvList, "type") || "";
|
||||
const eventType: string = getKvStringValue(objectKvList, "type") || "";
|
||||
const reason: string = getKvStringValue(objectKvList, "reason") || "";
|
||||
const note: string = getKvStringValue(objectKvList, "note") || "";
|
||||
|
||||
@@ -400,8 +399,7 @@ export async function fetchPodLogs(options: {
|
||||
: "",
|
||||
body: typeof log.body === "string" ? log.body : "",
|
||||
severity: log.severityText || "INFO",
|
||||
containerName:
|
||||
(attrs["resource.k8s.container.name"] as string) || "",
|
||||
containerName: (attrs["resource.k8s.container.name"] as string) || "",
|
||||
};
|
||||
});
|
||||
} catch {
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import { JSONObject } from "Common/Types/JSON";
|
||||
|
||||
// ============================================================
|
||||
// OTLP kvlistValue parsing helpers
|
||||
// ============================================================
|
||||
/*
|
||||
* ============================================================
|
||||
* OTLP kvlistValue parsing helpers
|
||||
* ============================================================
|
||||
*/
|
||||
|
||||
/**
|
||||
* Extract a value from an OTLP kvlistValue by key.
|
||||
@@ -141,9 +143,11 @@ export function getArrayValues(
|
||||
.filter(Boolean) as Array<JSONObject>;
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// TypeScript interfaces for parsed K8s objects
|
||||
// ============================================================
|
||||
/*
|
||||
* ============================================================
|
||||
* TypeScript interfaces for parsed K8s objects
|
||||
* ============================================================
|
||||
*/
|
||||
|
||||
export interface KubernetesObjectMetadata {
|
||||
name: string;
|
||||
@@ -336,15 +340,14 @@ export interface KubernetesNamespaceObject {
|
||||
};
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Parsers
|
||||
// ============================================================
|
||||
/*
|
||||
* ============================================================
|
||||
* Parsers
|
||||
* ============================================================
|
||||
*/
|
||||
|
||||
function parseMetadata(kvList: JSONObject): KubernetesObjectMetadata {
|
||||
const labelsKvList: string | JSONObject | null = getKvValue(
|
||||
kvList,
|
||||
"labels",
|
||||
);
|
||||
const labelsKvList: string | JSONObject | null = getKvValue(kvList, "labels");
|
||||
const annotationsKvList: string | JSONObject | null = getKvValue(
|
||||
kvList,
|
||||
"annotations",
|
||||
@@ -503,10 +506,7 @@ function parseContainerSpec(kvList: JSONObject): KubernetesContainerSpec {
|
||||
resourcesKv,
|
||||
"requests",
|
||||
);
|
||||
const limKv: string | JSONObject | null = getKvValue(
|
||||
resourcesKv,
|
||||
"limits",
|
||||
);
|
||||
const limKv: string | JSONObject | null = getKvValue(resourcesKv, "limits");
|
||||
if (reqKv && typeof reqKv !== "string") {
|
||||
requests = getKvListAsRecord(reqKv);
|
||||
}
|
||||
@@ -521,7 +521,8 @@ function parseContainerSpec(kvList: JSONObject): KubernetesContainerSpec {
|
||||
);
|
||||
const command: Array<string> = [];
|
||||
if (commandArray && typeof commandArray !== "string") {
|
||||
const cmdValues: Array<JSONObject> = (commandArray["values"] as Array<JSONObject>) || [];
|
||||
const cmdValues: Array<JSONObject> =
|
||||
(commandArray["values"] as Array<JSONObject>) || [];
|
||||
for (const v of cmdValues) {
|
||||
if (v["stringValue"]) {
|
||||
command.push(v["stringValue"] as string);
|
||||
@@ -532,7 +533,8 @@ function parseContainerSpec(kvList: JSONObject): KubernetesContainerSpec {
|
||||
const argsArray: string | JSONObject | null = getKvValue(kvList, "args");
|
||||
const args: Array<string> = [];
|
||||
if (argsArray && typeof argsArray !== "string") {
|
||||
const argValues: Array<JSONObject> = (argsArray["values"] as Array<JSONObject>) || [];
|
||||
const argValues: Array<JSONObject> =
|
||||
(argsArray["values"] as Array<JSONObject>) || [];
|
||||
for (const v of argValues) {
|
||||
if (v["stringValue"]) {
|
||||
args.push(v["stringValue"] as string);
|
||||
@@ -593,8 +595,7 @@ function parseContainerStatuses(
|
||||
return {
|
||||
name: getKvStringValue(kvList, "name"),
|
||||
ready: getKvStringValue(kvList, "ready") === "true",
|
||||
restartCount:
|
||||
parseInt(getKvStringValue(kvList, "restartCount")) || 0,
|
||||
restartCount: parseInt(getKvStringValue(kvList, "restartCount")) || 0,
|
||||
state,
|
||||
image: getKvStringValue(kvList, "image"),
|
||||
};
|
||||
@@ -618,10 +619,7 @@ export function parsePodObject(
|
||||
}
|
||||
const metadata: KubernetesObjectMetadata = parseMetadata(metadataKv);
|
||||
|
||||
const specKv: string | JSONObject | null = getKvValue(
|
||||
objectKvList,
|
||||
"spec",
|
||||
);
|
||||
const specKv: string | JSONObject | null = getKvValue(objectKvList, "spec");
|
||||
const statusKv: string | JSONObject | null = getKvValue(
|
||||
objectKvList,
|
||||
"status",
|
||||
@@ -683,8 +681,9 @@ export function parsePodObject(
|
||||
| JSONObject
|
||||
| undefined;
|
||||
if (innerVal && innerVal["kvlistValue"]) {
|
||||
const innerKv: JSONObject =
|
||||
innerVal["kvlistValue"] as JSONObject;
|
||||
const innerKv: JSONObject = innerVal[
|
||||
"kvlistValue"
|
||||
] as JSONObject;
|
||||
volSource =
|
||||
getKvStringValue(innerKv, "name") ||
|
||||
getKvStringValue(innerKv, "path") ||
|
||||
@@ -877,10 +876,12 @@ export function parseNodeObject(
|
||||
);
|
||||
if (addrArray && typeof addrArray !== "string") {
|
||||
const addrItems: Array<JSONObject> = getArrayValues(addrArray);
|
||||
addresses = addrItems.map((a: JSONObject) => ({
|
||||
type: getKvStringValue(a, "type"),
|
||||
address: getKvStringValue(a, "address"),
|
||||
}));
|
||||
addresses = addrItems.map((a: JSONObject) => {
|
||||
return {
|
||||
type: getKvStringValue(a, "type"),
|
||||
address: getKvStringValue(a, "address"),
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -911,10 +912,7 @@ export function parseDeploymentObject(
|
||||
return null;
|
||||
}
|
||||
|
||||
const specKv: string | JSONObject | null = getKvValue(
|
||||
objectKvList,
|
||||
"spec",
|
||||
);
|
||||
const specKv: string | JSONObject | null = getKvValue(objectKvList, "spec");
|
||||
const statusKv: string | JSONObject | null = getKvValue(
|
||||
objectKvList,
|
||||
"status",
|
||||
@@ -953,8 +951,7 @@ export function parseDeploymentObject(
|
||||
let unavailableReplicas: number = 0;
|
||||
let conditions: Array<KubernetesCondition> = [];
|
||||
if (statusKv && typeof statusKv !== "string") {
|
||||
statusReplicas =
|
||||
parseInt(getKvStringValue(statusKv, "replicas")) || 0;
|
||||
statusReplicas = parseInt(getKvStringValue(statusKv, "replicas")) || 0;
|
||||
readyReplicas =
|
||||
parseInt(getKvStringValue(statusKv, "readyReplicas")) || 0;
|
||||
availableReplicas =
|
||||
@@ -996,10 +993,7 @@ export function parseStatefulSetObject(
|
||||
return null;
|
||||
}
|
||||
|
||||
const specKv: string | JSONObject | null = getKvValue(
|
||||
objectKvList,
|
||||
"spec",
|
||||
);
|
||||
const specKv: string | JSONObject | null = getKvValue(objectKvList, "spec");
|
||||
const statusKv: string | JSONObject | null = getKvValue(
|
||||
objectKvList,
|
||||
"status",
|
||||
@@ -1058,10 +1052,7 @@ export function parseDaemonSetObject(
|
||||
return null;
|
||||
}
|
||||
|
||||
const specKv: string | JSONObject | null = getKvValue(
|
||||
objectKvList,
|
||||
"spec",
|
||||
);
|
||||
const specKv: string | JSONObject | null = getKvValue(objectKvList, "spec");
|
||||
const statusKv: string | JSONObject | null = getKvValue(
|
||||
objectKvList,
|
||||
"status",
|
||||
@@ -1099,9 +1090,8 @@ export function parseDaemonSetObject(
|
||||
) || 0
|
||||
: 0,
|
||||
numberReady: statusKv
|
||||
? parseInt(
|
||||
getKvStringValue(statusKv as JSONObject, "numberReady"),
|
||||
) || 0
|
||||
? parseInt(getKvStringValue(statusKv as JSONObject, "numberReady")) ||
|
||||
0
|
||||
: 0,
|
||||
numberMisscheduled: statusKv
|
||||
? parseInt(
|
||||
@@ -1132,10 +1122,7 @@ export function parseJobObject(
|
||||
return null;
|
||||
}
|
||||
|
||||
const specKv: string | JSONObject | null = getKvValue(
|
||||
objectKvList,
|
||||
"spec",
|
||||
);
|
||||
const specKv: string | JSONObject | null = getKvValue(objectKvList, "spec");
|
||||
const statusKv: string | JSONObject | null = getKvValue(
|
||||
objectKvList,
|
||||
"status",
|
||||
@@ -1145,19 +1132,14 @@ export function parseJobObject(
|
||||
metadata: parseMetadata(metadataKv),
|
||||
spec: {
|
||||
completions: specKv
|
||||
? parseInt(
|
||||
getKvStringValue(specKv as JSONObject, "completions"),
|
||||
) || 0
|
||||
? parseInt(getKvStringValue(specKv as JSONObject, "completions")) || 0
|
||||
: 0,
|
||||
parallelism: specKv
|
||||
? parseInt(
|
||||
getKvStringValue(specKv as JSONObject, "parallelism"),
|
||||
) || 0
|
||||
? parseInt(getKvStringValue(specKv as JSONObject, "parallelism")) || 0
|
||||
: 0,
|
||||
backoffLimit: specKv
|
||||
? parseInt(
|
||||
getKvStringValue(specKv as JSONObject, "backoffLimit"),
|
||||
) || 0
|
||||
? parseInt(getKvStringValue(specKv as JSONObject, "backoffLimit")) ||
|
||||
0
|
||||
: 0,
|
||||
},
|
||||
status: {
|
||||
@@ -1165,9 +1147,7 @@ export function parseJobObject(
|
||||
? parseInt(getKvStringValue(statusKv as JSONObject, "active")) || 0
|
||||
: 0,
|
||||
succeeded: statusKv
|
||||
? parseInt(
|
||||
getKvStringValue(statusKv as JSONObject, "succeeded"),
|
||||
) || 0
|
||||
? parseInt(getKvStringValue(statusKv as JSONObject, "succeeded")) || 0
|
||||
: 0,
|
||||
failed: statusKv
|
||||
? parseInt(getKvStringValue(statusKv as JSONObject, "failed")) || 0
|
||||
@@ -1205,10 +1185,7 @@ export function parseCronJobObject(
|
||||
return null;
|
||||
}
|
||||
|
||||
const specKv: string | JSONObject | null = getKvValue(
|
||||
objectKvList,
|
||||
"spec",
|
||||
);
|
||||
const specKv: string | JSONObject | null = getKvValue(objectKvList, "spec");
|
||||
const statusKv: string | JSONObject | null = getKvValue(
|
||||
objectKvList,
|
||||
"status",
|
||||
@@ -1220,10 +1197,9 @@ export function parseCronJobObject(
|
||||
schedule: specKv
|
||||
? getKvStringValue(specKv as JSONObject, "schedule")
|
||||
: "",
|
||||
suspend:
|
||||
specKv
|
||||
? getKvStringValue(specKv as JSONObject, "suspend") === "true"
|
||||
: false,
|
||||
suspend: specKv
|
||||
? getKvStringValue(specKv as JSONObject, "suspend") === "true"
|
||||
: false,
|
||||
concurrencyPolicy: specKv
|
||||
? getKvStringValue(specKv as JSONObject, "concurrencyPolicy")
|
||||
: "",
|
||||
@@ -1237,10 +1213,7 @@ export function parseCronJobObject(
|
||||
: 0,
|
||||
failedJobsHistoryLimit: specKv
|
||||
? parseInt(
|
||||
getKvStringValue(
|
||||
specKv as JSONObject,
|
||||
"failedJobsHistoryLimit",
|
||||
),
|
||||
getKvStringValue(specKv as JSONObject, "failedJobsHistoryLimit"),
|
||||
) || 0
|
||||
: 0,
|
||||
},
|
||||
@@ -1315,8 +1288,10 @@ export function extractObjectFromLogBody(
|
||||
return objectVal;
|
||||
}
|
||||
|
||||
// If no "object" key, the kvlist might BE the object (pull mode)
|
||||
// Check if it has typical K8s fields
|
||||
/*
|
||||
* If no "object" key, the kvlist might BE the object (pull mode)
|
||||
* Check if it has typical K8s fields
|
||||
*/
|
||||
const kind: string | JSONObject | null = getKvValue(topKvList, "kind");
|
||||
if (kind) {
|
||||
return topKvList;
|
||||
|
||||
@@ -43,31 +43,30 @@ export default class KubernetesResourceUtils {
|
||||
const endDate: Date = OneUptimeDate.getCurrentDate();
|
||||
const startDate: Date = OneUptimeDate.addRemoveHours(endDate, -hoursBack);
|
||||
|
||||
const cpuResult: AggregatedResult =
|
||||
await AnalyticsModelAPI.aggregate({
|
||||
modelType: Metric,
|
||||
aggregateBy: {
|
||||
query: {
|
||||
projectId: ProjectUtil.getCurrentProjectId()!,
|
||||
time: new InBetween(startDate, endDate),
|
||||
name: metricName,
|
||||
attributes: {
|
||||
"resource.k8s.cluster.name": clusterIdentifier,
|
||||
...filterAttributes,
|
||||
} as Dictionary<string | number | boolean>,
|
||||
},
|
||||
aggregationType: MetricsAggregationType.Avg,
|
||||
aggregateColumnName: "value",
|
||||
aggregationTimestampColumnName: "time",
|
||||
startTimestamp: startDate,
|
||||
endTimestamp: endDate,
|
||||
limit: LIMIT_PER_PROJECT,
|
||||
skip: 0,
|
||||
groupBy: {
|
||||
attributes: true,
|
||||
},
|
||||
const cpuResult: AggregatedResult = await AnalyticsModelAPI.aggregate({
|
||||
modelType: Metric,
|
||||
aggregateBy: {
|
||||
query: {
|
||||
projectId: ProjectUtil.getCurrentProjectId()!,
|
||||
time: new InBetween(startDate, endDate),
|
||||
name: metricName,
|
||||
attributes: {
|
||||
"resource.k8s.cluster.name": clusterIdentifier,
|
||||
...filterAttributes,
|
||||
} as Dictionary<string | number | boolean>,
|
||||
},
|
||||
});
|
||||
aggregationType: MetricsAggregationType.Avg,
|
||||
aggregateColumnName: "value",
|
||||
aggregationTimestampColumnName: "time",
|
||||
startTimestamp: startDate,
|
||||
endTimestamp: endDate,
|
||||
limit: LIMIT_PER_PROJECT,
|
||||
skip: 0,
|
||||
groupBy: {
|
||||
attributes: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const resourceMap: Map<string, KubernetesResource> = new Map();
|
||||
|
||||
@@ -128,31 +127,30 @@ export default class KubernetesResourceUtils {
|
||||
);
|
||||
|
||||
try {
|
||||
const memoryResult: AggregatedResult =
|
||||
await AnalyticsModelAPI.aggregate({
|
||||
modelType: Metric,
|
||||
aggregateBy: {
|
||||
query: {
|
||||
projectId: ProjectUtil.getCurrentProjectId()!,
|
||||
time: new InBetween(startDate, endDate),
|
||||
name: options.memoryMetricName,
|
||||
attributes: {
|
||||
"resource.k8s.cluster.name": options.clusterIdentifier,
|
||||
...(options.filterAttributes || {}),
|
||||
} as Dictionary<string | number | boolean>,
|
||||
},
|
||||
aggregationType: MetricsAggregationType.Avg,
|
||||
aggregateColumnName: "value",
|
||||
aggregationTimestampColumnName: "time",
|
||||
startTimestamp: startDate,
|
||||
endTimestamp: endDate,
|
||||
limit: LIMIT_PER_PROJECT,
|
||||
skip: 0,
|
||||
groupBy: {
|
||||
attributes: true,
|
||||
},
|
||||
const memoryResult: AggregatedResult = await AnalyticsModelAPI.aggregate({
|
||||
modelType: Metric,
|
||||
aggregateBy: {
|
||||
query: {
|
||||
projectId: ProjectUtil.getCurrentProjectId()!,
|
||||
time: new InBetween(startDate, endDate),
|
||||
name: options.memoryMetricName,
|
||||
attributes: {
|
||||
"resource.k8s.cluster.name": options.clusterIdentifier,
|
||||
...(options.filterAttributes || {}),
|
||||
} as Dictionary<string | number | boolean>,
|
||||
},
|
||||
});
|
||||
aggregationType: MetricsAggregationType.Avg,
|
||||
aggregateColumnName: "value",
|
||||
aggregationTimestampColumnName: "time",
|
||||
startTimestamp: startDate,
|
||||
endTimestamp: endDate,
|
||||
limit: LIMIT_PER_PROJECT,
|
||||
skip: 0,
|
||||
groupBy: {
|
||||
attributes: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const memoryMap: Map<string, number> = new Map();
|
||||
|
||||
@@ -179,7 +177,7 @@ export default class KubernetesResourceUtils {
|
||||
resource.memoryUsageBytes = memValue;
|
||||
}
|
||||
}
|
||||
} catch (_err) {
|
||||
} catch {
|
||||
// Memory data is optional, don't fail if not available
|
||||
}
|
||||
|
||||
|
||||
@@ -140,7 +140,10 @@ const KubernetesClusterContainerDetail: FunctionComponent<
|
||||
children: (
|
||||
<div className="p-4">
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
<InfoCard title="Container Name" value={containerName || "Unknown"} />
|
||||
<InfoCard
|
||||
title="Container Name"
|
||||
value={containerName || "Unknown"}
|
||||
/>
|
||||
<InfoCard title="Cluster" value={clusterIdentifier} />
|
||||
</div>
|
||||
</div>
|
||||
@@ -149,7 +152,10 @@ const KubernetesClusterContainerDetail: FunctionComponent<
|
||||
{
|
||||
name: "Logs",
|
||||
children: (
|
||||
<Card title="Container Logs" description="Logs for this container from the last 6 hours.">
|
||||
<Card
|
||||
title="Container Logs"
|
||||
description="Logs for this container from the last 6 hours."
|
||||
>
|
||||
<KubernetesLogsTab
|
||||
clusterIdentifier={clusterIdentifier}
|
||||
podName=""
|
||||
@@ -165,9 +171,7 @@ const KubernetesClusterContainerDetail: FunctionComponent<
|
||||
title={`Container Metrics: ${containerName}`}
|
||||
description="CPU and memory usage for this container over the last 6 hours."
|
||||
>
|
||||
<KubernetesMetricsTab
|
||||
queryConfigs={[cpuQuery, memoryQuery]}
|
||||
/>
|
||||
<KubernetesMetricsTab queryConfigs={[cpuQuery, memoryQuery]} />
|
||||
</Card>
|
||||
),
|
||||
},
|
||||
|
||||
@@ -71,22 +71,21 @@ const KubernetesClusterCronJobDetail: FunctionComponent<
|
||||
return;
|
||||
}
|
||||
|
||||
const fetchCronJobObject: () => Promise<void> =
|
||||
async (): Promise<void> => {
|
||||
setIsLoadingObject(true);
|
||||
try {
|
||||
const obj: KubernetesCronJobObject | null =
|
||||
await fetchLatestK8sObject<KubernetesCronJobObject>({
|
||||
clusterIdentifier: cluster.clusterIdentifier || "",
|
||||
resourceType: "cronjobs",
|
||||
resourceName: cronJobName,
|
||||
});
|
||||
setCronJobObject(obj);
|
||||
} catch {
|
||||
// Graceful degradation — overview tab shows empty state
|
||||
}
|
||||
setIsLoadingObject(false);
|
||||
};
|
||||
const fetchCronJobObject: () => Promise<void> = async (): Promise<void> => {
|
||||
setIsLoadingObject(true);
|
||||
try {
|
||||
const obj: KubernetesCronJobObject | null =
|
||||
await fetchLatestK8sObject<KubernetesCronJobObject>({
|
||||
clusterIdentifier: cluster.clusterIdentifier || "",
|
||||
resourceType: "cronjobs",
|
||||
resourceName: cronJobName,
|
||||
});
|
||||
setCronJobObject(obj);
|
||||
} catch {
|
||||
// Graceful degradation — overview tab shows empty state
|
||||
}
|
||||
setIsLoadingObject(false);
|
||||
};
|
||||
|
||||
fetchCronJobObject().catch(() => {});
|
||||
}, [cluster?.clusterIdentifier, cronJobName]);
|
||||
@@ -228,7 +227,10 @@ const KubernetesClusterCronJobDetail: FunctionComponent<
|
||||
{
|
||||
name: "Events",
|
||||
children: (
|
||||
<Card title="CronJob Events" description="Kubernetes events for this cronjob in the last 24 hours.">
|
||||
<Card
|
||||
title="CronJob Events"
|
||||
description="Kubernetes events for this cronjob in the last 24 hours."
|
||||
>
|
||||
<KubernetesEventsTab
|
||||
clusterIdentifier={clusterIdentifier}
|
||||
resourceKind="CronJob"
|
||||
@@ -245,9 +247,7 @@ const KubernetesClusterCronJobDetail: FunctionComponent<
|
||||
title={`CronJob Metrics: ${cronJobName}`}
|
||||
description="CPU and memory usage for pods in this cronjob over the last 6 hours."
|
||||
>
|
||||
<KubernetesMetricsTab
|
||||
queryConfigs={[cpuQuery, memoryQuery]}
|
||||
/>
|
||||
<KubernetesMetricsTab queryConfigs={[cpuQuery, memoryQuery]} />
|
||||
</Card>
|
||||
),
|
||||
},
|
||||
|
||||
@@ -239,9 +239,7 @@ const KubernetesClusterDaemonSetDetail: FunctionComponent<
|
||||
title={`DaemonSet Metrics: ${daemonSetName}`}
|
||||
description="CPU and memory usage for pods in this daemonset over the last 6 hours."
|
||||
>
|
||||
<KubernetesMetricsTab
|
||||
queryConfigs={[cpuQuery, memoryQuery]}
|
||||
/>
|
||||
<KubernetesMetricsTab queryConfigs={[cpuQuery, memoryQuery]} />
|
||||
</Card>
|
||||
),
|
||||
},
|
||||
|
||||
@@ -236,9 +236,7 @@ const KubernetesClusterDeploymentDetail: FunctionComponent<
|
||||
title={`Deployment Metrics: ${deploymentName}`}
|
||||
description="CPU and memory usage for pods in this deployment over the last 6 hours."
|
||||
>
|
||||
<KubernetesMetricsTab
|
||||
queryConfigs={[cpuQuery, memoryQuery]}
|
||||
/>
|
||||
<KubernetesMetricsTab queryConfigs={[cpuQuery, memoryQuery]} />
|
||||
</Card>
|
||||
),
|
||||
},
|
||||
|
||||
@@ -85,7 +85,9 @@ const KubernetesClusterDeployments: FunctionComponent<
|
||||
resources={resources}
|
||||
getViewRoute={(resource: KubernetesResource) => {
|
||||
return RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.KUBERNETES_CLUSTER_VIEW_DEPLOYMENT_DETAIL] as Route,
|
||||
RouteMap[
|
||||
PageMap.KUBERNETES_CLUSTER_VIEW_DEPLOYMENT_DETAIL
|
||||
] as Route,
|
||||
{
|
||||
modelId: modelId,
|
||||
subModelId: new ObjectID(resource.name),
|
||||
|
||||
@@ -24,10 +24,7 @@ import ErrorMessage from "Common/UI/Components/ErrorMessage/ErrorMessage";
|
||||
import { PromiseVoidFunction } from "Common/Types/FunctionTypes";
|
||||
import { JSONObject } from "Common/Types/JSON";
|
||||
import InBetween from "Common/Types/BaseDatabase/InBetween";
|
||||
import {
|
||||
getKvValue,
|
||||
getKvStringValue,
|
||||
} from "../Utils/KubernetesObjectParser";
|
||||
import { getKvValue, getKvStringValue } from "../Utils/KubernetesObjectParser";
|
||||
import { KubernetesEvent } from "../Utils/KubernetesObjectFetcher";
|
||||
|
||||
const KubernetesClusterEvents: FunctionComponent<
|
||||
@@ -131,12 +128,9 @@ const KubernetesClusterEvents: FunctionComponent<
|
||||
}
|
||||
const objectKvList: JSONObject = objectVal;
|
||||
|
||||
const eventType: string =
|
||||
getKvStringValue(objectKvList, "type") || "";
|
||||
const reason: string =
|
||||
getKvStringValue(objectKvList, "reason") || "";
|
||||
const note: string =
|
||||
getKvStringValue(objectKvList, "note") || "";
|
||||
const eventType: string = getKvStringValue(objectKvList, "type") || "";
|
||||
const reason: string = getKvStringValue(objectKvList, "reason") || "";
|
||||
const note: string = getKvStringValue(objectKvList, "note") || "";
|
||||
|
||||
// Get regarding object details using shared parser
|
||||
const regardingKv: string | JSONObject | null = getKvValue(
|
||||
@@ -160,17 +154,11 @@ const KubernetesClusterEvents: FunctionComponent<
|
||||
"metadata",
|
||||
);
|
||||
const metadataObj: JSONObject | undefined =
|
||||
metadataKv && typeof metadataKv !== "string"
|
||||
? metadataKv
|
||||
: undefined;
|
||||
metadataKv && typeof metadataKv !== "string" ? metadataKv : undefined;
|
||||
|
||||
const namespace: string =
|
||||
(regardingObj
|
||||
? getKvStringValue(regardingObj, "namespace")
|
||||
: "") ||
|
||||
(metadataObj
|
||||
? getKvStringValue(metadataObj, "namespace")
|
||||
: "") ||
|
||||
(regardingObj ? getKvStringValue(regardingObj, "namespace") : "") ||
|
||||
(metadataObj ? getKvStringValue(metadataObj, "namespace") : "") ||
|
||||
"";
|
||||
|
||||
if (eventType || reason) {
|
||||
@@ -254,10 +242,7 @@ const KubernetesClusterEvents: FunctionComponent<
|
||||
const isWarning: boolean =
|
||||
event.type.toLowerCase() === "warning";
|
||||
return (
|
||||
<tr
|
||||
key={index}
|
||||
className={isWarning ? "bg-yellow-50" : ""}
|
||||
>
|
||||
<tr key={index} className={isWarning ? "bg-yellow-50" : ""}>
|
||||
<td className="px-4 py-3 whitespace-nowrap text-sm text-gray-500">
|
||||
{event.timestamp}
|
||||
</td>
|
||||
|
||||
@@ -231,7 +231,10 @@ const KubernetesClusterJobDetail: FunctionComponent<
|
||||
{
|
||||
name: "Events",
|
||||
children: (
|
||||
<Card title="Job Events" description="Kubernetes events for this job in the last 24 hours.">
|
||||
<Card
|
||||
title="Job Events"
|
||||
description="Kubernetes events for this job in the last 24 hours."
|
||||
>
|
||||
<KubernetesEventsTab
|
||||
clusterIdentifier={clusterIdentifier}
|
||||
resourceKind="Job"
|
||||
@@ -248,9 +251,7 @@ const KubernetesClusterJobDetail: FunctionComponent<
|
||||
title={`Job Metrics: ${jobName}`}
|
||||
description="CPU and memory usage for pods in this job over the last 6 hours."
|
||||
>
|
||||
<KubernetesMetricsTab
|
||||
queryConfigs={[cpuQuery, memoryQuery]}
|
||||
/>
|
||||
<KubernetesMetricsTab queryConfigs={[cpuQuery, memoryQuery]} />
|
||||
</Card>
|
||||
),
|
||||
},
|
||||
|
||||
@@ -200,7 +200,10 @@ const KubernetesClusterNamespaceDetail: FunctionComponent<
|
||||
{
|
||||
name: "Events",
|
||||
children: (
|
||||
<Card title="Namespace Events" description="Kubernetes events for this namespace in the last 24 hours.">
|
||||
<Card
|
||||
title="Namespace Events"
|
||||
description="Kubernetes events for this namespace in the last 24 hours."
|
||||
>
|
||||
<KubernetesEventsTab
|
||||
clusterIdentifier={clusterIdentifier}
|
||||
resourceKind="Namespace"
|
||||
@@ -216,9 +219,7 @@ const KubernetesClusterNamespaceDetail: FunctionComponent<
|
||||
title={`Namespace Metrics: ${namespaceName}`}
|
||||
description="CPU and memory usage for pods in this namespace over the last 6 hours."
|
||||
>
|
||||
<KubernetesMetricsTab
|
||||
queryConfigs={[cpuQuery, memoryQuery]}
|
||||
/>
|
||||
<KubernetesMetricsTab queryConfigs={[cpuQuery, memoryQuery]} />
|
||||
</Card>
|
||||
),
|
||||
},
|
||||
|
||||
@@ -23,7 +23,10 @@ import { Tab } from "Common/UI/Components/Tabs/Tab";
|
||||
import KubernetesOverviewTab from "../../../Components/Kubernetes/KubernetesOverviewTab";
|
||||
import KubernetesEventsTab from "../../../Components/Kubernetes/KubernetesEventsTab";
|
||||
import KubernetesMetricsTab from "../../../Components/Kubernetes/KubernetesMetricsTab";
|
||||
import { KubernetesNodeObject } from "../Utils/KubernetesObjectParser";
|
||||
import {
|
||||
KubernetesCondition,
|
||||
KubernetesNodeObject,
|
||||
} from "../Utils/KubernetesObjectParser";
|
||||
import { fetchLatestK8sObject } from "../Utils/KubernetesObjectFetcher";
|
||||
|
||||
const KubernetesClusterNodeDetail: FunctionComponent<
|
||||
@@ -230,11 +233,10 @@ const KubernetesClusterNodeDetail: FunctionComponent<
|
||||
if (!nodeObject) {
|
||||
return { label: "Unknown", isReady: false };
|
||||
}
|
||||
const readyCondition = nodeObject.status.conditions.find(
|
||||
(c) => {
|
||||
const readyCondition: KubernetesCondition | undefined =
|
||||
nodeObject.status.conditions.find((c: KubernetesCondition) => {
|
||||
return c.type === "Ready";
|
||||
},
|
||||
);
|
||||
});
|
||||
if (readyCondition && readyCondition.status === "True") {
|
||||
return { label: "Ready", isReady: true };
|
||||
}
|
||||
@@ -249,7 +251,7 @@ const KubernetesClusterNodeDetail: FunctionComponent<
|
||||
];
|
||||
|
||||
if (nodeObject) {
|
||||
const nodeStatus = getNodeStatus();
|
||||
const nodeStatus: { label: string; isReady: boolean } = getNodeStatus();
|
||||
|
||||
summaryFields.push(
|
||||
{
|
||||
|
||||
@@ -294,7 +294,10 @@ const KubernetesClusterPodDetail: FunctionComponent<
|
||||
{
|
||||
name: "Events",
|
||||
children: (
|
||||
<Card title="Pod Events" description="Kubernetes events for this pod in the last 24 hours.">
|
||||
<Card
|
||||
title="Pod Events"
|
||||
description="Kubernetes events for this pod in the last 24 hours."
|
||||
>
|
||||
<KubernetesEventsTab
|
||||
clusterIdentifier={clusterIdentifier}
|
||||
resourceKind="Pod"
|
||||
@@ -307,7 +310,10 @@ const KubernetesClusterPodDetail: FunctionComponent<
|
||||
{
|
||||
name: "Logs",
|
||||
children: (
|
||||
<Card title="Application Logs" description="Container logs for this pod from the last 6 hours.">
|
||||
<Card
|
||||
title="Application Logs"
|
||||
description="Container logs for this pod from the last 6 hours."
|
||||
>
|
||||
<KubernetesLogsTab
|
||||
clusterIdentifier={clusterIdentifier}
|
||||
podName={podName}
|
||||
|
||||
@@ -239,9 +239,7 @@ const KubernetesClusterStatefulSetDetail: FunctionComponent<
|
||||
title={`StatefulSet Metrics: ${statefulSetName}`}
|
||||
description="CPU and memory usage for pods in this statefulset over the last 6 hours."
|
||||
>
|
||||
<KubernetesMetricsTab
|
||||
queryConfigs={[cpuQuery, memoryQuery]}
|
||||
/>
|
||||
<KubernetesMetricsTab queryConfigs={[cpuQuery, memoryQuery]} />
|
||||
</Card>
|
||||
),
|
||||
},
|
||||
@@ -251,10 +249,7 @@ const KubernetesClusterStatefulSetDetail: FunctionComponent<
|
||||
<Fragment>
|
||||
<div className="mb-5">
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4 mb-5">
|
||||
<InfoCard
|
||||
title="StatefulSet"
|
||||
value={statefulSetName || "Unknown"}
|
||||
/>
|
||||
<InfoCard title="StatefulSet" value={statefulSetName || "Unknown"} />
|
||||
<InfoCard title="Cluster" value={clusterIdentifier} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -85,7 +85,9 @@ const KubernetesClusterStatefulSets: FunctionComponent<
|
||||
resources={resources}
|
||||
getViewRoute={(resource: KubernetesResource) => {
|
||||
return RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.KUBERNETES_CLUSTER_VIEW_STATEFULSET_DETAIL] as Route,
|
||||
RouteMap[
|
||||
PageMap.KUBERNETES_CLUSTER_VIEW_STATEFULSET_DETAIL
|
||||
] as Route,
|
||||
{
|
||||
modelId: modelId,
|
||||
subModelId: new ObjectID(resource.name),
|
||||
|
||||
@@ -90,7 +90,10 @@ const DocsFeatureSet: FeatureSet = {
|
||||
})
|
||||
.join("\n")
|
||||
: "- No IP addresses configured.";
|
||||
contentInMarkdown = contentInMarkdown.replace("{{IP_WHITELIST}}", ipList);
|
||||
contentInMarkdown = contentInMarkdown.replace(
|
||||
"{{IP_WHITELIST}}",
|
||||
ipList,
|
||||
);
|
||||
}
|
||||
|
||||
// Render Markdown content to HTML
|
||||
|
||||
@@ -10,24 +10,21 @@ export default class IPWhitelistAPI {
|
||||
public static init(): ExpressRouter {
|
||||
const router: ExpressRouter = Express.getRouter();
|
||||
|
||||
router.get(
|
||||
"/ip-whitelist",
|
||||
(req: ExpressRequest, res: ExpressResponse) => {
|
||||
const ipList: Array<string> = IpWhitelist
|
||||
? IpWhitelist.split(",")
|
||||
.map((ip: string) => {
|
||||
return ip.trim();
|
||||
})
|
||||
.filter((ip: string) => {
|
||||
return ip.length > 0;
|
||||
})
|
||||
: [];
|
||||
router.get("/ip-whitelist", (req: ExpressRequest, res: ExpressResponse) => {
|
||||
const ipList: Array<string> = IpWhitelist
|
||||
? IpWhitelist.split(",")
|
||||
.map((ip: string) => {
|
||||
return ip.trim();
|
||||
})
|
||||
.filter((ip: string) => {
|
||||
return ip.length > 0;
|
||||
})
|
||||
: [];
|
||||
|
||||
Response.sendJsonObjectResponse(req, res, {
|
||||
ipWhitelist: ipList,
|
||||
});
|
||||
},
|
||||
);
|
||||
Response.sendJsonObjectResponse(req, res, {
|
||||
ipWhitelist: ipList,
|
||||
});
|
||||
});
|
||||
|
||||
return router;
|
||||
}
|
||||
|
||||
@@ -439,8 +439,7 @@ export const PageSEOConfig: Record<string, PageSEOData> = {
|
||||
},
|
||||
|
||||
"/product/scheduled-maintenance": {
|
||||
title:
|
||||
"Scheduled Maintenance | Plan & Communicate Downtime | OneUptime",
|
||||
title: "Scheduled Maintenance | Plan & Communicate Downtime | OneUptime",
|
||||
description:
|
||||
"Plan, schedule, and communicate maintenance windows to your users. Notify subscribers automatically, update status pages in real-time. Open source maintenance management.",
|
||||
canonicalPath: "/product/scheduled-maintenance",
|
||||
|
||||
Reference in New Issue
Block a user