Merge pull request #505 from benphelps/ping-and-redesign-status

Feature: Service ping, redesign docker status
pull/543/head
Jason Fischer 2 years ago committed by GitHub
commit 354f819041
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -56,7 +56,13 @@
"tx": "TX",
"mem": "MEM",
"cpu": "CPU",
"offline": "Offline"
"offline": "Offline",
"error": "Error",
"unknown": "Unknown"
},
"ping": {
"error": "Error",
"ping": "Ping"
},
"emby": {
"playing": "Playing",

@ -3,6 +3,7 @@ import { useContext, useState } from "react";
import Status from "./status";
import Widget from "./widget";
import Ping from "./ping";
import Docker from "widgets/docker/component";
import { SettingsContext } from "utils/contexts/settings";
@ -30,7 +31,7 @@ export default function Item({ service }) {
<div
className={`${
hasLink ? "cursor-pointer " : " "
}transition-all h-15 mb-3 p-1 rounded-md font-medium text-theme-700 dark:text-theme-200 dark:hover:text-theme-300 shadow-md shadow-theme-900/10 dark:shadow-theme-900/20 bg-theme-100/20 hover:bg-theme-300/20 dark:bg-white/5 dark:hover:bg-white/10`}
}transition-all h-15 mb-3 p-1 rounded-md font-medium text-theme-700 dark:text-theme-200 dark:hover:text-theme-300 shadow-md shadow-theme-900/10 dark:shadow-theme-900/20 bg-theme-100/20 hover:bg-theme-300/20 dark:bg-white/5 dark:hover:bg-white/10 relative`}
>
<div className="flex select-none">
{service.icon &&
@ -70,17 +71,26 @@ export default function Item({ service }) {
</div>
)}
<div className="absolute top-0 right-0 w-1/2 flex flex-row justify-end gap-2 mr-2">
{service.ping && (
<div className="flex-shrink-0 flex items-center justify-center cursor-pointer">
<Ping service={service} />
<span className="sr-only">Ping status</span>
</div>
)}
{service.container && (
<button
type="button"
onClick={() => (statsOpen ? closeStats() : setStatsOpen(true))}
className="flex-shrink-0 flex items-center justify-center w-12 cursor-pointer"
className="flex-shrink-0 flex items-center justify-center cursor-pointer"
>
<Status service={service} />
<span className="sr-only">View container stats</span>
</button>
)}
</div>
</div>
{service.container && service.server && (
<div

@ -0,0 +1,44 @@
import { useTranslation } from "react-i18next";
import useSWR from "swr";
export default function Ping({ service }) {
const { t } = useTranslation();
const { data, error } = useSWR(`/api/ping?${new URLSearchParams({ping: service.ping}).toString()}`, {
refreshInterval: 30000
});
if (error) {
return (
<div className="w-auto px-1.5 py-0.5 text-center bg-theme-500/10 dark:bg-theme-900/50 rounded-b-[3px] overflow-hidden">
<div className="text-[8px] font-bold text-rose-500 uppercase">{t("ping.error")}</div>
</div>
);
}
if (!data) {
return (
<div className="w-auto px-1.5 py-0.5 text-center bg-theme-500/10 dark:bg-theme-900/50 rounded-b-[3px] overflow-hidden">
<div className="text-[8px] font-bold text-black/20 dark:text-white/40 uppercase">{t("ping.ping")}</div>
</div>
);
}
const statusText = `${service.ping}: HTTP status ${data.status}`;
if (data && data.status !== 200) {
return (
<div className="w-auto px-1.5 py-0.5 text-center bg-theme-500/10 dark:bg-theme-900/50 rounded-b-[3px] overflow-hidden" title={statusText}>
<div className="text-[8px] font-bold text-rose-500/80">{data.status}</div>
</div>
);
}
if (data && data.status === 200) {
return (
<div className="w-auto px-1.5 py-0.5 text-center bg-theme-500/10 dark:bg-theme-900/50 rounded-b-[3px] overflow-hidden" title={statusText}>
<div className="text-[8px] font-bold text-emerald-500/80">{t("common.ms", { value: data.latency, style: "unit", unit: "millisecond", unitDisplay: "narrow", maximumFractionDigits: 0 })}</div>
</div>
);
}
}

@ -1,19 +1,36 @@
import { useTranslation } from "react-i18next";
import useSWR from "swr";
export default function Status({ service }) {
const { t } = useTranslation();
const { data, error } = useSWR(`/api/docker/status/${service.container}/${service.server || ""}`);
if (error) {
return <div className="w-3 h-3 bg-rose-300 dark:bg-rose-500 rounded-full" />;
<div className="w-auto px-1.5 py-0.5 text-center bg-theme-500/10 dark:bg-theme-900/50 rounded-b-[3px] overflow-hidden" title={data.status}>
<div className="text-[8px] font-bold text-rose-500/80 uppercase">{t("docker.error")}</div>
</div>
}
if (data && data.status === "running") {
return <div className="w-3 h-3 bg-emerald-300 dark:bg-emerald-500 rounded-full" />;
return (
<div className="w-auto px-1.5 py-0.5 text-center bg-theme-500/10 dark:bg-theme-900/50 rounded-b-[3px] overflow-hidden" title={data.status}>
<div className="text-[8px] font-bold text-emerald-500/80 uppercase">{data.status}</div>
</div>
);
}
if (data && data.status === "not found") {
return <div className="h-2.5 w-2.5 bg-orange-400/50 dark:bg-yellow-200/40 -rotate-45" />;
if (data && (data.status === "not found" || data.status === "exited")) {
return (
<div className="w-auto px-1.5 py-0.5 text-center bg-theme-500/10 dark:bg-theme-900/50 rounded-b-[3px] overflow-hidden" title={data.status}>
<div className="text-[8px] font-bold text-orange-400/50 dark:text-orange-400/80 uppercase">{data.status}</div>
</div>
);
}
return <div className="w-3 h-3 bg-black/20 dark:bg-white/40 rounded-full" />;
return (
<div className="w-auto px-1.5 py-0.5 text-center bg-theme-500/10 dark:bg-theme-900/50 rounded-b-[3px] overflow-hidden">
<div className="text-[8px] font-bold text-black/20 dark:text-white/40 uppercase">{t("docker.unknown")}</div>
</div>
);
}

@ -0,0 +1,28 @@
import { performance } from "perf_hooks";
import createLogger from "utils/logger";
import { httpProxy } from "utils/proxy/http";
const logger = createLogger("ping");
export default async function handler(req, res) {
const { ping: pingURL } = req.query;
if (!pingURL) {
logger.debug("No ping URL specified");
return res.status(400).send({
error: "No ping URL given",
});
}
const startTime = performance.now();
const [status] = await httpProxy(pingURL, {
method: "HEAD"
});
const endTime = performance.now();
return res.status(200).json({
status,
latency: endTime - startTime
});
}

@ -96,7 +96,7 @@ export async function httpProxy(url, params = {}) {
return [status, contentType, data, responseHeaders];
}
catch (err) {
logger.error("Error calling %s//%s%s...", url.protocol, url.hostname, url.pathname);
logger.error("Error calling %s//%s%s...", constructedUrl.protocol, constructedUrl.hostname, constructedUrl.pathname);
logger.error(err);
return [500, "application/json", { error: {message: err?.message ?? "Unknown error", url, rawError: err} }, null];
}

Loading…
Cancel
Save