uptime
This commit is contained in:
1 parent
c2dece862c
commit
a0a8ceb0db
27 files changed
+2326
-94
No files matched your search
@@ -0,0 +1,129 @@
|
||||
const MIN_OUT = 10;
|
||||
const MAX_OUT = 100;
|
||||
|
||||
export const normalizeResponseTimes = <T, K extends keyof T>(
|
||||
checks: T[],
|
||||
key: K
|
||||
): (T & { normalResponseTime: number })[] => {
|
||||
if (!Array.isArray(checks) || checks.length === 0)
|
||||
return checks as (T & {
|
||||
normalResponseTime: number;
|
||||
})[];
|
||||
|
||||
if (checks.length === 1) {
|
||||
return [
|
||||
{
|
||||
...checks[0],
|
||||
normalResponseTime: 50,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
const getVal = (check: T): number => {
|
||||
const v = check[key] as unknown;
|
||||
return typeof v === "number" && Number.isFinite(v) ? v : 0;
|
||||
};
|
||||
|
||||
const { min, max } = checks.reduce(
|
||||
(acc, check) => {
|
||||
const v = getVal(check);
|
||||
if (v > acc.max) acc.max = v;
|
||||
if (v < acc.min) acc.min = v;
|
||||
return acc;
|
||||
},
|
||||
{ max: -Infinity, min: Infinity }
|
||||
);
|
||||
|
||||
const range = max - min || 1;
|
||||
|
||||
return checks.map((check) => ({
|
||||
...check,
|
||||
normalResponseTime: MIN_OUT + ((getVal(check) - min) * (MAX_OUT - MIN_OUT)) / range,
|
||||
}));
|
||||
};
|
||||
|
||||
// Interpolate color between three theme colors over 0-100 range.
|
||||
// 0-50 => start (success.light), 50-75 => mid (warning.light), 75-100 => end (error.light)
|
||||
export const getResponseColor = (
|
||||
ms: number,
|
||||
colors: {
|
||||
start: string | undefined;
|
||||
mid: string | undefined;
|
||||
end: string | undefined;
|
||||
}
|
||||
): string => {
|
||||
// New ranges with open high end:
|
||||
// 0–300ms: interpolate start (good) -> mid (warning)
|
||||
// 300–600ms: interpolate mid (warning) -> end (error)
|
||||
// >600ms: solid end (error)
|
||||
const safe = { ...colors };
|
||||
if (!safe.start) safe.start = "#4caf50"; // fallback green
|
||||
if (!safe.mid) safe.mid = "#ff9800"; // fallback orange
|
||||
if (!safe.end) safe.end = "#f44336"; // fallback red
|
||||
|
||||
const toHex = (c: number) => c.toString(16).padStart(2, "0");
|
||||
const clamp = (n: number) => Math.min(255, Math.max(0, Math.round(n)));
|
||||
const rgbToHex = (r: number, g: number, b: number) =>
|
||||
`#${toHex(clamp(r))}${toHex(clamp(g))}${toHex(clamp(b))}`;
|
||||
|
||||
const normalizeToHex = (value: string) => {
|
||||
const v = value.trim();
|
||||
if (v.startsWith("#")) {
|
||||
const h = v.slice(1);
|
||||
const full =
|
||||
h.length === 3
|
||||
? h
|
||||
.split("")
|
||||
.map((c) => c + c)
|
||||
.join("")
|
||||
: h.substring(0, 6);
|
||||
return `#${full.toLowerCase()}`;
|
||||
}
|
||||
const m = v
|
||||
.replace(/\s+/g, "")
|
||||
.match(/^rgba?\((\d{1,3}),(\d{1,3}),(\d{1,3})(?:,(0|0?\.\d+|1))?\)$/i);
|
||||
if (m) {
|
||||
const r = parseInt(m[1], 10);
|
||||
const g = parseInt(m[2], 10);
|
||||
const b = parseInt(m[3], 10);
|
||||
return rgbToHex(r, g, b);
|
||||
}
|
||||
// Fallback neutral
|
||||
return "#7f7f7f";
|
||||
};
|
||||
|
||||
const parseHex = (hex: string) => {
|
||||
const h = hex.replace("#", "");
|
||||
const full =
|
||||
h.length === 3
|
||||
? h
|
||||
.split("")
|
||||
.map((c) => c + c)
|
||||
.join("")
|
||||
: h;
|
||||
const n = parseInt(full, 16);
|
||||
return { r: (n >> 16) & 255, g: (n >> 8) & 255, b: n & 255 };
|
||||
};
|
||||
const mix = (a: number, b: number, t: number) => Math.round(a + (b - a) * t);
|
||||
|
||||
const v = Math.max(0, ms);
|
||||
if (v <= 300) {
|
||||
const t = v / 300; // 0..1 from start->mid
|
||||
const s = parseHex(normalizeToHex(safe.start));
|
||||
const m = parseHex(normalizeToHex(safe.mid));
|
||||
const r = mix(s.r, m.r, t);
|
||||
const g = mix(s.g, m.g, t);
|
||||
const b = mix(s.b, m.b, t);
|
||||
return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
|
||||
}
|
||||
if (v <= 600) {
|
||||
const t = (v - 300) / 300; // 0..1 from mid->end
|
||||
const m = parseHex(normalizeToHex(safe.mid));
|
||||
const e = parseHex(normalizeToHex(safe.end));
|
||||
const r = mix(m.r, e.r, t);
|
||||
const g = mix(m.g, e.g, t);
|
||||
const b = mix(m.b, e.b, t);
|
||||
return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
|
||||
}
|
||||
return normalizeToHex(safe.end);
|
||||
};
|
||||
@@ -0,0 +1,120 @@
|
||||
import type { Monitor, MonitorStatus, MonitorType } from "@/Types/Monitor";
|
||||
import type { PaletteKey } from "@/Utils/Theme/v2Theme";
|
||||
import type { ValueType } from "@/Components/v2/design-elements/StatusLabel";
|
||||
|
||||
export const getMonitorPath = (type: MonitorType): string => {
|
||||
const pathMap: Record<MonitorType, string> = {
|
||||
http: "uptime",
|
||||
port: "uptime",
|
||||
ping: "uptime",
|
||||
hardware: "hardware",
|
||||
pagespeed: "pagespeed",
|
||||
docker: "docker",
|
||||
game: "game-servers",
|
||||
unknown: "uptime",
|
||||
};
|
||||
return pathMap[type];
|
||||
};
|
||||
|
||||
export const getStatusPalette = (status: MonitorStatus): PaletteKey => {
|
||||
if (status === true) {
|
||||
return "success";
|
||||
}
|
||||
if (status === false) {
|
||||
return "error";
|
||||
}
|
||||
return "warning";
|
||||
};
|
||||
|
||||
export const getValuePalette = (value: ValueType): PaletteKey => {
|
||||
const paletteMap: Record<ValueType, PaletteKey> = {
|
||||
positive: "success",
|
||||
negative: "error",
|
||||
neutral: "warning",
|
||||
};
|
||||
return paletteMap[value];
|
||||
};
|
||||
|
||||
export const getStatusColor = (status: MonitorStatus, theme: any): string => {
|
||||
if (status === true) {
|
||||
return theme.palette.success.light;
|
||||
}
|
||||
|
||||
if (status === false) {
|
||||
return theme.palette.error.light;
|
||||
}
|
||||
|
||||
return theme.palette.warning.light;
|
||||
};
|
||||
|
||||
export const getResponseTimeColor = (responseTime: number): PaletteKey => {
|
||||
if (responseTime < 200) {
|
||||
return "success";
|
||||
} else if (responseTime < 300) {
|
||||
return "warning";
|
||||
} else {
|
||||
return "error";
|
||||
}
|
||||
};
|
||||
|
||||
export const getInfraGaugeColor = (val: number, theme: any) => {
|
||||
if (val < 50) {
|
||||
return theme.palette.success.main;
|
||||
} else if (val < 80) {
|
||||
return theme.palette.warning.light;
|
||||
} else {
|
||||
return theme.palette.error.light;
|
||||
}
|
||||
};
|
||||
|
||||
export const getPageSpeedPalette = (score: number): PaletteKey => {
|
||||
if (score >= 90) return "success";
|
||||
else if (score >= 50) return "warning";
|
||||
else return "error";
|
||||
};
|
||||
|
||||
export const formatUrl = (url: string, maxLength: number = 55) => {
|
||||
if (!url) return "";
|
||||
|
||||
const strippedUrl = url.replace(/^https?:\/\//, "");
|
||||
return strippedUrl.length > maxLength
|
||||
? `${strippedUrl.slice(0, maxLength)}…`
|
||||
: strippedUrl;
|
||||
};
|
||||
|
||||
export interface IStatusPageHeaderConfig {
|
||||
paletteKey: PaletteKey;
|
||||
message: string;
|
||||
}
|
||||
export const getStatusPageHeaderConfig = (
|
||||
monitors: Monitor[],
|
||||
t: any
|
||||
): IStatusPageHeaderConfig => {
|
||||
if (!monitors || monitors.length === 0) {
|
||||
return { paletteKey: "error", message: "No monitors available" };
|
||||
}
|
||||
|
||||
const allUp = monitors.every((monitor) => monitor.status === true);
|
||||
const anyDown = monitors.some((monitor) => monitor.status === false);
|
||||
const allDown = monitors.every((monitor) => monitor.status === false);
|
||||
|
||||
if (allUp)
|
||||
return {
|
||||
paletteKey: "success",
|
||||
message: t("statusPage.details.statusHeader.allUp"),
|
||||
};
|
||||
if (allDown)
|
||||
return {
|
||||
paletteKey: "error",
|
||||
message: t("statusPage.details.statusHeader.allDown"),
|
||||
};
|
||||
if (anyDown)
|
||||
return {
|
||||
paletteKey: "warning",
|
||||
message: t("statusPage.details.statusHeader.anyDown"),
|
||||
};
|
||||
return {
|
||||
paletteKey: "warning",
|
||||
message: t("statusPage.details.statusHeader.anyDown"),
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,52 @@
|
||||
import dayjs from "dayjs";
|
||||
import duration from "dayjs/plugin/duration";
|
||||
import utc from "dayjs/plugin/utc";
|
||||
import timezone from "dayjs/plugin/timezone";
|
||||
import customParseFormat from "dayjs/plugin/customParseFormat";
|
||||
|
||||
dayjs.extend(utc);
|
||||
dayjs.extend(timezone);
|
||||
dayjs.extend(customParseFormat);
|
||||
dayjs.extend(duration);
|
||||
|
||||
export const MS_PER_SECOND = 1000;
|
||||
export const MS_PER_MINUTE = 60 * MS_PER_SECOND;
|
||||
export const MS_PER_HOUR = 60 * MS_PER_MINUTE;
|
||||
export const MS_PER_DAY = 24 * MS_PER_HOUR;
|
||||
export const MS_PER_WEEK = MS_PER_DAY * 7;
|
||||
|
||||
export const formatDateWithTz = (timestamp: string, format: string, timezone: string) => {
|
||||
if (!timestamp) {
|
||||
return "Unknown time";
|
||||
}
|
||||
const formattedDate = dayjs(timestamp).tz(timezone).format(format);
|
||||
return formattedDate;
|
||||
};
|
||||
|
||||
export const tickDateFormatLookup = (range: string) => {
|
||||
const tickFormatLookup: Record<string, string> = {
|
||||
"1h": "h:mm A",
|
||||
"24h": "h:mm A",
|
||||
"7d": "MM/D, h:mm A",
|
||||
"30d": "ddd. M/D",
|
||||
};
|
||||
const format = tickFormatLookup[range];
|
||||
if (format === undefined) {
|
||||
return "";
|
||||
}
|
||||
return format;
|
||||
};
|
||||
|
||||
export const tooltipDateFormatLookup = (range: string) => {
|
||||
const dateFormatLookup: Record<string, string> = {
|
||||
"1h": "ddd. MMMM D, YYYY, hh:mm A",
|
||||
"24h": "ddd. MMMM D, YYYY, hh:mm A",
|
||||
"7d": "ddd. MMMM D, YYYY, hh:mm A",
|
||||
"30d": "ddd. MMMM D, YYYY",
|
||||
};
|
||||
const format = dateFormatLookup[range];
|
||||
if (format === undefined) {
|
||||
return "";
|
||||
}
|
||||
return format;
|
||||
};
|
||||
Reference in new issue
Block a user