diff --git a/src/common/menu.js b/src/common/menu.js index 9c34d834c..0f1ce8bdb 100644 --- a/src/common/menu.js +++ b/src/common/menu.js @@ -100,6 +100,11 @@ export const menuData = [ path: "dict", locale: "SHENYU.MENU.SYSTEM.MANAGMENT.DICTIONARY", }, + { + name: getIntlContent("SHENYU.MENU.SYSTEM.MANAGMENT.INSTANCE"), + path: "instance", + locale: "SHENYU.MENU.SYSTEM.MANAGMENT.INSTANCE", + }, ], }, { diff --git a/src/common/router.js b/src/common/router.js index 8c178f55c..3847aa117 100644 --- a/src/common/router.js +++ b/src/common/router.js @@ -168,6 +168,13 @@ export const getRouterData = (app) => { () => import("../routes/System/Plugin"), ), }, + "/config/instance": { + component: dynamicWrapper( + app, + ["instance"], + () => import("../routes/System/Instance"), + ), + }, "/config/namespacePlugin": { component: dynamicWrapper( app, diff --git a/src/locales/en-US.json b/src/locales/en-US.json index bf3d11946..002e39b5e 100644 --- a/src/locales/en-US.json +++ b/src/locales/en-US.json @@ -75,6 +75,7 @@ "SHENYU.MENU.SYSTEM.MANAGMENT.AUTHEN": "Authentication", "SHENYU.MENU.SYSTEM.MANAGMENT.METADATA": "Metadata", "SHENYU.MENU.SYSTEM.MANAGMENT.DICTIONARY": "Dictionary", + "SHENYU.MENU.SYSTEM.MANAGMENT.INSTANCE": "Instance", "SHENYU.MENU.SYSTEM.MANAGMENT.NAMESPACE": "Namespace", "SHENYU.MENU.CONFIG.MANAGMENT": "BasicConfig", "SHENYU.PLUGIN.SELECTOR.LIST.TITLE": "SelectorList", @@ -334,6 +335,12 @@ "SHENYU.BUTTON.DATA.PERMISSION.CONFIG": "ConfigureDataPermission", "SHENYU.MESSAGE.SESSION.INVALID": "Session is invalid", "SHENYU.MESSAGE.SESSION.RELOGIN": "Please login in again", + "SHENYU.INSTANCE.IP": "Instance IP", + "SHENYU.INSTANCE.PORT": "Instance Port", + "SHENYU.INSTANCE.INFO": "Instance Info", + "SHENYU.INSTANCE.SELECT.TYPE": "Instance Type", + "SHENYU.INSTANCE.SELECT.TYPE.BOOTSTRAP": "Bootstrap", + "SHENYU.INSTANCE.SELECT.TYPE.CLIENT": "Client", "SHENYU.PLUGIN.SELECT.STATUS": "Select Status", "SHENYU.PLUGIN.REQUEST.HEADER.KEY": "Header Key", "SHENYU.PLUGIN.REQUEST.HEADER.VALUE": "Header Value", diff --git a/src/locales/zh-CN.json b/src/locales/zh-CN.json index 379744930..50fb8cd5d 100644 --- a/src/locales/zh-CN.json +++ b/src/locales/zh-CN.json @@ -76,6 +76,7 @@ "SHENYU.MENU.SYSTEM.MANAGMENT.AUTHEN": "认证管理", "SHENYU.MENU.SYSTEM.MANAGMENT.METADATA": "元数据管理", "SHENYU.MENU.SYSTEM.MANAGMENT.DICTIONARY": "字典管理", + "SHENYU.MENU.SYSTEM.MANAGMENT.INSTANCE": "实例管理", "SHENYU.MENU.CONFIG.MANAGMENT": "基础配置", "SHENYU.MENU.SYSTEM.MANAGMENT.NAMESPACE": "命名空间管理", "SHENYU.PLUGIN.SELECTOR.LIST.TITLE": "选择器列表", @@ -338,6 +339,12 @@ "SHENYU.BUTTON.DATA.PERMISSION.CONFIG": "配置数据权限", "SHENYU.MESSAGE.SESSION.INVALID": "会话超时", "SHENYU.MESSAGE.SESSION.RELOGIN": "会话超时,请重新登录", + "SHENYU.INSTANCE.IP": "实例IP", + "SHENYU.INSTANCE.PORT": "实例端口", + "SHENYU.INSTANCE.INFO": "实例信息", + "SHENYU.INSTANCE.SELECT.TYPE": "实例类型", + "SHENYU.INSTANCE.SELECT.TYPE.BOOTSTRAP": "网关实例", + "SHENYU.INSTANCE.SELECT.TYPE.CLIENT": "客户端实例", "SHENYU.PLUGIN.SELECT.STATUS": "选择状态", "SHENYU.PLUGIN.REQUEST.HEADER.KEY": "Header Key", "SHENYU.PLUGIN.REQUEST.HEADER.VALUE": "Header Value", diff --git a/src/models/instance.js b/src/models/instance.js new file mode 100644 index 000000000..89a049372 --- /dev/null +++ b/src/models/instance.js @@ -0,0 +1,79 @@ +/* + * 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 { getInstancesByNamespace, findInstance } from "../services/api"; + +export default { + namespace: "instance", + + state: { + instanceList: [], + total: 0, + }, + + effects: { + *fetch(params, { call, put }) { + const { payload } = params; + const json = yield call(getInstancesByNamespace, payload); + if (json.code === 200) { + let { page, dataList } = json.data; + dataList = dataList.map((item) => { + item.key = item.id; + return item; + }); + yield put({ + type: "saveInstances", + payload: { + total: page.totalCount, + dataList, + }, + }); + } + }, + *fetchItem(params, { call }) { + const { payload, callback } = params; + const json = yield call(findInstance, payload); + if (json.code === 200) { + const instance = json.data; + callback(instance); + } + }, + *reload(params, { put }) { + const { fetchValue } = params; + const { name, currentPage, instanceType, instanceIp, pageSize } = + fetchValue; + const payload = { + name, + instanceType, + instanceIp, + currentPage, + pageSize, + }; + yield put({ type: "fetch", payload }); + }, + }, + + reducers: { + saveInstances(state, { payload }) { + return { + ...state, + instanceList: payload.dataList, + total: payload.total, + }; + }, + }, +}; diff --git a/src/routes/System/Instance/index.js b/src/routes/System/Instance/index.js new file mode 100644 index 000000000..e368a7e6b --- /dev/null +++ b/src/routes/System/Instance/index.js @@ -0,0 +1,316 @@ +/* + * 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 React, { Component } from "react"; +import { Button, Input, Popover, Select, Table, Tag, Typography } from "antd"; +import { connect } from "dva"; +import { resizableComponents } from "../../../utils/resizable"; +import { getCurrentLocale, getIntlContent } from "../../../utils/IntlUtils"; +import AuthButton from "../../../utils/AuthButton"; + +const { Text } = Typography; + +const { Option } = Select; + +@connect(({ instance, loading, global }) => ({ + instance, + language: global.language, + currentNamespaceId: global.currentNamespaceId, + loading: loading.effects["instance/fetch"], +})) +export default class Instance extends Component { + components = resizableComponents; + + constructor(props) { + super(props); + this.state = { + currentPage: 1, + pageSize: 12, + selectedRowKeys: [], + instanceIp: "", + instanceType: null, + localeName: window.sessionStorage.getItem("locale") + ? window.sessionStorage.getItem("locale") + : "en-US", + columns: [], + }; + } + + componentDidMount() { + this.query(); + this.initInstanceColumns(); + } + + componentDidUpdate(prevProps) { + const { language, currentNamespaceId } = this.props; + const { localeName } = this.state; + if (language !== localeName) { + this.initInstanceColumns(); + this.changeLocale(language); + } + if (prevProps.currentNamespaceId !== currentNamespaceId) { + this.query(); + } + } + + handleResize = + (index) => + (e, { size }) => { + this.setState(({ columns }) => { + const nextColumns = [...columns]; + nextColumns[index] = { + ...nextColumns[index], + width: size.width, + }; + return { columns: nextColumns }; + }); + }; + + onSelectChange = (selectedRowKeys) => { + this.setState({ selectedRowKeys }); + }; + + currentQueryPayload = (override) => { + const { instanceIp, instanceType, currentPage, pageSize } = this.state; + const { currentNamespaceId } = this.props; + return { + instanceIp, + instanceType, + namespaceId: currentNamespaceId, + currentPage, + pageSize, + ...override, + }; + }; + + query = () => { + const { dispatch } = this.props; + dispatch({ + type: "instance/fetch", + payload: this.currentQueryPayload(), + }); + }; + + pageOnchange = (page) => { + this.setState({ currentPage: page }, this.query); + }; + + onShowSizeChange = (currentPage, pageSize) => { + this.setState({ currentPage: 1, pageSize }, this.query); + }; + + instanceIpOnchange = (e) => { + this.setState({ instanceIp: e.target.value }, this.query); + }; + + instanceTypeOnchange = (e) => { + this.setState({ instanceType: e }, this.query); + }; + + searchClick = () => { + this.setState({ currentPage: 1 }, this.query); + }; + + changeLocale(locale) { + this.setState({ + localeName: locale, + }); + getCurrentLocale(this.state.localeName); + } + + initInstanceColumns() { + this.setState({ + columns: [ + { + align: "center", + title: getIntlContent("SHENYU.INSTANCE.IP"), + dataIndex: "instanceIp", + key: "instanceIp", + ellipsis: true, + width: 120, + render: (text, record) => { + return record.instanceIp ? ( +
+ {t}
+
+