diff --git a/frontend/pages/status/index.js b/frontend/pages/status/index.js
new file mode 100644
index 00000000..d9812f8e
--- /dev/null
+++ b/frontend/pages/status/index.js
@@ -0,0 +1,263 @@
+import React, { useEffect, useState, useLayoutEffect } from "react";
+import { useStatus } from "../../src/core/hooks";
+import {
+ Heading,
+ Text,
+ Flex,
+ Spacer,
+ Stack,
+ chakra,
+ useMediaQuery,
+ useBreakpointValue,
+} from "@chakra-ui/react";
+import { AWS_ASSETS_PATH } from "../../src/core/constants";
+
+const assets = {
+ background720: `${AWS_ASSETS_PATH}/product-background-720x405.png`,
+ background1920: `${AWS_ASSETS_PATH}/product-background-720x405.png`,
+ background2880: `${AWS_ASSETS_PATH}/product-background-720x405.png`,
+ background3840: `${AWS_ASSETS_PATH}/product-background-720x405.png`,
+};
+
+const Status = () => {
+ const healthyStatusText = "Available";
+ const downStatusText = "Unavailable";
+ const healthyStatusColor = "green.900";
+ const downStatusColor = "red.600";
+
+ const shortTimestamp = (rawTimestamp) => {
+ return rawTimestamp.replace(/^.+T/, "").replace(/\..+/, "");
+ };
+
+ const {
+ apiServerStatusCache,
+ ethereumClusterServerStatusCache,
+ gethStatusCache,
+ crawlersStatusCache,
+ dbServerStatusCache,
+ latestBlockDBStatusCache,
+ } = useStatus();
+
+ const [background, setBackground] = useState("background720");
+ const [backgroundLoaded720, setBackgroundLoaded720] = useState(false);
+ const [backgroundLoaded1920, setBackgroundLoaded1920] = useState(false);
+ const [backgroundLoaded2880, setBackgroundLoaded2880] = useState(false);
+ const [backgroundLoaded3840, setBackgroundLoaded3840] = useState(false);
+
+ const [
+ isLargerThan720px,
+ isLargerThan1920px,
+ isLargerThan2880px,
+ isLargerThan3840px,
+ ] = useMediaQuery([
+ "(min-width: 720px)",
+ "(min-width: 1920px)",
+ "(min-width: 2880px)",
+ "(min-width: 3840px)",
+ ]);
+
+ useEffect(() => {
+ assets[
+ "background720"
+ ] = `${AWS_ASSETS_PATH}/product-background-720x405.png`;
+ assets[
+ "background1920"
+ ] = `${AWS_ASSETS_PATH}/product-background-1920x1080.png`;
+ assets[
+ "background2880"
+ ] = `${AWS_ASSETS_PATH}/product-background-2880x1620.png`;
+ assets[
+ "background3840"
+ ] = `${AWS_ASSETS_PATH}/product-background-3840x2160.png`;
+ }, []);
+
+ useLayoutEffect(() => {
+ if (backgroundLoaded3840) {
+ setBackground("background3840");
+ } else if (backgroundLoaded2880) {
+ setBackground("background2880");
+ } else if (backgroundLoaded1920) {
+ setBackground("background1920");
+ } else {
+ setBackground("background720");
+ }
+ }, [
+ isLargerThan720px,
+ isLargerThan1920px,
+ isLargerThan2880px,
+ isLargerThan3840px,
+ backgroundLoaded720,
+ backgroundLoaded1920,
+ backgroundLoaded2880,
+ backgroundLoaded3840,
+ ]);
+
+ useLayoutEffect(() => {
+ const imageLoader720 = new Image();
+ imageLoader720.src = `${AWS_ASSETS_PATH}/product-background-720x405.png`;
+ imageLoader720.onload = () => {
+ setBackgroundLoaded720(true);
+ };
+ }, []);
+
+ useLayoutEffect(() => {
+ const imageLoader1920 = new Image();
+ imageLoader1920.src = `${AWS_ASSETS_PATH}/product-background-1920x1080.png`;
+ imageLoader1920.onload = () => {
+ setBackgroundLoaded1920(true);
+ };
+ }, []);
+
+ useLayoutEffect(() => {
+ const imageLoader2880 = new Image();
+ imageLoader2880.src = `${AWS_ASSETS_PATH}/product-background-2880x1620.png`;
+ imageLoader2880.onload = () => {
+ setBackgroundLoaded2880(true);
+ };
+ }, []);
+
+ useLayoutEffect(() => {
+ const imageLoader3840 = new Image();
+ imageLoader3840.src = `${AWS_ASSETS_PATH}/product-background-3840x2160.png`;
+ imageLoader3840.onload = () => {
+ setBackgroundLoaded3840(true);
+ };
+ }, []);
+
+ const margin = useBreakpointValue({
+ base: "1%",
+ sm: "2%",
+ md: "3%",
+ lg: "15%",
+ xl: "20%",
+ "2xl": "25%",
+ });
+
+ return (
+
+
+
+ {`Status page`}
+
+
+
+ Backend server
+
+
+ {!apiServerStatusCache.isLoading &&
+ apiServerStatusCache?.data?.status == "ok"
+ ? healthyStatusText
+ : downStatusText}
+
+
+
+
+ Crawlers server
+
+
+ {!ethereumClusterServerStatusCache.isLoading &&
+ ethereumClusterServerStatusCache?.data
+ ? healthyStatusText
+ : downStatusText}
+
+
+
+ Latest block in Geth
+
+
+ {!gethStatusCache.isLoading &&
+ gethStatusCache?.data?.current_block
+ ? gethStatusCache.data.current_block
+ : 0}
+
+
+
+ Txpool latest record ts
+
+
+ {!crawlersStatusCache.isLoading &&
+ crawlersStatusCache?.data?.ethereum_txpool_timestamp
+ ? shortTimestamp(
+ crawlersStatusCache?.data?.ethereum_txpool_timestamp
+ )
+ : downStatusText}
+
+
+
+ Trending latest record ts
+
+
+ {!crawlersStatusCache.isLoading &&
+ crawlersStatusCache?.data?.ethereum_trending_timestamp
+ ? shortTimestamp(
+ crawlersStatusCache?.data?.ethereum_trending_timestamp
+ )
+ : downStatusText}
+
+
+
+
+ Database server
+
+
+ {!dbServerStatusCache.isLoading &&
+ dbServerStatusCache?.data?.status == "ok"
+ ? healthyStatusText
+ : downStatusText}
+
+
+
+ Latest block in Database
+
+
+ {!latestBlockDBStatusCache.isLoading &&
+ latestBlockDBStatusCache?.data?.block_number
+ ? latestBlockDBStatusCache.data.block_number
+ : 0}
+
+
+
+
+
+ );
+};
+
+export default Status;
diff --git a/frontend/sample.env b/frontend/sample.env
index d2031fd0..ff12708b 100644
--- a/frontend/sample.env
+++ b/frontend/sample.env
@@ -1,3 +1,5 @@
export NEXT_PUBLIC_MIXPANEL_TOKEN=""
export NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=""
-export NEXT_PUBLIC_MOONSTREAM_API_URL=http://localhost:7481
\ No newline at end of file
+export NEXT_PUBLIC_MOONSTREAM_API_URL=""
+export NEXT_PUBLIC_MOONSTREAM_ETHEREUM_CLUSTER_URL=""
+export NEXT_PUBLIC_MOONSTREAM_DB_URL=""
diff --git a/frontend/src/core/hooks/index.js b/frontend/src/core/hooks/index.js
index 0327ba50..f5fcb76a 100644
--- a/frontend/src/core/hooks/index.js
+++ b/frontend/src/core/hooks/index.js
@@ -14,6 +14,7 @@ export { default as useQuery } from "./useQuery";
export { default as useResetPassword } from "./useResetPassword";
export { default as useRouter } from "./useRouter";
export { default as useSignUp } from "./useSignUp";
+export { default as useStatus } from "./useStatus";
export { default as useStorage } from "./useStorage";
export { default as useStream } from "./useStream";
export { default as useStripe } from "./useStripe";
diff --git a/frontend/src/core/hooks/useStatus.js b/frontend/src/core/hooks/useStatus.js
new file mode 100644
index 00000000..40908a09
--- /dev/null
+++ b/frontend/src/core/hooks/useStatus.js
@@ -0,0 +1,68 @@
+import { useQuery } from "react-query";
+import { queryCacheProps } from "./hookCommon";
+import { StatusService } from "../../core/services";
+
+const useStatus = () => {
+ const getAPIServerStatus = async () => {
+ const response = await StatusService.apiServerStatus();
+ return response.data;
+ };
+ const getEthereumClusterServerStatus = async () => {
+ const response = await StatusService.ethereumClusterServerStatus();
+ return response.data;
+ };
+ const getGethStatus = async () => {
+ const response = await StatusService.gethStatus();
+ return response.data;
+ };
+ const getCrawlersStatus = async () => {
+ const response = await StatusService.crawlersStatus();
+ return response.data;
+ };
+ const getDBServerStatus = async () => {
+ const response = await StatusService.dbServerStatus();
+ return response.data;
+ };
+ const getLatestBlockDBStatus = async () => {
+ const response = await StatusService.latestBlockDBStatus();
+ return response.data;
+ };
+
+ const apiServerStatusCache = useQuery("apiServer", getAPIServerStatus, {
+ ...queryCacheProps,
+ });
+ const ethereumClusterServerStatusCache = useQuery(
+ "ethereumClusterServer",
+ getEthereumClusterServerStatus,
+ {
+ ...queryCacheProps,
+ }
+ );
+ const gethStatusCache = useQuery("geth", getGethStatus, {
+ ...queryCacheProps,
+ });
+ const crawlersStatusCache = useQuery("crawlers", getCrawlersStatus, {
+ ...queryCacheProps,
+ });
+ const dbServerStatusCache = useQuery("dbServer", getDBServerStatus, {
+ ...queryCacheProps,
+ });
+ const latestBlockDBStatusCache = useQuery(
+ "latestBlockDB",
+ getLatestBlockDBStatus,
+ {
+ ...queryCacheProps,
+ }
+ );
+
+ return {
+ apiServerStatusCache,
+ ethereumClusterServerStatusCache,
+ gethStatusCache,
+ crawlersStatusCache,
+ dbServerStatusCache,
+ latestBlockDBStatusCache,
+ };
+};
+
+export default useStatus;
diff --git a/frontend/src/core/services/index.js b/frontend/src/core/services/index.js
index 6edb674a..ef4341d5 100644
--- a/frontend/src/core/services/index.js
+++ b/frontend/src/core/services/index.js
@@ -6,6 +6,7 @@ import * as GroupService from "./group.service";
import * as PreferencesService from "./preferences.service";
import * as HumbugService from "./humbug.service";
import * as InvitesService from "./invites.service";
+import * as StatusService from "./status.service";
import * as SubscriptionsService from "./subscriptions.service";
import * as StreamService from "./stream.service";
import * as TxInfoService from "./txinfo.service";
@@ -18,6 +19,7 @@ export {
PreferencesService,
HumbugService,
InvitesService,
+ StatusService,
SubscriptionsService,
StreamService,
TxInfoService,
diff --git a/frontend/src/core/services/status.service.js b/frontend/src/core/services/status.service.js
new file mode 100644
index 00000000..8666ccd7
--- /dev/null
+++ b/frontend/src/core/services/status.service.js
@@ -0,0 +1,48 @@
+import { http } from "../utils";
+
+const API_URL = process.env.NEXT_PUBLIC_MOONSTREAM_API_URL;
+const DB_URL = process.env.NEXT_PUBLIC_MOONSTREAM_DB_URL;
+const ETHEREUM_CLUSTER_URL =
+ process.env.NEXT_PUBLIC_MOONSTREAM_ETHEREUM_CLUSTER_URL;
+
+export const apiServerStatus = () => {
+ return http({
+ method: "GET",
+ url: `${API_URL}/ping`,
+ });
+};
+
+export const ethereumClusterServerStatus = () => {
+ return http({
+ method: "GET",
+ url: `${ETHEREUM_CLUSTER_URL}/ping`,
+ });
+};
+
+export const gethStatus = () => {
+ return http({
+ method: "GET",
+ url: `${ETHEREUM_CLUSTER_URL}/status`,
+ });
+};
+
+export const crawlersStatus = () => {
+ return http({
+ method: "GET",
+ url: `${API_URL}/status`,
+ });
+};
+
+export const dbServerStatus = () => {
+ return http({
+ method: "GET",
+ url: `${DB_URL}/ping`,
+ });
+};
+
+export const latestBlockDBStatus = () => {
+ return http({
+ method: "GET",
+ url: `${DB_URL}/block/latest`,
+ });
+};