diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 39274e0..7a98e39 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -11,12 +11,14 @@ "@fluentui/react-components": "^9.54.16", "@fluentui/react-icons": "^2.0.258", "jotai": "^2.10.0", + "lodash": "^4.17.21", "react": "^18.2.0", "react-dom": "^18.2.0", "react-router-dom": "^6.26.2", "zustand": "^5.0.0-rc.2" }, "devDependencies": { + "@types/lodash": "^4.17.10", "@types/react": "^18.0.17", "@types/react-dom": "^18.0.6", "@vitejs/plugin-react": "^2.0.1", @@ -2372,6 +2374,13 @@ "tslib": "^2.4.0" } }, + "node_modules/@types/lodash": { + "version": "4.17.10", + "resolved": "https://registry.npmmirror.com/@types/lodash/-/lodash-4.17.10.tgz", + "integrity": "sha512-YpS0zzoduEhuOWjAotS6A5AVCva7X4lVlYLF0FYHAY9sdraBfnatttHItlWeZdGhuEkf+OzMNg2ZYAx8t+52uQ==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/prop-types": { "version": "15.7.13", "resolved": "https://registry.npmmirror.com/@types/prop-types/-/prop-types-15.7.13.tgz", @@ -3060,6 +3069,12 @@ "integrity": "sha512-o5kvLbuTF+o326CMVYpjlaykxqYP9DphFQZ2ZpgrvBouyvOxyEB7oqe8nOLFpiV5VCtz0D3pt8gXQYWpLpBnmA==", "license": "MIT" }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "license": "MIT" + }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmmirror.com/loose-envify/-/loose-envify-1.4.0.tgz", @@ -5245,6 +5260,12 @@ "tslib": "^2.4.0" } }, + "@types/lodash": { + "version": "4.17.10", + "resolved": "https://registry.npmmirror.com/@types/lodash/-/lodash-4.17.10.tgz", + "integrity": "sha512-YpS0zzoduEhuOWjAotS6A5AVCva7X4lVlYLF0FYHAY9sdraBfnatttHItlWeZdGhuEkf+OzMNg2ZYAx8t+52uQ==", + "dev": true + }, "@types/prop-types": { "version": "15.7.13", "resolved": "https://registry.npmmirror.com/@types/prop-types/-/prop-types-15.7.13.tgz", @@ -5629,6 +5650,11 @@ "resolved": "https://registry.npmmirror.com/keyborg/-/keyborg-2.6.0.tgz", "integrity": "sha512-o5kvLbuTF+o326CMVYpjlaykxqYP9DphFQZ2ZpgrvBouyvOxyEB7oqe8nOLFpiV5VCtz0D3pt8gXQYWpLpBnmA==" }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, "loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmmirror.com/loose-envify/-/loose-envify-1.4.0.tgz", diff --git a/frontend/package.json b/frontend/package.json index 89e4872..5321ffd 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -12,12 +12,14 @@ "@fluentui/react-components": "^9.54.16", "@fluentui/react-icons": "^2.0.258", "jotai": "^2.10.0", + "lodash": "^4.17.21", "react": "^18.2.0", "react-dom": "^18.2.0", "react-router-dom": "^6.26.2", "zustand": "^5.0.0-rc.2" }, "devDependencies": { + "@types/lodash": "^4.17.10", "@types/react": "^18.0.17", "@types/react-dom": "^18.0.6", "@vitejs/plugin-react": "^2.0.1", diff --git a/frontend/package.json.md5 b/frontend/package.json.md5 index ad24e7d..58dd7b9 100644 --- a/frontend/package.json.md5 +++ b/frontend/package.json.md5 @@ -1 +1 @@ -f23304e575da740e9b738508a43df31e \ No newline at end of file +b20ef5a27687e07e09878451f9a2e1aa \ No newline at end of file diff --git a/frontend/src/component/connection/new.tsx b/frontend/src/component/connection/new.tsx index e3b4e18..0efddfa 100644 --- a/frontend/src/component/connection/new.tsx +++ b/frontend/src/component/connection/new.tsx @@ -25,11 +25,15 @@ const useActionStyle = makeStyles({ test: {} }); -export function ConnectionCreate(){ +export interface ConnectionCreateProps { + openFn: (open: boolean) => void; +} + +export function ConnectionCreate(props: ConnectionCreateProps) { const actionStyle = useActionStyle(); const {dispatchMessage} = useToast(); const [testLoading, setTestLoading] = useState<"initial" | "loading" | "success" | "error">("initial"); - const {conn_list, get_conn} = useStoreConnection(); + const {conn_get} = useStoreConnection(); const buttonIcon = testLoading === "loading" ? ( @@ -54,12 +58,19 @@ export function ConnectionCreate(){ } async function create() { + // self // qUvfW8xpOTc23O96 // eTcuc8BebHPVpZZwIaNmzfwxRxPYGfTj + + // 48-dev + // OSIsqPrl0TkAUj3R + // FYF4BBzL2j2ObbVYH0FrvOZqJf1EACRy let res = await Dial("/api/connection/create", value) dispatchMessage(res.msg, res.status === 200 ? "success" : "error"); if (res.status === 200) { - get_conn() + dispatchMessage("新建连接成功", "success"); + conn_get() + props.openFn(false) } } diff --git a/frontend/src/component/home/home.tsx b/frontend/src/component/home/home.tsx index f0f1661..68a35b0 100644 --- a/frontend/src/component/home/home.tsx +++ b/frontend/src/component/home/home.tsx @@ -1,7 +1,7 @@ import {useEffect, useState} from 'react'; import './home.css'; import { - Button, Dialog, DialogTrigger, makeStyles,mergeClasses, MenuItem, MenuList, tokens, Tooltip, + Button, Dialog, DialogTrigger, makeStyles, mergeClasses, MenuItem, MenuList, tokens, Tooltip, } from "@fluentui/react-components"; import { CloudAddFilled, DismissRegular @@ -11,6 +11,7 @@ import {useToast} from "../../message"; import {Connection} from "../../interfaces/connection"; import {ConnectionCreate} from "../connection/new"; import {useStoreConnection} from "../../store/connection"; +import {useStoreBucket} from "../../store/bucket"; const useMenuListContainerStyles = makeStyles({ container: { @@ -43,57 +44,43 @@ const useMenuListContainerStyles = makeStyles({ marginLeft: 'auto', border: 'none', background: 'transparent', - "&:hover" : { + "&:hover": { color: tokens.colorNeutralForeground2BrandHover, } } }); -function Home() { +export function Home() { const styles = useMenuListContainerStyles(); const {dispatchMessage} = useToast(); const [openCreate, setOpenCreate] = useState(false); - const [connectionFilterKeywords, setConnectionFilterKeywords] = useState(''); - const [connections, setConnections] = useState([]); - const {conn_list, get_conn} = useStoreConnection(); + const [conn_filter, set_conn_filter] = useState(''); + const {conn_list, conn_get, conn_update} = useStoreConnection(); + const {bucket_list, bucket_get} = useStoreBucket() useEffect(() => { - get_conn() + conn_get() }, []); async function handleConnect(item: Connection) { - console.log('[DEBUG] db click item =', item) - for (const c of connections) { - if (item.id === c.id && c.active) { - console.log('[DEBUG] conn is already connected:', c) - return - } + let res = await Dial('/api/connection/connect', {id: item.id}); + if (res.status !== 200) { + dispatchMessage(res.msg, "error") + return } - let res = await Dial("/api/connection/connect", {id: item.id}) - if (res.status === 200) { - dispatchMessage("连接成功", "success") - setConnections(connections.map(one => { - if (one.id === item.id) { - one.active = true - } - - return one - })) - } + conn_update({...item, active: true}) + bucket_get(item, true) } async function handleDisconnect(item: Connection) { let res = await Dial('/api/connection/disconnect', {id: item.id}) - if (res.status === 200) { - setConnections(connections.map(c => { - if (item.id === c.id) { - c.active = false - } - return c - })) + if (res.status !== 200) { + dispatchMessage(res.msg, "error") + return } + conn_update({...item, active: false}) } return ( @@ -107,17 +94,17 @@ function Home() { 新建连接 - +
setConnectionFilterKeywords(e.target.value)}/> + value={conn_filter} + onChange={(e) => set_conn_filter(e.target.value)}/>
{ - setConnectionFilterKeywords('') + set_conn_filter('') }}>
@@ -125,7 +112,7 @@ function Home() {
- {conn_list.map(item => { + {conn_list.filter(item => item.name.includes(conn_filter)).map(item => { return { await handleConnect(item) @@ -134,7 +121,9 @@ function Home() { key={item.id}> {item.name} -
-
+
+
+
+ + {bucket_list.map(((item, idx) => { + return {item.name} + }))} + +
+
) -} - -export default Home +} \ No newline at end of file diff --git a/frontend/src/interfaces/connection.ts b/frontend/src/interfaces/connection.ts index 95dfd02..1402c39 100644 --- a/frontend/src/interfaces/connection.ts +++ b/frontend/src/interfaces/connection.ts @@ -7,3 +7,8 @@ export interface Connection { endpoint: string; active: boolean; } + +export interface Bucket { + name: string; + created_at: number; +} \ No newline at end of file diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx index 7f5ce8b..ddf05b5 100644 --- a/frontend/src/main.tsx +++ b/frontend/src/main.tsx @@ -3,7 +3,7 @@ import {createRoot} from 'react-dom/client' import './style.css' import {FluentProvider, webLightTheme} from '@fluentui/react-components'; import {createBrowserRouter, RouterProvider} from "react-router-dom"; -import Home from "./component/home/home"; +import {Home} from "./component/home/home"; import {ToastProvider} from "./message"; const container = document.getElementById('root') diff --git a/frontend/src/store/bucket.tsx b/frontend/src/store/bucket.tsx new file mode 100644 index 0000000..c165115 --- /dev/null +++ b/frontend/src/store/bucket.tsx @@ -0,0 +1,32 @@ +import {create} from 'zustand' +import {Bucket, Connection} from "../interfaces/connection"; +import {Dial, Resp} from "../api"; + +interface StoreBucket { + bucket_list: Bucket[]; + bucket_get: (conn: Connection, refresh: boolean) => void; +} + +let bucket_map: { [id: number]: Bucket[] }; + +export const useStoreBucket = create()((set) => ({ + bucket_list: [], + bucket_get: async (conn: Connection, refresh: boolean) => { + let res: Resp<{ list: Bucket[]; }>; + if (refresh) { + res = await Dial<{list: Bucket[]}>('/api/connection/buckets', {id: conn.id}); + if (res.status !== 200) { + return + } + } + + set((state) => { + if (refresh) { + bucket_map = {...bucket_map, [conn.id]: res.data.list} + return {bucket_list: res.data.list}; + } + + return {bucket_list: bucket_map[conn.id]}; + }) + } +})) diff --git a/frontend/src/store/connection.tsx b/frontend/src/store/connection.tsx index 5290ddd..9504523 100644 --- a/frontend/src/store/connection.tsx +++ b/frontend/src/store/connection.tsx @@ -2,15 +2,37 @@ import {create} from 'zustand' import {Connection} from "../interfaces/connection"; import {Dial} from "../api"; -type StoreConnection = { - conn_list: Connection[] - get_conn: () => void +interface StoreConnection { + conn_active: Connection | null; + conn_list: Connection[]; + conn_get: () => void; + conn_update: (connection: Connection) => void; } export const useStoreConnection = create()((set) => ({ + conn_active: null, conn_list: [], - get_conn: async () => { - const res = await Dial<{list: Connection[]}>('/api/connection/list'); + conn_get: async () => { + const res = await Dial<{ list: Connection[] }>('/api/connection/list'); + if (res.status !== 200) { + return + } + set({conn_list: res.data.list}) + }, + + conn_update: async (connection: Connection) => { + set((state) => { + return { + conn_active: connection.active? connection: null, + conn_list: state.conn_list.map(item => { + if (item.id === connection.id) { + return connection + } + + return item + }) + } + }) } })) diff --git a/internal/s3/list.go b/internal/s3/list.go index 0056096..0c11202 100644 --- a/internal/s3/list.go +++ b/internal/s3/list.go @@ -9,8 +9,8 @@ import ( ) type ListBucketRes struct { - CreatedAt int64 - Name string + CreatedAt int64 `json:"created_at"` + Name string `json:"name"` } func (c *Client) ListBucket(ctx context.Context) ([]*ListBucketRes, error) {