Skip to content

Commit d9f8c40

Browse files
authored
feat(webui): use nextjs middleware to dynamically redirect API calls (#360)
* feat(webui): use nextjs middleware to dynamically redirect API calls * lint
1 parent b6e5fb5 commit d9f8c40

File tree

9 files changed

+56
-45
lines changed

9 files changed

+56
-45
lines changed

webui/next.config.mjs

Lines changed: 2 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -17,25 +17,10 @@
1717
* under the License.
1818
*/
1919

20-
import { PHASE_DEVELOPMENT_SERVER } from "next/constants.js";
21-
22-
const apiPrefix = "/api/v1";
23-
const devHost = "127.0.0.1:9379";
24-
const prodHost = "production-api.yourdomain.com";
25-
2620
const nextConfig = (phase, { defaultConfig }) => {
27-
const isDev = phase === PHASE_DEVELOPMENT_SERVER;
28-
const host = isDev ? devHost : prodHost;
29-
3021
return {
31-
async rewrites() {
32-
return [
33-
{
34-
source: `${apiPrefix}/:slug*`,
35-
destination: `http://${host}${apiPrefix}/:slug*`,
36-
},
37-
];
38-
},
22+
reactStrictMode: true,
23+
output: "standalone",
3924
};
4025
};
4126

webui/package.json

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,11 @@
33
"version": "0.1.0",
44
"private": true,
55
"scripts": {
6-
"dev": "next dev",
6+
"dev": "next dev --turbopack",
77
"build": "next build",
88
"start": "next start",
9-
"lint": "next lint"
9+
"lint": "next lint",
10+
"deploy": "next build && cp -r .next/static .next/standalone/.next/ && cp -r public .next/standalone/"
1011
},
1112
"dependencies": {
1213
"@emotion/react": "^11.11.3",
@@ -20,22 +21,26 @@
2021
"@types/js-yaml": "^4.0.9",
2122
"axios": "^1.6.7",
2223
"js-yaml": "^4.1.0",
23-
"next": "^14.2.29",
24-
"react": "^18",
25-
"react-dom": "^18"
24+
"next": "15.5.4",
25+
"react": "19.2.0",
26+
"react-dom": "19.2.0"
2627
},
2728
"devDependencies": {
2829
"@types/node": "^20",
29-
"@types/react": "^18",
30-
"@types/react-dom": "^18",
30+
"@types/react": "19.2.2",
31+
"@types/react-dom": "19.2.1",
3132
"autoprefixer": "^10.0.1",
3233
"eslint": "^8",
33-
"eslint-config-next": "14.1.0",
34+
"eslint-config-next": "15.5.4",
3435
"eslint-config-prettier": "^10.1.1",
3536
"postcss": "^8",
36-
"prettier": "^3.5.3",
37+
"prettier": "^3.6.2",
3738
"prettier-plugin-tailwindcss": "^0.6.11",
3839
"tailwindcss": "^3.3.0",
3940
"typescript": "^5"
41+
},
42+
"overrides": {
43+
"@types/react": "19.2.2",
44+
"@types/react-dom": "19.2.1"
4045
}
4146
}

webui/src/app/namespaces/[namespace]/clusters/[cluster]/page.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ import {
3838
Divider,
3939
} from "@mui/material";
4040
import { ClusterSidebar } from "../../../../ui/sidebar";
41-
import { useState, useEffect } from "react";
41+
import { useState, useEffect, use } from "react";
4242
import { listShards, listNodes, fetchCluster, deleteShard } from "@/app/lib/api";
4343
import { AddShardCard, ResourceCard } from "@/app/ui/createCard";
4444
import Link from "next/link";
@@ -95,7 +95,10 @@ type FilterOption =
9595
| "with-importing";
9696
type SortOption = "index-asc" | "index-desc" | "nodes-desc" | "nodes-asc";
9797

