Skip to content

Commit

Permalink
Support namespace CRUD (#149)
Browse files Browse the repository at this point in the history
* Add banner and namespace list

* Support namespace creation and deletion

* Add error message when fail to create namespace

---------

Co-authored-by: datavisorwenjiejiang <[email protected]>
  • Loading branch information
Venjy and datavisorwenjiejiang authored Mar 4, 2024
1 parent 1cc64d4 commit 5fc828c
Show file tree
Hide file tree
Showing 14 changed files with 741 additions and 34 deletions.
4 changes: 4 additions & 0 deletions webui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,11 @@
"dependencies": {
"@emotion/react": "^11.11.3",
"@emotion/styled": "^11.11.0",
"@mui/icons-material": "^5.15.7",
"@mui/material": "^5.15.5",
"@types/js-yaml": "^4.0.9",
"axios": "^1.6.7",
"js-yaml": "^4.1.0",
"next": "14.1.0",
"react": "^18",
"react-dom": "^18"
Expand Down
240 changes: 240 additions & 0 deletions webui/public/logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
26 changes: 26 additions & 0 deletions webui/src/app/cluster/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

export default function Layout({children}: {children: React.ReactNode}) {
return (
<>
{children}
</>
)
}
34 changes: 34 additions & 0 deletions webui/src/app/cluster/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

import { Box, Container } from "@mui/material";
import Sidebar from "../ui/sidebar";

export default function Cluster() {
return (
<div className="flex h-full">
<Sidebar />
<Container maxWidth={false} disableGutters sx={{height: '100%', overflowY: 'auto', marginLeft: '16px'}}>
<div>
todo: show all clusters in selected namespace here
</div>
</Container>
</div>
)
}
27 changes: 0 additions & 27 deletions webui/src/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -20,30 +20,3 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

:root {
--foreground-rgb: 0, 0, 0;
--background-start-rgb: 214, 219, 220;
--background-end-rgb: 255, 255, 255;
}

@media (prefers-color-scheme: dark) {
:root {
--foreground-rgb: 255, 255, 255;
--background-start-rgb: 0, 0, 0;
--background-end-rgb: 0, 0, 0;
}
}

body {
color: rgb(var(--foreground-rgb));
background: linear-gradient(to bottom,
transparent,
rgb(var(--background-end-rgb))) rgb(var(--background-start-rgb));
}

@layer utilities {
.text-balance {
text-wrap: balance;
}
}
9 changes: 8 additions & 1 deletion webui/src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import "./globals.css";
import Banner from "./ui/banner";
import { Container } from "@mui/material";

const inter = Inter({ subsets: ["latin"] });

Expand All @@ -35,7 +37,12 @@ export default function RootLayout({
}>) {
return (
<html lang="en">
<body className={inter.className}>{children}</body>
<body className={inter.className}>
<Banner />
<Container sx={{marginTop: '64px', height: 'calc(100vh - 64px)'}} maxWidth={false} disableGutters>
{children}
</Container>
</body>
</html>
);
}
37 changes: 37 additions & 0 deletions webui/src/app/lib/actions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
'use server';

import { redirect } from "next/navigation";
import { createNamespace, deleteNamespace } from "./api";
import { revalidatePath } from "next/cache";

export async function createNamespaceAction(name: string): Promise<string> {
const errMsg = await createNamespace(name);
if(!errMsg) {
revalidatePath('/cluster');
}
return errMsg;
}

export async function deleteNamespaceAction(name: string): Promise<string> {
const result = deleteNamespace(name);
revalidatePath('/cluster');
return result;
}
82 changes: 82 additions & 0 deletions webui/src/app/lib/api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

import yaml from 'js-yaml';
import fs from 'fs';
import path from 'path';
import axios, { AxiosError } from 'axios';

const configFile = './config/config.yaml';
const apiPrefix = '/api/v1';
let host;
try {
const wholeFilePath = path.join(process.cwd(), '..', configFile);
const doc = yaml.load(fs.readFileSync(wholeFilePath, 'utf8'));
host = (doc as any)['addr'];
} catch (error) {
host = '127.0.0.1:9379';
}
const apiHost = `http://${host}${apiPrefix}`;

export async function fetchNamespaces(): Promise<string[]> {
try {
const { data: responseData } = await axios.get(`${apiHost}/namespaces`);
return responseData.data.namespaces || [];
} catch (error) {
handleError(error);
return [];
}
}
export async function createNamespace(name: string): Promise<string> {
try {
const { data: responseData } = await axios.post(`${apiHost}/namespaces`, {namespace: name});
if(responseData?.data == 'created') {
return '';
} else {
return handleError(responseData);
}
} catch (error) {
return handleError(error);
}
}

export async function deleteNamespace(name: string): Promise<string> {
try {
const { data: responseData } = await axios.delete(`${apiHost}/namespaces/${name}`);
if(responseData?.data == 'ok') {
return '';
} else {
return handleError(responseData);
}
} catch (error) {
return handleError(error);
}
}

function handleError(error: any): string {
let message: string = '';
if(error instanceof AxiosError) {
message = error.response?.data?.error?.message || error.message;
} else if (error instanceof Error) {
message = error.message;
} else if (typeof error === 'object') {
message = error?.error?.message || error?.message;
}
return message || 'Unknown error';
}
14 changes: 8 additions & 6 deletions webui/src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,19 @@
* under the License.
*/

import { Button } from "@mui/material";
import Image from "next/image";
import { Button, Container, Typography } from "@mui/material";

export default function Home() {
return (
<main className="flex flex-col items-center justify-center min-h-screen space-y-4">
<h1 className="text-4xl font-bold">Kvrocks Controler UI</h1>
<p className="text-xl">Work in progress...</p>
<div
style={{minHeight: 'calc(100vh - 64px)', height: 'calc(100vh - 64px)'}}
className={'flex flex-col items-center justify-center space-y-2 h-full'}
>
<Typography variant="h3">Kvrocks Controler UI</Typography>
<Typography variant="body1">Work in progress...</Typography>
<Button size="large" variant="outlined" sx={{ textTransform: 'none' }} href="https://github.com/apache/kvrocks-controller/issues/135">
Click here to submit your suggestions
</Button>
</main>
</div>
);
}
47 changes: 47 additions & 0 deletions webui/src/app/ui/banner.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

import { AppBar, Container, Toolbar } from "@mui/material";
import Image from "next/image";
import NavLinks from "./nav-links";

const links = [
{
url: '/',
title: 'Home'
},{
url: '/cluster',
title: 'cluster'
},{
url: 'https://kvrocks.apache.org',
title: 'community',
_blank: true
},
]

export default function Banner() {
return (<AppBar>
<Container maxWidth={false}>
<Toolbar className="space-x-4">
<Image src="/logo.svg" width={40} height={40} alt='logo'></Image>
<NavLinks links={links}/>
</Toolbar>
</Container>
</AppBar>)
}
Loading

0 comments on commit 5fc828c

Please sign in to comment.