From 1c55ca5fb6bca305da19a25b9ecafb7e3dd41396 Mon Sep 17 00:00:00 2001 From: Amine Afia Date: Tue, 30 Jul 2024 01:30:37 +0200 Subject: [PATCH] removed serverless endpoints --- .../src/@3rdweb-sdk/react/hooks/useEngine.ts | 4 ++ .../src/app/api/engine-status/errors/route.ts | 45 ------------ .../app/api/engine-status/prometheus/route.ts | 54 --------------- .../api/engine-status/status-codes/route.ts | 61 ---------------- .../src/app/api/engine-status/volume/route.ts | 44 ------------ .../engine/status/components/ErrorRate.tsx | 44 ++++++++---- .../engine/status/components/Healthcheck.tsx | 9 +-- .../engine/status/components/StatusCodes.tsx | 69 +++++++++++++++---- .../status/components/TransactionVolume.tsx | 43 ++++++++---- .../src/components/engine/status/status.tsx | 23 ++++++- 10 files changed, 142 insertions(+), 254 deletions(-) delete mode 100644 apps/dashboard/src/app/api/engine-status/errors/route.ts delete mode 100644 apps/dashboard/src/app/api/engine-status/prometheus/route.ts delete mode 100644 apps/dashboard/src/app/api/engine-status/status-codes/route.ts delete mode 100644 apps/dashboard/src/app/api/engine-status/volume/route.ts diff --git a/apps/dashboard/src/@3rdweb-sdk/react/hooks/useEngine.ts b/apps/dashboard/src/@3rdweb-sdk/react/hooks/useEngine.ts index 341e844c490..33f71606536 100644 --- a/apps/dashboard/src/@3rdweb-sdk/react/hooks/useEngine.ts +++ b/apps/dashboard/src/@3rdweb-sdk/react/hooks/useEngine.ts @@ -1,4 +1,5 @@ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; +import type { ResultItem } from "components/engine/status/components/StatusCodes"; import { THIRDWEB_API_HOST } from "constants/urls"; import { useState } from "react"; import { useActiveAccount, useActiveWalletChain } from "thirdweb/react"; @@ -1450,6 +1451,9 @@ export interface EngineResourceMetrics { cpu: number; memory: number; }; + errorRate: { data: { result: ResultItem[] } }; + statusCodes: { data: { result: ResultItem[] } }; + requestVolume: { data: { result: ResultItem[] } }; } export function useEngineResourceMetrics(engineId: string) { diff --git a/apps/dashboard/src/app/api/engine-status/errors/route.ts b/apps/dashboard/src/app/api/engine-status/errors/route.ts deleted file mode 100644 index 009775dd583..00000000000 --- a/apps/dashboard/src/app/api/engine-status/errors/route.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { NextResponse } from "next/server"; -import { fetchPrometheusData } from "../prometheus/route"; - -interface ResultItem { - metric: { - code: string; - }; - values: [number, number][]; -} - -function transformPrometheusData(prometheusResult: { - data: { result: ResultItem[] }; -}) { - if ( - !prometheusResult || - !prometheusResult.data || - !prometheusResult.data.result - ) { - throw new TypeError("Invalid Prometheus result format"); - } - - const results = []; - for (const resultItem of prometheusResult.data.result) { - for (const [timestamp, value] of resultItem.values) { - results.push({ - time: new Date(timestamp * 1000).toISOString(), - value: value, - }); - } - } - - return { - data: results, - tags: ["value"], - }; -} - -export const POST = async () => { - const query = - 'sum(increase(traefik_service_requests_total{code=~"[4-9].."}[1h]))'; - const data = await fetchPrometheusData(query); - return NextResponse.json(transformPrometheusData(data), { - status: 200, - }); -}; diff --git a/apps/dashboard/src/app/api/engine-status/prometheus/route.ts b/apps/dashboard/src/app/api/engine-status/prometheus/route.ts deleted file mode 100644 index c798d6004f9..00000000000 --- a/apps/dashboard/src/app/api/engine-status/prometheus/route.ts +++ /dev/null @@ -1,54 +0,0 @@ -// This file should be removed, it's just fro testing purposes -import { NextResponse } from "next/server"; - -// Where can I put this function as a shared utility? -export async function fetchPrometheusData(query: string, step = "1h") { - // TODO: choose the right prometheus url and creds, based on the engine instance - const baseUrl = - "https://zeet-prometheus.engine-dedi-npclabs.zeet.app/api/v1/query_range"; - - const endTime = new Date().toISOString(); - const startTime = new Date(Date.now() - 1000 * 60 * 60 * 24).toISOString(); // 24 hours - const params = new URLSearchParams({ - query: query, - start: startTime, - end: endTime, - step: step, - }); - - const username = process.env.PROMETHEUS_USERNAME; - const password = process.env.PROMETHEUS_PASSWORD; - - const headers = new Headers({ - Authorization: `Basic ${Buffer.from(`${username}:${password}`).toString("base64")}`, - }); - - try { - const response = await fetch(`${baseUrl}?${params.toString()}`, { - method: "GET", - headers: headers, - }); - - if (!response.ok) { - throw new Error( - `HTTP error! [${response.status}] ${await response.text()}`, - ); - } - - const data = await response.json(); - return data; - } catch (error) { - console.error("There was a problem with the fetch operation:", error); - throw error; - } -} - -export const POST = async () => { - const query = - 'sum(increase(traefik_service_requests_total{code=~"[4-9].."}[1h])) by (code)'; - const data = await fetchPrometheusData(query); - - return NextResponse.json({ - data: data, - }); -}; diff --git a/apps/dashboard/src/app/api/engine-status/status-codes/route.ts b/apps/dashboard/src/app/api/engine-status/status-codes/route.ts deleted file mode 100644 index b9c2d72a26d..00000000000 --- a/apps/dashboard/src/app/api/engine-status/status-codes/route.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { NextResponse } from "next/server"; -import { fetchPrometheusData } from "../prometheus/route"; - -interface ResultItem { - metric: { - code: string; - }; - values: [number, number][]; -} - -function getReturnedCodes(result: ResultItem[]) { - const seriesCodes: { [key: string]: string | null } = {}; - for (const item of result.reverse()) { - seriesCodes[item.metric.code] = null; - } - return seriesCodes; -} - -function transformPrometheusData(prometheusResult: { - data: { result: ResultItem[] }; -}) { - if ( - !prometheusResult || - !prometheusResult.data || - !prometheusResult.data.result - ) { - throw new TypeError("Invalid Prometheus result format"); - } - - const codes = getReturnedCodes(prometheusResult.data.result); - const timeMap = new Map(); - - for (const resultItem of prometheusResult.data.result) { - const code = resultItem.metric.code; - - for (const [timestamp, value] of resultItem.values) { - if (!timeMap.has(timestamp)) { - timeMap.set(timestamp, { - time: new Date(timestamp * 1000).toISOString(), - ...codes, - }); - } - - const timeItem = timeMap.get(timestamp); - timeItem[code] = value; - } - } - - return { - data: Array.from(timeMap.values()), - tags: Object.keys(codes), - }; -} - -export const POST = async () => { - const query = "sum(increase(traefik_service_requests_total{}[1h])) by (code)"; - const data = await fetchPrometheusData(query); - return NextResponse.json(transformPrometheusData(data), { - status: 200, - }); -}; diff --git a/apps/dashboard/src/app/api/engine-status/volume/route.ts b/apps/dashboard/src/app/api/engine-status/volume/route.ts deleted file mode 100644 index 3137bc7ad4c..00000000000 --- a/apps/dashboard/src/app/api/engine-status/volume/route.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { NextResponse } from "next/server"; -import { fetchPrometheusData } from "../prometheus/route"; - -interface ResultItem { - metric: { - code: string; - }; - values: [number, number][]; -} - -function transformPrometheusData(prometheusResult: { - data: { result: ResultItem[] }; -}) { - if ( - !prometheusResult || - !prometheusResult.data || - !prometheusResult.data.result - ) { - throw new TypeError("Invalid Prometheus result format"); - } - - const results = []; - for (const resultItem of prometheusResult.data.result) { - for (const [timestamp, value] of resultItem.values) { - results.push({ - time: new Date(timestamp * 1000).toISOString(), - value: value, - }); - } - } - - return { - data: results, - tags: ["value"], - }; -} - -export const POST = async () => { - const query = "sum(increase(traefik_service_requests_total{}[1h]))"; - const data = await fetchPrometheusData(query); - return NextResponse.json(transformPrometheusData(data), { - status: 200, - }); -}; diff --git a/apps/dashboard/src/components/engine/status/components/ErrorRate.tsx b/apps/dashboard/src/components/engine/status/components/ErrorRate.tsx index 71c25bbe8fb..e1c12a8a2cd 100644 --- a/apps/dashboard/src/components/engine/status/components/ErrorRate.tsx +++ b/apps/dashboard/src/components/engine/status/components/ErrorRate.tsx @@ -1,6 +1,5 @@ "use client"; -import { useQuery } from "@tanstack/react-query"; import { Bar, BarChart, CartesianGrid, XAxis, YAxis } from "recharts"; import { @@ -18,20 +17,39 @@ import { } from "@/components/ui/chart"; import { Skeleton } from "@chakra-ui/react"; import { useMemo } from "react"; +import type { ResultItem } from "./StatusCodes"; -export function ErrorRate() { - const monitorData = useQuery(["errorRate"], async () => { - const res = await fetch("/api/engine-status/errors", { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({}), - }); +function transformPrometheusData( + prometheusResult: { data: { result: ResultItem[] } } | undefined, +) { + if ( + !prometheusResult || + !prometheusResult.data || + !prometheusResult.data.result + ) { + throw new TypeError("Invalid Prometheus result format"); + } - const json = await res.json(); - return json; - }); + const results = []; + for (const resultItem of prometheusResult.data.result) { + for (const [timestamp, value] of resultItem.values) { + results.push({ + time: new Date(timestamp * 1000).toISOString(), + value: value, + }); + } + } + + return { + data: results, + tags: ["value"], + }; +} + +export function ErrorRate( + errorRateData: { data: { result: ResultItem[] } } | undefined, +) { + const monitorData = transformPrometheusData(errorRateData); const chartConfig: { [key: string]: { label: string; color: string } } = { value: { diff --git a/apps/dashboard/src/components/engine/status/components/Healthcheck.tsx b/apps/dashboard/src/components/engine/status/components/Healthcheck.tsx index 2c2a2ce99b1..025e38a0c53 100644 --- a/apps/dashboard/src/components/engine/status/components/Healthcheck.tsx +++ b/apps/dashboard/src/components/engine/status/components/Healthcheck.tsx @@ -5,13 +5,11 @@ import { ToolTipLabel } from "@/components/ui/tooltip"; import { useQuery } from "@tanstack/react-query"; import { PrimaryInfoItem } from "app/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/primary-info-item"; import { CircleCheck, XIcon } from "lucide-react"; -import { useState } from "react"; function useEngineHealthStats(engineUrlBase: string) { - const [shouldRefetch, setShouldRefetch] = useState(true); const engineUrl = `${engineUrlBase}/system/health`; return useQuery({ - queryKey: ["engine-stats", { engineUrl }], + queryKey: ["engine-stats", engineUrlBase, "healthcheck", { engineUrl }], queryFn: async () => { const startTimeStamp = performance.now(); const res = await fetch(engineUrl); @@ -24,12 +22,9 @@ function useEngineHealthStats(engineUrlBase: string) { status: json.status, }; }, - refetchInterval: shouldRefetch ? 5 * 1000 : undefined, + refetchInterval: 5 * 1000, enabled: !!engineUrl, refetchOnWindowFocus: false, - onError: () => { - setShouldRefetch(false); - }, }); } diff --git a/apps/dashboard/src/components/engine/status/components/StatusCodes.tsx b/apps/dashboard/src/components/engine/status/components/StatusCodes.tsx index 3c4161088dc..fdc93397682 100644 --- a/apps/dashboard/src/components/engine/status/components/StatusCodes.tsx +++ b/apps/dashboard/src/components/engine/status/components/StatusCodes.tsx @@ -1,6 +1,5 @@ "use client"; -import { useQuery } from "@tanstack/react-query"; import { Area, AreaChart, CartesianGrid, XAxis, YAxis } from "recharts"; import { @@ -20,19 +19,61 @@ import { import { Skeleton } from "@/components/ui/skeleton"; import { type JSX, useMemo } from "react"; -export function StatusCodes() { - const monitorData = useQuery(["statusCodes"], async () => { - const res = await fetch("/api/engine-status/status-codes", { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({}), - }); +export interface ResultItem { + metric: { + code: string; + }; + values: [number, number][]; +} + +function getReturnedCodes(result: ResultItem[]) { + const seriesCodes: { [key: string]: string | null } = {}; + for (const item of result.reverse()) { + seriesCodes[item.metric.code] = null; + } + return seriesCodes; +} + +function transformPrometheusData( + prometheusResult: { data: { result: ResultItem[] } } | undefined, +) { + if ( + !prometheusResult || + !prometheusResult.data || + !prometheusResult.data.result + ) { + throw new TypeError("Invalid Prometheus result format"); + } + + const codes = getReturnedCodes(prometheusResult.data.result); + const timeMap = new Map(); + + for (const resultItem of prometheusResult.data.result) { + const code = resultItem.metric.code; + + for (const [timestamp, value] of resultItem.values) { + if (!timeMap.has(timestamp)) { + timeMap.set(timestamp, { + time: new Date(timestamp * 1000).toISOString(), + ...codes, + }); + } + + const timeItem = timeMap.get(timestamp); + timeItem[code] = value; + } + } + + return { + data: Array.from(timeMap.values()), + tags: Object.keys(codes), + }; +} - const json = await res.json(); - return json; - }); +export function StatusCodes( + statusCodesData: { data: { result: ResultItem[] } } | undefined, +) { + const monitorData = transformPrometheusData(statusCodesData); const chartConfig = useMemo(() => { if (!monitorData || !monitorData.data || !monitorData.data.tags) { @@ -58,7 +99,7 @@ export function StatusCodes() { const areaCharts = useMemo(() => { const charts: JSX.Element[] = []; if (!monitorData || !monitorData.data || !monitorData.data.tags) { - return ; // Show that data is not available + return ; // Show that data is not available } for (const tag of monitorData.data.tags) { diff --git a/apps/dashboard/src/components/engine/status/components/TransactionVolume.tsx b/apps/dashboard/src/components/engine/status/components/TransactionVolume.tsx index b9ce21853ca..2a9689cd969 100644 --- a/apps/dashboard/src/components/engine/status/components/TransactionVolume.tsx +++ b/apps/dashboard/src/components/engine/status/components/TransactionVolume.tsx @@ -1,6 +1,5 @@ "use client"; -import { useQuery } from "@tanstack/react-query"; import { Area, AreaChart, CartesianGrid, XAxis, YAxis } from "recharts"; import { @@ -16,6 +15,7 @@ import { ChartTooltip, ChartTooltipContent, } from "@/components/ui/chart"; +import type { ResultItem } from "./StatusCodes"; const chartConfig = { value: { @@ -24,20 +24,37 @@ const chartConfig = { }, } satisfies ChartConfig; -export function TransactionVolume() { - const monitorData = useQuery(["monitorData"], async () => { - const res = await fetch("/api/engine-status/volume", { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({}), - }); +function transformPrometheusData( + prometheusResult: { data: { result: ResultItem[] } } | undefined, +) { + if ( + !prometheusResult || + !prometheusResult.data || + !prometheusResult.data.result + ) { + throw new TypeError("Invalid Prometheus result format"); + } - const json = await res.json(); + const results = []; + for (const resultItem of prometheusResult.data.result) { + for (const [timestamp, value] of resultItem.values) { + results.push({ + time: new Date(timestamp * 1000).toISOString(), + value: value, + }); + } + } - return json.data; - }); + return { + data: results, + tags: ["value"], + }; +} + +export function TransactionVolume( + requestVolumeData: { data: { result: ResultItem[] } } | undefined, +) { + const monitorData = transformPrometheusData(requestVolumeData); return ( diff --git a/apps/dashboard/src/components/engine/status/status.tsx b/apps/dashboard/src/components/engine/status/status.tsx index a2f2e6e6b23..3e199b49762 100644 --- a/apps/dashboard/src/components/engine/status/status.tsx +++ b/apps/dashboard/src/components/engine/status/status.tsx @@ -1,3 +1,4 @@ +import { useEngineResourceMetrics } from "@3rdweb-sdk/react/hooks/useEngine"; import { Flex, Icon, Stack } from "@chakra-ui/react"; import { FaChartArea } from "react-icons/fa"; import { Card, Heading } from "tw-components"; @@ -9,7 +10,21 @@ interface EngineStatusProps { instanceUrl: string; } +function extractDomainBeforeDotSafe(url: string): string | null { + try { + const urlObject = new URL(url); + const parts = urlObject.hostname.split("."); + return parts.length > 1 ? parts[0] : null; + } catch (error) { + console.error("Invalid URL:", error); + return null; + } +} + export const EngineStatus: React.FC = ({ instanceUrl }) => { + const instanceId = extractDomainBeforeDotSafe(instanceUrl); + const prometheusMetrics = useEngineResourceMetrics(instanceId || ""); + return ( <> @@ -21,9 +36,11 @@ export const EngineStatus: React.FC = ({ instanceUrl }) => {
- - - + + +