From be6a5f3ecbb8c319eead3135c78775673003c7ae Mon Sep 17 00:00:00 2001 From: Bugen Zhao Date: Thu, 4 Jan 2024 13:46:41 +0800 Subject: [PATCH 1/3] trim `/api` from get Signed-off-by: Bugen Zhao --- dashboard/pages/api/api.ts | 1 + dashboard/pages/api/cluster.ts | 6 +++--- dashboard/pages/api/metric.ts | 2 +- dashboard/pages/api/streaming.ts | 12 ++++++------ dashboard/pages/await_tree.tsx | 2 +- dashboard/pages/heap_profiling.tsx | 6 +++--- dashboard/pages/settings.tsx | 8 -------- 7 files changed, 15 insertions(+), 22 deletions(-) diff --git a/dashboard/pages/api/api.ts b/dashboard/pages/api/api.ts index c5e62cb989378..9a010246b9ee5 100644 --- a/dashboard/pages/api/api.ts +++ b/dashboard/pages/api/api.ts @@ -18,6 +18,7 @@ class Api { async get(url: string) { try { + url = `http://localhost:5691/api${url}` const res = await fetch(url) const data = await res.json() return data diff --git a/dashboard/pages/api/cluster.ts b/dashboard/pages/api/cluster.ts index 6d8d4850529ed..0efc083309389 100644 --- a/dashboard/pages/api/cluster.ts +++ b/dashboard/pages/api/cluster.ts @@ -18,19 +18,19 @@ import { WorkerNode } from "../../proto/gen/common" import api from "./api" export async function getClusterMetrics() { - const res = await api.get("/api/metrics/cluster") + const res = await api.get("/metrics/cluster") return res } export async function getClusterInfoFrontend() { - const res: WorkerNode[] = (await api.get("/api/clusters/1")).map( + const res: WorkerNode[] = (await api.get("/clusters/1")).map( WorkerNode.fromJSON ) return res } export async function getClusterInfoComputeNode() { - const res: WorkerNode[] = (await api.get("/api/clusters/2")).map( + const res: WorkerNode[] = (await api.get("/clusters/2")).map( WorkerNode.fromJSON ) return res diff --git a/dashboard/pages/api/metric.ts b/dashboard/pages/api/metric.ts index 55103881277c2..c799e52e71a70 100644 --- a/dashboard/pages/api/metric.ts +++ b/dashboard/pages/api/metric.ts @@ -18,7 +18,7 @@ import { MetricsSample } from "../../components/metrics" import api from "./api" export async function getActorBackPressures() { - const res = await api.get("/api/metrics/actor/back_pressures") + const res = await api.get("/metrics/actor/back_pressures") return res } diff --git a/dashboard/pages/api/streaming.ts b/dashboard/pages/api/streaming.ts index 97d8a45582da5..134219cd931f8 100644 --- a/dashboard/pages/api/streaming.ts +++ b/dashboard/pages/api/streaming.ts @@ -23,11 +23,11 @@ import { ColumnCatalog, Field } from "../../proto/gen/plan_common" import api from "./api" export async function getActors(): Promise { - return (await api.get("/api/actors")).map(ActorLocation.fromJSON) + return (await api.get("/actors")).map(ActorLocation.fromJSON) } export async function getFragments(): Promise { - let fragmentList: TableFragments[] = (await api.get("/api/fragments2")).map( + let fragmentList: TableFragments[] = (await api.get("/fragments2")).map( TableFragments.fromJSON ) fragmentList = sortBy(fragmentList, (x) => x.tableId) @@ -75,7 +75,7 @@ export async function getRelations() { async function getTableCatalogsInner( path: "tables" | "materialized_views" | "indexes" | "internal_tables" ) { - let list: Table[] = (await api.get(`/api/${path}`)).map(Table.fromJSON) + let list: Table[] = (await api.get(`/${path}`)).map(Table.fromJSON) list = sortBy(list, (x) => x.id) return list } @@ -97,13 +97,13 @@ export async function getInternalTables() { } export async function getSinks() { - let sinkList: Sink[] = (await api.get("/api/sinks")).map(Sink.fromJSON) + let sinkList: Sink[] = (await api.get("/sinks")).map(Sink.fromJSON) sinkList = sortBy(sinkList, (x) => x.id) return sinkList } export async function getSources() { - let sourceList: Source[] = (await api.get("/api/sources")).map( + let sourceList: Source[] = (await api.get("/sources")).map( Source.fromJSON ) sourceList = sortBy(sourceList, (x) => x.id) @@ -111,7 +111,7 @@ export async function getSources() { } export async function getViews() { - let views: View[] = (await api.get("/api/views")).map(View.fromJSON) + let views: View[] = (await api.get("/views")).map(View.fromJSON) views = sortBy(views, (x) => x.id) return views } diff --git a/dashboard/pages/await_tree.tsx b/dashboard/pages/await_tree.tsx index 1be88ab0f0c19..4d5f428c3ec67 100644 --- a/dashboard/pages/await_tree.tsx +++ b/dashboard/pages/await_tree.tsx @@ -67,7 +67,7 @@ export default function AwaitTreeDump() { try { const response: StackTraceResponse = StackTraceResponse.fromJSON( - await api.get(`/api/monitor/await_tree/${computeNodeId}`) + await api.get(`/monitor/await_tree/${computeNodeId}`) ) const actorTraces = _(response.actorTraces) diff --git a/dashboard/pages/heap_profiling.tsx b/dashboard/pages/heap_profiling.tsx index 88b737dc4f927..a457d84824593 100644 --- a/dashboard/pages/heap_profiling.tsx +++ b/dashboard/pages/heap_profiling.tsx @@ -75,7 +75,7 @@ export default function HeapProfiling() { try { let list: ListHeapProfilingResponse = ListHeapProfilingResponse.fromJSON( - await api.get(`/api/monitor/list_heap_profile/${computeNodeId}`) + await api.get(`/monitor/list_heap_profile/${computeNodeId}`) ) setProfileList(list) } catch (e: any) { @@ -119,7 +119,7 @@ export default function HeapProfiling() { }, [selectedProfileList]) async function dumpProfile() { - api.get(`/api/monitor/dump_heap_profile/${computeNodeId}`) + api.get(`/monitor/dump_heap_profile/${computeNodeId}`) getProfileList(computeNodes, computeNodeId) } @@ -149,7 +149,7 @@ export default function HeapProfiling() { try { let analyzeFilePathBase64 = base64url(analyzeFilePath) let resObj = await fetch( - `/api/monitor/analyze/${computeNodeId}/${analyzeFilePathBase64}` + `/monitor/analyze/${computeNodeId}/${analyzeFilePathBase64}` ).then(async (res) => ({ filename: res.headers.get("content-disposition"), blob: await res.blob(), diff --git a/dashboard/pages/settings.tsx b/dashboard/pages/settings.tsx index 8301aa227fe14..56293a1116d63 100644 --- a/dashboard/pages/settings.tsx +++ b/dashboard/pages/settings.tsx @@ -33,14 +33,6 @@ export default function Settings() { RisingWave Meta Node HTTP API - - Grafana HTTP API - - - - Prometheus HTTP API - - From a8d86563a5d8c02828ffd795c3e937780956e197 Mon Sep 17 00:00:00 2001 From: Bugen Zhao Date: Thu, 4 Jan 2024 15:06:25 +0800 Subject: [PATCH 2/3] store endpoint in local storage Signed-off-by: Bugen Zhao --- dashboard/mock-server.js | 3 +++ dashboard/next.config.js | 10 --------- dashboard/package-lock.json | 43 ++++++++++++++++++++++++++++++++++++ dashboard/package.json | 2 ++ dashboard/pages/api/api.ts | 29 ++++++++++++++++++++++-- dashboard/pages/settings.tsx | 28 ++++++++++++++++++++++- 6 files changed, 102 insertions(+), 13 deletions(-) diff --git a/dashboard/mock-server.js b/dashboard/mock-server.js index ba10e464e54aa..31d67bd87b772 100644 --- a/dashboard/mock-server.js +++ b/dashboard/mock-server.js @@ -16,8 +16,11 @@ */ const express = require("express") +const cors = require("cors") const app = express() +app.use(cors()) + app.listen(32333, () => { console.log("Server running on port 32333") }) diff --git a/dashboard/next.config.js b/dashboard/next.config.js index 17622bb794440..ca73de39b634e 100644 --- a/dashboard/next.config.js +++ b/dashboard/next.config.js @@ -20,16 +20,6 @@ */ const nextConfig = { trailingSlash: true, - - rewrites: () => { - return [ - { - source: "/api/:path*", - // To test with a RisingWave Meta node, use "http://127.0.0.1:5691/api/:path*" - destination: "http://localhost:32333/:path*", - }, - ] - }, } module.exports = nextConfig diff --git a/dashboard/package-lock.json b/dashboard/package-lock.json index 7ef31ee76bd76..bcb4687ce202c 100644 --- a/dashboard/package-lock.json +++ b/dashboard/package-lock.json @@ -13,6 +13,7 @@ "@monaco-editor/react": "^4.4.6", "@types/d3": "^7.4.0", "@types/lodash": "^4.14.184", + "@uidotdev/usehooks": "^2.4.1", "base64url": "^3.0.1", "bootstrap-icons": "^1.9.1", "d3": "^7.6.1", @@ -42,6 +43,7 @@ "@types/node": "^18.7.14", "@types/styled-components": "^5.1.26", "@typescript-eslint/eslint-plugin": "^5.52.0", + "cors": "^2.8.5", "eslint": "^8.45.0", "eslint-config-next": "13.4.12", "eslint-config-prettier": "^8.8.0", @@ -3224,6 +3226,18 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@uidotdev/usehooks": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@uidotdev/usehooks/-/usehooks-2.4.1.tgz", + "integrity": "sha512-1I+RwWyS+kdv3Mv0Vmc+p0dPYH0DTRAo04HLyXReYBL9AeseDWUJyi4THuksBJcu9F0Pih69Ak150VDnqbVnXg==", + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "react": ">=18.0.0", + "react-dom": ">=18.0.0" + } + }, "node_modules/@zag-js/element-size": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/@zag-js/element-size/-/element-size-0.1.0.tgz", @@ -4118,6 +4132,19 @@ "url": "https://opencollective.com/core-js" } }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dev": true, + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/cosmiconfig": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", @@ -13342,6 +13369,12 @@ "eslint-visitor-keys": "^3.3.0" } }, + "@uidotdev/usehooks": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@uidotdev/usehooks/-/usehooks-2.4.1.tgz", + "integrity": "sha512-1I+RwWyS+kdv3Mv0Vmc+p0dPYH0DTRAo04HLyXReYBL9AeseDWUJyi4THuksBJcu9F0Pih69Ak150VDnqbVnXg==", + "requires": {} + }, "@zag-js/element-size": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/@zag-js/element-size/-/element-size-0.1.0.tgz", @@ -14004,6 +14037,16 @@ "integrity": "sha512-IeHpLwk3uoci37yoI2Laty59+YqH9x5uR65/yiA0ARAJrTrN4YU0rmauLWfvqOuk77SlNJXj2rM6oT/dBD87+A==", "dev": true }, + "cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dev": true, + "requires": { + "object-assign": "^4", + "vary": "^1" + } + }, "cosmiconfig": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", diff --git a/dashboard/package.json b/dashboard/package.json index 67d2ff0ef1715..18377b4ef25e5 100644 --- a/dashboard/package.json +++ b/dashboard/package.json @@ -19,6 +19,7 @@ "@monaco-editor/react": "^4.4.6", "@types/d3": "^7.4.0", "@types/lodash": "^4.14.184", + "@uidotdev/usehooks": "^2.4.1", "base64url": "^3.0.1", "bootstrap-icons": "^1.9.1", "d3": "^7.6.1", @@ -48,6 +49,7 @@ "@types/node": "^18.7.14", "@types/styled-components": "^5.1.26", "@typescript-eslint/eslint-plugin": "^5.52.0", + "cors": "^2.8.5", "eslint": "^8.45.0", "eslint-config-next": "13.4.12", "eslint-config-prettier": "^8.8.0", diff --git a/dashboard/pages/api/api.ts b/dashboard/pages/api/api.ts index 9a010246b9ee5..0b9514ed16531 100644 --- a/dashboard/pages/api/api.ts +++ b/dashboard/pages/api/api.ts @@ -15,10 +15,35 @@ * */ +const PROD_API_ENDPOINT = "/api" +const MOCK_API_ENDPOINT = "http://localhost:32333" +const EXTERNAL_META_NODE_API_ENDPOINT = "http://localhost:5691/api" + +export const PREDEFINED_API_ENDPOINTS = [ + PROD_API_ENDPOINT, + MOCK_API_ENDPOINT, + EXTERNAL_META_NODE_API_ENDPOINT, +] + +export const DEFAULT_API_ENDPOINT: string = + process.env.NODE_ENV === "production" ? PROD_API_ENDPOINT : MOCK_API_ENDPOINT + +export const API_ENDPOINT_KEY = "risingwave.dashboard.api.endpoint" + class Api { - async get(url: string) { + urlFor(path: string) { + let apiEndpoint: string = ( + JSON.parse(localStorage.getItem(API_ENDPOINT_KEY) || "null") || + DEFAULT_API_ENDPOINT + ).replace(/\/+$/, "") // remove trailing slashes + + return `${apiEndpoint}${path}` + } + + async get(path: string) { + const url = this.urlFor(path) + try { - url = `http://localhost:5691/api${url}` const res = await fetch(url) const data = await res.json() return data diff --git a/dashboard/pages/settings.tsx b/dashboard/pages/settings.tsx index 56293a1116d63..3214bbdf5c746 100644 --- a/dashboard/pages/settings.tsx +++ b/dashboard/pages/settings.tsx @@ -16,11 +16,28 @@ */ import { Box, FormControl, FormLabel, Input, VStack } from "@chakra-ui/react" +import { useIsClient, useLocalStorage } from "@uidotdev/usehooks" import Head from "next/head" import { Fragment } from "react" import Title from "../components/Title" +import { + API_ENDPOINT_KEY, + DEFAULT_API_ENDPOINT, + PREDEFINED_API_ENDPOINTS, +} from "./api/api" export default function Settings() { + const isClient = useIsClient() + return isClient && +} + +// Local storage is only available on the client side. +function ClientSettings() { + const [apiEndpoint, saveApiEndpoint] = useLocalStorage( + API_ENDPOINT_KEY, + DEFAULT_API_ENDPOINT + ) + return ( @@ -31,7 +48,16 @@ export default function Settings() { RisingWave Meta Node HTTP API - + saveApiEndpoint(event.target.value)} + list="predefined" + /> + + {PREDEFINED_API_ENDPOINTS.map((endpoint) => ( + From a1935ca0f2ac4282a30b8f639c9086a9f7a87af1 Mon Sep 17 00:00:00 2001 From: Bugen Zhao Date: Thu, 4 Jan 2024 15:32:37 +0800 Subject: [PATCH 3/3] fix prettier Signed-off-by: Bugen Zhao --- dashboard/pages/api/streaming.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/dashboard/pages/api/streaming.ts b/dashboard/pages/api/streaming.ts index 134219cd931f8..a77a165357b9f 100644 --- a/dashboard/pages/api/streaming.ts +++ b/dashboard/pages/api/streaming.ts @@ -103,9 +103,7 @@ export async function getSinks() { } export async function getSources() { - let sourceList: Source[] = (await api.get("/sources")).map( - Source.fromJSON - ) + let sourceList: Source[] = (await api.get("/sources")).map(Source.fromJSON) sourceList = sortBy(sourceList, (x) => x.id) return sourceList }