From 1105e041be95d0f36a080cbb9636e01c99f82682 Mon Sep 17 00:00:00 2001 From: Mariusz Klochowicz Date: Thu, 4 Nov 2021 14:55:38 +1030 Subject: [PATCH] Display connection errors to the daemon in the UI Periodically send a keepalive message; if the daemon does not come back with a response, display a persistent toast notification (at the top of the screen, so it's harder to miss or confuse). Notification goes away when the connection to the daemon is re-established. --- frontend/src/MakerApp.tsx | 2 + frontend/src/TakerApp.tsx | 2 + frontend/src/components/BackendMonitor.tsx | 50 ++++++++++++++++++++++ frontend/vite.config.ts | 1 + 4 files changed, 55 insertions(+) create mode 100644 frontend/src/components/BackendMonitor.tsx diff --git a/frontend/src/MakerApp.tsx b/frontend/src/MakerApp.tsx index 768562d..0e7d5cf 100644 --- a/frontend/src/MakerApp.tsx +++ b/frontend/src/MakerApp.tsx @@ -19,6 +19,7 @@ import { import React, { useEffect, useState } from "react"; import { useAsync } from "react-async"; import { useEventSource } from "react-sse-hooks"; +import { useBackendMonitor } from "./components/BackendMonitor"; import { CfdTable } from "./components/cfdtables/CfdTable"; import CurrencyInputField from "./components/CurrencyInputField"; import CurrentPrice from "./components/CurrentPrice"; @@ -43,6 +44,7 @@ export default function App() { const priceInfo = useLatestEvent(source, "quote"); const toast = useToast(); + useBackendMonitor(toast, 5000); // 5s timeout let [minQuantity, setMinQuantity] = useState("10"); let [maxQuantity, setMaxQuantity] = useState("100"); diff --git a/frontend/src/TakerApp.tsx b/frontend/src/TakerApp.tsx index c50af4c..57ad223 100644 --- a/frontend/src/TakerApp.tsx +++ b/frontend/src/TakerApp.tsx @@ -18,6 +18,7 @@ import { import React, { useEffect, useState } from "react"; import { useAsync } from "react-async"; import { useEventSource } from "react-sse-hooks"; +import { useBackendMonitor } from "./components/BackendMonitor"; import { CfdTable } from "./components/cfdtables/CfdTable"; import CurrencyInputField from "./components/CurrencyInputField"; import createErrorToast from "./components/ErrorToast"; @@ -63,6 +64,7 @@ async function getMargin(payload: MarginRequestPayload): Promise export default function App() { const toast = useToast(); + useBackendMonitor(toast, 5000); // 5s timeout document.title = "Hermes Taker"; diff --git a/frontend/src/components/BackendMonitor.tsx b/frontend/src/components/BackendMonitor.tsx new file mode 100644 index 0000000..f899a1d --- /dev/null +++ b/frontend/src/components/BackendMonitor.tsx @@ -0,0 +1,50 @@ +import { useEffect, useRef } from "react"; + +async function fetchWithTimeout(resource: any, timeout: number) { + const controller = new AbortController(); + const id = setTimeout(() => controller.abort(), timeout); + const response = await fetch(resource, { + signal: controller.signal, + }); + clearTimeout(id); + return response; +} + +// Check for backend's presence by sending a request to '/alive' endpoint. +// When the backend's not there, the request is likely to timeout, triggering a +// persistent toast notification that goes away when the daemon is back online. +export function useBackendMonitor(toast: any, timeout_ms: number): void { + const toastIdRef = useRef(); + + const checkForBackend: () => void = async () => { + try { + const res = await fetchWithTimeout("/alive", timeout_ms); + // In case we get a response, but it's not what we expected + if (res.status.toString().startsWith("2")) { + if (toastIdRef.current) { + toast.close(toastIdRef.current); + } + } else { + throw new Error(res.statusText); + } + } catch (e) { + if (!toastIdRef.current) { + toastIdRef.current = toast({ + title: "Connection Error", + description: "Daemon process is not running", + status: "error", + position: "top", + duration: timeout_ms * 100000, // we don't want this to be closed + isClosable: false, + }); + } + } + }; + + useEffect(() => { + const interval = setInterval(() => checkForBackend(), timeout_ms); + return () => { + clearInterval(interval); + }; + }); +} diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index 3003308..d1ad0a5 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -33,6 +33,7 @@ export default defineConfig({ server: { proxy: { "/api": `http://localhost:${backendPorts[app]}`, + "/alive": `http://localhost:${backendPorts[app]}`, }, }, });