98-
export default function Cluster({ params }: { params: { namespace: string; cluster: string } }) {
98+
export default function Cluster(props: {
99+
params: Promise<{ namespace: string; cluster: string }>;
100+
}) {
101+
const params = use(props.params);
99102
const { namespace, cluster } = params;
100103
const [shardsData, setShardsData] = useState<ShardData[]>([]);
101104
const [resourceCounts, setResourceCounts] = useState<ResourceCounts>({

webui/src/app/namespaces/[namespace]/clusters/[cluster]/shards/[shard]/nodes/[node]/page.tsx

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
import { listNodes } from "@/app/lib/api";
2323
import { NodeSidebar } from "@/app/ui/sidebar";
2424
import { Box, Typography, Chip, Paper, Divider, Grid, Alert, IconButton } from "@mui/material";
25-
import { useEffect, useState } from "react";
25+
import { useEffect, useState, use } from "react";
2626
import { useRouter } from "next/navigation";
2727
import { LoadingSpinner } from "@/app/ui/loadingSpinner";
2828
import { truncateText } from "@/app/utils";
@@ -39,11 +39,10 @@ import NetworkCheckIcon from "@mui/icons-material/NetworkCheck";
3939
import SecurityIcon from "@mui/icons-material/Security";
4040
import LinkIcon from "@mui/icons-material/Link";
4141

42-
export default function Node({
43-
params,
44-
}: {
45-
params: { namespace: string; cluster: string; shard: string; node: string };
42+
export default function Node(props: {
43+
params: Promise<{ namespace: string; cluster: string; shard: string; node: string }>;
4644
}) {
45+
const params = use(props.params);
4746
const { namespace, cluster, shard, node } = params;
4847
const router = useRouter();
4948
const [nodeData, setNodeData] = useState<any[]>([]);

webui/src/app/namespaces/[namespace]/clusters/[cluster]/shards/[shard]/page.tsx

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ import {
3737
import { ShardSidebar } from "@/app/ui/sidebar";
3838
import { fetchShard, deleteNode } from "@/app/lib/api";
3939
import { useRouter } from "next/navigation";
40-
import { useState, useEffect } from "react";
40+
import { useState, useEffect, use } from "react";
4141
import { AddNodeCard } from "@/app/ui/createCard";
4242
import Link from "next/link";
4343
import { LoadingSpinner } from "@/app/ui/loadingSpinner";
@@ -60,11 +60,10 @@ import DeleteIcon from "@mui/icons-material/Delete";
6060
import SwapHorizIcon from "@mui/icons-material/SwapHoriz";
6161
import { FailoverDialog } from "@/app/ui/failoverDialog";
6262

63-
export default function Shard({
64-
params,
65-
}: {
66-
params: { namespace: string; cluster: string; shard: string };
63+
export default function Shard(props: {
64+
params: Promise<{ namespace: string; cluster: string; shard: string }>;
6765
}) {
66+
const params = use(props.params);
6867
const { namespace, cluster, shard } = params;
6968
const [nodesData, setNodesData] = useState<any>(null);
7069
const [loading, setLoading] = useState<boolean>(true);

webui/src/app/namespaces/[namespace]/page.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ import {
4545
} from "@/app/lib/api";
4646
import Link from "next/link";
4747
import { useRouter, notFound } from "next/navigation";
48-
import { useState, useEffect } from "react";
48+
import { useState, useEffect, use } from "react";
4949
import { LoadingSpinner } from "@/app/ui/loadingSpinner";
5050
import StorageIcon from "@mui/icons-material/Storage";
5151
import FolderIcon from "@mui/icons-material/Folder";
@@ -106,7 +106,8 @@ type SortOption =
106106
| "nodes-desc"
107107
| "nodes-asc";
108108

109-
export default function Namespace({ params }: { params: { namespace: string } }) {
109+
export default function Namespace(props: { params: Promise<{ namespace: string }> }) {
110+
const params = use(props.params);
110111
const [clusterData, setClusterData] = useState<ClusterData[]>([]);
111112
const [resourceCounts, setResourceCounts] = useState<ResourceCounts>({
112113
clusters: 0,

webui/src/app/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ export default function Home() {
6060
const [scrollY, setScrollY] = useState(0);
6161
const [cursorPosition, setCursorPosition] = useState({ x: 0, y: 0 });
6262
const [cursorVisible, setCursorVisible] = useState(true);
63-
const requestRef = useRef<number>();
63+
const requestRef = useRef<number>(undefined);
6464
const prevScrollY = useRef(0);
6565

6666
const terminalRef = useRef({ lineIndex: 0, charIndex: 0 });

webui/src/middleware.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { NextResponse } from "next/server";
2+
import type { NextRequest } from "next/server";
3+
4+
export function middleware(req: NextRequest) {
5+
const url = req.nextUrl.clone();
6+
7+
if (url.pathname.startsWith("/api/v1")) {
8+
const host = process.env.KVCTL_API_HOST || "localhost:9379";
9+
url.host = host;
10+
11+
return NextResponse.rewrite(url);
12+
}
13+
14+
return NextResponse.next();
15+
}
16+
17+
export const config = {
18+
matcher: "/api/v1/:path*",
19+
runtime: "nodejs",
20+
};

webui/tsconfig.json

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,7 @@
1515
* KIND, either express or implied. See the License for the
1616
* specific language governing permissions and limitations
1717
* under the License.
18-
*/
19-
20-
{
18+
*/ {
2119
"compilerOptions": {
2220
"lib": ["dom", "dom.iterable", "esnext"],
2321
"allowJs": true,
@@ -38,7 +36,8 @@
3836
],
3937
"paths": {
4038
"@/*": ["./src/*"]
41-
}
39+
},
40+
"target": "ES2017"
4241
},
4342
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
4443
"exclude": ["node_modules"]

0 commit comments

Comments
 (0)