diff --git a/LICENSE-binary b/LICENSE-binary index feab9965e..a52ea95fb 100644 --- a/LICENSE-binary +++ b/LICENSE-binary @@ -456,6 +456,8 @@ is auto-generated by `pnpm licenses list --prod`. ├────────────────────────────────────┼──────────────┤ │ csstype │ MIT │ ├────────────────────────────────────┼──────────────┤ +│ date-fns │ MIT │ +├────────────────────────────────────┼──────────────┤ │ dayjs │ MIT │ ├────────────────────────────────────┼──────────────┤ │ delayed-stream │ MIT │ diff --git a/kyuubi-server/web-ui/.eslintrc b/kyuubi-server/web-ui/.eslintrc index ebbf40199..f2bff2cd6 100644 --- a/kyuubi-server/web-ui/.eslintrc +++ b/kyuubi-server/web-ui/.eslintrc @@ -69,6 +69,9 @@ "exports": "never", "functions": "never" }], + "prettier/prettier": ["error", { + "bracketSameLine": true + }], "vue/multi-word-component-names": "off", "vue/component-definition-name-casing": "off", "vue/require-valid-default-prop": "off", diff --git a/kyuubi-server/web-ui/package.json b/kyuubi-server/web-ui/package.json index 63fdc7221..131e69b7f 100644 --- a/kyuubi-server/web-ui/package.json +++ b/kyuubi-server/web-ui/package.json @@ -17,6 +17,7 @@ "dependencies": { "@element-plus/icons-vue": "^2.0.9", "axios": "^0.27.2", + "date-fns": "^2.29.3", "element-plus": "^2.2.12", "pinia": "^2.0.18", "pinia-plugin-persistedstate": "^2.1.1", diff --git a/kyuubi-server/web-ui/pnpm-lock.yaml b/kyuubi-server/web-ui/pnpm-lock.yaml index 61fc5124d..1926352ab 100644 --- a/kyuubi-server/web-ui/pnpm-lock.yaml +++ b/kyuubi-server/web-ui/pnpm-lock.yaml @@ -12,6 +12,7 @@ specifiers: '@vue/eslint-config-typescript': ^11.0.0 '@vue/test-utils': ^2.0.2 axios: ^0.27.2 + date-fns: ^2.29.3 element-plus: ^2.2.12 eslint: ^8.21.0 eslint-plugin-prettier: ^4.2.1 @@ -32,6 +33,7 @@ specifiers: dependencies: '@element-plus/icons-vue': 2.0.9_vue@3.2.37 axios: 0.27.2 + date-fns: 2.29.3 element-plus: 2.2.13_vue@3.2.37 pinia: 2.0.18_j6bzmzd4ujpabbp5objtwxyjp4 pinia-plugin-persistedstate: 2.1.1_pinia@2.0.18 @@ -907,6 +909,11 @@ packages: whatwg-url: 11.0.0 dev: true + /date-fns/2.29.3: + resolution: {integrity: sha512-dDCnyH2WnnKusqvZZ6+jA1O51Ibt8ZMRNkDZdyAyK4YfbDwa/cEmuztzG5pk6hqlp9aSBPYcjOlktquahGwGeA==} + engines: {node: '>=0.11'} + dev: false + /dayjs/1.11.5: resolution: {integrity: sha512-CAdX5Q3YW3Gclyo5Vpqkgpj8fSdLQcRuzfX6mC6Phy0nfJ0eGYOeS7m4mt2plDWLAtA4TqTakvbboHvUxfe4iA==} dev: false diff --git a/kyuubi-server/web-ui/src/api/session/index.ts b/kyuubi-server/web-ui/src/api/session/index.ts new file mode 100644 index 000000000..6af5a817f --- /dev/null +++ b/kyuubi-server/web-ui/src/api/session/index.ts @@ -0,0 +1,32 @@ +/* + * 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 request from '@/utils/request' + +export function getAllSessions() { + return request({ + url: 'api/v1/sessions', + method: 'get' + }) +} + +export function deleteSession(sessionId: string) { + return request({ + url: `api/v1/sessions/${sessionId}`, + method: 'delete' + }) +} diff --git a/kyuubi-server/web-ui/src/locales/en_US/index.ts b/kyuubi-server/web-ui/src/locales/en_US/index.ts index 92df340d8..99e851651 100644 --- a/kyuubi-server/web-ui/src/locales/en_US/index.ts +++ b/kyuubi-server/web-ui/src/locales/en_US/index.ts @@ -16,5 +16,16 @@ */ export default { - test: 'test' + test: 'test', + user: 'User', + client_ip: 'Client IP', + kyuubi_instance: 'Kyuubi Instance', + session_id: 'Session ID', + create_time: 'Create Time', + operation: 'Operation', + delete_confirm: 'Delete Confirm', + message: { + delete_succeeded: 'Delete {name} Succeeded', + delete_failed: 'Delete {name} Failed' + } } diff --git a/kyuubi-server/web-ui/src/locales/zh_CN/index.ts b/kyuubi-server/web-ui/src/locales/zh_CN/index.ts index 7272109f5..016aaa8e7 100644 --- a/kyuubi-server/web-ui/src/locales/zh_CN/index.ts +++ b/kyuubi-server/web-ui/src/locales/zh_CN/index.ts @@ -16,5 +16,16 @@ */ export default { - test: '测试' + test: '测试', + user: '用户', + client_ip: '客户端地址', + kyuubi_instance: '服务端地址', + session_id: 'Session ID', + create_time: '创建时间', + operation: '操作', + delete_confirm: '确认删除', + message: { + delete_succeeded: '删除 {name} 成功', + delete_failed: '删除 {name} 失败' + } } diff --git a/kyuubi-server/web-ui/src/router/index.ts b/kyuubi-server/web-ui/src/router/index.ts index 207a22d56..4d01da552 100644 --- a/kyuubi-server/web-ui/src/router/index.ts +++ b/kyuubi-server/web-ui/src/router/index.ts @@ -20,6 +20,7 @@ import overviewRoutes from './overview' import workloadRoutes from './workload' import operationRoutes from './operation' import contactRoutes from './contact' +import sessionRoutes from './session' const routes = [ { @@ -36,6 +37,7 @@ const routes = [ redirect: 'overview', children: [ ...overviewRoutes, + ...sessionRoutes, ...workloadRoutes, ...operationRoutes, ...contactRoutes diff --git a/kyuubi-server/web-ui/src/router/session/index.ts b/kyuubi-server/web-ui/src/router/session/index.ts new file mode 100644 index 000000000..fca49f211 --- /dev/null +++ b/kyuubi-server/web-ui/src/router/session/index.ts @@ -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. + */ + +const routes = [ + { + path: '/session/session-statistics', + name: 'session-statistics', + component: () => import('@/views/session/session-statistics/index.vue') + } +] + +export default routes diff --git a/kyuubi-server/web-ui/src/views/common/use-table.ts b/kyuubi-server/web-ui/src/views/common/use-table.ts new file mode 100644 index 000000000..441feb1f1 --- /dev/null +++ b/kyuubi-server/web-ui/src/views/common/use-table.ts @@ -0,0 +1,78 @@ +/* + * 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 { ref, Ref } from 'vue' + +export function useTable() { + const list: Ref = ref([]) + const tableData: Ref = ref([]) + const loading = ref(false) + const currentPage = ref(1) + const pageSize = ref(10) + const totalPage = ref(1) + + const handleSizeChange = (val: number) => { + if ( + currentPage.value === 1 || + (currentPage.value > 1 && totalPage.value > (currentPage.value - 1) * val) + ) { + loading.value = true + setTimeout(() => { + setTableData() + }, 200) + } + } + + const handleCurrentChange = () => { + loading.value = true + setTimeout(() => { + setTableData() + }, 200) + } + + const setTableData = () => { + tableData.value = [...list.value].splice( + (currentPage.value - 1) * pageSize.value, + pageSize.value + ) + loading.value = false + } + + const getList = (func: Function, data?: any) => { + loading.value = true + func(data) + .then((res: any[]) => (list.value = res || [])) + .catch(() => (list.value = [])) + .finally(() => { + currentPage.value = 1 + pageSize.value = 10 + totalPage.value = list.value.length + setTableData() + }) + } + + return { + tableData, + loading, + currentPage, + pageSize, + totalPage, + handleSizeChange, + handleCurrentChange, + getList + } +} diff --git a/kyuubi-server/web-ui/src/views/layout/components/aside/types.ts b/kyuubi-server/web-ui/src/views/layout/components/aside/types.ts index ddd32bef4..71d1d0128 100644 --- a/kyuubi-server/web-ui/src/views/layout/components/aside/types.ts +++ b/kyuubi-server/web-ui/src/views/layout/components/aside/types.ts @@ -21,6 +21,16 @@ export const MENUS = [ icon: 'Odometer', router: '/overview' }, + { + label: 'Session Management', + icon: 'List', + children: [ + { + label: 'Session Statistics', + router: '/session/session-statistics' + } + ] + }, { label: 'Workload', icon: 'List', diff --git a/kyuubi-server/web-ui/src/views/session/session-statistics/index.vue b/kyuubi-server/web-ui/src/views/session/session-statistics/index.vue new file mode 100644 index 000000000..40a9b7568 --- /dev/null +++ b/kyuubi-server/web-ui/src/views/session/session-statistics/index.vue @@ -0,0 +1,104 @@ + + + + +