diff --git a/backend/moonstream/providers/bugout.py b/backend/moonstream/providers/bugout.py index 1ace3ce9..f7b29f76 100644 --- a/backend/moonstream/providers/bugout.py +++ b/backend/moonstream/providers/bugout.py @@ -21,6 +21,8 @@ from ..settings import ETHTXPOOL_HUMBUG_CLIENT_ID logger = logging.getLogger(__name__) logger.setLevel(logging.WARN) +allowed_tags = ["tag:erc721"] + class BugoutEventProviderError(Exception): """ @@ -315,14 +317,17 @@ class EthereumTXPoolProvider(BugoutEventProvider): ] subscriptions_filters = [] for address in addresses: - subscriptions_filters.extend( - [f"?#from_address:{address}", f"?#to_address:{address}"] - ) + if address in allowed_tags: + subscriptions_filters.append(address) + else: + subscriptions_filters.extend( + [f"?#from_address:{address}", f"?#to_address:{address}"] + ) return subscriptions_filters -class NftProvider(BugoutEventProvider): +class PublicDataProvider(BugoutEventProvider): def __init__( self, event_type: str, @@ -359,7 +364,7 @@ Shows the top 10 addresses active on the Ethereum blockchain over the last hour 4. Amount (in WEI) received To restrict your queries to this provider, add a filter of \"type:ethereum_whalewatch\" to your query (query parameter: \"q\") on the /streams endpoint.""" -whalewatch_provider = BugoutEventProvider( +whalewatch_provider = PublicDataProvider( event_type="ethereum_whalewatch", description=whalewatch_description, default_time_interval_seconds=310, @@ -389,7 +394,7 @@ Currently, it summarizes the activities on the following NFT markets: This provider is currently not accessible for subscription. The data from this provider is publicly available at the /nft endpoint.""" -nft_summary_provider = NftProvider( +nft_summary_provider = PublicDataProvider( event_type="nft_summary", description=nft_summary_description, # 40 blocks per summary, 15 seconds per block + 2 seconds wiggle room. diff --git a/backend/moonstream/providers/ethereum_blockchain.py b/backend/moonstream/providers/ethereum_blockchain.py index 1eb87bde..dc607012 100644 --- a/backend/moonstream/providers/ethereum_blockchain.py +++ b/backend/moonstream/providers/ethereum_blockchain.py @@ -8,11 +8,14 @@ from bugout.data import BugoutResource from moonstreamdb.models import ( EthereumBlock, EthereumTransaction, + EthereumAddress, + EthereumLabel, ) from sqlalchemy import or_, and_, text from sqlalchemy.orm import Session, Query from sqlalchemy.sql.functions import user + from .. import data from ..stream_boundaries import validate_stream_boundary from ..stream_queries import StreamQuery @@ -23,6 +26,7 @@ logger.setLevel(logging.WARN) event_type = "ethereum_blockchain" +allowed_tags = ["tag:erc721"] description = f"""Event provider for transactions from the Ethereum blockchain. @@ -79,6 +83,7 @@ class Filters: from_addresses: List[str] = field(default_factory=list) to_addresses: List[str] = field(default_factory=list) + labels: List[str] = field(default_factory=list) def default_filters(subscriptions: List[BugoutResource]) -> Filters: @@ -91,8 +96,11 @@ def default_filters(subscriptions: List[BugoutResource]) -> Filters: Optional[str], subscription.resource_data.get("address") ) if subscription_address is not None: - filters.from_addresses.append(subscription_address) - filters.to_addresses.append(subscription_address) + if subscription_address in allowed_tags: + filters.labels.append(subscription_address.split(":")[1]) + else: + filters.from_addresses.append(subscription_address) + filters.to_addresses.append(subscription_address) else: logger.warn( f"Could not find subscription address for subscription with resource id: {subscription.id}" @@ -157,14 +165,20 @@ def parse_filters( parsed_filters.from_addresses.append(address) parsed_filters.to_addresses.append(address) - if not (parsed_filters.from_addresses or parsed_filters.to_addresses): + if not ( + parsed_filters.from_addresses + or parsed_filters.to_addresses + or parsed_filters.labels + ): return None return parsed_filters def query_ethereum_transactions( - db_session: Session, stream_boundary: data.StreamBoundary, parsed_filters: Filters + db_session: Session, + stream_boundary: data.StreamBoundary, + parsed_filters: Filters, ) -> Query: """ Builds a database query for Ethereum transactions that occurred within the window of time that @@ -198,15 +212,41 @@ def query_ethereum_transactions( query = query.filter(EthereumBlock.timestamp <= stream_boundary.end_time) # We want to take a big disjunction (OR) over ALL the filters, be they on "from" address or "to" address - address_clauses = [ - EthereumTransaction.from_address == address - for address in parsed_filters.from_addresses - ] + [ - EthereumTransaction.to_address == address - for address in parsed_filters.to_addresses - ] - if address_clauses: - query = query.filter(or_(*address_clauses)) + address_clauses = [] + + address_clauses.extend( + [ + EthereumTransaction.from_address == address + for address in parsed_filters.from_addresses + ] + + [ + EthereumTransaction.to_address == address + for address in parsed_filters.to_addresses + ] + ) + + labels_clause = [] + + if parsed_filters.labels: + label_clause = ( + db_session.query(EthereumAddress) + .join(EthereumLabel, EthereumAddress.id == EthereumLabel.address_id) + .filter( + or_( + *[ + EthereumLabel.label.contains(label) + for label in list(set(parsed_filters.labels)) + ] + ) + ) + .exists() + ) + labels_clause.append(label_clause) + + subscriptions_clause = address_clauses + labels_clause + + if subscriptions_clause: + query = query.filter(or_(*subscriptions_clause)) return query @@ -353,8 +393,7 @@ def next_event( query_ethereum_transactions(db_session, next_stream_boundary, parsed_filters) .order_by(text("timestamp asc")) .limit(1) - .one_or_none() - ) + ).one_or_none() if maybe_ethereum_transaction is None: return None @@ -394,9 +433,7 @@ def previous_event( ) .order_by(text("timestamp desc")) .limit(1) - .one_or_none() - ) - + ).one_or_none() if maybe_ethereum_transaction is None: return None return ethereum_transaction_event(maybe_ethereum_transaction) diff --git a/backend/moonstream/routes/whales.py b/backend/moonstream/routes/whales.py new file mode 100644 index 00000000..2c089f4c --- /dev/null +++ b/backend/moonstream/routes/whales.py @@ -0,0 +1,93 @@ +""" +Moonstream's /whales endpoints. + +These endpoints provide public access to whale watch summaries. No authentication required. +""" +from datetime import datetime +import logging +from typing import Optional + +from bugout.data import BugoutResource + +from fastapi import Depends, FastAPI, Query +from moonstreamdb import db +from fastapi.middleware.cors import CORSMiddleware +from sqlalchemy.orm import Session + +from .. import data +from ..providers.bugout import whalewatch_provider +from ..settings import ( + bugout_client, + DOCS_TARGET_PATH, + MOONSTREAM_ADMIN_ACCESS_TOKEN, + MOONSTREAM_DATA_JOURNAL_ID, + ORIGINS, +) +from ..stream_queries import StreamQuery +from ..version import MOONSTREAM_VERSION + +logger = logging.getLogger(__name__) + +tags_metadata = [ + {"name": "whales", "description": "Whales summaries"}, +] + +app = FastAPI( + title=f"Moonstream /whales API", + description="User, token and password handlers.", + version=MOONSTREAM_VERSION, + openapi_tags=tags_metadata, + openapi_url="/openapi.json", + docs_url=None, + redoc_url=f"/{DOCS_TARGET_PATH}", +) + +app.add_middleware( + CORSMiddleware, + allow_origins=ORIGINS, + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], +) + + +@app.get("/", tags=["whales"], response_model=data.GetEventsResponse) +async def stream_handler( + start_time: int = Query(0), + end_time: Optional[int] = Query(None), + include_start: bool = Query(False), + include_end: bool = Query(False), + db_session: Session = Depends(db.yield_db_session), +) -> data.GetEventsResponse: + """ + Retrieves the list of whales spotted over given stream boundary + + - **start_time**: Timestamp. Must be provided otherwise this request will hang + - **end_time**: Timestamp. Optional. + - **include_start** (string): is start_time inclusive or not + - **include_end** (string): is end_time inclusive or not + """ + stream_boundary = data.StreamBoundary( + start_time=start_time, + end_time=end_time, + include_start=include_start, + include_end=include_end, + ) + + result = whalewatch_provider.get_events( + db_session=db_session, + bugout_client=bugout_client, + data_journal_id=MOONSTREAM_DATA_JOURNAL_ID, + data_access_token=MOONSTREAM_ADMIN_ACCESS_TOKEN, + stream_boundary=stream_boundary, + user_subscriptions={whalewatch_provider.event_type: []}, + query=StreamQuery(subscription_types=[whalewatch_provider.event_type]), + ) + + if result is None: + return data.GetEventsResponse(stream_boundary=stream_boundary, events=[]) + + provider_stream_boundary, events = result + return data.GetEventsResponse( + stream_boundary=provider_stream_boundary, events=events + ) diff --git a/crawlers/server/main.go b/crawlers/server/main.go index 923d1574..f0bd58db 100644 --- a/crawlers/server/main.go +++ b/crawlers/server/main.go @@ -13,6 +13,7 @@ import ( ) var MOONSTREAM_IPC_PATH = os.Getenv("MOONSTREAM_IPC_PATH") +var MOONSTREAM_CORS_ALLOWED_ORIGINS = os.Getenv("MOONSTREAM_CORS_ALLOWED_ORIGINS") type GethResponse struct { Result string `json:"result"` @@ -26,8 +27,25 @@ type PingResponse struct { Status string `json:"status"` } +// Extends handler with allowed CORS policies +func setupCorsResponse(w *http.ResponseWriter, req *http.Request) { + for _, allowedOrigin := range strings.Split(MOONSTREAM_CORS_ALLOWED_ORIGINS, ",") { + for _, reqOrigin := range req.Header["Origin"] { + if reqOrigin == allowedOrigin { + (*w).Header().Set("Access-Control-Allow-Origin", allowedOrigin) + } + } + + } + (*w).Header().Set("Access-Control-Allow-Methods", "GET,OPTIONS") +} + func ping(w http.ResponseWriter, req *http.Request) { + setupCorsResponse(&w, req) log.Printf("%s, %s, %q", req.RemoteAddr, req.Method, req.URL.String()) + if (*req).Method == "OPTIONS" { + return + } w.Header().Set("Content-Type", "application/json") response := PingResponse{Status: "ok"} @@ -35,7 +53,11 @@ func ping(w http.ResponseWriter, req *http.Request) { } func pingGeth(w http.ResponseWriter, req *http.Request) { + setupCorsResponse(&w, req) log.Printf("%s, %s, %q", req.RemoteAddr, req.Method, req.URL.String()) + if (*req).Method == "OPTIONS" { + return + } postBody, err := json.Marshal(map[string]interface{}{ "jsonrpc": "2.0", diff --git a/crawlers/server/sample.env b/crawlers/server/sample.env index fc9b5a4a..053b87b4 100644 --- a/crawlers/server/sample.env +++ b/crawlers/server/sample.env @@ -1,2 +1,3 @@ export MOONSTREAM_CRAWLERS_SERVER_PORT="8080" export MOONSTREAM_IPC_PATH=null +export MOONSTREAM_CORS_ALLOWED_ORIGINS="http://localhost:3000,https://moonstream.to,https://www.moonstream.to,https://alpha.moonstream.to" diff --git a/db/server/main.go b/db/server/main.go index fd5f4899..da9e452c 100644 --- a/db/server/main.go +++ b/db/server/main.go @@ -6,12 +6,14 @@ import ( "log" "net/http" "os" + "strings" "gorm.io/driver/postgres" "gorm.io/gorm" ) var MOONSTREAM_DB_URI = os.Getenv("MOONSTREAM_DB_URI") +var MOONSTREAM_CORS_ALLOWED_ORIGINS = os.Getenv("MOONSTREAM_CORS_ALLOWED_ORIGINS") type PingResponse struct { Status string `json:"status"` @@ -21,8 +23,36 @@ type BlockResponse struct { BlockNumber uint64 `json:"block_number"` } +var dbConnection *gorm.DB + +func InitDB() *gorm.DB { + db, err := gorm.Open(postgres.Open(MOONSTREAM_DB_URI), &gorm.Config{}) + if err != nil { + log.Println("Database unavailable") + return nil + } + return db +} + +// Extends handler with allowed CORS policies +func setupCorsResponse(w *http.ResponseWriter, req *http.Request) { + for _, allowedOrigin := range strings.Split(MOONSTREAM_CORS_ALLOWED_ORIGINS, ",") { + for _, reqOrigin := range req.Header["Origin"] { + if reqOrigin == allowedOrigin { + (*w).Header().Set("Access-Control-Allow-Origin", allowedOrigin) + } + } + + } + (*w).Header().Set("Access-Control-Allow-Methods", "GET,OPTIONS") +} + func ping(w http.ResponseWriter, req *http.Request) { + setupCorsResponse(&w, req) log.Printf("%s, %s, %q", req.RemoteAddr, req.Method, req.URL.String()) + if (*req).Method == "OPTIONS" { + return + } w.Header().Set("Content-Type", "application/json") response := PingResponse{Status: "ok"} @@ -30,19 +60,22 @@ func ping(w http.ResponseWriter, req *http.Request) { } func blockLatest(w http.ResponseWriter, req *http.Request) { + setupCorsResponse(&w, req) log.Printf("%s, %s, %q", req.RemoteAddr, req.Method, req.URL.String()) + if (*req).Method == "OPTIONS" { + return + } w.Header().Set("Content-Type", "application/json") var latestBlock BlockResponse - db, err := gorm.Open(postgres.Open(MOONSTREAM_DB_URI), &gorm.Config{}) - if err != nil { + + if dbConnection == nil { http.Error(w, http.StatusText(500), 500) return } - query := "SELECT block_number FROM ethereum_blocks ORDER BY block_number DESC LIMIT 1" - db.Raw(query, 1).Scan(&latestBlock.BlockNumber) + dbConnection.Raw(query, 1).Scan(&latestBlock.BlockNumber) json.NewEncoder(w).Encode(latestBlock) } @@ -54,6 +87,8 @@ func main() { flag.StringVar(&listenPort, "port", "8080", "Server listen port") flag.Parse() + dbConnection = InitDB() + address := listenAddr + ":" + listenPort log.Printf("Starting server at %s\n", address) diff --git a/db/server/sample.env b/db/server/sample.env index 412c015d..ef5a4be3 100644 --- a/db/server/sample.env +++ b/db/server/sample.env @@ -1,2 +1,3 @@ export MOONSTREAM_DB_SERVER_PORT="8080" export MOONSTREAM_DB_URI="postgresql://:@:/" +export MOONSTREAM_CORS_ALLOWED_ORIGINS="http://localhost:3000,https://moonstream.to,https://www.moonstream.to,https://alpha.moonstream.to" 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`, + }); +};