mirror of
https://github.com/apache/rocketmq-dashboard.git
synced 2025-09-12 13:39:10 +08:00
Compare commits
4 Commits
6531929124
...
07793d8aae
Author | SHA1 | Date | |
---|---|---|---|
|
07793d8aae | ||
|
4b9ed97f8f | ||
|
ff73529a75 | ||
|
706082c62f |
@@ -6,19 +6,14 @@ This project was bootstrapped with [Create React App](https://github.com/faceboo
|
|||||||
|
|
||||||
In the project directory, you can run:
|
In the project directory, you can run:
|
||||||
|
|
||||||
### `npm start`
|
### `npm run start`
|
||||||
|
|
||||||
Runs the app in the development mode.\
|
Runs the app in the development mode.\
|
||||||
Open [http://localhost:3000](http://localhost:3000) to view it in your browser.
|
Open [http://localhost:3003](http://localhost:3000) to view it in your browser.
|
||||||
|
|
||||||
The page will reload when you make changes.\
|
The page will reload when you make changes.\
|
||||||
You may also see any lint errors in the console.
|
You may also see any lint errors in the console.
|
||||||
|
|
||||||
### `npm test`
|
|
||||||
|
|
||||||
Launches the test runner in the interactive watch mode.\
|
|
||||||
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
|
|
||||||
|
|
||||||
### `npm run build`
|
### `npm run build`
|
||||||
|
|
||||||
Builds the app for production to the `build` folder.\
|
Builds the app for production to the `build` folder.\
|
||||||
|
@@ -18,12 +18,12 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8"/>
|
||||||
<link rel="shortcut icon" href="./favicon.ico" type="image/x-icon" />
|
<link rel="shortcut icon" href="./favicon.ico" type="image/x-icon"/>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||||
<meta name="description" content="" />
|
<meta name="description" content=""/>
|
||||||
<meta name="keywords" content="" />
|
<meta name="keywords" content=""/>
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
|
||||||
<title>RocketMQ Dashboard</title>
|
<title>RocketMQ Dashboard</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
@@ -17,7 +17,7 @@
|
|||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import AppRouter from './router'; // 你 router/index.jsx 导出的组件
|
import AppRouter from './router'; // 你 router/index.jsx 导出的组件
|
||||||
import { ToastContainer } from 'react-toastify';
|
import {ToastContainer} from 'react-toastify';
|
||||||
import 'react-toastify/dist/ReactToastify.css';
|
import 'react-toastify/dist/ReactToastify.css';
|
||||||
import {ConfigProvider} from "antd";
|
import {ConfigProvider} from "antd";
|
||||||
import {useTheme} from "./store/context/ThemeContext";
|
import {useTheme} from "./store/context/ThemeContext";
|
||||||
@@ -28,8 +28,8 @@ function App() {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ConfigProvider theme={currentTheme}>
|
<ConfigProvider theme={currentTheme}>
|
||||||
<ToastContainer />
|
<ToastContainer/>
|
||||||
<AppRouter />
|
<AppRouter/>
|
||||||
</ConfigProvider>
|
</ConfigProvider>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@@ -15,11 +15,11 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { render, screen } from '@testing-library/react';
|
import {render, screen} from '@testing-library/react';
|
||||||
import App from './App';
|
import App from './App';
|
||||||
|
|
||||||
test('renders learn react link', () => {
|
test('renders learn react link', () => {
|
||||||
render(<App />);
|
render(<App/>);
|
||||||
const linkElement = screen.getByText(/learn react/i);
|
const linkElement = screen.getByText(/learn react/i);
|
||||||
expect(linkElement).toBeInTheDocument();
|
expect(linkElement).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
@@ -49,7 +49,7 @@ const remoteApi = {
|
|||||||
if (_redirectHandler) {
|
if (_redirectHandler) {
|
||||||
_redirectHandler(); // 如果设置了重定向处理函数,则调用它
|
_redirectHandler(); // 如果设置了重定向处理函数,则调用它
|
||||||
}
|
}
|
||||||
return { __isRedirectHandled: true };
|
return {__isRedirectHandled: true};
|
||||||
}
|
}
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
@@ -77,24 +77,24 @@ const remoteApi = {
|
|||||||
listUsers: async (brokerAddress) => {
|
listUsers: async (brokerAddress) => {
|
||||||
const params = new URLSearchParams();
|
const params = new URLSearchParams();
|
||||||
if (brokerAddress) params.append('brokerAddress', brokerAddress);
|
if (brokerAddress) params.append('brokerAddress', brokerAddress);
|
||||||
const response = await remoteApi._fetch(remoteApi.buildUrl(`/acl/listUsers?${params.toString()}`));
|
const response = await remoteApi._fetch(remoteApi.buildUrl(`/acl/acls.query?${params.toString()}`));
|
||||||
return await response.json();
|
return await response.json();
|
||||||
},
|
},
|
||||||
|
|
||||||
createUser: async (brokerAddress, userInfo) => {
|
createUser: async (brokerAddress, userInfo) => {
|
||||||
const response = await remoteApi._fetch(remoteApi.buildUrl('/acl/createUser'), {
|
const response = await remoteApi._fetch(remoteApi.buildUrl('/acl/createUser.do'), {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: {'Content-Type': 'application/json'},
|
||||||
body: JSON.stringify({ brokerAddress, userInfo })
|
body: JSON.stringify({brokerAddress, userInfo})
|
||||||
});
|
});
|
||||||
return await response.json(); // 返回字符串消息
|
return await response.json(); // 返回字符串消息
|
||||||
},
|
},
|
||||||
|
|
||||||
updateUser: async (brokerAddress, userInfo) => {
|
updateUser: async (brokerAddress, userInfo) => {
|
||||||
const response = await remoteApi._fetch(remoteApi.buildUrl('/acl/updateUser'), {
|
const response = await remoteApi._fetch(remoteApi.buildUrl('/acl/updateUser.do'), {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: {'Content-Type': 'application/json'},
|
||||||
body: JSON.stringify({ brokerAddress, userInfo })
|
body: JSON.stringify({brokerAddress, userInfo})
|
||||||
});
|
});
|
||||||
return await response.json();
|
return await response.json();
|
||||||
},
|
},
|
||||||
@@ -103,7 +103,7 @@ const remoteApi = {
|
|||||||
const params = new URLSearchParams();
|
const params = new URLSearchParams();
|
||||||
if (brokerAddress) params.append('brokerAddress', brokerAddress);
|
if (brokerAddress) params.append('brokerAddress', brokerAddress);
|
||||||
params.append('username', username);
|
params.append('username', username);
|
||||||
const response = await remoteApi._fetch(remoteApi.buildUrl(`/acl/deleteUser?${params.toString()}`), {
|
const response = await remoteApi._fetch(remoteApi.buildUrl(`/acl/deleteUser.do?${params.toString()}`), {
|
||||||
method: 'DELETE'
|
method: 'DELETE'
|
||||||
});
|
});
|
||||||
return await response.json();
|
return await response.json();
|
||||||
@@ -114,24 +114,24 @@ const remoteApi = {
|
|||||||
const params = new URLSearchParams();
|
const params = new URLSearchParams();
|
||||||
if (brokerAddress) params.append('brokerAddress', brokerAddress);
|
if (brokerAddress) params.append('brokerAddress', brokerAddress);
|
||||||
if (searchParam) params.append('searchParam', searchParam);
|
if (searchParam) params.append('searchParam', searchParam);
|
||||||
const response = await remoteApi._fetch(remoteApi.buildUrl(`/acl/listAcls?${params.toString()}`));
|
const response = await remoteApi._fetch(remoteApi.buildUrl(`/acl/acls.query?${params.toString()}`));
|
||||||
return await response.json();
|
return await response.json();
|
||||||
},
|
},
|
||||||
|
|
||||||
createAcl: async (brokerAddress, subject, policies) => {
|
createAcl: async (brokerAddress, subject, policies) => {
|
||||||
const response = await remoteApi._fetch(remoteApi.buildUrl('/acl/createAcl'), {
|
const response = await remoteApi._fetch(remoteApi.buildUrl('/acl/createAcl.do'), {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: {'Content-Type': 'application/json'},
|
||||||
body: JSON.stringify({ brokerAddress, subject, policies })
|
body: JSON.stringify({brokerAddress, subject, policies})
|
||||||
});
|
});
|
||||||
return await response.json();
|
return await response.json();
|
||||||
},
|
},
|
||||||
|
|
||||||
updateAcl: async (brokerAddress, subject, policies) => {
|
updateAcl: async (brokerAddress, subject, policies) => {
|
||||||
const response = await remoteApi._fetch(remoteApi.buildUrl('/acl/updateAcl'), {
|
const response = await remoteApi._fetch(remoteApi.buildUrl('/acl/updateAcl.do'), {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: {'Content-Type': 'application/json'},
|
||||||
body: JSON.stringify({ brokerAddress, subject, policies })
|
body: JSON.stringify({brokerAddress, subject, policies})
|
||||||
});
|
});
|
||||||
return await response.json();
|
return await response.json();
|
||||||
},
|
},
|
||||||
@@ -141,7 +141,7 @@ const remoteApi = {
|
|||||||
if (brokerAddress) params.append('brokerAddress', brokerAddress);
|
if (brokerAddress) params.append('brokerAddress', brokerAddress);
|
||||||
params.append('subject', subject);
|
params.append('subject', subject);
|
||||||
if (resource) params.append('resource', resource);
|
if (resource) params.append('resource', resource);
|
||||||
const response = await remoteApi._fetch(remoteApi.buildUrl(`/acl/deleteAcl?${params.toString()}`), {
|
const response = await remoteApi._fetch(remoteApi.buildUrl(`/acl/deleteAcl.do?${params.toString()}`), {
|
||||||
method: 'DELETE'
|
method: 'DELETE'
|
||||||
});
|
});
|
||||||
return await response.json();
|
return await response.json();
|
||||||
@@ -159,7 +159,7 @@ const remoteApi = {
|
|||||||
return data
|
return data
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error querying message by ID:", error);
|
console.error("Error querying message by ID:", error);
|
||||||
callback({ status: 1, errMsg: "Failed to query message by ID" });
|
callback({status: 1, errMsg: "Failed to query message by ID"});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -197,7 +197,7 @@ const remoteApi = {
|
|||||||
return data;
|
return data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error querying DLQ messages by consumer group:", error);
|
console.error("Error querying DLQ messages by consumer group:", error);
|
||||||
return { status: 1, errMsg: "Failed to query DLQ messages by consumer group" };
|
return {status: 1, errMsg: "Failed to query DLQ messages by consumer group"};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
resendDlqMessage: async (msgId, consumerGroup, topic) => {
|
resendDlqMessage: async (msgId, consumerGroup, topic) => {
|
||||||
@@ -217,7 +217,7 @@ const remoteApi = {
|
|||||||
return data;
|
return data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error resending DLQ message:", error);
|
console.error("Error resending DLQ message:", error);
|
||||||
return { status: 1, errMsg: "Failed to resend DLQ message" };
|
return {status: 1, errMsg: "Failed to resend DLQ message"};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
exportDlqMessage: async (msgId, consumerGroup) => {
|
exportDlqMessage: async (msgId, consumerGroup) => {
|
||||||
@@ -236,7 +236,7 @@ const remoteApi = {
|
|||||||
|
|
||||||
if (!newWindow) {
|
if (!newWindow) {
|
||||||
// 浏览器可能会阻止弹窗,需要用户允许
|
// 浏览器可能会阻止弹窗,需要用户允许
|
||||||
return { status: 1, errMsg: "Failed to open new window. Please allow pop-ups for this site." };
|
return {status: 1, errMsg: "Failed to open new window. Please allow pop-ups for this site."};
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. 将 JSON 数据格式化后写入新窗口
|
// 2. 将 JSON 数据格式化后写入新窗口
|
||||||
@@ -247,10 +247,10 @@ const remoteApi = {
|
|||||||
newWindow.document.write('</body></html>');
|
newWindow.document.write('</body></html>');
|
||||||
newWindow.document.close(); // 关闭文档流,确保内容显示
|
newWindow.document.close(); // 关闭文档流,确保内容显示
|
||||||
|
|
||||||
return { status: 0, msg: "导出请求成功,内容已在新页面显示" };
|
return {status: 0, msg: "导出请求成功,内容已在新页面显示"};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error exporting DLQ message:", error);
|
console.error("Error exporting DLQ message:", error);
|
||||||
return { status: 1, errMsg: "Failed to export DLQ message: " + error.message };
|
return {status: 1, errMsg: "Failed to export DLQ message: " + error.message};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -267,7 +267,7 @@ const remoteApi = {
|
|||||||
return data;
|
return data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error batch resending DLQ messages:", error);
|
console.error("Error batch resending DLQ messages:", error);
|
||||||
return { status: 1, errMsg: "Failed to batch resend DLQ messages" };
|
return {status: 1, errMsg: "Failed to batch resend DLQ messages"};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -372,18 +372,18 @@ const remoteApi = {
|
|||||||
callback(data);
|
callback(data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error fetching producer connection list:", error);
|
console.error("Error fetching producer connection list:", error);
|
||||||
callback({ status: 1, errMsg: "Failed to fetch producer connection list" }); // Simulate error response
|
callback({status: 1, errMsg: "Failed to fetch producer connection list"}); // Simulate error response
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
queryConsumerGroupList: async (skipSysGroup = false) => {
|
queryConsumerGroupList: async (skipSysGroup, address) => {
|
||||||
try {
|
try {
|
||||||
const response = await remoteApi._fetch(remoteApi.buildUrl(`/consumer/groupList.query?skipSysGroup=${skipSysGroup}`));
|
const response = await remoteApi._fetch(remoteApi.buildUrl(`/consumer/groupList.query?skipSysGroup=${skipSysGroup}&address=${address}`));
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
return data;
|
return data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error fetching consumer group list:", error);
|
console.error("Error fetching consumer group list:", error);
|
||||||
return { status: 1, errMsg: "Failed to fetch consumer group list" };
|
return {status: 1, errMsg: "Failed to fetch consumer group list"};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -394,7 +394,7 @@ const remoteApi = {
|
|||||||
return data;
|
return data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`Error refreshing consumer group ${consumerGroup}:`, error);
|
console.error(`Error refreshing consumer group ${consumerGroup}:`, error);
|
||||||
return { status: 1, errMsg: `Failed to refresh consumer group ${consumerGroup}` };
|
return {status: 1, errMsg: `Failed to refresh consumer group ${consumerGroup}`};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -405,7 +405,7 @@ const remoteApi = {
|
|||||||
return data;
|
return data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error refreshing all consumer groups:", error);
|
console.error("Error refreshing all consumer groups:", error);
|
||||||
return { status: 1, errMsg: "Failed to refresh all consumer groups" };
|
return {status: 1, errMsg: "Failed to refresh all consumer groups"};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -416,7 +416,7 @@ const remoteApi = {
|
|||||||
return data;
|
return data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`Error fetching monitor config for ${consumeGroupName}:`, error);
|
console.error(`Error fetching monitor config for ${consumeGroupName}:`, error);
|
||||||
return { status: 1, errMsg: `Failed to fetch monitor config for ${consumeGroupName}` };
|
return {status: 1, errMsg: `Failed to fetch monitor config for ${consumeGroupName}`};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -428,13 +428,13 @@ const remoteApi = {
|
|||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
},
|
},
|
||||||
body: JSON.stringify({ consumeGroupName, minCount, maxDiffTotal })
|
body: JSON.stringify({consumeGroupName, minCount, maxDiffTotal})
|
||||||
});
|
});
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
return data;
|
return data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error creating or updating consumer monitor:", error);
|
console.error("Error creating or updating consumer monitor:", error);
|
||||||
return { status: 1, errMsg: "Failed to create or update consumer monitor" };
|
return {status: 1, errMsg: "Failed to create or update consumer monitor"};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -445,7 +445,7 @@ const remoteApi = {
|
|||||||
return data;
|
return data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`Error fetching broker name list for ${consumerGroup}:`, error);
|
console.error(`Error fetching broker name list for ${consumerGroup}:`, error);
|
||||||
return { status: 1, errMsg: `Failed to fetch broker name list for ${consumerGroup}` };
|
return {status: 1, errMsg: `Failed to fetch broker name list for ${consumerGroup}`};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -456,13 +456,13 @@ const remoteApi = {
|
|||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
},
|
},
|
||||||
body: JSON.stringify({ groupName, brokerNameList })
|
body: JSON.stringify({groupName, brokerNameList})
|
||||||
});
|
});
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
return data;
|
return data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`Error deleting consumer group ${groupName}:`, error);
|
console.error(`Error deleting consumer group ${groupName}:`, error);
|
||||||
return { status: 1, errMsg: `Failed to delete consumer group ${groupName}` };
|
return {status: 1, errMsg: `Failed to delete consumer group ${groupName}`};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -473,7 +473,7 @@ const remoteApi = {
|
|||||||
return data;
|
return data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`Error fetching consumer config for ${consumerGroup}:`, error);
|
console.error(`Error fetching consumer config for ${consumerGroup}:`, error);
|
||||||
return { status: 1, errMsg: `Failed to fetch consumer config for ${consumerGroup}` };
|
return {status: 1, errMsg: `Failed to fetch consumer config for ${consumerGroup}`};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -490,7 +490,7 @@ const remoteApi = {
|
|||||||
return data;
|
return data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error creating or updating consumer:", error);
|
console.error("Error creating or updating consumer:", error);
|
||||||
return { status: 1, errMsg: "Failed to create or update consumer" };
|
return {status: 1, errMsg: "Failed to create or update consumer"};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -501,7 +501,7 @@ const remoteApi = {
|
|||||||
return data;
|
return data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`Error fetching topics for consumer group ${consumerGroup}:`, error);
|
console.error(`Error fetching topics for consumer group ${consumerGroup}:`, error);
|
||||||
return { status: 1, errMsg: `Failed to fetch topics for consumer group ${consumerGroup}` };
|
return {status: 1, errMsg: `Failed to fetch topics for consumer group ${consumerGroup}`};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -512,7 +512,7 @@ const remoteApi = {
|
|||||||
return data;
|
return data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`Error fetching consumer connections for ${consumerGroup}:`, error);
|
console.error(`Error fetching consumer connections for ${consumerGroup}:`, error);
|
||||||
return { status: 1, errMsg: `Failed to fetch consumer connections for ${consumerGroup}` };
|
return {status: 1, errMsg: `Failed to fetch consumer connections for ${consumerGroup}`};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -523,7 +523,7 @@ const remoteApi = {
|
|||||||
return data;
|
return data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`Error fetching running info for client ${clientId} in group ${consumerGroup}:`, error);
|
console.error(`Error fetching running info for client ${clientId} in group ${consumerGroup}:`, error);
|
||||||
return { status: 1, errMsg: `Failed to fetch running info for client ${clientId} in group ${consumerGroup}` };
|
return {status: 1, errMsg: `Failed to fetch running info for client ${clientId} in group ${consumerGroup}`};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
queryTopicList: async () => {
|
queryTopicList: async () => {
|
||||||
@@ -532,7 +532,7 @@ const remoteApi = {
|
|||||||
return await response.json();
|
return await response.json();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error fetching topic list:", error);
|
console.error("Error fetching topic list:", error);
|
||||||
return { status: 1, errMsg: "Failed to fetch topic list" };
|
return {status: 1, errMsg: "Failed to fetch topic list"};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -549,7 +549,7 @@ const remoteApi = {
|
|||||||
return await response.json();
|
return await response.json();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error deleting topic:", error);
|
console.error("Error deleting topic:", error);
|
||||||
return { status: 1, errMsg: "Failed to delete topic" };
|
return {status: 1, errMsg: "Failed to delete topic"};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -559,7 +559,7 @@ const remoteApi = {
|
|||||||
return await response.json();
|
return await response.json();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error fetching topic stats:", error);
|
console.error("Error fetching topic stats:", error);
|
||||||
return { status: 1, errMsg: "Failed to fetch topic stats" };
|
return {status: 1, errMsg: "Failed to fetch topic stats"};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -569,7 +569,7 @@ const remoteApi = {
|
|||||||
return await response.json();
|
return await response.json();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error fetching topic route:", error);
|
console.error("Error fetching topic route:", error);
|
||||||
return { status: 1, errMsg: "Failed to fetch topic route" };
|
return {status: 1, errMsg: "Failed to fetch topic route"};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -579,7 +579,7 @@ const remoteApi = {
|
|||||||
return await response.json();
|
return await response.json();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error fetching topic consumers:", error);
|
console.error("Error fetching topic consumers:", error);
|
||||||
return { status: 1, errMsg: "Failed to fetch topic consumers" };
|
return {status: 1, errMsg: "Failed to fetch topic consumers"};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -589,7 +589,7 @@ const remoteApi = {
|
|||||||
return await response.json();
|
return await response.json();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error fetching consumer groups:", error);
|
console.error("Error fetching consumer groups:", error);
|
||||||
return { status: 1, errMsg: "Failed to fetch consumer groups" };
|
return {status: 1, errMsg: "Failed to fetch consumer groups"};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -599,7 +599,7 @@ const remoteApi = {
|
|||||||
return await response.json();
|
return await response.json();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error fetching topic config:", error);
|
console.error("Error fetching topic config:", error);
|
||||||
return { status: 1, errMsg: "Failed to fetch topic config" };
|
return {status: 1, errMsg: "Failed to fetch topic config"};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -609,7 +609,7 @@ const remoteApi = {
|
|||||||
return await response.json();
|
return await response.json();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error fetching cluster list:", error);
|
console.error("Error fetching cluster list:", error);
|
||||||
return { status: 1, errMsg: "Failed to fetch cluster list" };
|
return {status: 1, errMsg: "Failed to fetch cluster list"};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -625,7 +625,7 @@ const remoteApi = {
|
|||||||
return await response.json();
|
return await response.json();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error creating/updating topic:", error);
|
console.error("Error creating/updating topic:", error);
|
||||||
return { status: 1, errMsg: "Failed to create/update topic" };
|
return {status: 1, errMsg: "Failed to create/update topic"};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -641,7 +641,7 @@ const remoteApi = {
|
|||||||
return await response.json();
|
return await response.json();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error resetting consumer offset:", error);
|
console.error("Error resetting consumer offset:", error);
|
||||||
return { status: 1, errMsg: "Failed to reset consumer offset" };
|
return {status: 1, errMsg: "Failed to reset consumer offset"};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -657,7 +657,7 @@ const remoteApi = {
|
|||||||
return await response.json();
|
return await response.json();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error skipping message accumulate:", error);
|
console.error("Error skipping message accumulate:", error);
|
||||||
return { status: 1, errMsg: "Failed to skip message accumulate" };
|
return {status: 1, errMsg: "Failed to skip message accumulate"};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -673,7 +673,7 @@ const remoteApi = {
|
|||||||
return await response.json();
|
return await response.json();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error sending topic message:", error);
|
console.error("Error sending topic message:", error);
|
||||||
return { status: 1, errMsg: "Failed to send topic message" };
|
return {status: 1, errMsg: "Failed to send topic message"};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -684,12 +684,12 @@ const remoteApi = {
|
|||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
},
|
},
|
||||||
body: JSON.stringify({ brokerName, topic })
|
body: JSON.stringify({brokerName, topic})
|
||||||
});
|
});
|
||||||
return await response.json();
|
return await response.json();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error deleting topic by broker:", error);
|
console.error("Error deleting topic by broker:", error);
|
||||||
return { status: 1, errMsg: "Failed to delete topic by broker" };
|
return {status: 1, errMsg: "Failed to delete topic by broker"};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -700,7 +700,7 @@ const remoteApi = {
|
|||||||
return await response.json();
|
return await response.json();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error fetching ops home page data:", error);
|
console.error("Error fetching ops home page data:", error);
|
||||||
return { status: 1, errMsg: "Failed to fetch ops home page data" };
|
return {status: 1, errMsg: "Failed to fetch ops home page data"};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -712,7 +712,7 @@ const remoteApi = {
|
|||||||
return await response.json();
|
return await response.json();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error updating NameServer address:", error);
|
console.error("Error updating NameServer address:", error);
|
||||||
return { status: 1, errMsg: "Failed to update NameServer address" };
|
return {status: 1, errMsg: "Failed to update NameServer address"};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -724,7 +724,7 @@ const remoteApi = {
|
|||||||
return await response.json();
|
return await response.json();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error adding NameServer address:", error);
|
console.error("Error adding NameServer address:", error);
|
||||||
return { status: 1, errMsg: "Failed to add NameServer address" };
|
return {status: 1, errMsg: "Failed to add NameServer address"};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -736,7 +736,7 @@ const remoteApi = {
|
|||||||
return await response.json();
|
return await response.json();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error updating VIP Channel status:", error);
|
console.error("Error updating VIP Channel status:", error);
|
||||||
return { status: 1, errMsg: "Failed to update VIP Channel status" };
|
return {status: 1, errMsg: "Failed to update VIP Channel status"};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -748,7 +748,7 @@ const remoteApi = {
|
|||||||
return await response.json();
|
return await response.json();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error updating TLS status:", error);
|
console.error("Error updating TLS status:", error);
|
||||||
return { status: 1, errMsg: "Failed to update TLS status" };
|
return {status: 1, errMsg: "Failed to update TLS status"};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -759,7 +759,7 @@ const remoteApi = {
|
|||||||
callback(data);
|
callback(data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error fetching cluster list:", error);
|
console.error("Error fetching cluster list:", error);
|
||||||
callback({ status: 1, errMsg: "Failed to fetch cluster list" });
|
callback({status: 1, errMsg: "Failed to fetch cluster list"});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -767,16 +767,16 @@ const remoteApi = {
|
|||||||
try {
|
try {
|
||||||
const url = new URL(remoteApi.buildUrl('/dashboard/broker.query'));
|
const url = new URL(remoteApi.buildUrl('/dashboard/broker.query'));
|
||||||
url.searchParams.append('date', date);
|
url.searchParams.append('date', date);
|
||||||
const response = await remoteApi._fetch(url.toString(), { signal: AbortSignal.timeout(15000) }); // 15s timeout
|
const response = await remoteApi._fetch(url.toString(), {signal: AbortSignal.timeout(15000)}); // 15s timeout
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
callback(data);
|
callback(data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error.name === 'TimeoutError') {
|
if (error.name === 'TimeoutError') {
|
||||||
console.error("Broker history data request timed out:", error);
|
console.error("Broker history data request timed out:", error);
|
||||||
callback({ status: 1, errMsg: "Request timed out for broker history data" });
|
callback({status: 1, errMsg: "Request timed out for broker history data"});
|
||||||
} else {
|
} else {
|
||||||
console.error("Error fetching broker history data:", error);
|
console.error("Error fetching broker history data:", error);
|
||||||
callback({ status: 1, errMsg: "Failed to fetch broker history data" });
|
callback({status: 1, errMsg: "Failed to fetch broker history data"});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -786,32 +786,32 @@ const remoteApi = {
|
|||||||
const url = new URL(remoteApi.buildUrl('/dashboard/topic.query'));
|
const url = new URL(remoteApi.buildUrl('/dashboard/topic.query'));
|
||||||
url.searchParams.append('date', date);
|
url.searchParams.append('date', date);
|
||||||
url.searchParams.append('topicName', topicName);
|
url.searchParams.append('topicName', topicName);
|
||||||
const response = await remoteApi._fetch(url.toString(), { signal: AbortSignal.timeout(15000) }); // 15s timeout
|
const response = await remoteApi._fetch(url.toString(), {signal: AbortSignal.timeout(15000)}); // 15s timeout
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
callback(data);
|
callback(data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error.name === 'TimeoutError') {
|
if (error.name === 'TimeoutError') {
|
||||||
console.error("Topic history data request timed out:", error);
|
console.error("Topic history data request timed out:", error);
|
||||||
callback({ status: 1, errMsg: "Request timed out for topic history data" });
|
callback({status: 1, errMsg: "Request timed out for topic history data"});
|
||||||
} else {
|
} else {
|
||||||
console.error("Error fetching topic history data:", error);
|
console.error("Error fetching topic history data:", error);
|
||||||
callback({ status: 1, errMsg: "Failed to fetch topic history data" });
|
callback({status: 1, errMsg: "Failed to fetch topic history data"});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
queryTopicCurrentData: async (callback) => {
|
queryTopicCurrentData: async (callback) => {
|
||||||
try {
|
try {
|
||||||
const response = await remoteApi._fetch(remoteApi.buildUrl('/dashboard/topicCurrent.query'), { signal: AbortSignal.timeout(15000) }); // 15s timeout
|
const response = await remoteApi._fetch(remoteApi.buildUrl('/dashboard/topicCurrent.query'), {signal: AbortSignal.timeout(15000)}); // 15s timeout
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
callback(data);
|
callback(data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error.name === 'TimeoutError') {
|
if (error.name === 'TimeoutError') {
|
||||||
console.error("Topic current data request timed out:", error);
|
console.error("Topic current data request timed out:", error);
|
||||||
callback({ status: 1, errMsg: "Request timed out for topic current data" });
|
callback({status: 1, errMsg: "Request timed out for topic current data"});
|
||||||
} else {
|
} else {
|
||||||
console.error("Error fetching topic current data:", error);
|
console.error("Error fetching topic current data:", error);
|
||||||
callback({ status: 1, errMsg: "Failed to fetch topic current data" });
|
callback({status: 1, errMsg: "Failed to fetch topic current data"});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -825,7 +825,7 @@ const remoteApi = {
|
|||||||
callback(data);
|
callback(data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error fetching broker config:", error);
|
console.error("Error fetching broker config:", error);
|
||||||
callback({ status: 1, errMsg: "Failed to fetch broker config" });
|
callback({status: 1, errMsg: "Failed to fetch broker config"});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -839,7 +839,7 @@ const remoteApi = {
|
|||||||
callback(data);
|
callback(data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error fetching proxy home page:", error);
|
console.error("Error fetching proxy home page:", error);
|
||||||
callback({ status: 1, errMsg: "Failed to fetch proxy home page" });
|
callback({status: 1, errMsg: "Failed to fetch proxy home page"});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -850,14 +850,14 @@ const remoteApi = {
|
|||||||
try {
|
try {
|
||||||
const response = await remoteApi._fetch(remoteApi.buildUrl("/proxy/addProxyAddr.do"), {
|
const response = await remoteApi._fetch(remoteApi.buildUrl("/proxy/addProxyAddr.do"), {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||||
body: new URLSearchParams({ newProxyAddr }).toString()
|
body: new URLSearchParams({newProxyAddr}).toString()
|
||||||
});
|
});
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
callback(data);
|
callback(data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error adding proxy address:", error);
|
console.error("Error adding proxy address:", error);
|
||||||
callback({ status: 1, errMsg: "Failed to add proxy address" });
|
callback({status: 1, errMsg: "Failed to add proxy address"});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
login: async (username, password) => {
|
login: async (username, password) => {
|
||||||
@@ -882,19 +882,19 @@ const remoteApi = {
|
|||||||
return data;
|
return data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error logging in:", error);
|
console.error("Error logging in:", error);
|
||||||
return { status: 1, errMsg: "Failed to log in" };
|
return {status: 1, errMsg: "Failed to log in"};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
logout: async () => {
|
logout: async () => {
|
||||||
try {
|
try {
|
||||||
const response = await remoteApi._fetch(remoteApi.buildUrl("/login/logout.do"),{
|
const response = await remoteApi._fetch(remoteApi.buildUrl("/login/logout.do"), {
|
||||||
method: 'POST'
|
method: 'POST'
|
||||||
});
|
});
|
||||||
return await response.json()
|
return await response.json()
|
||||||
}catch (error) {
|
} catch (error) {
|
||||||
console.error("Error logging out:", error);
|
console.error("Error logging out:", error);
|
||||||
return { status: 1, errMsg: "Failed to log out" };
|
return {status: 1, errMsg: "Failed to log out"};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -939,4 +939,4 @@ const tools = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export { remoteApi, tools };
|
export {remoteApi, tools};
|
||||||
|
@@ -16,20 +16,20 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Form, Input, Typography, Modal } from 'antd';
|
import {Form, Input, Typography} from 'antd';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import { useLanguage } from '../i18n/LanguageContext'; // 根据实际路径调整
|
import {useLanguage} from '../i18n/LanguageContext'; // 根据实际路径调整
|
||||||
|
|
||||||
const { Text } = Typography;
|
const {Text} = Typography;
|
||||||
|
|
||||||
const DlqMessageDetailViewDialog = ({ ngDialogData }) => {
|
const DlqMessageDetailViewDialog = ({ngDialogData}) => {
|
||||||
const { t } = useLanguage();
|
const {t} = useLanguage();
|
||||||
|
|
||||||
const messageView = ngDialogData?.messageView || {};
|
const messageView = ngDialogData?.messageView || {};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{ padding: '20px' }}>
|
<div style={{padding: '20px'}}>
|
||||||
<Form layout="horizontal" labelCol={{ span: 4 }} wrapperCol={{ span: 20 }}>
|
<Form layout="horizontal" labelCol={{span: 4}} wrapperCol={{span: 20}}>
|
||||||
<Form.Item label="Message ID:">
|
<Form.Item label="Message ID:">
|
||||||
<Text strong>{messageView.msgId}</Text>
|
<Text strong>{messageView.msgId}</Text>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
@@ -39,7 +39,7 @@ const DlqMessageDetailViewDialog = ({ ngDialogData }) => {
|
|||||||
<Form.Item label="Properties:">
|
<Form.Item label="Properties:">
|
||||||
<Input.TextArea
|
<Input.TextArea
|
||||||
value={typeof messageView.properties === 'object' ? JSON.stringify(messageView.properties, null, 2) : messageView.properties}
|
value={typeof messageView.properties === 'object' ? JSON.stringify(messageView.properties, null, 2) : messageView.properties}
|
||||||
style={{ minHeight: 100, resize: 'none' }}
|
style={{minHeight: 100, resize: 'none'}}
|
||||||
readOnly
|
readOnly
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
@@ -61,7 +61,7 @@ const DlqMessageDetailViewDialog = ({ ngDialogData }) => {
|
|||||||
<Form.Item label="Message body:">
|
<Form.Item label="Message body:">
|
||||||
<Input.TextArea
|
<Input.TextArea
|
||||||
value={messageView.messageBody}
|
value={messageView.messageBody}
|
||||||
style={{ minHeight: 100, resize: 'none' }}
|
style={{minHeight: 100, resize: 'none'}}
|
||||||
readOnly
|
readOnly
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
@@ -16,16 +16,16 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Modal, Button, Typography, Descriptions, Tag, Spin, notification } from 'antd';
|
import {Button, Descriptions, Modal, notification, Spin, Tag, Typography} from 'antd';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import { ExclamationCircleOutlined, SyncOutlined } from '@ant-design/icons';
|
import {SyncOutlined} from '@ant-design/icons';
|
||||||
import { useLanguage } from '../i18n/LanguageContext';
|
import {useLanguage} from '../i18n/LanguageContext';
|
||||||
import { remoteApi } from '../api/remoteApi/remoteApi'; // 确保这个路径正确
|
import {remoteApi} from '../api/remoteApi/remoteApi'; // 确保这个路径正确
|
||||||
|
|
||||||
const { Text, Paragraph } = Typography;
|
const {Text, Paragraph} = Typography;
|
||||||
|
|
||||||
const MessageDetailViewDialog = ({ visible, onCancel, messageId, topic, onResendMessage }) => {
|
const MessageDetailViewDialog = ({visible, onCancel, messageId, topic, onResendMessage}) => {
|
||||||
const { t } = useLanguage();
|
const {t} = useLanguage();
|
||||||
const [loading, setLoading] = React.useState(true);
|
const [loading, setLoading] = React.useState(true);
|
||||||
const [messageDetail, setMessageDetail] = React.useState(null);
|
const [messageDetail, setMessageDetail] = React.useState(null);
|
||||||
const [error, setError] = React.useState(null);
|
const [error, setError] = React.useState(null);
|
||||||
@@ -89,17 +89,21 @@ const MessageDetailViewDialog = ({ visible, onCancel, messageId, topic, onResend
|
|||||||
>
|
>
|
||||||
<Spin spinning={loading} tip={t.LOADING}>
|
<Spin spinning={loading} tip={t.LOADING}>
|
||||||
{error && (
|
{error && (
|
||||||
<Paragraph type="danger" style={{ textAlign: 'center' }}>
|
<Paragraph type="danger" style={{textAlign: 'center'}}>
|
||||||
{error}
|
{error}
|
||||||
</Paragraph>
|
</Paragraph>
|
||||||
)}
|
)}
|
||||||
{messageDetail ? ( // 确保 messageDetail 存在时才渲染内容
|
{messageDetail ? ( // 确保 messageDetail 存在时才渲染内容
|
||||||
<>
|
<>
|
||||||
{/* 消息信息部分 */}
|
{/* 消息信息部分 */}
|
||||||
<Descriptions title={<Text strong>{t.MESSAGE_INFO}</Text>} bordered column={2} size="small" style={{ marginBottom: 20 }}>
|
<Descriptions title={<Text strong>{t.MESSAGE_INFO}</Text>} bordered column={2} size="small"
|
||||||
<Descriptions.Item label="Topic" span={2}><Text copyable>{messageDetail.messageView.topic}</Text></Descriptions.Item>
|
style={{marginBottom: 20}}>
|
||||||
<Descriptions.Item label="Message ID" span={2}><Text copyable>{messageDetail.messageView.msgId}</Text></Descriptions.Item>
|
<Descriptions.Item label="Topic" span={2}><Text
|
||||||
<Descriptions.Item label="StoreHost">{messageDetail.messageView.storeHost}</Descriptions.Item>
|
copyable>{messageDetail.messageView.topic}</Text></Descriptions.Item>
|
||||||
|
<Descriptions.Item label="Message ID" span={2}><Text
|
||||||
|
copyable>{messageDetail.messageView.msgId}</Text></Descriptions.Item>
|
||||||
|
<Descriptions.Item
|
||||||
|
label="StoreHost">{messageDetail.messageView.storeHost}</Descriptions.Item>
|
||||||
<Descriptions.Item label="BornHost">{messageDetail.messageView.bornHost}</Descriptions.Item>
|
<Descriptions.Item label="BornHost">{messageDetail.messageView.bornHost}</Descriptions.Item>
|
||||||
<Descriptions.Item label="StoreTime">
|
<Descriptions.Item label="StoreTime">
|
||||||
{moment(messageDetail.messageView.storeTimestamp).format("YYYY-MM-DD HH:mm:ss")}
|
{moment(messageDetail.messageView.storeTimestamp).format("YYYY-MM-DD HH:mm:ss")}
|
||||||
@@ -108,26 +112,33 @@ const MessageDetailViewDialog = ({ visible, onCancel, messageId, topic, onResend
|
|||||||
{moment(messageDetail.messageView.bornTimestamp).format("YYYY-MM-DD HH:mm:ss")}
|
{moment(messageDetail.messageView.bornTimestamp).format("YYYY-MM-DD HH:mm:ss")}
|
||||||
</Descriptions.Item>
|
</Descriptions.Item>
|
||||||
<Descriptions.Item label="Queue ID">{messageDetail.messageView.queueId}</Descriptions.Item>
|
<Descriptions.Item label="Queue ID">{messageDetail.messageView.queueId}</Descriptions.Item>
|
||||||
<Descriptions.Item label="Queue Offset">{messageDetail.messageView.queueOffset}</Descriptions.Item>
|
<Descriptions.Item
|
||||||
<Descriptions.Item label="StoreSize">{messageDetail.messageView.storeSize} bytes</Descriptions.Item>
|
label="Queue Offset">{messageDetail.messageView.queueOffset}</Descriptions.Item>
|
||||||
<Descriptions.Item label="ReconsumeTimes">{messageDetail.messageView.reconsumeTimes}</Descriptions.Item>
|
<Descriptions.Item
|
||||||
|
label="StoreSize">{messageDetail.messageView.storeSize} bytes</Descriptions.Item>
|
||||||
|
<Descriptions.Item
|
||||||
|
label="ReconsumeTimes">{messageDetail.messageView.reconsumeTimes}</Descriptions.Item>
|
||||||
<Descriptions.Item label="BodyCRC">{messageDetail.messageView.bodyCRC}</Descriptions.Item>
|
<Descriptions.Item label="BodyCRC">{messageDetail.messageView.bodyCRC}</Descriptions.Item>
|
||||||
<Descriptions.Item label="SysFlag">{messageDetail.messageView.sysFlag}</Descriptions.Item>
|
<Descriptions.Item label="SysFlag">{messageDetail.messageView.sysFlag}</Descriptions.Item>
|
||||||
<Descriptions.Item label="Flag">{messageDetail.messageView.flag}</Descriptions.Item>
|
<Descriptions.Item label="Flag">{messageDetail.messageView.flag}</Descriptions.Item>
|
||||||
<Descriptions.Item label="PreparedTransactionOffset">{messageDetail.messageView.preparedTransactionOffset}</Descriptions.Item>
|
<Descriptions.Item
|
||||||
|
label="PreparedTransactionOffset">{messageDetail.messageView.preparedTransactionOffset}</Descriptions.Item>
|
||||||
</Descriptions>
|
</Descriptions>
|
||||||
|
|
||||||
{/* 消息属性部分 */}
|
{/* 消息属性部分 */}
|
||||||
{Object.keys(messageDetail.messageView.properties).length > 0 && (
|
{Object.keys(messageDetail.messageView.properties).length > 0 && (
|
||||||
<Descriptions title={<Text strong>{t.MESSAGE_PROPERTIES}</Text>} bordered column={1} size="small" style={{ marginBottom: 20 }}>
|
<Descriptions title={<Text strong>{t.MESSAGE_PROPERTIES}</Text>} bordered column={1}
|
||||||
|
size="small" style={{marginBottom: 20}}>
|
||||||
{Object.entries(messageDetail.messageView.properties).map(([key, value]) => (
|
{Object.entries(messageDetail.messageView.properties).map(([key, value]) => (
|
||||||
<Descriptions.Item label={key} key={key}><Text copyable>{value}</Text></Descriptions.Item>
|
<Descriptions.Item label={key} key={key}><Text
|
||||||
|
copyable>{value}</Text></Descriptions.Item>
|
||||||
))}
|
))}
|
||||||
</Descriptions>
|
</Descriptions>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* 消息体部分 */}
|
{/* 消息体部分 */}
|
||||||
<Descriptions title={<Text strong>{t.MESSAGE_BODY}</Text>} bordered column={1} size="small" style={{ marginBottom: 20 }}>
|
<Descriptions title={<Text strong>{t.MESSAGE_BODY}</Text>} bordered column={1} size="small"
|
||||||
|
style={{marginBottom: 20}}>
|
||||||
<Descriptions.Item>
|
<Descriptions.Item>
|
||||||
<Paragraph
|
<Paragraph
|
||||||
copyable
|
copyable
|
||||||
@@ -146,9 +157,10 @@ const MessageDetailViewDialog = ({ visible, onCancel, messageId, topic, onResend
|
|||||||
{messageDetail.messageTrackList && messageDetail.messageTrackList.length > 0 ? (
|
{messageDetail.messageTrackList && messageDetail.messageTrackList.length > 0 ? (
|
||||||
<>
|
<>
|
||||||
<Text strong>{t.MESSAGE_TRACKING}</Text>
|
<Text strong>{t.MESSAGE_TRACKING}</Text>
|
||||||
<div style={{ marginTop: 10 }}>
|
<div style={{marginTop: 10}}>
|
||||||
{messageDetail.messageTrackList.map((track, index) => (
|
{messageDetail.messageTrackList.map((track, index) => (
|
||||||
<Descriptions bordered column={1} size="small" key={index} style={{ marginBottom: 15 }}>
|
<Descriptions bordered column={1} size="small" key={index}
|
||||||
|
style={{marginBottom: 15}}>
|
||||||
<Descriptions.Item label={t.CONSUMER_GROUP}>
|
<Descriptions.Item label={t.CONSUMER_GROUP}>
|
||||||
{track.consumerGroup}
|
{track.consumerGroup}
|
||||||
</Descriptions.Item>
|
</Descriptions.Item>
|
||||||
@@ -165,10 +177,10 @@ const MessageDetailViewDialog = ({ visible, onCancel, messageId, topic, onResend
|
|||||||
</Descriptions.Item>
|
</Descriptions.Item>
|
||||||
<Descriptions.Item label={t.OPERATION}>
|
<Descriptions.Item label={t.OPERATION}>
|
||||||
<Button
|
<Button
|
||||||
icon={<SyncOutlined />}
|
icon={<SyncOutlined/>}
|
||||||
onClick={() => onResendMessage(messageDetail.messageView, track.consumerGroup)}
|
onClick={() => onResendMessage(messageDetail.messageView, track.consumerGroup)}
|
||||||
size="small"
|
size="small"
|
||||||
style={{ marginRight: 8 }}
|
style={{marginRight: 8}}
|
||||||
>
|
>
|
||||||
{t.RESEND_MESSAGE}
|
{t.RESEND_MESSAGE}
|
||||||
</Button>
|
</Button>
|
||||||
@@ -181,7 +193,10 @@ const MessageDetailViewDialog = ({ visible, onCancel, messageId, topic, onResend
|
|||||||
ellipsis={{
|
ellipsis={{
|
||||||
rows: 2, // 默认显示2行
|
rows: 2, // 默认显示2行
|
||||||
expandable: true,
|
expandable: true,
|
||||||
symbol: <Text style={{ color: '#1890ff', cursor: 'pointer' }}>{t.READ_MORE}</Text>, // 蓝色展开文本
|
symbol: <Text style={{
|
||||||
|
color: '#1890ff',
|
||||||
|
cursor: 'pointer'
|
||||||
|
}}>{t.READ_MORE}</Text>, // 蓝色展开文本
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{track.exceptionDesc}
|
{track.exceptionDesc}
|
||||||
@@ -198,7 +213,8 @@ const MessageDetailViewDialog = ({ visible, onCancel, messageId, topic, onResend
|
|||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
// 当 messageDetail 为 null 时,可以显示一个占位符或者不显示内容
|
// 当 messageDetail 为 null 时,可以显示一个占位符或者不显示内容
|
||||||
!loading && !error && <Paragraph style={{ textAlign: 'center' }}>{t.NO_MESSAGE_DETAIL_AVAILABLE}</Paragraph>
|
!loading && !error &&
|
||||||
|
<Paragraph style={{textAlign: 'center'}}>{t.NO_MESSAGE_DETAIL_AVAILABLE}</Paragraph>
|
||||||
)}
|
)}
|
||||||
</Spin>
|
</Spin>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
@@ -15,15 +15,15 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { useEffect, useRef } from 'react';
|
import React, {useEffect, useRef} from 'react';
|
||||||
import { Form, Input, Typography, Collapse, Table, Tag } from 'antd';
|
import {Collapse, Form, Input, Table, Tag, Typography} from 'antd';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import { useLanguage } from '../i18n/LanguageContext';
|
import {useLanguage} from '../i18n/LanguageContext';
|
||||||
import Paragraph from "antd/es/skeleton/Paragraph";
|
import Paragraph from "antd/es/skeleton/Paragraph";
|
||||||
import * as echarts from 'echarts'; // Import ECharts
|
import * as echarts from 'echarts'; // Import ECharts
|
||||||
|
|
||||||
const { Text } = Typography;
|
const {Text} = Typography;
|
||||||
const { Panel } = Collapse;
|
const {Panel} = Collapse;
|
||||||
|
|
||||||
// Constants for styling and formatting, derived from the example
|
// Constants for styling and formatting, derived from the example
|
||||||
const SUCCESS_COLOR = '#75d874';
|
const SUCCESS_COLOR = '#75d874';
|
||||||
@@ -36,8 +36,8 @@ const TIME_FORMAT_PATTERN = "YYYY-MM-DD HH:mm:ss.SSS";
|
|||||||
const DEFAULT_DISPLAY_DURATION = 10 * 1000;
|
const DEFAULT_DISPLAY_DURATION = 10 * 1000;
|
||||||
const TRANSACTION_CHECK_COST_TIME = 50; // transactionTraceNode do not have costTime, assume it cost 50ms
|
const TRANSACTION_CHECK_COST_TIME = 50; // transactionTraceNode do not have costTime, assume it cost 50ms
|
||||||
|
|
||||||
const MessageTraceDetailViewDialog = ({ ngDialogData }) => {
|
const MessageTraceDetailViewDialog = ({ngDialogData}) => {
|
||||||
const { t } = useLanguage();
|
const {t} = useLanguage();
|
||||||
const messageTraceGraphRef = useRef(null);
|
const messageTraceGraphRef = useRef(null);
|
||||||
|
|
||||||
const producerNode = ngDialogData?.producerNode;
|
const producerNode = ngDialogData?.producerNode;
|
||||||
@@ -125,6 +125,7 @@ const MessageTraceDetailViewDialog = ({ ngDialogData }) => {
|
|||||||
}
|
}
|
||||||
return `Cost Time: ${formatCostTimeStr(costTime)}<br/>`
|
return `Cost Time: ${formatCostTimeStr(costTime)}<br/>`
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildTimeStamp(timestamp) {
|
function buildTimeStamp(timestamp) {
|
||||||
if (timestamp < 0) {
|
if (timestamp < 0) {
|
||||||
return 'N/A';
|
return 'N/A';
|
||||||
@@ -323,94 +324,181 @@ const MessageTraceDetailViewDialog = ({ ngDialogData }) => {
|
|||||||
|
|
||||||
// ... (rest of your existing component code)
|
// ... (rest of your existing component code)
|
||||||
const transactionColumns = [
|
const transactionColumns = [
|
||||||
{ title: t.TIMESTAMP, dataIndex: 'beginTimestamp', key: 'beginTimestamp', align: 'center', render: (text) => moment(text).format('YYYY-MM-DD HH:mm:ss.SSS') },
|
{
|
||||||
{ title: t.TRANSACTION_STATE, dataIndex: 'transactionState', key: 'transactionState', align: 'center', render: (text) => <Tag color={text === 'COMMIT_MESSAGE' ? 'green' : (text === 'ROLLBACK_MESSAGE' ? 'red' : 'default')}>{text}</Tag> },
|
title: t.TIMESTAMP,
|
||||||
{ title: t.FROM_TRANSACTION_CHECK, dataIndex: 'fromTransactionCheck', key: 'fromTransactionCheck', align: 'center', render: (text) => (text ? <Tag color="blue">{t.YES}</Tag> : <Tag color="purple">{t.NO}</Tag>) },
|
dataIndex: 'beginTimestamp',
|
||||||
{ title: t.CLIENT_HOST, dataIndex: 'clientHost', key: 'clientHost', align: 'center' },
|
key: 'beginTimestamp',
|
||||||
{ title: t.STORE_HOST, dataIndex: 'storeHost', key: 'storeHost', align: 'center' },
|
align: 'center',
|
||||||
|
render: (text) => moment(text).format('YYYY-MM-DD HH:mm:ss.SSS')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t.TRANSACTION_STATE,
|
||||||
|
dataIndex: 'transactionState',
|
||||||
|
key: 'transactionState',
|
||||||
|
align: 'center',
|
||||||
|
render: (text) => <Tag
|
||||||
|
color={text === 'COMMIT_MESSAGE' ? 'green' : (text === 'ROLLBACK_MESSAGE' ? 'red' : 'default')}>{text}</Tag>
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t.FROM_TRANSACTION_CHECK,
|
||||||
|
dataIndex: 'fromTransactionCheck',
|
||||||
|
key: 'fromTransactionCheck',
|
||||||
|
align: 'center',
|
||||||
|
render: (text) => (text ? <Tag color="blue">{t.YES}</Tag> : <Tag color="purple">{t.NO}</Tag>)
|
||||||
|
},
|
||||||
|
{title: t.CLIENT_HOST, dataIndex: 'clientHost', key: 'clientHost', align: 'center'},
|
||||||
|
{title: t.STORE_HOST, dataIndex: 'storeHost', key: 'storeHost', align: 'center'},
|
||||||
];
|
];
|
||||||
|
|
||||||
const consumeColumns = [
|
const consumeColumns = [
|
||||||
{ title: t.BEGIN_TIMESTAMP, dataIndex: 'beginTimestamp', key: 'beginTimestamp', align: 'center', render: (text) => text < 0 ? 'N/A' : moment(text).format('YYYY-MM-DD HH:mm:ss.SSS') },
|
{
|
||||||
{ title: t.END_TIMESTAMP, dataIndex: 'endTimestamp', key: 'endTimestamp', align: 'center', render: (text) => text < 0 ? 'N/A' : moment(text).format('YYYY-MM-DD HH:mm:ss.SSS') },
|
title: t.BEGIN_TIMESTAMP,
|
||||||
{ title: t.COST_TIME, dataIndex: 'costTime', key: 'costTime', align: 'center', render: (text) => text < 0 ? 'N/A' : `${text === 0 ? '<1' : text}ms` },
|
dataIndex: 'beginTimestamp',
|
||||||
{ title: t.STATUS, dataIndex: 'status', key: 'status', align: 'center', render: (text) => <Tag color={text === 'SUCCESS' ? 'green' : (text === 'FAILED' ? 'red' : 'default')}>{text}</Tag> },
|
key: 'beginTimestamp',
|
||||||
{ title: t.RETRY_TIMES, dataIndex: 'retryTimes', key: 'retryTimes', align: 'center', render: (text) => text < 0 ? 'N/A' : text },
|
align: 'center',
|
||||||
{ title: t.CLIENT_HOST, dataIndex: 'clientHost', key: 'clientHost', align: 'center' },
|
render: (text) => text < 0 ? 'N/A' : moment(text).format('YYYY-MM-DD HH:mm:ss.SSS')
|
||||||
{ title: t.STORE_HOST, dataIndex: 'storeHost', key: 'storeHost', align: 'center' },
|
},
|
||||||
|
{
|
||||||
|
title: t.END_TIMESTAMP,
|
||||||
|
dataIndex: 'endTimestamp',
|
||||||
|
key: 'endTimestamp',
|
||||||
|
align: 'center',
|
||||||
|
render: (text) => text < 0 ? 'N/A' : moment(text).format('YYYY-MM-DD HH:mm:ss.SSS')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t.COST_TIME,
|
||||||
|
dataIndex: 'costTime',
|
||||||
|
key: 'costTime',
|
||||||
|
align: 'center',
|
||||||
|
render: (text) => text < 0 ? 'N/A' : `${text === 0 ? '<1' : text}ms`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t.STATUS,
|
||||||
|
dataIndex: 'status',
|
||||||
|
key: 'status',
|
||||||
|
align: 'center',
|
||||||
|
render: (text) => <Tag
|
||||||
|
color={text === 'SUCCESS' ? 'green' : (text === 'FAILED' ? 'red' : 'default')}>{text}</Tag>
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t.RETRY_TIMES,
|
||||||
|
dataIndex: 'retryTimes',
|
||||||
|
key: 'retryTimes',
|
||||||
|
align: 'center',
|
||||||
|
render: (text) => text < 0 ? 'N/A' : text
|
||||||
|
},
|
||||||
|
{title: t.CLIENT_HOST, dataIndex: 'clientHost', key: 'clientHost', align: 'center'},
|
||||||
|
{title: t.STORE_HOST, dataIndex: 'storeHost', key: 'storeHost', align: 'center'},
|
||||||
];
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{ padding: '20px', backgroundColor: '#f0f2f5' }}>
|
<div style={{padding: '20px', backgroundColor: '#f0f2f5'}}>
|
||||||
<div style={{ marginBottom: '20px', borderRadius: '8px', overflow: 'hidden', boxShadow: '0 4px 12px rgba(0, 0, 0, 0.08)' }}>
|
<div style={{
|
||||||
|
marginBottom: '20px',
|
||||||
|
borderRadius: '8px',
|
||||||
|
overflow: 'hidden',
|
||||||
|
boxShadow: '0 4px 12px rgba(0, 0, 0, 0.08)'
|
||||||
|
}}>
|
||||||
<Collapse defaultActiveKey={['messageTraceGraph']} expandIconPosition="right">
|
<Collapse defaultActiveKey={['messageTraceGraph']} expandIconPosition="right">
|
||||||
<Panel header={<Typography.Title level={3} style={{ margin: 0, color: '#333' }}>{t.MESSAGE_TRACE_GRAPH}</Typography.Title>} key="messageTraceGraph">
|
<Panel header={<Typography.Title level={3} style={{
|
||||||
<div ref={messageTraceGraphRef} style={{ height: 500, width: '100%', backgroundColor: '#fff', padding: '10px' }}>
|
margin: 0,
|
||||||
|
color: '#333'
|
||||||
|
}}>{t.MESSAGE_TRACE_GRAPH}</Typography.Title>} key="messageTraceGraph">
|
||||||
|
<div ref={messageTraceGraphRef}
|
||||||
|
style={{height: 500, width: '100%', backgroundColor: '#fff', padding: '10px'}}>
|
||||||
{/* ECharts message trace graph will be rendered here */}
|
{/* ECharts message trace graph will be rendered here */}
|
||||||
{(!producerNode && subscriptionNodeList.length === 0) && (
|
{(!producerNode && subscriptionNodeList.length === 0) && (
|
||||||
<Text type="secondary" style={{ display: 'block', textAlign: 'center', marginTop: '150px' }}>{t.TRACE_GRAPH_PLACEHOLDER}</Text>
|
<Text type="secondary" style={{
|
||||||
|
display: 'block',
|
||||||
|
textAlign: 'center',
|
||||||
|
marginTop: '150px'
|
||||||
|
}}>{t.TRACE_GRAPH_PLACEHOLDER}</Text>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</Panel>
|
</Panel>
|
||||||
</Collapse>
|
</Collapse>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style={{ marginBottom: '20px', borderRadius: '8px', overflow: 'hidden', boxShadow: '0 4px 12px rgba(0, 0, 0, 0.08)' }}>
|
<div style={{
|
||||||
|
marginBottom: '20px',
|
||||||
|
borderRadius: '8px',
|
||||||
|
overflow: 'hidden',
|
||||||
|
boxShadow: '0 4px 12px rgba(0, 0, 0, 0.08)'
|
||||||
|
}}>
|
||||||
<Collapse defaultActiveKey={['sendMessageTrace']} expandIconPosition="right">
|
<Collapse defaultActiveKey={['sendMessageTrace']} expandIconPosition="right">
|
||||||
<Panel header={<Typography.Title level={3} style={{ margin: 0, color: '#333' }}>{t.SEND_MESSAGE_TRACE}</Typography.Title>} key="sendMessageTrace">
|
<Panel header={<Typography.Title level={3} style={{
|
||||||
|
margin: 0,
|
||||||
|
color: '#333'
|
||||||
|
}}>{t.SEND_MESSAGE_TRACE}</Typography.Title>} key="sendMessageTrace">
|
||||||
{!producerNode ? (
|
{!producerNode ? (
|
||||||
<Paragraph style={{ padding: '16px', textAlign: 'center', color: '#666' }}>{t.NO_PRODUCER_TRACE_DATA}</Paragraph>
|
<Paragraph style={{
|
||||||
|
padding: '16px',
|
||||||
|
textAlign: 'center',
|
||||||
|
color: '#666'
|
||||||
|
}}>{t.NO_PRODUCER_TRACE_DATA}</Paragraph>
|
||||||
) : (
|
) : (
|
||||||
<div style={{ padding: '16px', backgroundColor: '#fff' }}>
|
<div style={{padding: '16px', backgroundColor: '#fff'}}>
|
||||||
<Typography.Title level={4} style={{ marginBottom: '20px' }}>
|
<Typography.Title level={4} style={{marginBottom: '20px'}}>
|
||||||
{t.SEND_MESSAGE_INFO} : ( {t.MESSAGE_ID} <Text strong copyable>{producerNode.msgId}</Text> )
|
{t.SEND_MESSAGE_INFO} : ( {t.MESSAGE_ID} <Text strong
|
||||||
|
copyable>{producerNode.msgId}</Text> )
|
||||||
</Typography.Title>
|
</Typography.Title>
|
||||||
<Form layout="vertical" colon={false}>
|
<Form layout="vertical" colon={false}>
|
||||||
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(280px, 1fr))', gap: '16px' }}>
|
<div style={{
|
||||||
|
display: 'grid',
|
||||||
|
gridTemplateColumns: 'repeat(auto-fill, minmax(280px, 1fr))',
|
||||||
|
gap: '16px'
|
||||||
|
}}>
|
||||||
<Form.Item label={<Text strong>{t.TOPIC}</Text>}>
|
<Form.Item label={<Text strong>{t.TOPIC}</Text>}>
|
||||||
<Input value={producerNode.topic} readOnly />
|
<Input value={producerNode.topic} readOnly/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label={<Text strong>{t.PRODUCER_GROUP}</Text>}>
|
<Form.Item label={<Text strong>{t.PRODUCER_GROUP}</Text>}>
|
||||||
<Input value={producerNode.groupName} readOnly />
|
<Input value={producerNode.groupName} readOnly/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label={<Text strong>{t.MESSAGE_KEY}</Text>}>
|
<Form.Item label={<Text strong>{t.MESSAGE_KEY}</Text>}>
|
||||||
<Input value={producerNode.keys} readOnly />
|
<Input value={producerNode.keys} readOnly/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label={<Text strong>{t.TAG}</Text>}>
|
<Form.Item label={<Text strong>{t.TAG}</Text>}>
|
||||||
<Input value={producerNode.tags} readOnly />
|
<Input value={producerNode.tags} readOnly/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
<Form.Item label={<Text strong>{t.BEGIN_TIMESTAMP}</Text>}>
|
<Form.Item label={<Text strong>{t.BEGIN_TIMESTAMP}</Text>}>
|
||||||
<Input value={moment(producerNode.traceNode.beginTimestamp).format('YYYY-MM-DD HH:mm:ss.SSS')} readOnly />
|
<Input
|
||||||
|
value={moment(producerNode.traceNode.beginTimestamp).format('YYYY-MM-DD HH:mm:ss.SSS')}
|
||||||
|
readOnly/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label={<Text strong>{t.END_TIMESTAMP}</Text>}>
|
<Form.Item label={<Text strong>{t.END_TIMESTAMP}</Text>}>
|
||||||
<Input value={moment(producerNode.traceNode.endTimestamp).format('YYYY-MM-DD HH:mm:ss.SSS')} readOnly />
|
<Input
|
||||||
|
value={moment(producerNode.traceNode.endTimestamp).format('YYYY-MM-DD HH:mm:ss.SSS')}
|
||||||
|
readOnly/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label={<Text strong>{t.COST_TIME}</Text>}>
|
<Form.Item label={<Text strong>{t.COST_TIME}</Text>}>
|
||||||
<Input value={`${producerNode.traceNode.costTime === 0 ? '<1' : producerNode.traceNode.costTime}ms`} readOnly />
|
<Input
|
||||||
|
value={`${producerNode.traceNode.costTime === 0 ? '<1' : producerNode.traceNode.costTime}ms`}
|
||||||
|
readOnly/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label={<Text strong>{t.MSG_TYPE}</Text>}>
|
<Form.Item label={<Text strong>{t.MSG_TYPE}</Text>}>
|
||||||
<Input value={producerNode.traceNode.msgType} readOnly />
|
<Input value={producerNode.traceNode.msgType} readOnly/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
<Form.Item label={<Text strong>{t.CLIENT_HOST}</Text>}>
|
<Form.Item label={<Text strong>{t.CLIENT_HOST}</Text>}>
|
||||||
<Input value={producerNode.traceNode.clientHost} readOnly />
|
<Input value={producerNode.traceNode.clientHost} readOnly/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label={<Text strong>{t.STORE_HOST}</Text>}>
|
<Form.Item label={<Text strong>{t.STORE_HOST}</Text>}>
|
||||||
<Input value={producerNode.traceNode.storeHost} readOnly />
|
<Input value={producerNode.traceNode.storeHost} readOnly/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label={<Text strong>{t.RETRY_TIMES}</Text>}>
|
<Form.Item label={<Text strong>{t.RETRY_TIMES}</Text>}>
|
||||||
<Input value={producerNode.traceNode.retryTimes} readOnly />
|
<Input value={producerNode.traceNode.retryTimes} readOnly/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label={<Text strong>{t.OFFSET_MSG_ID}</Text>}>
|
<Form.Item label={<Text strong>{t.OFFSET_MSG_ID}</Text>}>
|
||||||
<Input value={producerNode.offSetMsgId} readOnly />
|
<Input value={producerNode.offSetMsgId} readOnly/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</div>
|
</div>
|
||||||
</Form>
|
</Form>
|
||||||
|
|
||||||
{producerNode.transactionNodeList && producerNode.transactionNodeList.length > 0 && (
|
{producerNode.transactionNodeList && producerNode.transactionNodeList.length > 0 && (
|
||||||
<div style={{ marginTop: '30px' }}>
|
<div style={{marginTop: '30px'}}>
|
||||||
<Typography.Title level={4} style={{ marginBottom: '15px' }}>{t.CHECK_TRANSACTION_INFO}:</Typography.Title>
|
<Typography.Title level={4}
|
||||||
|
style={{marginBottom: '15px'}}>{t.CHECK_TRANSACTION_INFO}:</Typography.Title>
|
||||||
<Table
|
<Table
|
||||||
columns={transactionColumns}
|
columns={transactionColumns}
|
||||||
dataSource={producerNode.transactionNodeList}
|
dataSource={producerNode.transactionNodeList}
|
||||||
@@ -418,7 +506,7 @@ const MessageTraceDetailViewDialog = ({ ngDialogData }) => {
|
|||||||
bordered
|
bordered
|
||||||
pagination={false}
|
pagination={false}
|
||||||
size="middle"
|
size="middle"
|
||||||
scroll={{ x: 'max-content' }}
|
scroll={{x: 'max-content'}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -428,22 +516,31 @@ const MessageTraceDetailViewDialog = ({ ngDialogData }) => {
|
|||||||
</Collapse>
|
</Collapse>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style={{ borderRadius: '8px', overflow: 'hidden', boxShadow: '0 4px 12px rgba(0, 0, 0, 0.08)' }}>
|
<div style={{borderRadius: '8px', overflow: 'hidden', boxShadow: '0 4px 12px rgba(0, 0, 0, 0.08)'}}>
|
||||||
<Collapse defaultActiveKey={['consumeMessageTrace']} expandIconPosition="right">
|
<Collapse defaultActiveKey={['consumeMessageTrace']} expandIconPosition="right">
|
||||||
<Panel header={<Typography.Title level={3} style={{ margin: 0, color: '#333' }}>{t.CONSUME_MESSAGE_TRACE}</Typography.Title>} key="consumeMessageTrace">
|
<Panel header={<Typography.Title level={3} style={{
|
||||||
|
margin: 0,
|
||||||
|
color: '#333'
|
||||||
|
}}>{t.CONSUME_MESSAGE_TRACE}</Typography.Title>} key="consumeMessageTrace">
|
||||||
{subscriptionNodeList.length === 0 ? (
|
{subscriptionNodeList.length === 0 ? (
|
||||||
<Paragraph style={{ padding: '16px', textAlign: 'center', color: '#666' }}>{t.NO_CONSUMER_TRACE_DATA}</Paragraph>
|
<Paragraph style={{
|
||||||
|
padding: '16px',
|
||||||
|
textAlign: 'center',
|
||||||
|
color: '#666'
|
||||||
|
}}>{t.NO_CONSUMER_TRACE_DATA}</Paragraph>
|
||||||
) : (
|
) : (
|
||||||
<div style={{ padding: '16px', backgroundColor: '#fff' }}>
|
<div style={{padding: '16px', backgroundColor: '#fff'}}>
|
||||||
{subscriptionNodeList.map(subscriptionNode => (
|
{subscriptionNodeList.map(subscriptionNode => (
|
||||||
<Collapse
|
<Collapse
|
||||||
key={subscriptionNode.subscriptionGroup}
|
key={subscriptionNode.subscriptionGroup}
|
||||||
style={{ marginBottom: '10px', border: '1px solid #e0e0e0', borderRadius: '4px' }}
|
style={{marginBottom: '10px', border: '1px solid #e0e0e0', borderRadius: '4px'}}
|
||||||
defaultActiveKey={[subscriptionNode.subscriptionGroup]}
|
defaultActiveKey={[subscriptionNode.subscriptionGroup]}
|
||||||
ghost
|
ghost
|
||||||
>
|
>
|
||||||
<Panel
|
<Panel
|
||||||
header={<Typography.Title level={4} style={{ margin: 0 }}>{t.SUBSCRIPTION_GROUP}: <Text strong>{subscriptionNode.subscriptionGroup}</Text></Typography.Title>}
|
header={<Typography.Title level={4}
|
||||||
|
style={{margin: 0}}>{t.SUBSCRIPTION_GROUP}: <Text
|
||||||
|
strong>{subscriptionNode.subscriptionGroup}</Text></Typography.Title>}
|
||||||
key={subscriptionNode.subscriptionGroup}
|
key={subscriptionNode.subscriptionGroup}
|
||||||
>
|
>
|
||||||
<Table
|
<Table
|
||||||
@@ -453,7 +550,7 @@ const MessageTraceDetailViewDialog = ({ ngDialogData }) => {
|
|||||||
bordered
|
bordered
|
||||||
pagination={false}
|
pagination={false}
|
||||||
size="middle"
|
size="middle"
|
||||||
scroll={{ x: 'max-content' }}
|
scroll={{x: 'max-content'}}
|
||||||
/>
|
/>
|
||||||
</Panel>
|
</Panel>
|
||||||
</Collapse>
|
</Collapse>
|
||||||
|
@@ -16,29 +16,29 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import React, {useEffect, useState} from 'react';
|
import React, {useEffect, useState} from 'react';
|
||||||
import { Layout, Menu, Dropdown, Button, Drawer, Grid, Space } from 'antd';
|
import {Button, Drawer, Dropdown, Grid, Layout, Menu, Space} from 'antd';
|
||||||
import {GlobalOutlined, DownOutlined, UserOutlined, MenuOutlined, BgColorsOutlined} from '@ant-design/icons';
|
import {BgColorsOutlined, DownOutlined, GlobalOutlined, MenuOutlined, UserOutlined} from '@ant-design/icons';
|
||||||
import { useLocation, useNavigate } from 'react-router-dom';
|
import {useLocation, useNavigate} from 'react-router-dom';
|
||||||
import { useLanguage } from '../i18n/LanguageContext';
|
import {useLanguage} from '../i18n/LanguageContext';
|
||||||
import {useTheme} from "../store/context/ThemeContext";
|
import {useTheme} from "../store/context/ThemeContext";
|
||||||
import {remoteApi} from "../api/remoteApi/remoteApi";
|
import {remoteApi} from "../api/remoteApi/remoteApi";
|
||||||
|
|
||||||
const { Header } = Layout;
|
const {Header} = Layout;
|
||||||
const { useBreakpoint } = Grid; // Used to determine screen breakpoints
|
const {useBreakpoint} = Grid; // Used to determine screen breakpoints
|
||||||
|
|
||||||
const Navbar = ({ rmqVersion = true, showAcl = true}) => {
|
const Navbar = ({rmqVersion = true, showAcl = true}) => {
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { lang, setLang, t } = useLanguage();
|
const {lang, setLang, t} = useLanguage();
|
||||||
const screens = useBreakpoint(); // Get current screen size breakpoints
|
const screens = useBreakpoint(); // Get current screen size breakpoints
|
||||||
const { currentThemeName, setCurrentThemeName } = useTheme();
|
const {currentThemeName, setCurrentThemeName} = useTheme();
|
||||||
const [userName, setUserName] = useState(null);
|
const [userName, setUserName] = useState(null);
|
||||||
const [drawerVisible, setDrawerVisible] = useState(false); // Controls drawer visibility
|
const [drawerVisible, setDrawerVisible] = useState(false); // Controls drawer visibility
|
||||||
|
|
||||||
// Get selected menu item key based on current route path
|
// Get selected menu item key based on current route path
|
||||||
const getPath = () => location.pathname.replace('/', '');
|
const getPath = () => location.pathname.replace('/', '');
|
||||||
|
|
||||||
const handleMenuClick = ({ key }) => {
|
const handleMenuClick = ({key}) => {
|
||||||
navigate(`/${key}`);
|
navigate(`/${key}`);
|
||||||
setDrawerVisible(false); // Close drawer after clicking a menu item
|
setDrawerVisible(false); // Close drawer after clicking a menu item
|
||||||
};
|
};
|
||||||
@@ -63,13 +63,13 @@ const Navbar = ({ rmqVersion = true, showAcl = true}) => {
|
|||||||
const storedUsername = window.localStorage.getItem("username");
|
const storedUsername = window.localStorage.getItem("username");
|
||||||
if (storedUsername) {
|
if (storedUsername) {
|
||||||
setUserName(storedUsername);
|
setUserName(storedUsername);
|
||||||
}else {
|
} else {
|
||||||
setUserName(null);
|
setUserName(null);
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const langMenu = (
|
const langMenu = (
|
||||||
<Menu onClick={({ key }) => setLang(key)}>
|
<Menu onClick={({key}) => setLang(key)}>
|
||||||
<Menu.Item key="en">{t.ENGLISH}</Menu.Item>
|
<Menu.Item key="en">{t.ENGLISH}</Menu.Item>
|
||||||
<Menu.Item key="zh">{t.CHINESE}</Menu.Item>
|
<Menu.Item key="zh">{t.CHINESE}</Menu.Item>
|
||||||
</Menu>
|
</Menu>
|
||||||
@@ -82,7 +82,7 @@ const Navbar = ({ rmqVersion = true, showAcl = true}) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const themeMenu = (
|
const themeMenu = (
|
||||||
<Menu onClick={({ key }) => setCurrentThemeName(key)}>
|
<Menu onClick={({key}) => setCurrentThemeName(key)}>
|
||||||
<Menu.Item key="default">{t.BLUE} ({t.DEFAULT})</Menu.Item>
|
<Menu.Item key="default">{t.BLUE} ({t.DEFAULT})</Menu.Item>
|
||||||
<Menu.Item key="pink">{t.PINK}</Menu.Item>
|
<Menu.Item key="pink">{t.PINK}</Menu.Item>
|
||||||
<Menu.Item key="green">{t.GREEN}</Menu.Item>
|
<Menu.Item key="green">{t.GREEN}</Menu.Item>
|
||||||
@@ -92,17 +92,17 @@ const Navbar = ({ rmqVersion = true, showAcl = true}) => {
|
|||||||
|
|
||||||
// Menu item configuration
|
// Menu item configuration
|
||||||
const menuItems = [
|
const menuItems = [
|
||||||
{ key: 'ops', label: t.OPS },
|
{key: 'ops', label: t.OPS},
|
||||||
...(rmqVersion ? [{ key: 'proxy', label: t.PROXY }] : []),
|
...(rmqVersion ? [{key: 'proxy', label: t.PROXY}] : []),
|
||||||
{ key: '', label: t.DASHBOARD }, // Dashboard corresponds to root path
|
{key: '', label: t.DASHBOARD}, // Dashboard corresponds to root path
|
||||||
{ key: 'cluster', label: t.CLUSTER },
|
{key: 'cluster', label: t.CLUSTER},
|
||||||
{ key: 'topic', label: t.TOPIC },
|
{key: 'topic', label: t.TOPIC},
|
||||||
{ key: 'consumer', label: t.CONSUMER },
|
{key: 'consumer', label: t.CONSUMER},
|
||||||
{ key: 'producer', label: t.PRODUCER },
|
{key: 'producer', label: t.PRODUCER},
|
||||||
{ key: 'message', label: t.MESSAGE },
|
{key: 'message', label: t.MESSAGE},
|
||||||
{ key: 'dlqMessage', label: t.DLQ_MESSAGE },
|
{key: 'dlqMessage', label: t.DLQ_MESSAGE},
|
||||||
{ key: 'messageTrace', label: t.MESSAGETRACE },
|
{key: 'messageTrace', label: t.MESSAGETRACE},
|
||||||
...(showAcl ? [{ key: 'acl', label: t.WHITE_LIST }] : []),
|
...(showAcl ? [{key: 'acl', label: t.ACL_MANAGEMENT}] : []),
|
||||||
];
|
];
|
||||||
|
|
||||||
// Determine if it's a small screen (e.g., less than md)
|
// Determine if it's a small screen (e.g., less than md)
|
||||||
@@ -120,7 +120,7 @@ const Navbar = ({ rmqVersion = true, showAcl = true}) => {
|
|||||||
padding: isExtraSmallScreen ? '0 16px' : '0 24px', // Smaller padding on extra small screens
|
padding: isExtraSmallScreen ? '0 16px' : '0 24px', // Smaller padding on extra small screens
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className="navbar-left" style={{ display: 'flex', alignItems: 'center' }}>
|
<div className="navbar-left" style={{display: 'flex', alignItems: 'center'}}>
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
fontWeight: 'bold',
|
fontWeight: 'bold',
|
||||||
@@ -141,33 +141,33 @@ const Navbar = ({ rmqVersion = true, showAcl = true}) => {
|
|||||||
mode="horizontal"
|
mode="horizontal"
|
||||||
items={menuItems}
|
items={menuItems}
|
||||||
theme="dark" // Use dark theme to match Header background
|
theme="dark" // Use dark theme to match Header background
|
||||||
style={{ flex: 1, minWidth: 0 }} // Allow menu items to adapt width
|
style={{flex: 1, minWidth: 0}} // Allow menu items to adapt width
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Space size={isExtraSmallScreen ? 8 : 16} > {/* Adjust spacing for buttons */}
|
<Space size={isExtraSmallScreen ? 8 : 16}> {/* Adjust spacing for buttons */}
|
||||||
{/* Theme switch button */}
|
{/* Theme switch button */}
|
||||||
<Dropdown overlay={themeMenu}>
|
<Dropdown overlay={themeMenu}>
|
||||||
<Button icon={<BgColorsOutlined />} size="small">
|
<Button icon={<BgColorsOutlined/>} size="small">
|
||||||
{!isExtraSmallScreen && `${t.TOPIC}: ${currentThemeName}`}
|
{!isExtraSmallScreen && `${t.TOPIC}: ${currentThemeName}`}
|
||||||
<DownOutlined />
|
<DownOutlined/>
|
||||||
</Button>
|
</Button>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
<Dropdown overlay={langMenu}>
|
<Dropdown overlay={langMenu}>
|
||||||
<Button icon={<GlobalOutlined />} size="small">
|
<Button icon={<GlobalOutlined/>} size="small">
|
||||||
{!isExtraSmallScreen && t.CHANGE_LANG} {/* Hide text on extra small screens */}
|
{!isExtraSmallScreen && t.CHANGE_LANG} {/* Hide text on extra small screens */}
|
||||||
<DownOutlined />
|
<DownOutlined/>
|
||||||
</Button>
|
</Button>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
|
|
||||||
{userName && (
|
{userName && (
|
||||||
<Dropdown overlay={userMenu}>
|
<Dropdown overlay={userMenu}>
|
||||||
{/* 使用一个可点击的元素作为 Dropdown 的唯一子元素 */}
|
{/* 使用一个可点击的元素作为 Dropdown 的唯一子元素 */}
|
||||||
<a onClick={e => e.preventDefault()} style={{ display: 'flex', alignItems: 'center' }}>
|
<a onClick={e => e.preventDefault()} style={{display: 'flex', alignItems: 'center'}}>
|
||||||
<UserOutlined style={{ marginRight: 8 }} /> {/* 添加一些间距 */}
|
<UserOutlined style={{marginRight: 8}}/> {/* 添加一些间距 */}
|
||||||
{userName}
|
{userName}
|
||||||
<DownOutlined style={{ marginLeft: 8 }} />
|
<DownOutlined style={{marginLeft: 8}}/>
|
||||||
</a>
|
</a>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
)}
|
)}
|
||||||
@@ -175,9 +175,9 @@ const Navbar = ({ rmqVersion = true, showAcl = true}) => {
|
|||||||
{isSmallScreen && ( // Display hamburger icon on small screens
|
{isSmallScreen && ( // Display hamburger icon on small screens
|
||||||
<Button
|
<Button
|
||||||
type="primary"
|
type="primary"
|
||||||
icon={<MenuOutlined />}
|
icon={<MenuOutlined/>}
|
||||||
onClick={() => setDrawerVisible(true)}
|
onClick={() => setDrawerVisible(true)}
|
||||||
style={{ marginLeft: isExtraSmallScreen ? 8 : 16 }} // Adjust margin for hamburger icon
|
style={{marginLeft: isExtraSmallScreen ? 8 : 16}} // Adjust margin for hamburger icon
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Space>
|
</Space>
|
||||||
@@ -192,7 +192,7 @@ const Navbar = ({ rmqVersion = true, showAcl = true}) => {
|
|||||||
open={drawerVisible}
|
open={drawerVisible}
|
||||||
// If you want the Drawer's background to match the Menu's background color, you can set bodyStyle like this
|
// If you want the Drawer's background to match the Menu's background color, you can set bodyStyle like this
|
||||||
// or set components.Drawer.colorBgElevated in theme.js, etc.
|
// or set components.Drawer.colorBgElevated in theme.js, etc.
|
||||||
bodyStyle={{ padding: 0, backgroundColor: '#1c324a' }} // Set Drawer body background to dark
|
bodyStyle={{padding: 0, backgroundColor: '#1c324a'}} // Set Drawer body background to dark
|
||||||
width={200} // Set drawer width
|
width={200} // Set drawer width
|
||||||
>
|
>
|
||||||
<Menu
|
<Menu
|
||||||
@@ -201,7 +201,7 @@ const Navbar = ({ rmqVersion = true, showAcl = true}) => {
|
|||||||
mode="inline" // Use vertical menu in drawer
|
mode="inline" // Use vertical menu in drawer
|
||||||
items={menuItems}
|
items={menuItems}
|
||||||
theme="dark"
|
theme="dark"
|
||||||
style={{ height: '100%', borderRight: 0 }} // Ensure menu fills the drawer
|
style={{height: '100%', borderRight: 0}} // Ensure menu fills the drawer
|
||||||
/>
|
/>
|
||||||
</Drawer>
|
</Drawer>
|
||||||
</Header>
|
</Header>
|
||||||
|
@@ -15,23 +15,23 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Input, Select, Tag, Space } from 'antd';
|
import {Input, Select, Space, Tag} from 'antd';
|
||||||
import { PlusOutlined } from '@ant-design/icons';
|
import {PlusOutlined} from '@ant-design/icons';
|
||||||
import React, { useState } from 'react';
|
import React, {useState} from 'react';
|
||||||
|
|
||||||
const { Option } = Select;
|
const {Option} = Select;
|
||||||
|
|
||||||
// 资源类型枚举
|
// 资源类型枚举
|
||||||
const resourceTypes = [
|
const resourceTypes = [
|
||||||
{ value: 0, label: 'Unknown', prefix: 'UNKNOWN' },
|
{value: 0, label: 'Unknown', prefix: 'UNKNOWN'},
|
||||||
{ value: 1, label: 'Any', prefix: 'ANY' },
|
{value: 1, label: 'Any', prefix: 'ANY'},
|
||||||
{ value: 2, label: 'Cluster', prefix: 'CLUSTER' },
|
{value: 2, label: 'Cluster', prefix: 'CLUSTER'},
|
||||||
{ value: 3, label: 'Namespace', prefix: 'NAMESPACE' },
|
{value: 3, label: 'Namespace', prefix: 'NAMESPACE'},
|
||||||
{ value: 4, label: 'Topic', prefix: 'TOPIC' },
|
{value: 4, label: 'Topic', prefix: 'TOPIC'},
|
||||||
{ value: 5, label: 'Group', prefix: 'GROUP' },
|
{value: 5, label: 'Group', prefix: 'GROUP'},
|
||||||
];
|
];
|
||||||
|
|
||||||
const ResourceInput = ({ value = [], onChange }) => {
|
const ResourceInput = ({value = [], onChange}) => {
|
||||||
// 确保 value 始终是数组
|
// 确保 value 始终是数组
|
||||||
const safeValue = Array.isArray(value) ? value : [];
|
const safeValue = Array.isArray(value) ? value : [];
|
||||||
|
|
||||||
@@ -96,7 +96,7 @@ const ResourceInput = ({ value = [], onChange }) => {
|
|||||||
<Space>
|
<Space>
|
||||||
<Select
|
<Select
|
||||||
value={selectedType}
|
value={selectedType}
|
||||||
style={{ width: 120 }}
|
style={{width: 120}}
|
||||||
onChange={handleTypeChange}
|
onChange={handleTypeChange}
|
||||||
>
|
>
|
||||||
{resourceTypes.map(type => (
|
{resourceTypes.map(type => (
|
||||||
@@ -107,7 +107,7 @@ const ResourceInput = ({ value = [], onChange }) => {
|
|||||||
</Select>
|
</Select>
|
||||||
<Input
|
<Input
|
||||||
ref={inputRef}
|
ref={inputRef}
|
||||||
style={{ width: 180 }}
|
style={{width: 180}}
|
||||||
value={resourceName}
|
value={resourceName}
|
||||||
onChange={handleNameChange}
|
onChange={handleNameChange}
|
||||||
onPressEnter={handleAddResource}
|
onPressEnter={handleAddResource}
|
||||||
@@ -116,8 +116,8 @@ const ResourceInput = ({ value = [], onChange }) => {
|
|||||||
/>
|
/>
|
||||||
</Space>
|
</Space>
|
||||||
) : (
|
) : (
|
||||||
<Tag onClick={showInput} style={{ background: '#fff', borderStyle: 'dashed' }}>
|
<Tag onClick={showInput} style={{background: '#fff', borderStyle: 'dashed'}}>
|
||||||
<PlusOutlined /> 添加资源
|
<PlusOutlined/> 添加资源
|
||||||
</Tag>
|
</Tag>
|
||||||
)}
|
)}
|
||||||
</Space>
|
</Space>
|
||||||
|
@@ -15,27 +15,27 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Input, Select } from 'antd';
|
import {Input, Select} from 'antd';
|
||||||
import React, { useState, useEffect } from 'react';
|
import React, {useEffect, useState} from 'react';
|
||||||
|
|
||||||
const { Option } = Select;
|
const {Option} = Select;
|
||||||
|
|
||||||
// Subject 类型枚举
|
// Subject 类型枚举
|
||||||
const subjectTypes = [
|
const subjectTypes = [
|
||||||
{ value: 'User', label: 'User' },
|
{value: 'User', label: 'User'},
|
||||||
];
|
];
|
||||||
|
|
||||||
const SubjectInput = ({ value, onChange, disabled }) => {
|
const SubjectInput = ({value, onChange, disabled}) => {
|
||||||
// 解析传入的 value,将其拆分为 type 和 name
|
// 解析传入的 value,将其拆分为 type 和 name
|
||||||
const parseValue = (val) => {
|
const parseValue = (val) => {
|
||||||
if (!val || typeof val !== 'string') {
|
if (!val || typeof val !== 'string') {
|
||||||
return { type: subjectTypes[0].value, name: '' }; // 默认值
|
return {type: subjectTypes[0].value, name: ''}; // 默认值
|
||||||
}
|
}
|
||||||
const parts = val.split(':');
|
const parts = val.split(':');
|
||||||
if (parts.length === 2 && subjectTypes.some(t => t.value === parts[0])) {
|
if (parts.length === 2 && subjectTypes.some(t => t.value === parts[0])) {
|
||||||
return { type: parts[0], name: parts[1] };
|
return {type: parts[0], name: parts[1]};
|
||||||
}
|
}
|
||||||
return { type: subjectTypes[0].value, name: val }; // 如果格式不匹配,将整个值作为 name,类型设为默认
|
return {type: subjectTypes[0].value, name: val}; // 如果格式不匹配,将整个值作为 name,类型设为默认
|
||||||
};
|
};
|
||||||
|
|
||||||
const [currentType, setCurrentType] = useState(() => parseValue(value).type);
|
const [currentType, setCurrentType] = useState(() => parseValue(value).type);
|
||||||
@@ -76,7 +76,7 @@ const SubjectInput = ({ value, onChange, disabled }) => {
|
|||||||
return (
|
return (
|
||||||
<Input.Group compact>
|
<Input.Group compact>
|
||||||
<Select
|
<Select
|
||||||
style={{ width: '30%' }}
|
style={{width: '30%'}}
|
||||||
value={currentType}
|
value={currentType}
|
||||||
onChange={onTypeChange}
|
onChange={onTypeChange}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
@@ -88,7 +88,7 @@ const SubjectInput = ({ value, onChange, disabled }) => {
|
|||||||
))}
|
))}
|
||||||
</Select>
|
</Select>
|
||||||
<Input
|
<Input
|
||||||
style={{ width: '70%' }}
|
style={{width: '70%'}}
|
||||||
value={currentName}
|
value={currentName}
|
||||||
onChange={onNameChange}
|
onChange={onNameChange}
|
||||||
placeholder="请输入名称 (例如: yourUsername)"
|
placeholder="请输入名称 (例如: yourUsername)"
|
||||||
|
@@ -15,13 +15,13 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { useState, useEffect } from 'react';
|
import React, {useEffect, useState} from 'react';
|
||||||
import { Modal, Table, Spin } from 'antd';
|
import {Modal, Spin, Table} from 'antd';
|
||||||
import { remoteApi } from '../../api/remoteApi/remoteApi';
|
import {remoteApi} from '../../api/remoteApi/remoteApi';
|
||||||
import { useLanguage } from '../../i18n/LanguageContext';
|
import {useLanguage} from '../../i18n/LanguageContext';
|
||||||
|
|
||||||
const ClientInfoModal = ({ visible, group, address, onCancel }) => {
|
const ClientInfoModal = ({visible, group, address, onCancel}) => {
|
||||||
const { t } = useLanguage();
|
const {t} = useLanguage();
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [connectionData, setConnectionData] = useState(null);
|
const [connectionData, setConnectionData] = useState(null);
|
||||||
const [subscriptionData, setSubscriptionData] = useState(null);
|
const [subscriptionData, setSubscriptionData] = useState(null);
|
||||||
@@ -46,15 +46,15 @@ const ClientInfoModal = ({ visible, group, address, onCancel }) => {
|
|||||||
}, [visible, group, address]);
|
}, [visible, group, address]);
|
||||||
|
|
||||||
const connectionColumns = [
|
const connectionColumns = [
|
||||||
{ title: 'ClientId', dataIndex: 'clientId' },
|
{title: 'ClientId', dataIndex: 'clientId'},
|
||||||
{ title: 'ClientAddr', dataIndex: 'clientAddr' },
|
{title: 'ClientAddr', dataIndex: 'clientAddr'},
|
||||||
{ title: 'Language', dataIndex: 'language' },
|
{title: 'Language', dataIndex: 'language'},
|
||||||
{ title: 'Version', dataIndex: 'versionDesc' },
|
{title: 'Version', dataIndex: 'versionDesc'},
|
||||||
];
|
];
|
||||||
|
|
||||||
const subscriptionColumns = [
|
const subscriptionColumns = [
|
||||||
{ title: 'Topic', dataIndex: 'topic' },
|
{title: 'Topic', dataIndex: 'topic'},
|
||||||
{ title: 'SubExpression', dataIndex: 'subString' },
|
{title: 'SubExpression', dataIndex: 'subString'},
|
||||||
];
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -88,7 +88,7 @@ const ClientInfoModal = ({ visible, group, address, onCancel }) => {
|
|||||||
rowKey="topic"
|
rowKey="topic"
|
||||||
pagination={false}
|
pagination={false}
|
||||||
locale={{
|
locale={{
|
||||||
emptyText: loading ? <Spin size="small" /> : t.NO_DATA
|
emptyText: loading ? <Spin size="small"/> : t.NO_DATA
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<p>ConsumeType: {connectionData.consumeType}</p>
|
<p>ConsumeType: {connectionData.consumeType}</p>
|
||||||
|
@@ -15,13 +15,23 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, {useEffect, useState} from 'react';
|
||||||
import { Button, Descriptions, Form, Input, Select, Switch, message } from 'antd';
|
import {Button, Descriptions, Form, Input, message, Select, Switch} from 'antd';
|
||||||
import { remoteApi } from '../../api/remoteApi/remoteApi'; // 确保路径正确
|
import {remoteApi} from '../../api/remoteApi/remoteApi'; // 确保路径正确
|
||||||
|
|
||||||
const { Option } = Select;
|
const {Option} = Select;
|
||||||
|
|
||||||
const ConsumerConfigItem = ({ initialConfig, isAddConfig, group, brokerName, allBrokerList, allClusterNames,onCancel, onSuccess, t }) => {
|
const ConsumerConfigItem = ({
|
||||||
|
initialConfig,
|
||||||
|
isAddConfig,
|
||||||
|
group,
|
||||||
|
brokerName,
|
||||||
|
allBrokerList,
|
||||||
|
allClusterNames,
|
||||||
|
onCancel,
|
||||||
|
onSuccess,
|
||||||
|
t
|
||||||
|
}) => {
|
||||||
const [form] = Form.useForm();
|
const [form] = Form.useForm();
|
||||||
const [currentBrokerName, setCurrentBrokerName] = useState(brokerName);
|
const [currentBrokerName, setCurrentBrokerName] = useState(brokerName);
|
||||||
|
|
||||||
|
@@ -34,7 +34,7 @@ const ConsumerConfigModal = ({visible, isAddConfig, group, onCancel, setIsAddCon
|
|||||||
setLoading(true);
|
setLoading(true);
|
||||||
try {
|
try {
|
||||||
// Fetch cluster list for broker names and cluster names
|
// Fetch cluster list for broker names and cluster names
|
||||||
if(isAddConfig) {
|
if (isAddConfig) {
|
||||||
const clusterResponse = await remoteApi.getClusterList();
|
const clusterResponse = await remoteApi.getClusterList();
|
||||||
if (clusterResponse.status === 0 && clusterResponse.data) {
|
if (clusterResponse.status === 0 && clusterResponse.data) {
|
||||||
const clusterInfo = clusterResponse.data.clusterInfo;
|
const clusterInfo = clusterResponse.data.clusterInfo;
|
||||||
|
@@ -15,13 +15,13 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { useState, useEffect } from 'react';
|
import React, {useEffect, useState} from 'react';
|
||||||
import { Modal, Table, Spin } from 'antd';
|
import {Modal, Spin, Table} from 'antd';
|
||||||
import { remoteApi } from '../../api/remoteApi/remoteApi';
|
import {remoteApi} from '../../api/remoteApi/remoteApi';
|
||||||
import { useLanguage } from '../../i18n/LanguageContext';
|
import {useLanguage} from '../../i18n/LanguageContext';
|
||||||
|
|
||||||
const ConsumerDetailModal = ({ visible, group, address, onCancel }) => {
|
const ConsumerDetailModal = ({visible, group, address, onCancel}) => {
|
||||||
const { t } = useLanguage();
|
const {t} = useLanguage();
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [details, setDetails] = useState([]);
|
const [details, setDetails] = useState([]);
|
||||||
|
|
||||||
@@ -44,12 +44,12 @@ const ConsumerDetailModal = ({ visible, group, address, onCancel }) => {
|
|||||||
}, [visible, group, address]);
|
}, [visible, group, address]);
|
||||||
|
|
||||||
const queueColumns = [
|
const queueColumns = [
|
||||||
{ title: 'Broker', dataIndex: 'brokerName' },
|
{title: 'Broker', dataIndex: 'brokerName'},
|
||||||
{ title: 'Queue', dataIndex: 'queueId' },
|
{title: 'Queue', dataIndex: 'queueId'},
|
||||||
{ title: 'BrokerOffset', dataIndex: 'brokerOffset' },
|
{title: 'BrokerOffset', dataIndex: 'brokerOffset'},
|
||||||
{ title: 'ConsumerOffset', dataIndex: 'consumerOffset' },
|
{title: 'ConsumerOffset', dataIndex: 'consumerOffset'},
|
||||||
{ title: 'DiffTotal', dataIndex: 'diffTotal' },
|
{title: 'DiffTotal', dataIndex: 'diffTotal'},
|
||||||
{ title: 'LastTimestamp', dataIndex: 'lastTimestamp' },
|
{title: 'LastTimestamp', dataIndex: 'lastTimestamp'},
|
||||||
];
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@@ -15,13 +15,13 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { useState, useEffect } from 'react';
|
import React, {useEffect, useState} from 'react';
|
||||||
import { Modal, Spin, Checkbox, Button, notification } from 'antd';
|
import {Button, Checkbox, Modal, notification, Spin} from 'antd';
|
||||||
import { remoteApi } from '../../api/remoteApi/remoteApi';
|
import {remoteApi} from '../../api/remoteApi/remoteApi';
|
||||||
import { useLanguage } from '../../i18n/LanguageContext';
|
import {useLanguage} from '../../i18n/LanguageContext';
|
||||||
|
|
||||||
const DeleteConsumerModal = ({ visible, group, onCancel, onSuccess }) => {
|
const DeleteConsumerModal = ({visible, group, onCancel, onSuccess}) => {
|
||||||
const { t } = useLanguage();
|
const {t} = useLanguage();
|
||||||
const [brokerList, setBrokerList] = useState([]);
|
const [brokerList, setBrokerList] = useState([]);
|
||||||
const [selectedBrokers, setSelectedBrokers] = useState([]);
|
const [selectedBrokers, setSelectedBrokers] = useState([]);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
@@ -48,7 +48,7 @@ const DeleteConsumerModal = ({ visible, group, onCancel, onSuccess }) => {
|
|||||||
// 处理删除提交
|
// 处理删除提交
|
||||||
const handleDelete = async () => {
|
const handleDelete = async () => {
|
||||||
if (selectedBrokers.length === 0) {
|
if (selectedBrokers.length === 0) {
|
||||||
notification.warning({ message: t.PLEASE_SELECT_BROKER });
|
notification.warning({message: t.PLEASE_SELECT_BROKER});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,7 +60,7 @@ const DeleteConsumerModal = ({ visible, group, onCancel, onSuccess }) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (response.status === 0) {
|
if (response.status === 0) {
|
||||||
notification.success({ message: t.DELETE_SUCCESS });
|
notification.success({message: t.DELETE_SUCCESS});
|
||||||
onSuccess();
|
onSuccess();
|
||||||
onCancel();
|
onCancel();
|
||||||
}
|
}
|
||||||
@@ -90,9 +90,9 @@ const DeleteConsumerModal = ({ visible, group, onCancel, onSuccess }) => {
|
|||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<Spin spinning={loading}>
|
<Spin spinning={loading}>
|
||||||
<div style={{ marginBottom: 16 }}>{t.SELECT_DELETE_BROKERS}:</div>
|
<div style={{marginBottom: 16}}>{t.SELECT_DELETE_BROKERS}:</div>
|
||||||
<Checkbox.Group
|
<Checkbox.Group
|
||||||
style={{ width: '100%' }}
|
style={{width: '100%'}}
|
||||||
value={selectedBrokers}
|
value={selectedBrokers}
|
||||||
onChange={values => setSelectedBrokers(values)}
|
onChange={values => setSelectedBrokers(values)}
|
||||||
>
|
>
|
||||||
|
@@ -15,10 +15,10 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Button, DatePicker, Form, Modal, Select } from "antd";
|
import {Button, DatePicker, Form, Modal, Select} from "antd";
|
||||||
import React, { useEffect, useState } from "react";
|
import React, {useEffect, useState} from "react";
|
||||||
|
|
||||||
const ConsumerResetOffsetDialog = ({ visible, onClose, topic, allConsumerGroupList, handleResetOffset, t }) => {
|
const ConsumerResetOffsetDialog = ({visible, onClose, topic, allConsumerGroupList, handleResetOffset, t}) => {
|
||||||
const [form] = Form.useForm();
|
const [form] = Form.useForm();
|
||||||
const [selectedConsumerGroup, setSelectedConsumerGroup] = useState([]);
|
const [selectedConsumerGroup, setSelectedConsumerGroup] = useState([]);
|
||||||
const [selectedTime, setSelectedTime] = useState(null);
|
const [selectedTime, setSelectedTime] = useState(null);
|
||||||
@@ -49,14 +49,14 @@ const ConsumerResetOffsetDialog = ({ visible, onClose, topic, allConsumerGroupLi
|
|||||||
</Button>,
|
</Button>,
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<Form form={form} layout="horizontal" labelCol={{ span: 6 }} wrapperCol={{ span: 18 }}>
|
<Form form={form} layout="horizontal" labelCol={{span: 6}} wrapperCol={{span: 18}}>
|
||||||
<Form.Item label={t.SUBSCRIPTION_GROUP} required>
|
<Form.Item label={t.SUBSCRIPTION_GROUP} required>
|
||||||
<Select
|
<Select
|
||||||
mode="multiple"
|
mode="multiple"
|
||||||
placeholder={t.SELECT_CONSUMER_GROUP}
|
placeholder={t.SELECT_CONSUMER_GROUP}
|
||||||
value={selectedConsumerGroup}
|
value={selectedConsumerGroup}
|
||||||
onChange={setSelectedConsumerGroup}
|
onChange={setSelectedConsumerGroup}
|
||||||
options={allConsumerGroupList.map(group => ({ value: group, label: group }))}
|
options={allConsumerGroupList.map(group => ({value: group, label: group}))}
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label={t.TIME} required>
|
<Form.Item label={t.TIME} required>
|
||||||
@@ -65,7 +65,7 @@ const ConsumerResetOffsetDialog = ({ visible, onClose, topic, allConsumerGroupLi
|
|||||||
format="YYYY-MM-DD HH:mm:ss"
|
format="YYYY-MM-DD HH:mm:ss"
|
||||||
value={selectedTime}
|
value={selectedTime}
|
||||||
onChange={setSelectedTime}
|
onChange={setSelectedTime}
|
||||||
style={{ width: '100%' }}
|
style={{width: '100%'}}
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Form>
|
</Form>
|
||||||
|
@@ -19,13 +19,13 @@ import moment from "moment/moment";
|
|||||||
import {Button, Modal, Table} from "antd";
|
import {Button, Modal, Table} from "antd";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
const ConsumerViewDialog = ({ visible, onClose, topic, consumerData, consumerGroupCount, t }) => {
|
const ConsumerViewDialog = ({visible, onClose, topic, consumerData, consumerGroupCount, t}) => {
|
||||||
const columns = [
|
const columns = [
|
||||||
{ title: t.BROKER, dataIndex: 'brokerName', key: 'brokerName', align: 'center' },
|
{title: t.BROKER, dataIndex: 'brokerName', key: 'brokerName', align: 'center'},
|
||||||
{ title: t.QUEUE, dataIndex: 'queueId', key: 'queueId', align: 'center' },
|
{title: t.QUEUE, dataIndex: 'queueId', key: 'queueId', align: 'center'},
|
||||||
{ title: t.CONSUMER_CLIENT, dataIndex: 'clientInfo', key: 'clientInfo', align: 'center' },
|
{title: t.CONSUMER_CLIENT, dataIndex: 'clientInfo', key: 'clientInfo', align: 'center'},
|
||||||
{ title: t.BROKER_OFFSET, dataIndex: 'brokerOffset', key: 'brokerOffset', align: 'center' },
|
{title: t.BROKER_OFFSET, dataIndex: 'brokerOffset', key: 'brokerOffset', align: 'center'},
|
||||||
{ title: t.CONSUMER_OFFSET, dataIndex: 'consumerOffset', key: 'consumerOffset', align: 'center' },
|
{title: t.CONSUMER_OFFSET, dataIndex: 'consumerOffset', key: 'consumerOffset', align: 'center'},
|
||||||
{
|
{
|
||||||
title: t.DIFF_TOTAL,
|
title: t.DIFF_TOTAL,
|
||||||
dataIndex: 'diffTotal',
|
dataIndex: 'diffTotal',
|
||||||
@@ -58,15 +58,19 @@ const ConsumerViewDialog = ({ visible, onClose, topic, consumerData, consumerGro
|
|||||||
<div>{t.NO_DATA} {t.SUBSCRIPTION_GROUP}</div>
|
<div>{t.NO_DATA} {t.SUBSCRIPTION_GROUP}</div>
|
||||||
) : (
|
) : (
|
||||||
consumerData && Object.entries(consumerData).map(([consumerGroup, consumeDetail]) => (
|
consumerData && Object.entries(consumerData).map(([consumerGroup, consumeDetail]) => (
|
||||||
<div key={consumerGroup} style={{ marginBottom: '24px' }}>
|
<div key={consumerGroup} style={{marginBottom: '24px'}}>
|
||||||
<Table
|
<Table
|
||||||
bordered
|
bordered
|
||||||
pagination={false}
|
pagination={false}
|
||||||
showHeader={false}
|
showHeader={false}
|
||||||
dataSource={[{ consumerGroup, diffTotal: consumeDetail.diffTotal, lastTimestamp: consumeDetail.lastTimestamp }]}
|
dataSource={[{
|
||||||
|
consumerGroup,
|
||||||
|
diffTotal: consumeDetail.diffTotal,
|
||||||
|
lastTimestamp: consumeDetail.lastTimestamp
|
||||||
|
}]}
|
||||||
columns={[
|
columns={[
|
||||||
{ title: t.SUBSCRIPTION_GROUP, dataIndex: 'consumerGroup', key: 'consumerGroup' },
|
{title: t.SUBSCRIPTION_GROUP, dataIndex: 'consumerGroup', key: 'consumerGroup'},
|
||||||
{ title: t.DELAY, dataIndex: 'diffTotal', key: 'diffTotal' },
|
{title: t.DELAY, dataIndex: 'diffTotal', key: 'diffTotal'},
|
||||||
{
|
{
|
||||||
title: t.LAST_CONSUME_TIME,
|
title: t.LAST_CONSUME_TIME,
|
||||||
dataIndex: 'lastTimestamp',
|
dataIndex: 'lastTimestamp',
|
||||||
@@ -76,7 +80,7 @@ const ConsumerViewDialog = ({ visible, onClose, topic, consumerData, consumerGro
|
|||||||
]}
|
]}
|
||||||
rowKey="consumerGroup"
|
rowKey="consumerGroup"
|
||||||
size="small"
|
size="small"
|
||||||
style={{ marginBottom: '12px' }}
|
style={{marginBottom: '12px'}}
|
||||||
/>
|
/>
|
||||||
<Table
|
<Table
|
||||||
bordered
|
bordered
|
||||||
|
@@ -18,7 +18,7 @@
|
|||||||
import {Button, Modal, Table} from "antd";
|
import {Button, Modal, Table} from "antd";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
const ResetOffsetResultDialog = ({ visible, onClose, result, t }) => {
|
const ResetOffsetResultDialog = ({visible, onClose, result, t}) => {
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
title="ResetResult"
|
title="ResetResult"
|
||||||
@@ -31,12 +31,12 @@ const ResetOffsetResultDialog = ({ visible, onClose, result, t }) => {
|
|||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
{result && Object.entries(result).map(([groupName, groupData]) => (
|
{result && Object.entries(result).map(([groupName, groupData]) => (
|
||||||
<div key={groupName} style={{ marginBottom: '16px', border: '1px solid #f0f0f0', padding: '10px' }}>
|
<div key={groupName} style={{marginBottom: '16px', border: '1px solid #f0f0f0', padding: '10px'}}>
|
||||||
<Table
|
<Table
|
||||||
dataSource={[{ groupName, status: groupData.status }]}
|
dataSource={[{groupName, status: groupData.status}]}
|
||||||
columns={[
|
columns={[
|
||||||
{ title: 'GroupName', dataIndex: 'groupName', key: 'groupName' },
|
{title: 'GroupName', dataIndex: 'groupName', key: 'groupName'},
|
||||||
{ title: 'State', dataIndex: 'status', key: 'status' },
|
{title: 'State', dataIndex: 'status', key: 'status'},
|
||||||
]}
|
]}
|
||||||
pagination={false}
|
pagination={false}
|
||||||
rowKey="groupName"
|
rowKey="groupName"
|
||||||
@@ -47,8 +47,8 @@ const ResetOffsetResultDialog = ({ visible, onClose, result, t }) => {
|
|||||||
<div>You Should Check It Yourself</div>
|
<div>You Should Check It Yourself</div>
|
||||||
) : (
|
) : (
|
||||||
<Table
|
<Table
|
||||||
dataSource={groupData.rollbackStatsList.map((item, index) => ({ key: index, item }))}
|
dataSource={groupData.rollbackStatsList.map((item, index) => ({key: index, item}))}
|
||||||
columns={[{ dataIndex: 'item', key: 'item' }]}
|
columns={[{dataIndex: 'item', key: 'item'}]}
|
||||||
pagination={false}
|
pagination={false}
|
||||||
rowKey="key"
|
rowKey="key"
|
||||||
size="small"
|
size="small"
|
||||||
|
@@ -15,10 +15,10 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Button, Modal, Table } from "antd";
|
import {Button, Modal, Table} from "antd";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
const RouterViewDialog = ({ visible, onClose, topic, routeData, t }) => {
|
const RouterViewDialog = ({visible, onClose, topic, routeData, t}) => {
|
||||||
const brokerColumns = [
|
const brokerColumns = [
|
||||||
{
|
{
|
||||||
title: 'Broker',
|
title: 'Broker',
|
||||||
@@ -30,10 +30,14 @@ const RouterViewDialog = ({ visible, onClose, topic, routeData, t }) => {
|
|||||||
key: 'brokerAddrs',
|
key: 'brokerAddrs',
|
||||||
render: (_, record) => (
|
render: (_, record) => (
|
||||||
<Table
|
<Table
|
||||||
dataSource={Object.entries(record.brokerAddrs || []).map(([key, value]) => ({ key, idx: key, address: value }))}
|
dataSource={Object.entries(record.brokerAddrs || []).map(([key, value]) => ({
|
||||||
|
key,
|
||||||
|
idx: key,
|
||||||
|
address: value
|
||||||
|
}))}
|
||||||
columns={[
|
columns={[
|
||||||
{ title: 'Index', dataIndex: 'idx', key: 'idx' },
|
{title: 'Index', dataIndex: 'idx', key: 'idx'},
|
||||||
{ title: 'Address', dataIndex: 'address', key: 'address' },
|
{title: 'Address', dataIndex: 'address', key: 'address'},
|
||||||
]}
|
]}
|
||||||
pagination={false}
|
pagination={false}
|
||||||
bordered
|
bordered
|
||||||
@@ -82,7 +86,7 @@ const RouterViewDialog = ({ visible, onClose, topic, routeData, t }) => {
|
|||||||
<div>
|
<div>
|
||||||
<h3>Broker Datas:</h3>
|
<h3>Broker Datas:</h3>
|
||||||
{routeData?.brokerDatas?.map((item, index) => (
|
{routeData?.brokerDatas?.map((item, index) => (
|
||||||
<div key={index} style={{ marginBottom: '15px', border: '1px solid #d9d9d9', padding: '10px' }}>
|
<div key={index} style={{marginBottom: '15px', border: '1px solid #d9d9d9', padding: '10px'}}>
|
||||||
<Table
|
<Table
|
||||||
dataSource={[item]}
|
dataSource={[item]}
|
||||||
columns={brokerColumns}
|
columns={brokerColumns}
|
||||||
@@ -93,7 +97,7 @@ const RouterViewDialog = ({ visible, onClose, topic, routeData, t }) => {
|
|||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
<div style={{ marginTop: '20px' }}>
|
<div style={{marginTop: '20px'}}>
|
||||||
<h3>{t.QUEUE_DATAS}:</h3>
|
<h3>{t.QUEUE_DATAS}:</h3>
|
||||||
<Table
|
<Table
|
||||||
dataSource={routeData?.queueDatas || []}
|
dataSource={routeData?.queueDatas || []}
|
||||||
|
@@ -18,7 +18,7 @@
|
|||||||
import {Button, Form, Modal, Table} from "antd";
|
import {Button, Form, Modal, Table} from "antd";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
const SendResultDialog = ({ visible, onClose, result, t }) => {
|
const SendResultDialog = ({visible, onClose, result, t}) => {
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
title="SendResult"
|
title="SendResult"
|
||||||
@@ -43,11 +43,11 @@ const SendResultDialog = ({ visible, onClose, result, t }) => {
|
|||||||
: []
|
: []
|
||||||
}
|
}
|
||||||
columns={[
|
columns={[
|
||||||
{ dataIndex: 'label', key: 'label' },
|
{dataIndex: 'label', key: 'label'},
|
||||||
{
|
{
|
||||||
dataIndex: 'value',
|
dataIndex: 'value',
|
||||||
key: 'value',
|
key: 'value',
|
||||||
render: (text) => <pre style={{ whiteSpace: 'pre-wrap', margin: 0 }}>{text}</pre>,
|
render: (text) => <pre style={{whiteSpace: 'pre-wrap', margin: 0}}>{text}</pre>,
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
pagination={false}
|
pagination={false}
|
||||||
@@ -61,5 +61,4 @@ const SendResultDialog = ({ visible, onClose, result, t }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export default SendResultDialog;
|
export default SendResultDialog;
|
||||||
|
@@ -76,24 +76,24 @@ const SendTopicMessageDialog = ({
|
|||||||
</Button>,
|
</Button>,
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<Form form={form} layout="horizontal" labelCol={{ span: 6 }} wrapperCol={{ span: 18 }}>
|
<Form form={form} layout="horizontal" labelCol={{span: 6}} wrapperCol={{span: 18}}>
|
||||||
<Form.Item label={t.TOPIC} name="topic">
|
<Form.Item label={t.TOPIC} name="topic">
|
||||||
<Input disabled />
|
<Input disabled/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label={t.TAG} name="tag">
|
<Form.Item label={t.TAG} name="tag">
|
||||||
<Input />
|
<Input/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label={t.KEY} name="key">
|
<Form.Item label={t.KEY} name="key">
|
||||||
<Input />
|
<Input/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label={t.MESSAGE_BODY} name="messageBody" rules={[{ required: true, message: t.REQUIRED }]}>
|
<Form.Item label={t.MESSAGE_BODY} name="messageBody" rules={[{required: true, message: t.REQUIRED}]}>
|
||||||
<Input.TextArea
|
<Input.TextArea
|
||||||
style={{ maxHeight: '200px', minHeight: '200px', resize: 'none' }}
|
style={{maxHeight: '200px', minHeight: '200px', resize: 'none'}}
|
||||||
rows={8}
|
rows={8}
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label={t.ENABLE_MESSAGE_TRACE} name="traceEnabled" valuePropName="checked">
|
<Form.Item label={t.ENABLE_MESSAGE_TRACE} name="traceEnabled" valuePropName="checked">
|
||||||
<Checkbox />
|
<Checkbox/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Form>
|
</Form>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
@@ -15,10 +15,17 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Button, Form, message, Modal, Select } from "antd";
|
import {Button, Form, message, Modal, Select} from "antd";
|
||||||
import React, { useEffect, useState } from "react";
|
import React, {useEffect, useState} from "react";
|
||||||
|
|
||||||
const SkipMessageAccumulateDialog = ({ visible, onClose, topic, allConsumerGroupList, handleSkipMessageAccumulate, t }) => {
|
const SkipMessageAccumulateDialog = ({
|
||||||
|
visible,
|
||||||
|
onClose,
|
||||||
|
topic,
|
||||||
|
allConsumerGroupList,
|
||||||
|
handleSkipMessageAccumulate,
|
||||||
|
t
|
||||||
|
}) => {
|
||||||
const [form] = Form.useForm();
|
const [form] = Form.useForm();
|
||||||
const [selectedConsumerGroup, setSelectedConsumerGroup] = useState([]);
|
const [selectedConsumerGroup, setSelectedConsumerGroup] = useState([]);
|
||||||
|
|
||||||
@@ -52,14 +59,14 @@ const SkipMessageAccumulateDialog = ({ visible, onClose, topic, allConsumerGroup
|
|||||||
</Button>,
|
</Button>,
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<Form form={form} layout="horizontal" labelCol={{ span: 6 }} wrapperCol={{ span: 18 }}>
|
<Form form={form} layout="horizontal" labelCol={{span: 6}} wrapperCol={{span: 18}}>
|
||||||
<Form.Item label={t.SUBSCRIPTION_GROUP} required>
|
<Form.Item label={t.SUBSCRIPTION_GROUP} required>
|
||||||
<Select
|
<Select
|
||||||
mode="multiple"
|
mode="multiple"
|
||||||
placeholder={t.SELECT_CONSUMER_GROUP}
|
placeholder={t.SELECT_CONSUMER_GROUP}
|
||||||
value={selectedConsumerGroup}
|
value={selectedConsumerGroup}
|
||||||
onChange={setSelectedConsumerGroup}
|
onChange={setSelectedConsumerGroup}
|
||||||
options={allConsumerGroupList.map(group => ({ value: group, label: group }))}
|
options={allConsumerGroupList.map(group => ({value: group, label: group}))}
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Form>
|
</Form>
|
||||||
|
@@ -19,11 +19,11 @@ import moment from "moment/moment";
|
|||||||
import {Button, Modal, Table} from "antd";
|
import {Button, Modal, Table} from "antd";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
const StatsViewDialog = ({ visible, onClose, topic, statsData, t }) => {
|
const StatsViewDialog = ({visible, onClose, topic, statsData, t}) => {
|
||||||
const columns = [
|
const columns = [
|
||||||
{ title: t.QUEUE, dataIndex: 'queue', key: 'queue', align: 'center' },
|
{title: t.QUEUE, dataIndex: 'queue', key: 'queue', align: 'center'},
|
||||||
{ title: t.MIN_OFFSET, dataIndex: 'minOffset', key: 'minOffset', align: 'center' },
|
{title: t.MIN_OFFSET, dataIndex: 'minOffset', key: 'minOffset', align: 'center'},
|
||||||
{ title: t.MAX_OFFSET, dataIndex: 'maxOffset', key: 'maxOffset', align: 'center' },
|
{title: t.MAX_OFFSET, dataIndex: 'maxOffset', key: 'maxOffset', align: 'center'},
|
||||||
{
|
{
|
||||||
title: t.LAST_UPDATE_TIME_STAMP,
|
title: t.LAST_UPDATE_TIME_STAMP,
|
||||||
dataIndex: 'lastUpdateTimestamp',
|
dataIndex: 'lastUpdateTimestamp',
|
||||||
|
@@ -16,7 +16,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// TopicModifyDialog.js
|
// TopicModifyDialog.js
|
||||||
import { Button, Modal } from "antd";
|
import {Button, Modal} from "antd";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import TopicSingleModifyForm from './TopicSingleModifyForm';
|
import TopicSingleModifyForm from './TopicSingleModifyForm';
|
||||||
|
|
||||||
@@ -43,7 +43,7 @@ const TopicModifyDialog = ({
|
|||||||
{t.CLOSE}
|
{t.CLOSE}
|
||||||
</Button>,
|
</Button>,
|
||||||
]}
|
]}
|
||||||
Style={{ maxHeight: '70vh', overflowY: 'auto' }}
|
Style={{maxHeight: '70vh', overflowY: 'auto'}}
|
||||||
>
|
>
|
||||||
{initialData.map((data, index) => (
|
{initialData.map((data, index) => (
|
||||||
<TopicSingleModifyForm
|
<TopicSingleModifyForm
|
||||||
|
@@ -16,8 +16,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// TopicSingleModifyForm.js
|
// TopicSingleModifyForm.js
|
||||||
import React, { useEffect } from "react";
|
import React, {useEffect} from "react";
|
||||||
import {Button, Form, Input, Select, Divider, Row, Col} from "antd";
|
import {Button, Col, Divider, Form, Input, Row, Select} from "antd";
|
||||||
|
|
||||||
const TopicSingleModifyForm = ({
|
const TopicSingleModifyForm = ({
|
||||||
initialData,
|
initialData,
|
||||||
@@ -42,9 +42,9 @@ const TopicSingleModifyForm = ({
|
|||||||
const handleFormSubmit = () => {
|
const handleFormSubmit = () => {
|
||||||
form.validateFields()
|
form.validateFields()
|
||||||
.then(values => {
|
.then(values => {
|
||||||
const updatedValues = { ...values };
|
const updatedValues = {...values};
|
||||||
// 提交时,如果 clusterNameList 或 brokerNameList 为空,则填充所有可用的名称
|
// 提交时,如果 clusterNameList 或 brokerNameList 为空,则填充所有可用的名称
|
||||||
if(!bIsUpdate){
|
if (!bIsUpdate) {
|
||||||
if (!updatedValues.clusterNameList || updatedValues.clusterNameList.length === 0) {
|
if (!updatedValues.clusterNameList || updatedValues.clusterNameList.length === 0) {
|
||||||
updatedValues.clusterNameList = allClusterNameList;
|
updatedValues.clusterNameList = allClusterNameList;
|
||||||
}
|
}
|
||||||
@@ -60,29 +60,30 @@ const TopicSingleModifyForm = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const messageTypeOptions = [
|
const messageTypeOptions = [
|
||||||
{ value: 'TRANSACTION', label: 'TRANSACTION' },
|
{value: 'TRANSACTION', label: 'TRANSACTION'},
|
||||||
{ value: 'FIFO', label: 'FIFO' },
|
{value: 'FIFO', label: 'FIFO'},
|
||||||
{ value: 'DELAY', label: 'DELAY' },
|
{value: 'DELAY', label: 'DELAY'},
|
||||||
{ value: 'NORMAL', label: 'NORMAL' },
|
{value: 'NORMAL', label: 'NORMAL'},
|
||||||
];
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{ paddingBottom: 24 }}>
|
<div style={{paddingBottom: 24}}>
|
||||||
{bIsUpdate && <Divider orientation="left">{`${t.TOPIC_CONFIG} - ${initialData.brokerNameList ? initialData.brokerNameList.join(', ') : t.UNKNOWN_BROKER}`}</Divider>}
|
{bIsUpdate && <Divider
|
||||||
|
orientation="left">{`${t.TOPIC_CONFIG} - ${initialData.brokerNameList ? initialData.brokerNameList.join(', ') : t.UNKNOWN_BROKER}`}</Divider>}
|
||||||
<Row justify="center"> {/* 使用 Row 居中内容 */}
|
<Row justify="center"> {/* 使用 Row 居中内容 */}
|
||||||
<Col span={16}> {/* 表单内容占据 12 栅格宽度,并自动居中 */}
|
<Col span={16}> {/* 表单内容占据 12 栅格宽度,并自动居中 */}
|
||||||
<Form
|
<Form
|
||||||
form={form}
|
form={form}
|
||||||
layout="horizontal"
|
layout="horizontal"
|
||||||
labelCol={{ span: 8 }}
|
labelCol={{span: 8}}
|
||||||
wrapperCol={{ span: 16 }}
|
wrapperCol={{span: 16}}
|
||||||
>
|
>
|
||||||
<Form.Item label={t.CLUSTER_NAME} name="clusterNameList">
|
<Form.Item label={t.CLUSTER_NAME} name="clusterNameList">
|
||||||
<Select
|
<Select
|
||||||
mode="multiple"
|
mode="multiple"
|
||||||
disabled={bIsUpdate}
|
disabled={bIsUpdate}
|
||||||
placeholder={t.SELECT_CLUSTER_NAME}
|
placeholder={t.SELECT_CLUSTER_NAME}
|
||||||
options={allClusterNameList.map(name => ({ value: name, label: name }))}
|
options={allClusterNameList.map(name => ({value: name, label: name}))}
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label="BROKER_NAME" name="brokerNameList">
|
<Form.Item label="BROKER_NAME" name="brokerNameList">
|
||||||
@@ -90,15 +91,15 @@ const TopicSingleModifyForm = ({
|
|||||||
mode="multiple"
|
mode="multiple"
|
||||||
disabled={bIsUpdate}
|
disabled={bIsUpdate}
|
||||||
placeholder={t.SELECT_BROKER_NAME}
|
placeholder={t.SELECT_BROKER_NAME}
|
||||||
options={allBrokerNameList.map(name => ({ value: name, label: name }))}
|
options={allBrokerNameList.map(name => ({value: name, label: name}))}
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t.TOPIC_NAME}
|
label={t.TOPIC_NAME}
|
||||||
name="topicName"
|
name="topicName"
|
||||||
rules={[{ required: true, message: `${t.TOPIC_NAME}${t.CANNOT_BE_EMPTY}` }]}
|
rules={[{required: true, message: `${t.TOPIC_NAME}${t.CANNOT_BE_EMPTY}`}]}
|
||||||
>
|
>
|
||||||
<Input disabled={bIsUpdate} />
|
<Input disabled={bIsUpdate}/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label={t.MESSAGE_TYPE} name="messageType">
|
<Form.Item label={t.MESSAGE_TYPE} name="messageType">
|
||||||
<Select
|
<Select
|
||||||
@@ -109,26 +110,26 @@ const TopicSingleModifyForm = ({
|
|||||||
<Form.Item
|
<Form.Item
|
||||||
label={t.WRITE_QUEUE_NUMS}
|
label={t.WRITE_QUEUE_NUMS}
|
||||||
name="writeQueueNums"
|
name="writeQueueNums"
|
||||||
rules={[{ required: true, message: `${t.WRITE_QUEUE_NUMS}${t.CANNOT_BE_EMPTY}` }]}
|
rules={[{required: true, message: `${t.WRITE_QUEUE_NUMS}${t.CANNOT_BE_EMPTY}`}]}
|
||||||
>
|
>
|
||||||
<Input disabled={!writeOperationEnabled} />
|
<Input disabled={!writeOperationEnabled}/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t.READ_QUEUE_NUMS}
|
label={t.READ_QUEUE_NUMS}
|
||||||
name="readQueueNums"
|
name="readQueueNums"
|
||||||
rules={[{ required: true, message: `${t.READ_QUEUE_NUMS}${t.CANNOT_BE_EMPTY}` }]}
|
rules={[{required: true, message: `${t.READ_QUEUE_NUMS}${t.CANNOT_BE_EMPTY}`}]}
|
||||||
>
|
>
|
||||||
<Input disabled={!writeOperationEnabled} />
|
<Input disabled={!writeOperationEnabled}/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t.PERM}
|
label={t.PERM}
|
||||||
name="perm"
|
name="perm"
|
||||||
rules={[{ required: true, message: `${t.PERM}${t.CANNOT_BE_EMPTY}` }]}
|
rules={[{required: true, message: `${t.PERM}${t.CANNOT_BE_EMPTY}`}]}
|
||||||
>
|
>
|
||||||
<Input disabled={!writeOperationEnabled} />
|
<Input disabled={!writeOperationEnabled}/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
{!initialData.sysFlag && writeOperationEnabled && (
|
{!initialData.sysFlag && writeOperationEnabled && (
|
||||||
<Form.Item wrapperCol={{ offset: 8, span: 16 }}>
|
<Form.Item wrapperCol={{offset: 8, span: 16}}>
|
||||||
<Button type="primary" onClick={handleFormSubmit}>
|
<Button type="primary" onClick={handleFormSubmit}>
|
||||||
{t.COMMIT}
|
{t.COMMIT}
|
||||||
</Button>
|
</Button>
|
||||||
|
@@ -15,20 +15,21 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { createContext, useState, useContext } from 'react';
|
import React, {createContext, useContext, useState} from 'react';
|
||||||
import { translations } from '../i18n';
|
import {translations} from '../i18n';
|
||||||
|
|
||||||
const LanguageContext = createContext({
|
const LanguageContext = createContext({
|
||||||
lang: 'en',
|
lang: 'en',
|
||||||
setLang: () => {},
|
setLang: () => {
|
||||||
|
},
|
||||||
t: translations['en'], // 当前语言的文本资源
|
t: translations['en'], // 当前语言的文本资源
|
||||||
});
|
});
|
||||||
|
|
||||||
export const LanguageProvider = ({ children }) => {
|
export const LanguageProvider = ({children}) => {
|
||||||
const [lang, setLang] = useState('en');
|
const [lang, setLang] = useState('en');
|
||||||
const t = translations[lang] || translations['en'];
|
const t = translations[lang] || translations['en'];
|
||||||
return (
|
return (
|
||||||
<LanguageContext.Provider value={{ lang, setLang, t }}>
|
<LanguageContext.Provider value={{lang, setLang, t}}>
|
||||||
{children}
|
{children}
|
||||||
</LanguageContext.Provider>
|
</LanguageContext.Provider>
|
||||||
);
|
);
|
||||||
|
@@ -47,10 +47,10 @@ export const translations = {
|
|||||||
"FETCH_TOPIC_FAILED": "获取主题列表失败",
|
"FETCH_TOPIC_FAILED": "获取主题列表失败",
|
||||||
"CONFIRM_DELETE": "确认删除",
|
"CONFIRM_DELETE": "确认删除",
|
||||||
"CANCEL": "取消",
|
"CANCEL": "取消",
|
||||||
"SELECT_DELETE_BROKERS":"请选择在哪个Broker删除消费者组",
|
"SELECT_DELETE_BROKERS": "请选择在哪个Broker删除消费者组",
|
||||||
"DELETE_CONSUMER_GROUP":"删除消费者组",
|
"DELETE_CONSUMER_GROUP": "删除消费者组",
|
||||||
"ENGLISH": "英文",
|
"ENGLISH": "英文",
|
||||||
"ADD_CONSUMER":"添加消费者",
|
"ADD_CONSUMER": "添加消费者",
|
||||||
"CHINESE": "简体中文",
|
"CHINESE": "简体中文",
|
||||||
"CANNOT_BE_EMPTY": "不能为空",
|
"CANNOT_BE_EMPTY": "不能为空",
|
||||||
"TITLE": "RocketMQ仪表板",
|
"TITLE": "RocketMQ仪表板",
|
||||||
@@ -70,16 +70,16 @@ export const translations = {
|
|||||||
"CLUSTER_DETAIL": "集群详情",
|
"CLUSTER_DETAIL": "集群详情",
|
||||||
"COMMIT": "提交",
|
"COMMIT": "提交",
|
||||||
"TOPIC": "主题",
|
"TOPIC": "主题",
|
||||||
"SUBSCRIPTION_GROUP":"订阅组",
|
"SUBSCRIPTION_GROUP": "订阅组",
|
||||||
"PRODUCER_GROUP":"生产组",
|
"PRODUCER_GROUP": "生产组",
|
||||||
"CONSUMER":"消费者",
|
"CONSUMER": "消费者",
|
||||||
"PRODUCER":"生产者",
|
"PRODUCER": "生产者",
|
||||||
"MESSAGE":"消息",
|
"MESSAGE": "消息",
|
||||||
"MESSAGE_DETAIL":"消息详情",
|
"MESSAGE_DETAIL": "消息详情",
|
||||||
"RESEND_MESSAGE":"重新发送",
|
"RESEND_MESSAGE": "重新发送",
|
||||||
"VIEW_EXCEPTION":"查看异常",
|
"VIEW_EXCEPTION": "查看异常",
|
||||||
"DLQ_MESSAGE":"死信消息",
|
"DLQ_MESSAGE": "死信消息",
|
||||||
"MESSAGETRACE":"消息轨迹",
|
"MESSAGETRACE": "消息轨迹",
|
||||||
"OPERATION": "操作",
|
"OPERATION": "操作",
|
||||||
"ADD": "新增",
|
"ADD": "新增",
|
||||||
"UPDATE": "更新",
|
"UPDATE": "更新",
|
||||||
@@ -89,7 +89,7 @@ export const translations = {
|
|||||||
"CONFIG": "配置",
|
"CONFIG": "配置",
|
||||||
"SEND_MSG": "发送消息",
|
"SEND_MSG": "发送消息",
|
||||||
"RESET_CUS_OFFSET": "重置消费位点",
|
"RESET_CUS_OFFSET": "重置消费位点",
|
||||||
"SKIP_MESSAGE_ACCUMULATE":"跳过堆积",
|
"SKIP_MESSAGE_ACCUMULATE": "跳过堆积",
|
||||||
"DELETE": "删除",
|
"DELETE": "删除",
|
||||||
"CHANGE_LANG": "更换语言",
|
"CHANGE_LANG": "更换语言",
|
||||||
"CHANGE_VERSION": "更换版本",
|
"CHANGE_VERSION": "更换版本",
|
||||||
@@ -100,73 +100,72 @@ export const translations = {
|
|||||||
"TRANSACTION": "事务",
|
"TRANSACTION": "事务",
|
||||||
"UNSPECIFIED": "未指定",
|
"UNSPECIFIED": "未指定",
|
||||||
"DLQ": "死信",
|
"DLQ": "死信",
|
||||||
"QUANTITY":"数量",
|
"QUANTITY": "数量",
|
||||||
"TYPE":"类型",
|
"TYPE": "类型",
|
||||||
"MODE":"模式",
|
"MODE": "模式",
|
||||||
"DELAY":"延迟",
|
"DELAY": "延迟",
|
||||||
"DASHBOARD":"驾驶舱",
|
"DASHBOARD": "驾驶舱",
|
||||||
"CONSUME_DETAIL":"消费详情",
|
"CONSUME_DETAIL": "消费详情",
|
||||||
"CLIENT":"终端",
|
"CLIENT": "终端",
|
||||||
"LAST_CONSUME_TIME":"最后消费时间",
|
"LAST_CONSUME_TIME": "最后消费时间",
|
||||||
"TIME":"时间点",
|
"TIME": "时间点",
|
||||||
"RESET":"重置",
|
"RESET": "重置",
|
||||||
"DATE":"日期",
|
"DATE": "日期",
|
||||||
"NO_DATA":"暂无数据",
|
"NO_DATA": "暂无数据",
|
||||||
"SEARCH":"搜索",
|
"SEARCH": "搜索",
|
||||||
"BEGIN":"开始",
|
"BEGIN": "开始",
|
||||||
"END":"结束",
|
"END": "结束",
|
||||||
"TOPIC_CHANGE":"修改主题",
|
"TOPIC_CHANGE": "修改主题",
|
||||||
"SEND":"发送",
|
"SEND": "发送",
|
||||||
"SUBSCRIPTION_CHANGE":"修改订阅",
|
"SUBSCRIPTION_CHANGE": "修改订阅",
|
||||||
"QUEUE":"队列",
|
"QUEUE": "队列",
|
||||||
"MIN_OFFSET":"最小位点",
|
"MIN_OFFSET": "最小位点",
|
||||||
"MAX_OFFSET":"最大位点",
|
"MAX_OFFSET": "最大位点",
|
||||||
"LAST_UPDATE_TIME_STAMP":"上次更新时间",
|
"LAST_UPDATE_TIME_STAMP": "上次更新时间",
|
||||||
"QUEUE_DATAS":"队列信息",
|
"QUEUE_DATAS": "队列信息",
|
||||||
"READ_QUEUE_NUMS":"读队列数量",
|
"READ_QUEUE_NUMS": "读队列数量",
|
||||||
"WRITE_QUEUE_NUMS":"写队列数量",
|
"WRITE_QUEUE_NUMS": "写队列数量",
|
||||||
"PERM":"perm",
|
"PERM": "perm",
|
||||||
"TAG":"标签",
|
"TAG": "标签",
|
||||||
"KEY":"值",
|
"KEY": "值",
|
||||||
"MESSAGE_BODY":"消息主体",
|
"MESSAGE_BODY": "消息主体",
|
||||||
"TOPIC_NAME":"主题名",
|
"TOPIC_NAME": "主题名",
|
||||||
"ORDER":"顺序",
|
"ORDER": "顺序",
|
||||||
"CONSUMER_CLIENT":"消费者终端",
|
"CONSUMER_CLIENT": "消费者终端",
|
||||||
"BROKER_OFFSET":"代理者位点",
|
"BROKER_OFFSET": "代理者位点",
|
||||||
"CONSUMER_OFFSET":"消费者位点",
|
"CONSUMER_OFFSET": "消费者位点",
|
||||||
"DIFF_TOTAL":"差值",
|
"DIFF_TOTAL": "差值",
|
||||||
"LAST_TIME_STAMP":"上次时间",
|
"LAST_TIME_STAMP": "上次时间",
|
||||||
"RESET_OFFSET":"重置位点",
|
"RESET_OFFSET": "重置位点",
|
||||||
"CLUSTER_NAME":"集群名",
|
"CLUSTER_NAME": "集群名",
|
||||||
"OPS":"运维",
|
"OPS": "运维",
|
||||||
"PROXY":"代理",
|
"PROXY": "代理",
|
||||||
"AUTO_REFRESH":"自动刷新",
|
"AUTO_REFRESH": "自动刷新",
|
||||||
"REFRESH":"刷新",
|
"REFRESH": "刷新",
|
||||||
"LOGOUT":"退出",
|
"LOGOUT": "退出",
|
||||||
"LOGIN":"登录",
|
"LOGIN": "登录",
|
||||||
"USER_NAME":"用户名",
|
"USER_NAME": "用户名",
|
||||||
"PASSWORD":"密码",
|
"PASSWORD": "密码",
|
||||||
"SYSTEM":"系统",
|
"SYSTEM": "系统",
|
||||||
"WELCOME":"您好,欢迎使用RocketMQ仪表盘",
|
"WELCOME": "您好,欢迎使用RocketMQ仪表盘",
|
||||||
"ENABLE_MESSAGE_TRACE":"开启消息轨迹",
|
"ENABLE_MESSAGE_TRACE": "开启消息轨迹",
|
||||||
"MESSAGE_TRACE_DETAIL":"消息轨迹详情",
|
"MESSAGE_TRACE_DETAIL": "消息轨迹详情",
|
||||||
"TRACE_TOPIC":"消息轨迹主题",
|
"TRACE_TOPIC": "消息轨迹主题",
|
||||||
"SELECT_TRACE_TOPIC":"选择消息轨迹主题",
|
"SELECT_TRACE_TOPIC": "选择消息轨迹主题",
|
||||||
"EXPORT": "导出",
|
"EXPORT": "导出",
|
||||||
"NO_MATCH_RESULT": "没有查到符合条件的结果",
|
"NO_MATCH_RESULT": "没有查到符合条件的结果",
|
||||||
"BATCH_RESEND": "批量重发",
|
"BATCH_RESEND": "批量重发",
|
||||||
"BATCH_EXPORT": "批量导出",
|
"BATCH_EXPORT": "批量导出",
|
||||||
"WHITE_LIST":"白名单",
|
"ACCOUNT_INFO": "账户信息",
|
||||||
"ACCOUNT_INFO":"账户信息",
|
"IS_ADMIN": "是否管理员",
|
||||||
"IS_ADMIN":"是否管理员",
|
"DEFAULT_TOPIC_PERM": "topic默认权限",
|
||||||
"DEFAULT_TOPIC_PERM":"topic默认权限",
|
"DEFAULT_GROUP_PERM": "消费组默认权限",
|
||||||
"DEFAULT_GROUP_PERM":"消费组默认权限",
|
"TOPIC_PERM": "topic权限",
|
||||||
"TOPIC_PERM":"topic权限",
|
"GROUP_PERM": "消费组权限",
|
||||||
"GROUP_PERM":"消费组权限",
|
"SYNCHRONIZE": "同步",
|
||||||
"SYNCHRONIZE":"同步",
|
"SHOW": "显示",
|
||||||
"SHOW":"显示",
|
"HIDE": "隐藏",
|
||||||
"HIDE":"隐藏",
|
"MESSAGE_TYPE": "消息类型",
|
||||||
"MESSAGE_TYPE":"消息类型",
|
|
||||||
"MESSAGE_TYPE_UNSPECIFIED": "未指定,为普通消息",
|
"MESSAGE_TYPE_UNSPECIFIED": "未指定,为普通消息",
|
||||||
"MESSAGE_TYPE_NORMAL": "普通消息",
|
"MESSAGE_TYPE_NORMAL": "普通消息",
|
||||||
"MESSAGE_TYPE_FIFO": "顺序消息",
|
"MESSAGE_TYPE_FIFO": "顺序消息",
|
||||||
@@ -279,6 +278,12 @@ export const translations = {
|
|||||||
"ENTER_IP_HINT": "请输入 IP 地址,按回车键添加,支持 IPv4、IPv6 和 CIDR",
|
"ENTER_IP_HINT": "请输入 IP 地址,按回车键添加,支持 IPv4、IPv6 和 CIDR",
|
||||||
"PLEASE_ENTER_DECISION": "请输入决策!",
|
"PLEASE_ENTER_DECISION": "请输入决策!",
|
||||||
"MENU": "菜单",
|
"MENU": "菜单",
|
||||||
|
"SELECT_PROXY": "选择代理",
|
||||||
|
"ENABLE_PROXY": "启用代理",
|
||||||
|
"PROXY_DISABLED": "代理禁用",
|
||||||
|
"PROXY_ENABLED": "代理启用",
|
||||||
|
"BROKER_OVERVIEW": "Broker概览",
|
||||||
|
"TOTAL_MSG_RECEIVED_TODAY": "今天接收的总消息数",
|
||||||
},
|
},
|
||||||
en: {
|
en: {
|
||||||
"DEFAULT": "Default",
|
"DEFAULT": "Default",
|
||||||
@@ -294,7 +299,7 @@ export const translations = {
|
|||||||
"SELECT_TOPIC_PLACEHOLDER": "Please select topic",
|
"SELECT_TOPIC_PLACEHOLDER": "Please select topic",
|
||||||
"MESSAGE_ID_TOPIC_HINT": "Message ID Topic",
|
"MESSAGE_ID_TOPIC_HINT": "Message ID Topic",
|
||||||
"TOPIC_ADD": "Add Topic",
|
"TOPIC_ADD": "Add Topic",
|
||||||
"SKIP_MESSAGE_ACCUMULATE":"Skip Message Accumulate",
|
"SKIP_MESSAGE_ACCUMULATE": "Skip Message Accumulate",
|
||||||
"OPERATION_FAILED": "Operation Failed",
|
"OPERATION_FAILED": "Operation Failed",
|
||||||
"FORM_VALIDATION_FAILED": "Form Validation Failed",
|
"FORM_VALIDATION_FAILED": "Form Validation Failed",
|
||||||
"ADD_CONSUMER": "Add Consumer",
|
"ADD_CONSUMER": "Add Consumer",
|
||||||
@@ -325,7 +330,7 @@ export const translations = {
|
|||||||
"ADDRESS": "Address",
|
"ADDRESS": "Address",
|
||||||
"VERSION": "Version",
|
"VERSION": "Version",
|
||||||
"PRO_MSG_TPS": "Produce Message TPS",
|
"PRO_MSG_TPS": "Produce Message TPS",
|
||||||
"CUS_MSG_TPS": "Consume Message TPS",
|
"CUS_MSG_TPS": "Consumer Message TPS",
|
||||||
"YESTERDAY_PRO_COUNT": "Yesterday Produce Count",
|
"YESTERDAY_PRO_COUNT": "Yesterday Produce Count",
|
||||||
"YESTERDAY_CUS_COUNT": "Yesterday Consume Count",
|
"YESTERDAY_CUS_COUNT": "Yesterday Consume Count",
|
||||||
"TODAY_PRO_COUNT": "Today Produce Count",
|
"TODAY_PRO_COUNT": "Today Produce Count",
|
||||||
@@ -335,16 +340,16 @@ export const translations = {
|
|||||||
"CLUSTER": "Cluster",
|
"CLUSTER": "Cluster",
|
||||||
"CLUSTER_DETAIL": "Cluster Detail",
|
"CLUSTER_DETAIL": "Cluster Detail",
|
||||||
"TOPIC": "Topic",
|
"TOPIC": "Topic",
|
||||||
"SUBSCRIPTION_GROUP":"SubscriptionGroup",
|
"SUBSCRIPTION_GROUP": "SubscriptionGroup",
|
||||||
"PRODUCER_GROUP":"ProducerGroup",
|
"PRODUCER_GROUP": "ProducerGroup",
|
||||||
"CONSUMER":"Consumer",
|
"CONSUMER": "Consumer",
|
||||||
"PRODUCER":"Producer",
|
"PRODUCER": "Producer",
|
||||||
"MESSAGE":"Message",
|
"MESSAGE": "Message",
|
||||||
"MESSAGE_DETAIL":"Message Detail",
|
"MESSAGE_DETAIL": "Message Detail",
|
||||||
"RESEND_MESSAGE":"Resend Message",
|
"RESEND_MESSAGE": "Resend Message",
|
||||||
"VIEW_EXCEPTION":"View Exception",
|
"VIEW_EXCEPTION": "View Exception",
|
||||||
"MESSAGETRACE":"MessageTrace",
|
"MESSAGETRACE": "MessageTrace",
|
||||||
"DLQ_MESSAGE":"DLQMessage",
|
"DLQ_MESSAGE": "DLQMessage",
|
||||||
"COMMIT": "Commit",
|
"COMMIT": "Commit",
|
||||||
"OPERATION": "Operation",
|
"OPERATION": "Operation",
|
||||||
"ADD": "Add",
|
"ADD": "Add",
|
||||||
@@ -365,73 +370,72 @@ export const translations = {
|
|||||||
"TRANSACTION": "TRANSACTION",
|
"TRANSACTION": "TRANSACTION",
|
||||||
"UNSPECIFIED": "UNSPECIFIED",
|
"UNSPECIFIED": "UNSPECIFIED",
|
||||||
"DLQ": "DLQ",
|
"DLQ": "DLQ",
|
||||||
"QUANTITY":"Quantity",
|
"QUANTITY": "Quantity",
|
||||||
"TYPE":"Type",
|
"TYPE": "Type",
|
||||||
"MODE":"Mode",
|
"MODE": "Mode",
|
||||||
"DELAY":"Delay",
|
"DELAY": "Delay",
|
||||||
"DASHBOARD":"Dashboard",
|
"DASHBOARD": "Dashboard",
|
||||||
"CONSUME_DETAIL":"CONSUME DETAIL",
|
"CONSUME_DETAIL": "CONSUME DETAIL",
|
||||||
"CLIENT":"CLIENT",
|
"CLIENT": "CLIENT",
|
||||||
"LAST_CONSUME_TIME":"LastConsumeTime",
|
"LAST_CONSUME_TIME": "LastConsumeTime",
|
||||||
"TIME":"Time",
|
"TIME": "Time",
|
||||||
"RESET":"RESET",
|
"RESET": "RESET",
|
||||||
"DATE":"Date",
|
"DATE": "Date",
|
||||||
"NO_DATA":"NO DATA",
|
"NO_DATA": "NO DATA",
|
||||||
"SEARCH":"Search",
|
"SEARCH": "Search",
|
||||||
"BEGIN":"Begin",
|
"BEGIN": "Begin",
|
||||||
"END":"End",
|
"END": "End",
|
||||||
"TOPIC_CHANGE":"Topic Change",
|
"TOPIC_CHANGE": "Topic Change",
|
||||||
"SEND":"Send",
|
"SEND": "Send",
|
||||||
"SUBSCRIPTION_CHANGE":"Subscription Change",
|
"SUBSCRIPTION_CHANGE": "Subscription Change",
|
||||||
"QUEUE":"Queue",
|
"QUEUE": "Queue",
|
||||||
"MIN_OFFSET":"minOffset",
|
"MIN_OFFSET": "minOffset",
|
||||||
"MAX_OFFSET":"maxOffset",
|
"MAX_OFFSET": "maxOffset",
|
||||||
"LAST_UPDATE_TIME_STAMP":"lastUpdateTimeStamp",
|
"LAST_UPDATE_TIME_STAMP": "lastUpdateTimeStamp",
|
||||||
"QUEUE_DATAS":"queueDatas",
|
"QUEUE_DATAS": "queueDatas",
|
||||||
"READ_QUEUE_NUMS":"readQueueNums",
|
"READ_QUEUE_NUMS": "readQueueNums",
|
||||||
"WRITE_QUEUE_NUMS":"writeQueueNums",
|
"WRITE_QUEUE_NUMS": "writeQueueNums",
|
||||||
"PERM":"perm",
|
"PERM": "perm",
|
||||||
"TAG":"Tag",
|
"TAG": "Tag",
|
||||||
"KEY":"Key",
|
"KEY": "Key",
|
||||||
"MESSAGE_BODY":"Message Body",
|
"MESSAGE_BODY": "Message Body",
|
||||||
"TOPIC_NAME":"topicName",
|
"TOPIC_NAME": "topicName",
|
||||||
"ORDER":"order",
|
"ORDER": "order",
|
||||||
"CONSUMER_CLIENT":"consumerClient",
|
"CONSUMER_CLIENT": "consumerClient",
|
||||||
"BROKER_OFFSET":"brokerOffset",
|
"BROKER_OFFSET": "brokerOffset",
|
||||||
"CONSUMER_OFFSET":"consumerOffset",
|
"CONSUMER_OFFSET": "consumerOffset",
|
||||||
"DIFF_TOTAL":"diffTotal",
|
"DIFF_TOTAL": "diffTotal",
|
||||||
"LAST_TIME_STAMP":"lastTimeStamp",
|
"LAST_TIME_STAMP": "lastTimeStamp",
|
||||||
"RESET_OFFSET":"resetOffset",
|
"RESET_OFFSET": "resetOffset",
|
||||||
"CLUSTER_NAME":"clusterName",
|
"CLUSTER_NAME": "clusterName",
|
||||||
"OPS":"OPS",
|
"OPS": "OPS",
|
||||||
"PROXY":"Proxy",
|
"PROXY": "Proxy",
|
||||||
"AUTO_REFRESH":"AUTO_REFRESH",
|
"AUTO_REFRESH": "AUTO_REFRESH",
|
||||||
"REFRESH":"REFRESH",
|
"REFRESH": "REFRESH",
|
||||||
"LOGOUT":"Logout",
|
"LOGOUT": "Logout",
|
||||||
"LOGIN":"Login",
|
"LOGIN": "Login",
|
||||||
"USER_NAME":"Username",
|
"USER_NAME": "Username",
|
||||||
"PASSWORD":"Password",
|
"PASSWORD": "Password",
|
||||||
"SYSTEM":"SYSTEM",
|
"SYSTEM": "SYSTEM",
|
||||||
"WELCOME":"Hi, welcome using RocketMQ Dashboard",
|
"WELCOME": "Hi, welcome using RocketMQ Dashboard",
|
||||||
"ENABLE_MESSAGE_TRACE":"Enable Message Trace",
|
"ENABLE_MESSAGE_TRACE": "Enable Message Trace",
|
||||||
"MESSAGE_TRACE_DETAIL":"Message Trace Detail",
|
"MESSAGE_TRACE_DETAIL": "Message Trace Detail",
|
||||||
"TRACE_TOPIC":"TraceTopic",
|
"TRACE_TOPIC": "TraceTopic",
|
||||||
"SELECT_TRACE_TOPIC":"selectTraceTopic",
|
"SELECT_TRACE_TOPIC": "selectTraceTopic",
|
||||||
"EXPORT": "export",
|
"EXPORT": "export",
|
||||||
"NO_MATCH_RESULT": "no match result",
|
"NO_MATCH_RESULT": "no match result",
|
||||||
"BATCH_RESEND": "batchReSend",
|
"BATCH_RESEND": "batchReSend",
|
||||||
"BATCH_EXPORT": "batchExport",
|
"BATCH_EXPORT": "batchExport",
|
||||||
"WHITE_LIST":"White List",
|
"ACCOUNT_INFO": "Account Info",
|
||||||
"ACCOUNT_INFO":"Account Info",
|
"IS_ADMIN": "Is Admin",
|
||||||
"IS_ADMIN":"Is Admin",
|
"DEFAULT_TOPIC_PERM": "Default Topic Permission",
|
||||||
"DEFAULT_TOPIC_PERM":"Default Topic Permission",
|
"DEFAULT_GROUP_PERM": "Default Group Permission",
|
||||||
"DEFAULT_GROUP_PERM":"Default Group Permission",
|
"TOPIC_PERM": "Topic Permission",
|
||||||
"TOPIC_PERM":"Topic Permission",
|
"GROUP_PERM": "Group Permission",
|
||||||
"GROUP_PERM":"Group Permission",
|
"SYNCHRONIZE": "Synchronize Data",
|
||||||
"SYNCHRONIZE":"Synchronize Data",
|
"SHOW": "Show",
|
||||||
"SHOW":"Show",
|
"HIDE": "Hide",
|
||||||
"HIDE":"Hide",
|
"MESSAGE_TYPE": "messageType",
|
||||||
"MESSAGE_TYPE":"messageType",
|
|
||||||
"MESSAGE_TYPE_UNSPECIFIED": "UNSPECIFIED, is NORMAL",
|
"MESSAGE_TYPE_UNSPECIFIED": "UNSPECIFIED, is NORMAL",
|
||||||
"MESSAGE_TYPE_NORMAL": "NORMAL",
|
"MESSAGE_TYPE_NORMAL": "NORMAL",
|
||||||
"MESSAGE_TYPE_FIFO": "FIFO",
|
"MESSAGE_TYPE_FIFO": "FIFO",
|
||||||
@@ -536,6 +540,13 @@ export const translations = {
|
|||||||
"ENTER_IP_HINT": "Please enter IP address, press Enter to add. Supports IPv4, IPv6, and CIDR.",
|
"ENTER_IP_HINT": "Please enter IP address, press Enter to add. Supports IPv4, IPv6, and CIDR.",
|
||||||
"PLEASE_ENTER_DECISION": "Please enter decision!",
|
"PLEASE_ENTER_DECISION": "Please enter decision!",
|
||||||
"MENU": "Menu",
|
"MENU": "Menu",
|
||||||
|
"SELECT_PROXY": "Select Proxy",
|
||||||
|
"ENABLE_PROXY": "Enable Proxy",
|
||||||
|
"PROXY_DISABLED": "Proxy Disabled",
|
||||||
|
"PROXY_ENABLED": "Proxy Enabled",
|
||||||
|
"BROKER_OVERVIEW": "Broker Overview",
|
||||||
|
"TOTAL_MSG_RECEIVED_TODAY": "Total messages received today",
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -19,7 +19,7 @@ import React from 'react';
|
|||||||
import ReactDOM from 'react-dom/client';
|
import ReactDOM from 'react-dom/client';
|
||||||
import './index.css';
|
import './index.css';
|
||||||
import App from './App';
|
import App from './App';
|
||||||
import { App as AntdApp } from 'antd';
|
import {App as AntdApp} from 'antd';
|
||||||
import reportWebVitals from './reportWebVitals';
|
import reportWebVitals from './reportWebVitals';
|
||||||
import {LanguageProvider} from "./i18n/LanguageContext";
|
import {LanguageProvider} from "./i18n/LanguageContext";
|
||||||
import {Provider} from "react-redux";
|
import {Provider} from "react-redux";
|
||||||
@@ -27,7 +27,6 @@ import store from './store';
|
|||||||
|
|
||||||
const root = ReactDOM.createRoot(document.getElementById('root'));
|
const root = ReactDOM.createRoot(document.getElementById('root'));
|
||||||
root.render(
|
root.render(
|
||||||
|
|
||||||
<LanguageProvider>
|
<LanguageProvider>
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
<AntdApp>
|
<AntdApp>
|
||||||
@@ -37,7 +36,6 @@ root.render(
|
|||||||
</AntdApp>
|
</AntdApp>
|
||||||
</React.StrictMode>
|
</React.StrictMode>
|
||||||
</LanguageProvider>
|
</LanguageProvider>
|
||||||
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// If you want to start measuring performance in your app, pass a function
|
// If you want to start measuring performance in your app, pass a function
|
||||||
|
@@ -16,7 +16,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import React, {useCallback, useEffect, useState} from 'react';
|
import React, {useCallback, useEffect, useState} from 'react';
|
||||||
import {Button, Checkbox, Input, message, notification, Spin, Table} from 'antd';
|
import {Button, Checkbox, Input, message, notification, Select, Spin, Switch, Table} from 'antd';
|
||||||
import {useLanguage} from '../../i18n/LanguageContext';
|
import {useLanguage} from '../../i18n/LanguageContext';
|
||||||
import {remoteApi} from '../../api/remoteApi/remoteApi';
|
import {remoteApi} from '../../api/remoteApi/remoteApi';
|
||||||
import ClientInfoModal from "../../components/consumer/ClientInfoModal";
|
import ClientInfoModal from "../../components/consumer/ClientInfoModal";
|
||||||
@@ -44,8 +44,29 @@ const ConsumerGroupList = () => {
|
|||||||
const [isAddConfig, setIsAddConfig] = useState(false);
|
const [isAddConfig, setIsAddConfig] = useState(false);
|
||||||
const [showDeleteModal, setShowDeleteModal] = useState(false);
|
const [showDeleteModal, setShowDeleteModal] = useState(false);
|
||||||
const [messageApi, msgContextHolder] = message.useMessage();
|
const [messageApi, msgContextHolder] = message.useMessage();
|
||||||
const [notificationApi,notificationContextHolder] = notification.useNotification();
|
const [notificationApi, notificationContextHolder] = notification.useNotification();
|
||||||
|
|
||||||
|
const [proxyEnabled, setProxyEnabled] = useState(() => {
|
||||||
|
try {
|
||||||
|
const storedValue = localStorage.getItem('proxyEnabled');
|
||||||
|
return storedValue ? JSON.parse(storedValue) : false;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to read proxyEnabled from localStorage:", error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const [selectedProxy, setSelectedProxy] = useState(() => {
|
||||||
|
try {
|
||||||
|
const storedValue = localStorage.getItem('selectedProxy');
|
||||||
|
return storedValue || undefined;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to read selectedProxy from localStorage:", error);
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const [proxyOptions ,setProxyOptions]= useState([]);
|
||||||
const [paginationConf, setPaginationConf] = useState({
|
const [paginationConf, setPaginationConf] = useState({
|
||||||
current: 1,
|
current: 1,
|
||||||
pageSize: 10,
|
pageSize: 10,
|
||||||
@@ -60,12 +81,17 @@ const ConsumerGroupList = () => {
|
|||||||
const loadConsumerGroups = useCallback(async (currentPage) => {
|
const loadConsumerGroups = useCallback(async (currentPage) => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
try {
|
try {
|
||||||
const response = await remoteApi.queryConsumerGroupList(false);
|
var response;
|
||||||
|
if(!proxyEnabled){
|
||||||
|
response = await remoteApi.queryConsumerGroupList(false);
|
||||||
|
}else{
|
||||||
|
response = await remoteApi.queryConsumerGroupList(false, selectedProxy);
|
||||||
|
}
|
||||||
if (response.status === 0) {
|
if (response.status === 0) {
|
||||||
setAllConsumerGroupList(response.data);
|
setAllConsumerGroupList(response.data);
|
||||||
if(currentPage!=null){
|
if (currentPage != null) {
|
||||||
filterList(currentPage, response.data);
|
filterList(currentPage, response.data);
|
||||||
}else{
|
} else {
|
||||||
filterList(1, response.data);
|
filterList(1, response.data);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -87,7 +113,6 @@ const ConsumerGroupList = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const filterList = useCallback((currentPage, data) => {
|
const filterList = useCallback((currentPage, data) => {
|
||||||
// 排序处理
|
|
||||||
let sortedData = [...data];
|
let sortedData = [...data];
|
||||||
if (sortConfig.sortKey) {
|
if (sortConfig.sortKey) {
|
||||||
sortedData.sort((a, b) => {
|
sortedData.sort((a, b) => {
|
||||||
@@ -153,6 +178,48 @@ const ConsumerGroupList = () => {
|
|||||||
filterList(paginationConf.current, sortedList);
|
filterList(paginationConf.current, sortedList);
|
||||||
}, [sortConfig, allConsumerGroupList, paginationConf.current]);
|
}, [sortConfig, allConsumerGroupList, paginationConf.current]);
|
||||||
|
|
||||||
|
const fetchProxyList = useCallback(async () => {
|
||||||
|
remoteApi.queryProxyHomePage((resp) => {
|
||||||
|
setLoading(false);
|
||||||
|
if (resp.status === 0) {
|
||||||
|
const {proxyAddrList, currentProxyAddr} = resp.data;
|
||||||
|
const options = proxyAddrList.map(proxyAddress => ({
|
||||||
|
label: proxyAddress,
|
||||||
|
value: proxyAddress,
|
||||||
|
}));
|
||||||
|
setProxyOptions(options || []);
|
||||||
|
setSelectedProxy(prevSelectedProxy => {
|
||||||
|
if (prevSelectedProxy) {
|
||||||
|
return prevSelectedProxy;
|
||||||
|
}
|
||||||
|
if (options.length > 0) {
|
||||||
|
return options[0].value;
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
notificationApi.error({message: resp.errMsg || t.FETCH_PROXY_LIST_FAILED, duration: 2});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, [t]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
localStorage.setItem('proxyEnabled', JSON.stringify(proxyEnabled));
|
||||||
|
}, [proxyEnabled]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (selectedProxy) {
|
||||||
|
localStorage.setItem('selectedProxy', selectedProxy);
|
||||||
|
} else {
|
||||||
|
localStorage.removeItem('selectedProxy');
|
||||||
|
}
|
||||||
|
}, [selectedProxy]);
|
||||||
|
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetchProxyList();
|
||||||
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadConsumerGroups();
|
loadConsumerGroups();
|
||||||
}, [loadConsumerGroups]);
|
}, [loadConsumerGroups]);
|
||||||
@@ -380,7 +447,7 @@ const ConsumerGroupList = () => {
|
|||||||
filterList(pagination.current, allConsumerGroupList);
|
filterList(pagination.current, allConsumerGroupList);
|
||||||
};
|
};
|
||||||
|
|
||||||
const closeConfigModal = () =>{
|
const closeConfigModal = () => {
|
||||||
setShowConfig(false);
|
setShowConfig(false);
|
||||||
setIsAddConfig(false);
|
setIsAddConfig(false);
|
||||||
}
|
}
|
||||||
@@ -389,16 +456,18 @@ const ConsumerGroupList = () => {
|
|||||||
<>
|
<>
|
||||||
{msgContextHolder}
|
{msgContextHolder}
|
||||||
{notificationContextHolder}
|
{notificationContextHolder}
|
||||||
<div style={{padding: '20px'}}>
|
<div style={{ padding: '20px' }}>
|
||||||
<Spin spinning={loading} tip={t.LOADING}>
|
<Spin spinning={loading} tip={t.LOADING}>
|
||||||
<div style={{marginBottom: '20px'}}>
|
<div style={{ marginBottom: '20px', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||||
<div style={{display: 'flex', alignItems: 'center', gap: '15px'}}>
|
{/* 左侧:筛选和操作按钮 */}
|
||||||
<div style={{display: 'flex', alignItems: 'center'}}>
|
<div style={{ display: 'flex', alignItems: 'center', gap: '15px', flexWrap: 'wrap' }}>
|
||||||
<label style={{marginRight: '8px'}}>{t.SUBSCRIPTION_GROUP}:</label>
|
<div style={{ display: 'flex', alignItems: 'center' }}>
|
||||||
|
<label style={{ marginRight: '8px', whiteSpace: 'nowrap' }}>{t.SUBSCRIPTION_GROUP}:</label>
|
||||||
<Input
|
<Input
|
||||||
style={{width: '200px'}}
|
style={{ width: '200px' }}
|
||||||
value={filterStr}
|
value={filterStr}
|
||||||
onChange={(e) => handleFilterInputChange(e.target.value)}
|
onChange={(e) => handleFilterInputChange(e.target.value)}
|
||||||
|
placeholder="输入订阅组名称"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<Checkbox checked={filterNormal}
|
<Checkbox checked={filterNormal}
|
||||||
@@ -423,12 +492,35 @@ const ConsumerGroupList = () => {
|
|||||||
<Button type="primary" onClick={handleRefreshConsumerData}>
|
<Button type="primary" onClick={handleRefreshConsumerData}>
|
||||||
{t.REFRESH}
|
{t.REFRESH}
|
||||||
</Button>
|
</Button>
|
||||||
{/*<Switch*/}
|
</div>
|
||||||
{/* checked={intervalProcessSwitch}*/}
|
|
||||||
{/* onChange={(checked) => setIntervalProcessSwitch(checked)}*/}
|
{/* 右侧:代理选项 */}
|
||||||
{/* checkedChildren={t.AUTO_REFRESH}*/}
|
<div style={{ display: 'flex', alignItems: 'center', gap: '15px' }}>
|
||||||
{/* unCheckedChildren={t.AUTO_REFRESH}*/}
|
<label style={{ marginRight: '8px', whiteSpace: 'nowrap' }}>{t.SELECT_PROXY}:</label>
|
||||||
{/*/>*/}
|
<Select
|
||||||
|
style={{ width: '220px' }}
|
||||||
|
placeholder={t.SELECT_PROXY}
|
||||||
|
onChange={(value) => setSelectedProxy(value)}
|
||||||
|
value={selectedProxy}
|
||||||
|
options={proxyOptions}
|
||||||
|
disabled={!proxyEnabled}
|
||||||
|
allowClear
|
||||||
|
/>
|
||||||
|
<label style={{ marginRight: '8px', whiteSpace: 'nowrap' }}>{t.ENABLE_PROXY}:</label>
|
||||||
|
<Switch
|
||||||
|
checked={proxyEnabled}
|
||||||
|
onChange={(checked) => {
|
||||||
|
setProxyEnabled(checked);
|
||||||
|
if (!checked) {
|
||||||
|
setSelectedProxy(undefined);
|
||||||
|
messageApi.info(t.PROXY_DISABLED);
|
||||||
|
} else {
|
||||||
|
messageApi.info(t.PROXY_ENABLED);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
checkedChildren={t.ENABLED}
|
||||||
|
unCheckedChildren={t.DISABLED}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -443,6 +535,7 @@ const ConsumerGroupList = () => {
|
|||||||
/>
|
/>
|
||||||
</Spin>
|
</Spin>
|
||||||
|
|
||||||
|
{/* 模态框组件保持不变 */}
|
||||||
<ClientInfoModal
|
<ClientInfoModal
|
||||||
visible={showClientInfo}
|
visible={showClientInfo}
|
||||||
group={selectedGroup}
|
group={selectedGroup}
|
||||||
|
@@ -250,17 +250,18 @@ const DashboardPage = () => {
|
|||||||
const brokerAddrTable = resp.data.clusterInfo.brokerAddrTable; // Corrected to brokerAddrTable
|
const brokerAddrTable = resp.data.clusterInfo.brokerAddrTable; // Corrected to brokerAddrTable
|
||||||
const brokerDetail = resp.data.brokerServer;
|
const brokerDetail = resp.data.brokerServer;
|
||||||
const clusterMap = tools.generateBrokerMap(brokerDetail, clusterAddrTable, brokerAddrTable);
|
const clusterMap = tools.generateBrokerMap(brokerDetail, clusterAddrTable, brokerAddrTable);
|
||||||
|
console.log(brokerAddrTable)
|
||||||
let brokerArray = [];
|
let brokerArray = [];
|
||||||
Object.values(clusterMap).forEach(brokersInCluster => {
|
Object.values(clusterMap).forEach(brokersInCluster => {
|
||||||
brokerArray = brokerArray.concat(brokersInCluster);
|
brokerArray = brokerArray.concat(brokersInCluster);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Update broker table data
|
const newData = brokerArray.map(broker => ({
|
||||||
setBrokerTableData(brokerArray.map(broker => ({
|
|
||||||
...broker,
|
...broker,
|
||||||
key: broker.brokerName // Ant Design Table needs a unique key
|
key: broker.brokerName,
|
||||||
})));
|
}));
|
||||||
|
console.log("即将设置的数据:", newData); // 先打印
|
||||||
|
setBrokerTableData(newData); // 再设置状态
|
||||||
|
|
||||||
brokerArray.sort((firstBroker, lastBroker) => {
|
brokerArray.sort((firstBroker, lastBroker) => {
|
||||||
const firstTotalMsg = parseFloat(firstBroker.msgGetTotalTodayNow || 0);
|
const firstTotalMsg = parseFloat(firstBroker.msgGetTotalTodayNow || 0);
|
||||||
@@ -347,7 +348,7 @@ const DashboardPage = () => {
|
|||||||
|
|
||||||
const brokerColumns = [
|
const brokerColumns = [
|
||||||
{title: t.BROKER_NAME, dataIndex: 'brokerName', key: 'brokerName'},
|
{title: t.BROKER_NAME, dataIndex: 'brokerName', key: 'brokerName'},
|
||||||
{title: t.BROKER_ADDR, dataIndex: 'brokerAddress', key: 'brokerAddress'},
|
{title: t.BROKER_ADDR, dataIndex: 'address', key: 'address'},
|
||||||
{
|
{
|
||||||
title: t.TOTAL_MSG_RECEIVED_TODAY,
|
title: t.TOTAL_MSG_RECEIVED_TODAY,
|
||||||
dataIndex: 'msgGetTotalTodayNow',
|
dataIndex: 'msgGetTotalTodayNow',
|
||||||
|
@@ -179,7 +179,6 @@ const DlqMessageQueryPage = () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
// console.log("根据Message ID查询DLQ消息:", { msgId: messageId, consumerGroup: selectedConsumerGroup });
|
|
||||||
try {
|
try {
|
||||||
const resp = await remoteApi.viewMessage(messageId, DLQ_GROUP_TOPIC_PREFIX + selectedConsumerGroup);
|
const resp = await remoteApi.viewMessage(messageId, DLQ_GROUP_TOPIC_PREFIX + selectedConsumerGroup);
|
||||||
if (resp.status === 0) {
|
if (resp.status === 0) {
|
||||||
@@ -323,7 +322,6 @@ const DlqMessageQueryPage = () => {
|
|||||||
msgId: message.properties.ORIGIN_MESSAGE_ID,
|
msgId: message.properties.ORIGIN_MESSAGE_ID,
|
||||||
consumerGroup: selectedConsumerGroup,
|
consumerGroup: selectedConsumerGroup,
|
||||||
}));
|
}));
|
||||||
// console.log(`批量重发DLQ消息到 ${selectedConsumerGroup}:`, messagesToResend);
|
|
||||||
try {
|
try {
|
||||||
const resp = await remoteApi.batchResendDlqMessage(messagesToResend);
|
const resp = await remoteApi.batchResendDlqMessage(messagesToResend);
|
||||||
if (resp.status === 0) {
|
if (resp.status === 0) {
|
||||||
@@ -355,7 +353,6 @@ const DlqMessageQueryPage = () => {
|
|||||||
message: t.ERROR,
|
message: t.ERROR,
|
||||||
description: t.BATCH_RESEND_FAILED,
|
description: t.BATCH_RESEND_FAILED,
|
||||||
});
|
});
|
||||||
console.error("批量重发失败:", error);
|
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
|
@@ -16,17 +16,17 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Form, Input, Button, message, Typography } from 'antd';
|
import {Button, Form, Input, message, Typography} from 'antd';
|
||||||
import {remoteApi} from "../../api/remoteApi/remoteApi";
|
import {remoteApi} from "../../api/remoteApi/remoteApi";
|
||||||
|
|
||||||
const { Title } = Typography;
|
const {Title} = Typography;
|
||||||
|
|
||||||
const Login = () => {
|
const Login = () => {
|
||||||
const [form] = Form.useForm();
|
const [form] = Form.useForm();
|
||||||
const [messageApi, msgContextHolder] = message.useMessage();
|
const [messageApi, msgContextHolder] = message.useMessage();
|
||||||
|
|
||||||
const onFinish = async (values) => {
|
const onFinish = async (values) => {
|
||||||
const { username, password } = values;
|
const {username, password} = values;
|
||||||
remoteApi.login(username, password).then((res) => {
|
remoteApi.login(username, password).then((res) => {
|
||||||
if (res.status === 0) {
|
if (res.status === 0) {
|
||||||
messageApi.success('登录成功');
|
messageApi.success('登录成功');
|
||||||
|
@@ -146,7 +146,6 @@ const MessageQueryPage = () => {
|
|||||||
message: t.ERROR,
|
message: t.ERROR,
|
||||||
description: t.QUERY_FAILED,
|
description: t.QUERY_FAILED,
|
||||||
});
|
});
|
||||||
console.error("查询失败:", error);
|
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
@@ -182,7 +181,6 @@ const MessageQueryPage = () => {
|
|||||||
message: t.ERROR,
|
message: t.ERROR,
|
||||||
description: t.QUERY_FAILED,
|
description: t.QUERY_FAILED,
|
||||||
});
|
});
|
||||||
console.error("查询失败:", error);
|
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
@@ -241,7 +239,6 @@ const MessageQueryPage = () => {
|
|||||||
message: t.ERROR,
|
message: t.ERROR,
|
||||||
description: t.RESEND_FAILED,
|
description: t.RESEND_FAILED,
|
||||||
});
|
});
|
||||||
console.error("重发失败:", error);
|
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
// Optionally, you might want to refresh the message detail after resend
|
// Optionally, you might want to refresh the message detail after resend
|
||||||
@@ -455,7 +452,6 @@ const MessageQueryPage = () => {
|
|||||||
</Button>
|
</Button>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Form>
|
</Form>
|
||||||
{/* Message ID 查询结果通常直接弹窗显示,这里不需要表格 */}
|
|
||||||
</div>
|
</div>
|
||||||
</TabPane>
|
</TabPane>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
|
@@ -15,12 +15,12 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { useState, useEffect } from 'react';
|
import React, {useEffect, useState} from 'react';
|
||||||
import { Select, Button, Switch, Input, Typography, Space, message } from 'antd';
|
import {Button, Input, message, Select, Space, Switch, Typography} from 'antd';
|
||||||
import {remoteApi} from '../../api/remoteApi/remoteApi';
|
import {remoteApi} from '../../api/remoteApi/remoteApi';
|
||||||
|
|
||||||
const { Title } = Typography;
|
const {Title} = Typography;
|
||||||
const { Option } = Select;
|
const {Option} = Select;
|
||||||
|
|
||||||
const Ops = () => {
|
const Ops = () => {
|
||||||
const [namesrvAddrList, setNamesrvAddrList] = useState([]);
|
const [namesrvAddrList, setNamesrvAddrList] = useState([]);
|
||||||
|
@@ -15,16 +15,16 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { useState, useEffect } from 'react';
|
import React, {useEffect, useState} from 'react';
|
||||||
import { Modal, Button, Select, Input, Card, Row, Col, notification, Spin } from 'antd';
|
import {Button, Card, Col, Input, Modal, notification, Row, Select, Spin} from 'antd';
|
||||||
import { useLanguage } from '../../i18n/LanguageContext';
|
import {useLanguage} from '../../i18n/LanguageContext';
|
||||||
import { remoteApi } from "../../api/remoteApi/remoteApi";
|
import {remoteApi} from "../../api/remoteApi/remoteApi";
|
||||||
|
|
||||||
|
|
||||||
const { Option } = Select;
|
const {Option} = Select;
|
||||||
|
|
||||||
const ProxyManager = () => {
|
const ProxyManager = () => {
|
||||||
const { t } = useLanguage();
|
const {t} = useLanguage();
|
||||||
|
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [proxyAddrList, setProxyAddrList] = useState([]);
|
const [proxyAddrList, setProxyAddrList] = useState([]);
|
||||||
@@ -47,7 +47,7 @@ const ProxyManager = () => {
|
|||||||
remoteApi.queryProxyHomePage((resp) => {
|
remoteApi.queryProxyHomePage((resp) => {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
if (resp.status === 0) {
|
if (resp.status === 0) {
|
||||||
const { proxyAddrList, currentProxyAddr } = resp.data;
|
const {proxyAddrList, currentProxyAddr} = resp.data;
|
||||||
setProxyAddrList(proxyAddrList || []);
|
setProxyAddrList(proxyAddrList || []);
|
||||||
setSelectedProxy(currentProxyAddr || (proxyAddrList && proxyAddrList.length > 0 ? proxyAddrList[0] : ''));
|
setSelectedProxy(currentProxyAddr || (proxyAddrList && proxyAddrList.length > 0 ? proxyAddrList[0] : ''));
|
||||||
|
|
||||||
@@ -58,7 +58,7 @@ const ProxyManager = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
notificationApi.error({ message: resp.errMsg || t.FETCH_PROXY_LIST_FAILED, duration: 2 });
|
notificationApi.error({message: resp.errMsg || t.FETCH_PROXY_LIST_FAILED, duration: 2});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}, [t]);
|
}, [t]);
|
||||||
@@ -71,7 +71,10 @@ const ProxyManager = () => {
|
|||||||
|
|
||||||
const handleAddProxyAddr = () => {
|
const handleAddProxyAddr = () => {
|
||||||
if (!newProxyAddr.trim()) {
|
if (!newProxyAddr.trim()) {
|
||||||
notificationApi.warning({ message: t.INPUT_PROXY_ADDR_REQUIRED || "Please input a new proxy address.", duration: 2 });
|
notificationApi.warning({
|
||||||
|
message: t.INPUT_PROXY_ADDR_REQUIRED || "Please input a new proxy address.",
|
||||||
|
duration: 2
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
@@ -82,28 +85,28 @@ const ProxyManager = () => {
|
|||||||
setProxyAddrList(prevList => [...prevList, newProxyAddr.trim()]);
|
setProxyAddrList(prevList => [...prevList, newProxyAddr.trim()]);
|
||||||
}
|
}
|
||||||
setNewProxyAddr('');
|
setNewProxyAddr('');
|
||||||
notificationApi.info({ message: t.SUCCESS || "SUCCESS", duration: 2 });
|
notificationApi.info({message: t.SUCCESS || "SUCCESS", duration: 2});
|
||||||
} else {
|
} else {
|
||||||
notificationApi.error({ message: resp.errMsg || t.ADD_PROXY_FAILED, duration: 2 });
|
notificationApi.error({message: resp.errMsg || t.ADD_PROXY_FAILED, duration: 2});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Spin spinning={loading} tip={t.LOADING}>
|
<Spin spinning={loading} tip={t.LOADING}>
|
||||||
<div className="container-fluid" style={{ padding: '24px' }} id="deployHistoryList">
|
<div className="container-fluid" style={{padding: '24px'}} id="deployHistoryList">
|
||||||
<Card
|
<Card
|
||||||
title={
|
title={
|
||||||
<div style={{ fontSize: '20px', fontWeight: 'bold' }}>
|
<div style={{fontSize: '20px', fontWeight: 'bold'}}>
|
||||||
ProxyServerAddressList
|
ProxyServerAddressList
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
bordered={false}
|
bordered={false}
|
||||||
>
|
>
|
||||||
<Row gutter={[16, 16]} align="middle">
|
<Row gutter={[16, 16]} align="middle">
|
||||||
<Col flex="auto" style={{ minWidth: 300, maxWidth: 500 }}>
|
<Col flex="auto" style={{minWidth: 300, maxWidth: 500}}>
|
||||||
<Select
|
<Select
|
||||||
style={{ width: '100%' }}
|
style={{width: '100%'}}
|
||||||
value={selectedProxy}
|
value={selectedProxy}
|
||||||
onChange={handleSelectChange}
|
onChange={handleSelectChange}
|
||||||
placeholder={t.SELECT}
|
placeholder={t.SELECT}
|
||||||
@@ -122,14 +125,14 @@ const ProxyManager = () => {
|
|||||||
</Row>
|
</Row>
|
||||||
|
|
||||||
{writeOperationEnabled && (
|
{writeOperationEnabled && (
|
||||||
<Row gutter={[16, 16]} align="middle" style={{ marginTop: 16 }}>
|
<Row gutter={[16, 16]} align="middle" style={{marginTop: 16}}>
|
||||||
<Col>
|
<Col>
|
||||||
<label htmlFor="newProxyAddrInput">ProxyAddr:</label>
|
<label htmlFor="newProxyAddrInput">ProxyAddr:</label>
|
||||||
</Col>
|
</Col>
|
||||||
<Col>
|
<Col>
|
||||||
<Input
|
<Input
|
||||||
id="newProxyAddrInput"
|
id="newProxyAddrInput"
|
||||||
style={{ width: 300 }}
|
style={{width: 300}}
|
||||||
value={newProxyAddr}
|
value={newProxyAddr}
|
||||||
onChange={(e) => setNewProxyAddr(e.target.value)}
|
onChange={(e) => setNewProxyAddr(e.target.value)}
|
||||||
placeholder={t.INPUT_PROXY_ADDR}
|
placeholder={t.INPUT_PROXY_ADDR}
|
||||||
@@ -149,25 +152,26 @@ const ProxyManager = () => {
|
|||||||
onCancel={() => setShowModal(false)}
|
onCancel={() => setShowModal(false)}
|
||||||
title={`${t.PROXY_CONFIG} [${selectedProxy}]`}
|
title={`${t.PROXY_CONFIG} [${selectedProxy}]`}
|
||||||
footer={
|
footer={
|
||||||
<div style={{ textAlign: 'center' }}>
|
<div style={{textAlign: 'center'}}>
|
||||||
<Button onClick={() => setShowModal(false)}>{t.CLOSE}</Button>
|
<Button onClick={() => setShowModal(false)}>{t.CLOSE}</Button>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
width={800}
|
width={800}
|
||||||
bodyStyle={{ maxHeight: '60vh', overflowY: 'auto' }}
|
bodyStyle={{maxHeight: '60vh', overflowY: 'auto'}}
|
||||||
>
|
>
|
||||||
<table className="table table-bordered" style={{ width: '100%' }}>
|
<table className="table table-bordered" style={{width: '100%'}}>
|
||||||
<tbody>
|
<tbody>
|
||||||
{Object.entries(allProxyConfig).length > 0 ? (
|
{Object.entries(allProxyConfig).length > 0 ? (
|
||||||
Object.entries(allProxyConfig).map(([key, value]) => (
|
Object.entries(allProxyConfig).map(([key, value]) => (
|
||||||
<tr key={key}>
|
<tr key={key}>
|
||||||
<td style={{ fontWeight: 500, width: '30%' }}>{key}</td>
|
<td style={{fontWeight: 500, width: '30%'}}>{key}</td>
|
||||||
<td>{value}</td>
|
<td>{value}</td>
|
||||||
</tr>
|
</tr>
|
||||||
))
|
))
|
||||||
) : (
|
) : (
|
||||||
<tr>
|
<tr>
|
||||||
<td colSpan="2" style={{ textAlign: 'center' }}>{t.NO_CONFIG_DATA || "No configuration data available."}</td>
|
<td colSpan="2"
|
||||||
|
style={{textAlign: 'center'}}>{t.NO_CONFIG_DATA || "No configuration data available."}</td>
|
||||||
</tr>
|
</tr>
|
||||||
)}
|
)}
|
||||||
</tbody>
|
</tbody>
|
||||||
|
@@ -173,7 +173,6 @@ const DeployHistoryList = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const filterList = (currentPage) => {
|
const filterList = (currentPage) => {
|
||||||
const lowExceptStr = filterStr.toLowerCase();
|
const lowExceptStr = filterStr.toLowerCase();
|
||||||
const canShowList = allTopicList.filter((topic, index) => {
|
const canShowList = allTopicList.filter((topic, index) => {
|
||||||
@@ -257,7 +256,7 @@ const DeployHistoryList = () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!isUpdate){
|
if (!isUpdate) {
|
||||||
const clusterResult = await remoteApi.getClusterList();
|
const clusterResult = await remoteApi.getClusterList();
|
||||||
if (clusterResult.status === 0) {
|
if (clusterResult.status === 0) {
|
||||||
setAllClusterNameList(Object.keys(clusterResult.data.clusterInfo.clusterAddrTable));
|
setAllClusterNameList(Object.keys(clusterResult.data.clusterInfo.clusterAddrTable));
|
||||||
@@ -276,7 +275,7 @@ const DeployHistoryList = () => {
|
|||||||
if (result.status === 0) {
|
if (result.status === 0) {
|
||||||
messageApi.success(t.TOPIC_OPERATION_SUCCESS);
|
messageApi.success(t.TOPIC_OPERATION_SUCCESS);
|
||||||
closeAddUpdateDialog();
|
closeAddUpdateDialog();
|
||||||
if(!isUpdateMode) {
|
if (!isUpdateMode) {
|
||||||
await getTopicList()
|
await getTopicList()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@@ -17,7 +17,7 @@
|
|||||||
|
|
||||||
const reportWebVitals = onPerfEntry => {
|
const reportWebVitals = onPerfEntry => {
|
||||||
if (onPerfEntry && onPerfEntry instanceof Function) {
|
if (onPerfEntry && onPerfEntry instanceof Function) {
|
||||||
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
|
import('web-vitals').then(({getCLS, getFID, getFCP, getLCP, getTTFB}) => {
|
||||||
getCLS(onPerfEntry);
|
getCLS(onPerfEntry);
|
||||||
getFID(onPerfEntry);
|
getFID(onPerfEntry);
|
||||||
getFCP(onPerfEntry);
|
getFCP(onPerfEntry);
|
||||||
|
@@ -64,7 +64,7 @@ const AppRouter = () => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
remoteApi.setRedirectHandler(() => {
|
remoteApi.setRedirectHandler(() => {
|
||||||
navigate('/login', { replace: true });
|
navigate('/login', {replace: true});
|
||||||
});
|
});
|
||||||
}, [navigate]);
|
}, [navigate]);
|
||||||
|
|
||||||
|
@@ -14,10 +14,10 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import { useEffect } from 'react';
|
import {useEffect} from 'react';
|
||||||
import { useSelector, useDispatch } from 'react-redux';
|
import {useDispatch, useSelector} from 'react-redux';
|
||||||
import { themes, defaultTheme } from '../../assets/styles/theme';
|
import {defaultTheme, themes} from '../../assets/styles/theme';
|
||||||
import { setTheme } from '../actions/themeActions';
|
import {setTheme} from '../actions/themeActions';
|
||||||
|
|
||||||
export const useTheme = () => {
|
export const useTheme = () => {
|
||||||
// 从 Redux store 中取出 currentThemeName
|
// 从 Redux store 中取出 currentThemeName
|
||||||
|
@@ -14,7 +14,7 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import { createStore,combineReducers } from 'redux';
|
import {combineReducers, createStore} from 'redux';
|
||||||
import themeReducer from './reducers/themeReducer';
|
import themeReducer from './reducers/themeReducer';
|
||||||
|
|
||||||
// 组合所有的 reducers
|
// 组合所有的 reducers
|
||||||
|
@@ -15,7 +15,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { SET_THEME } from '../actions/themeActions';
|
import {SET_THEME} from '../actions/themeActions';
|
||||||
|
|
||||||
const getInitialTheme = () => {
|
const getInitialTheme = () => {
|
||||||
return localStorage.getItem('appTheme') || 'default';
|
return localStorage.getItem('appTheme') || 'default';
|
||||||
|
7
pom.xml
7
pom.xml
@@ -16,7 +16,8 @@
|
|||||||
~ limitations under the License.
|
~ limitations under the License.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>org.apache</groupId>
|
<groupId>org.apache</groupId>
|
||||||
@@ -432,7 +433,7 @@
|
|||||||
</configuration>
|
</configuration>
|
||||||
<executions>
|
<executions>
|
||||||
<execution>
|
<execution>
|
||||||
<id>install node </id>
|
<id>install node</id>
|
||||||
<goals>
|
<goals>
|
||||||
<goal>install-node-and-npm</goal>
|
<goal>install-node-and-npm</goal>
|
||||||
</goals>
|
</goals>
|
||||||
@@ -469,7 +470,7 @@
|
|||||||
<configuration>
|
<configuration>
|
||||||
<target>
|
<target>
|
||||||
<copy todir="${project.build.directory}/classes/public">
|
<copy todir="${project.build.directory}/classes/public">
|
||||||
<fileset dir="${project.basedir}/frontend-new/build" />
|
<fileset dir="${project.basedir}/frontend-new/build"/>
|
||||||
</copy>
|
</copy>
|
||||||
</target>
|
</target>
|
||||||
</configuration>
|
</configuration>
|
||||||
|
@@ -91,6 +91,10 @@ public class RMQConfigure {
|
|||||||
@Getter
|
@Getter
|
||||||
private Integer clientCallbackExecutorThreads = 4;
|
private Integer clientCallbackExecutorThreads = 4;
|
||||||
|
|
||||||
|
@Setter
|
||||||
|
@Getter
|
||||||
|
private String authMode = "file";
|
||||||
|
|
||||||
public void setProxyAddrs(List<String> proxyAddrs) {
|
public void setProxyAddrs(List<String> proxyAddrs) {
|
||||||
this.proxyAddrs = proxyAddrs;
|
this.proxyAddrs = proxyAddrs;
|
||||||
if (CollectionUtils.isNotEmpty(proxyAddrs)) {
|
if (CollectionUtils.isNotEmpty(proxyAddrs)) {
|
||||||
@@ -112,10 +116,12 @@ public class RMQConfigure {
|
|||||||
logger.info("setNameSrvAddrByProperty nameSrvAddr={}", namesrvAddr);
|
logger.info("setNameSrvAddrByProperty nameSrvAddr={}", namesrvAddr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isACLEnabled() {
|
public boolean isACLEnabled() {
|
||||||
return !(StringUtils.isAnyBlank(this.accessKey, this.secretKey) ||
|
return !(StringUtils.isAnyBlank(this.accessKey, this.secretKey) ||
|
||||||
StringUtils.isAnyEmpty(this.accessKey, this.secretKey));
|
StringUtils.isAnyEmpty(this.accessKey, this.secretKey));
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getRocketMqDashboardDataPath() {
|
public String getRocketMqDashboardDataPath() {
|
||||||
return dataPath;
|
return dataPath;
|
||||||
}
|
}
|
||||||
|
@@ -23,6 +23,7 @@ import org.apache.rocketmq.dashboard.model.request.UserUpdateRequest;
|
|||||||
import org.apache.rocketmq.dashboard.service.impl.AclServiceImpl;
|
import org.apache.rocketmq.dashboard.service.impl.AclServiceImpl;
|
||||||
import org.apache.rocketmq.remoting.protocol.body.UserInfo;
|
import org.apache.rocketmq.remoting.protocol.body.UserInfo;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Controller;
|
||||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
@@ -31,24 +32,23 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
|||||||
import org.springframework.web.bind.annotation.RequestMethod;
|
import org.springframework.web.bind.annotation.RequestMethod;
|
||||||
import org.springframework.web.bind.annotation.RequestParam;
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
import org.springframework.web.bind.annotation.ResponseBody;
|
import org.springframework.web.bind.annotation.ResponseBody;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@RestController
|
@Controller
|
||||||
@RequestMapping("/acl")
|
@RequestMapping("/acl")
|
||||||
public class AclController {
|
public class AclController {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private AclServiceImpl aclService;
|
private AclServiceImpl aclService;
|
||||||
|
|
||||||
@GetMapping("/listUsers")
|
@GetMapping("/users.query")
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
public List<UserInfo> listUsers(@RequestParam(required = false) String brokerAddress) {
|
public List<UserInfo> listUsers(@RequestParam(required = false) String brokerAddress) {
|
||||||
return aclService.listUsers(brokerAddress);
|
return aclService.listUsers(brokerAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/listAcls")
|
@GetMapping("/acls.query")
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
public Object listAcls(
|
public Object listAcls(
|
||||||
@RequestParam(required = false) String brokerAddress,
|
@RequestParam(required = false) String brokerAddress,
|
||||||
@@ -56,34 +56,34 @@ public class AclController {
|
|||||||
return aclService.listAcls(brokerAddress, searchParam);
|
return aclService.listAcls(brokerAddress, searchParam);
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/createAcl")
|
@PostMapping("/createAcl.do")
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
public Object createAcl(@RequestBody PolicyRequest request) {
|
public Object createAcl(@RequestBody PolicyRequest request) {
|
||||||
aclService.createAcl(request);
|
aclService.createAcl(request);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@DeleteMapping("/deleteUser")
|
@DeleteMapping("/deleteUser.do")
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
public Object deleteUser(@RequestParam(required = false) String brokerAddress, @RequestParam String username) {
|
public Object deleteUser(@RequestParam(required = false) String brokerAddress, @RequestParam String username) {
|
||||||
aclService.deleteUser(brokerAddress, username);
|
aclService.deleteUser(brokerAddress, username);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequestMapping(value = "/updateUser", method = RequestMethod.POST, produces = "application/json;charset=UTF-8")
|
@RequestMapping(value = "/updateUser.do", method = RequestMethod.POST, produces = "application/json;charset=UTF-8")
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
public Object updateUser(@RequestBody UserUpdateRequest request) {
|
public Object updateUser(@RequestBody UserUpdateRequest request) {
|
||||||
aclService.updateUser(request.getBrokerAddress(), request.getUserInfo());
|
aclService.updateUser(request.getBrokerAddress(), request.getUserInfo());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/createUser")
|
@PostMapping("/createUser.do")
|
||||||
public Object createUser(@RequestBody UserCreateRequest request) {
|
public Object createUser(@RequestBody UserCreateRequest request) {
|
||||||
aclService.createUser(request.getBrokerAddress(), request.getUserInfo());
|
aclService.createUser(request.getBrokerAddress(), request.getUserInfo());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@DeleteMapping("/deleteAcl")
|
@DeleteMapping("/deleteAcl.do")
|
||||||
public Object deleteAcl(
|
public Object deleteAcl(
|
||||||
@RequestParam(required = false) String brokerAddress,
|
@RequestParam(required = false) String brokerAddress,
|
||||||
@RequestParam String subject,
|
@RequestParam String subject,
|
||||||
@@ -92,7 +92,7 @@ public class AclController {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequestMapping(value = "/updateAcl", method = RequestMethod.POST, produces = "application/json;charset=UTF-8")
|
@RequestMapping(value = "/updateAcl.do", method = RequestMethod.POST, produces = "application/json;charset=UTF-8")
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
public Object updateAcl(@RequestBody PolicyRequest request) {
|
public Object updateAcl(@RequestBody PolicyRequest request) {
|
||||||
aclService.updateAcl(request);
|
aclService.updateAcl(request);
|
||||||
|
@@ -47,7 +47,7 @@ public class DashboardController {
|
|||||||
if (Strings.isNullOrEmpty(topicName)) {
|
if (Strings.isNullOrEmpty(topicName)) {
|
||||||
return dashboardService.queryTopicData(date);
|
return dashboardService.queryTopicData(date);
|
||||||
}
|
}
|
||||||
return dashboardService.queryTopicData(date,topicName);
|
return dashboardService.queryTopicData(date, topicName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequestMapping(value = "/topicCurrent.query", method = RequestMethod.GET)
|
@RequestMapping(value = "/topicCurrent.query", method = RequestMethod.GET)
|
||||||
|
@@ -31,6 +31,7 @@ import org.springframework.web.bind.annotation.ResponseBody;
|
|||||||
public class ProxyController {
|
public class ProxyController {
|
||||||
@Resource
|
@Resource
|
||||||
private ProxyService proxyService;
|
private ProxyService proxyService;
|
||||||
|
|
||||||
@RequestMapping(value = "/homePage.query", method = RequestMethod.GET)
|
@RequestMapping(value = "/homePage.query", method = RequestMethod.GET)
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
public Object homePage() {
|
public Object homePage() {
|
||||||
|
@@ -72,7 +72,8 @@ public class TestController {
|
|||||||
|
|
||||||
new Thread(new Runnable() {
|
new Thread(new Runnable() {
|
||||||
|
|
||||||
@Override public void run() {
|
@Override
|
||||||
|
public void run() {
|
||||||
|
|
||||||
int i = 0;
|
int i = 0;
|
||||||
while (true) {
|
while (true) {
|
||||||
@@ -85,13 +86,11 @@ public class TestController {
|
|||||||
Thread.sleep(1000L);
|
Thread.sleep(1000L);
|
||||||
SendResult sendResult = producer.send(msg);
|
SendResult sendResult = producer.send(msg);
|
||||||
logger.info("sendMessage={}", JsonUtil.obj2String(sendResult));
|
logger.info("sendMessage={}", JsonUtil.obj2String(sendResult));
|
||||||
}
|
} catch (Exception e) {
|
||||||
catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
try {
|
try {
|
||||||
Thread.sleep(1000);
|
Thread.sleep(1000);
|
||||||
}
|
} catch (Exception ignore) {
|
||||||
catch (Exception ignore) {
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -75,7 +75,7 @@ public class TopicController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@RequestMapping(value = "/createOrUpdate.do", method = { RequestMethod.POST})
|
@RequestMapping(value = "/createOrUpdate.do", method = {RequestMethod.POST})
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
public Object topicCreateOrUpdateRequest(@RequestBody TopicConfigInfo topicCreateOrUpdateRequest) {
|
public Object topicCreateOrUpdateRequest(@RequestBody TopicConfigInfo topicCreateOrUpdateRequest) {
|
||||||
Preconditions.checkArgument(CollectionUtils.isNotEmpty(topicCreateOrUpdateRequest.getBrokerNameList()) || CollectionUtils.isNotEmpty(topicCreateOrUpdateRequest.getClusterNameList()),
|
Preconditions.checkArgument(CollectionUtils.isNotEmpty(topicCreateOrUpdateRequest.getBrokerNameList()) || CollectionUtils.isNotEmpty(topicCreateOrUpdateRequest.getClusterNameList()),
|
||||||
|
@@ -29,7 +29,6 @@ import jakarta.servlet.http.HttpServletResponse;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@WebFilter(urlPatterns = "/*", filterName = "httpBasicAuthorizedFilter")
|
@WebFilter(urlPatterns = "/*", filterName = "httpBasicAuthorizedFilter")
|
||||||
public class HttpBasicAuthorizedFilter implements Filter {
|
public class HttpBasicAuthorizedFilter implements Filter {
|
||||||
|
|
||||||
|
@@ -25,7 +25,9 @@ import java.util.Map;
|
|||||||
|
|
||||||
public class MessageView {
|
public class MessageView {
|
||||||
|
|
||||||
/** from MessageExt **/
|
/**
|
||||||
|
* from MessageExt
|
||||||
|
**/
|
||||||
private int queueId;
|
private int queueId;
|
||||||
private int storeSize;
|
private int storeSize;
|
||||||
private long queueOffset;
|
private long queueOffset;
|
||||||
@@ -41,13 +43,17 @@ public class MessageView {
|
|||||||
private long preparedTransactionOffset;
|
private long preparedTransactionOffset;
|
||||||
/**from MessageExt**/
|
/**from MessageExt**/
|
||||||
|
|
||||||
/** from Message **/
|
/**
|
||||||
|
* from Message
|
||||||
|
**/
|
||||||
private String topic;
|
private String topic;
|
||||||
private int flag;
|
private int flag;
|
||||||
private Map<String, String> properties;
|
private Map<String, String> properties;
|
||||||
private String messageBody; // body
|
private String messageBody; // body
|
||||||
|
|
||||||
/** from Message **/
|
/**
|
||||||
|
* from Message
|
||||||
|
**/
|
||||||
|
|
||||||
public static MessageView fromMessageExt(MessageExt messageExt) {
|
public static MessageView fromMessageExt(MessageExt messageExt) {
|
||||||
MessageView messageView = new MessageView();
|
MessageView messageView = new MessageView();
|
||||||
|
@@ -19,8 +19,8 @@ package org.apache.rocketmq.dashboard.model;
|
|||||||
import org.hibernate.validator.constraints.Range;
|
import org.hibernate.validator.constraints.Range;
|
||||||
|
|
||||||
public class User {
|
public class User {
|
||||||
public static final int ORDINARY = 0;
|
public static final int SUPER = 0;
|
||||||
public static final int ADMIN = 1;
|
public static final int NORMAL = 1;
|
||||||
|
|
||||||
private long id;
|
private long id;
|
||||||
private String name;
|
private String name;
|
||||||
|
@@ -15,6 +15,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.apache.rocketmq.dashboard.model.request;
|
package org.apache.rocketmq.dashboard.model.request;
|
||||||
|
|
||||||
import com.google.common.base.Objects;
|
import com.google.common.base.Objects;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -24,7 +25,9 @@ public class TopicConfigInfo {
|
|||||||
private List<String> clusterNameList;
|
private List<String> clusterNameList;
|
||||||
private List<String> brokerNameList;
|
private List<String> brokerNameList;
|
||||||
|
|
||||||
/** topicConfig */
|
/**
|
||||||
|
* topicConfig
|
||||||
|
*/
|
||||||
private String topicName;
|
private String topicName;
|
||||||
private int writeQueueNums;
|
private int writeQueueNums;
|
||||||
private int readQueueNums;
|
private int readQueueNums;
|
||||||
@@ -32,6 +35,7 @@ public class TopicConfigInfo {
|
|||||||
private boolean order;
|
private boolean order;
|
||||||
|
|
||||||
private String messageType;
|
private String messageType;
|
||||||
|
|
||||||
public List<String> getClusterNameList() {
|
public List<String> getClusterNameList() {
|
||||||
return clusterNameList;
|
return clusterNameList;
|
||||||
}
|
}
|
||||||
@@ -40,8 +44,9 @@ public class TopicConfigInfo {
|
|||||||
this.clusterNameList = clusterNameList;
|
this.clusterNameList = clusterNameList;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** topicConfig */
|
/**
|
||||||
|
* topicConfig
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
public List<String> getBrokerNameList() {
|
public List<String> getBrokerNameList() {
|
||||||
@@ -102,8 +107,6 @@ public class TopicConfigInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
if (this == o)
|
if (this == o)
|
||||||
@@ -121,7 +124,7 @@ public class TopicConfigInfo {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return Objects.hashCode(topicName, writeQueueNums, readQueueNums, perm, order,messageType);
|
return Objects.hashCode(topicName, writeQueueNums, readQueueNums, perm, order, messageType);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -17,9 +17,11 @@
|
|||||||
|
|
||||||
package org.apache.rocketmq.dashboard.model.request;
|
package org.apache.rocketmq.dashboard.model.request;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
public class UserInfoParam {
|
public class UserInfoParam {
|
||||||
private String username;
|
private String username;
|
||||||
private String password;
|
private String password;
|
||||||
|
@@ -29,6 +29,7 @@ import java.util.Set;
|
|||||||
public abstract class AbstractCommonService {
|
public abstract class AbstractCommonService {
|
||||||
@Resource
|
@Resource
|
||||||
protected MQAdminExt mqAdminExt;
|
protected MQAdminExt mqAdminExt;
|
||||||
|
|
||||||
protected final Set<String> changeToBrokerNameSet(Map<String, Set<String>> clusterAddrTable,
|
protected final Set<String> changeToBrokerNameSet(Map<String, Set<String>> clusterAddrTable,
|
||||||
List<String> clusterNameList, List<String> brokerNameList) {
|
List<String> clusterNameList, List<String> brokerNameList) {
|
||||||
Set<String> finalBrokerNameList = Sets.newHashSet();
|
Set<String> finalBrokerNameList = Sets.newHashSet();
|
||||||
@@ -37,8 +38,7 @@ public abstract class AbstractCommonService {
|
|||||||
for (String clusterName : clusterNameList) {
|
for (String clusterName : clusterNameList) {
|
||||||
finalBrokerNameList.addAll(clusterAddrTable.get(clusterName));
|
finalBrokerNameList.addAll(clusterAddrTable.get(clusterName));
|
||||||
}
|
}
|
||||||
}
|
} catch (Exception e) {
|
||||||
catch (Exception e) {
|
|
||||||
Throwables.throwIfUnchecked(e);
|
Throwables.throwIfUnchecked(e);
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
|
@@ -31,7 +31,7 @@ import java.util.Map;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
public interface ConsumerService {
|
public interface ConsumerService {
|
||||||
List<GroupConsumeInfo> queryGroupList(boolean skipSysGroup,String address);
|
List<GroupConsumeInfo> queryGroupList(boolean skipSysGroup, String address);
|
||||||
|
|
||||||
GroupConsumeInfo queryGroup(String consumerGroup, String address);
|
GroupConsumeInfo queryGroup(String consumerGroup, String address);
|
||||||
|
|
||||||
|
@@ -39,8 +39,7 @@ public interface MessageService {
|
|||||||
/**
|
/**
|
||||||
* @param topic
|
* @param topic
|
||||||
* @param begin
|
* @param begin
|
||||||
* @param end
|
* @param end org.apache.rocketmq.tools.command.message.PrintMessageSubCommand
|
||||||
* org.apache.rocketmq.tools.command.message.PrintMessageSubCommand
|
|
||||||
*/
|
*/
|
||||||
List<MessageView> queryMessageByTopic(final String topic, final long begin,
|
List<MessageView> queryMessageByTopic(final String topic, final long begin,
|
||||||
final long end);
|
final long end);
|
||||||
@@ -54,6 +53,4 @@ public interface MessageService {
|
|||||||
MessagePage queryMessageByPage(MessageQuery query);
|
MessagePage queryMessageByPage(MessageQuery query);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -27,7 +27,7 @@ public interface OpsService {
|
|||||||
|
|
||||||
String getNameSvrList();
|
String getNameSvrList();
|
||||||
|
|
||||||
Map<CheckerType,Object> rocketMqStatusCheck();
|
Map<CheckerType, Object> rocketMqStatusCheck();
|
||||||
|
|
||||||
boolean updateIsVIPChannel(String useVIPChannel);
|
boolean updateIsVIPChannel(String useVIPChannel);
|
||||||
|
|
||||||
|
@@ -94,7 +94,8 @@ public class MQAdminExtImpl implements MQAdminExt {
|
|||||||
private Logger logger = LoggerFactory.getLogger(MQAdminExtImpl.class);
|
private Logger logger = LoggerFactory.getLogger(MQAdminExtImpl.class);
|
||||||
|
|
||||||
|
|
||||||
public MQAdminExtImpl() {}
|
public MQAdminExtImpl() {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -134,8 +135,7 @@ public class MQAdminExtImpl implements MQAdminExt {
|
|||||||
RemotingCommand response = null;
|
RemotingCommand response = null;
|
||||||
try {
|
try {
|
||||||
response = remotingClient.invokeSync(addr, request, 8000);
|
response = remotingClient.invokeSync(addr, request, 8000);
|
||||||
}
|
} catch (Exception err) {
|
||||||
catch (Exception err) {
|
|
||||||
Throwables.throwIfUnchecked(err);
|
Throwables.throwIfUnchecked(err);
|
||||||
throw new RuntimeException(err);
|
throw new RuntimeException(err);
|
||||||
}
|
}
|
||||||
@@ -566,17 +566,20 @@ public class MQAdminExtImpl implements MQAdminExt {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 4.0.0 added
|
// 4.0.0 added
|
||||||
@Override public void updateNameServerConfig(Properties properties,
|
@Override
|
||||||
|
public void updateNameServerConfig(Properties properties,
|
||||||
List<String> list) throws InterruptedException, RemotingConnectException, UnsupportedEncodingException, RemotingSendRequestException, RemotingTimeoutException, MQClientException, MQBrokerException {
|
List<String> list) throws InterruptedException, RemotingConnectException, UnsupportedEncodingException, RemotingSendRequestException, RemotingTimeoutException, MQClientException, MQBrokerException {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public Map<String, Properties> getNameServerConfig(
|
@Override
|
||||||
|
public Map<String, Properties> getNameServerConfig(
|
||||||
List<String> list) throws InterruptedException, RemotingTimeoutException, RemotingSendRequestException, RemotingConnectException, MQClientException, UnsupportedEncodingException {
|
List<String> list) throws InterruptedException, RemotingTimeoutException, RemotingSendRequestException, RemotingConnectException, MQClientException, UnsupportedEncodingException {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public QueryConsumeQueueResponseBody queryConsumeQueue(String brokerAddr, String topic,
|
@Override
|
||||||
|
public QueryConsumeQueueResponseBody queryConsumeQueue(String brokerAddr, String topic,
|
||||||
int queueId, long index, int count,
|
int queueId, long index, int count,
|
||||||
String consumerGroup) throws InterruptedException, RemotingTimeoutException, RemotingSendRequestException, RemotingConnectException, MQClientException {
|
String consumerGroup) throws InterruptedException, RemotingTimeoutException, RemotingSendRequestException, RemotingConnectException, MQClientException {
|
||||||
return null;
|
return null;
|
||||||
@@ -588,7 +591,8 @@ public class MQAdminExtImpl implements MQAdminExt {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override public boolean resumeCheckHalfMessage(String topic,
|
@Override
|
||||||
|
public boolean resumeCheckHalfMessage(String topic,
|
||||||
String msgId) throws RemotingException, MQClientException, InterruptedException, MQBrokerException {
|
String msgId) throws RemotingException, MQClientException, InterruptedException, MQBrokerException {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@@ -58,7 +58,7 @@ public abstract class AbstractFileStore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract void load(InputStream inputStream);
|
protected abstract void load(InputStream inputStream);
|
||||||
|
|
||||||
private void load() {
|
private void load() {
|
||||||
load(null);
|
load(null);
|
||||||
@@ -66,7 +66,7 @@ public abstract class AbstractFileStore {
|
|||||||
|
|
||||||
private boolean watch() {
|
private boolean watch() {
|
||||||
try {
|
try {
|
||||||
FileWatchService fileWatchService = new FileWatchService(new String[] {filePath}, new FileWatchService.Listener() {
|
FileWatchService fileWatchService = new FileWatchService(new String[]{filePath}, new FileWatchService.Listener() {
|
||||||
@Override
|
@Override
|
||||||
public void onChanged(String path) {
|
public void onChanged(String path) {
|
||||||
log.info("The file changed, reload the context");
|
log.info("The file changed, reload the context");
|
||||||
|
@@ -212,7 +212,6 @@ public class ConsumerServiceImpl extends AbstractCommonService implements Consum
|
|||||||
} else {
|
} else {
|
||||||
consumeInfo.setSubGroupType(subscriptionGroupTable.get(consumerGroup).isConsumeMessageOrderly() ? "FIFO" : "NORMAL");
|
consumeInfo.setSubGroupType(subscriptionGroupTable.get(consumerGroup).isConsumeMessageOrderly() ? "FIFO" : "NORMAL");
|
||||||
}
|
}
|
||||||
consumeInfo.setGroup(consumerGroup);
|
|
||||||
consumeInfo.setUpdateTime(new Date());
|
consumeInfo.setUpdateTime(new Date());
|
||||||
groupConsumeInfoList.add(consumeInfo);
|
groupConsumeInfoList.add(consumeInfo);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@@ -270,6 +269,7 @@ public class ConsumerServiceImpl extends AbstractCommonService implements Consum
|
|||||||
logger.warn("examineConsumeStats or examineConsumerConnectionInfo exception, "
|
logger.warn("examineConsumeStats or examineConsumerConnectionInfo exception, "
|
||||||
+ consumerGroup, e);
|
+ consumerGroup, e);
|
||||||
}
|
}
|
||||||
|
groupConsumeInfo.setGroup(consumerGroup);
|
||||||
return groupConsumeInfo;
|
return groupConsumeInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -96,6 +96,7 @@ public class DashboardCollectServiceImpl implements DashboardCollectService {
|
|||||||
public LoadingCache<String, List<String>> getBrokerMap() {
|
public LoadingCache<String, List<String>> getBrokerMap() {
|
||||||
return brokerMap;
|
return brokerMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public LoadingCache<String, List<String>> getTopicMap() {
|
public LoadingCache<String, List<String>> getTopicMap() {
|
||||||
return topicMap;
|
return topicMap;
|
||||||
@@ -106,8 +107,7 @@ public class DashboardCollectServiceImpl implements DashboardCollectService {
|
|||||||
List<String> strings;
|
List<String> strings;
|
||||||
try {
|
try {
|
||||||
strings = Files.readLines(file, Charsets.UTF_8);
|
strings = Files.readLines(file, Charsets.UTF_8);
|
||||||
}
|
} catch (IOException e) {
|
||||||
catch (IOException e) {
|
|
||||||
Throwables.throwIfUnchecked(e);
|
Throwables.throwIfUnchecked(e);
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
|
@@ -34,6 +34,7 @@ public class DashboardServiceImpl implements DashboardService {
|
|||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private DashboardCollectService dashboardCollectService;
|
private DashboardCollectService dashboardCollectService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param date format yyyy-MM-dd
|
* @param date format yyyy-MM-dd
|
||||||
*/
|
*/
|
||||||
|
@@ -17,13 +17,10 @@
|
|||||||
|
|
||||||
package org.apache.rocketmq.dashboard.service.impl;
|
package org.apache.rocketmq.dashboard.service.impl;
|
||||||
|
|
||||||
import jakarta.annotation.Resource;
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import org.apache.rocketmq.dashboard.config.RMQConfigure;
|
|
||||||
import org.apache.rocketmq.dashboard.service.LoginService;
|
import org.apache.rocketmq.dashboard.service.LoginService;
|
||||||
import org.apache.rocketmq.dashboard.service.UserService;
|
import org.apache.rocketmq.dashboard.service.strategy.UserContext;
|
||||||
import org.apache.rocketmq.dashboard.service.provider.UserInfoProvider;
|
|
||||||
import org.apache.rocketmq.dashboard.util.UserInfoContext;
|
import org.apache.rocketmq.dashboard.util.UserInfoContext;
|
||||||
import org.apache.rocketmq.dashboard.util.WebUtil;
|
import org.apache.rocketmq.dashboard.util.WebUtil;
|
||||||
import org.apache.rocketmq.remoting.protocol.body.UserInfo;
|
import org.apache.rocketmq.remoting.protocol.body.UserInfo;
|
||||||
@@ -33,34 +30,29 @@ import org.springframework.beans.factory.annotation.Autowired;
|
|||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.UnsupportedEncodingException;
|
|
||||||
import java.net.URLEncoder;
|
import java.net.URLEncoder;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class LoginServiceImpl implements LoginService {
|
public class LoginServiceImpl implements LoginService {
|
||||||
private final Logger logger = LoggerFactory.getLogger(this.getClass());
|
private final Logger logger = LoggerFactory.getLogger(this.getClass());
|
||||||
|
|
||||||
@Resource
|
|
||||||
private RMQConfigure rmqConfigure;
|
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private UserService userService;
|
private UserContext userContext;
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private UserInfoProvider userInfoProvider;
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean login(HttpServletRequest request, HttpServletResponse response) {
|
public boolean login(HttpServletRequest request, HttpServletResponse response) {
|
||||||
String username = (String) WebUtil.getValueFromSession(request, WebUtil.USER_NAME);
|
String username = (String) WebUtil.getValueFromSession(request, WebUtil.USER_NAME);
|
||||||
if (username != null) {
|
if (username != null) {
|
||||||
UserInfo userInfo = userInfoProvider.getUserInfoByUsername(username);
|
UserInfo userInfo = userContext.queryByUsername(username);
|
||||||
if (userInfo == null) {
|
if (userInfo == null) {
|
||||||
auth(request, response);
|
auth(request, response);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
UserInfoContext.set(WebUtil.USER_NAME, userInfo);
|
UserInfoContext.set(WebUtil.USER_NAME, userInfo);
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
}
|
}
|
||||||
auth(request, response);
|
auth(request, response);
|
||||||
return false;
|
return false;
|
||||||
@@ -69,11 +61,7 @@ public class LoginServiceImpl implements LoginService {
|
|||||||
protected void auth(HttpServletRequest request, HttpServletResponse response) {
|
protected void auth(HttpServletRequest request, HttpServletResponse response) {
|
||||||
try {
|
try {
|
||||||
String url = WebUtil.getUrl(request);
|
String url = WebUtil.getUrl(request);
|
||||||
try {
|
url = URLEncoder.encode(url, StandardCharsets.UTF_8);
|
||||||
url = URLEncoder.encode(url, "UTF-8");
|
|
||||||
} catch (UnsupportedEncodingException e) {
|
|
||||||
logger.error("url encode:{}", url, e);
|
|
||||||
}
|
|
||||||
logger.debug("redirect url : {}", url);
|
logger.debug("redirect url : {}", url);
|
||||||
WebUtil.redirect(response, request, "/#/login?redirect=" + url);
|
WebUtil.redirect(response, request, "/#/login?redirect=" + url);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
@@ -33,7 +33,6 @@ import org.apache.rocketmq.client.consumer.DefaultMQPullConsumer;
|
|||||||
import org.apache.rocketmq.client.consumer.PullResult;
|
import org.apache.rocketmq.client.consumer.PullResult;
|
||||||
import org.apache.rocketmq.client.consumer.PullStatus;
|
import org.apache.rocketmq.client.consumer.PullStatus;
|
||||||
import org.apache.rocketmq.client.exception.MQClientException;
|
import org.apache.rocketmq.client.exception.MQClientException;
|
||||||
import org.apache.rocketmq.common.MixAll;
|
|
||||||
import org.apache.rocketmq.common.Pair;
|
import org.apache.rocketmq.common.Pair;
|
||||||
import org.apache.rocketmq.common.message.MessageClientIDSetter;
|
import org.apache.rocketmq.common.message.MessageClientIDSetter;
|
||||||
import org.apache.rocketmq.common.message.MessageExt;
|
import org.apache.rocketmq.common.message.MessageExt;
|
||||||
@@ -74,6 +73,9 @@ import java.util.stream.Collectors;
|
|||||||
@Service
|
@Service
|
||||||
public class MessageServiceImpl implements MessageService {
|
public class MessageServiceImpl implements MessageService {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private AutoCloseConsumerWrapper autoCloseConsumerWrapper;
|
||||||
|
|
||||||
private Logger logger = LoggerFactory.getLogger(MessageServiceImpl.class);
|
private Logger logger = LoggerFactory.getLogger(MessageServiceImpl.class);
|
||||||
|
|
||||||
private static final Cache<String, List<QueueOffsetInfo>> CACHE = CacheBuilder.newBuilder()
|
private static final Cache<String, List<QueueOffsetInfo>> CACHE = CacheBuilder.newBuilder()
|
||||||
@@ -128,8 +130,8 @@ public class MessageServiceImpl implements MessageService {
|
|||||||
if (isEnableAcl) {
|
if (isEnableAcl) {
|
||||||
rpcHook = new AclClientRPCHook(new SessionCredentials(configure.getAccessKey(), configure.getSecretKey()));
|
rpcHook = new AclClientRPCHook(new SessionCredentials(configure.getAccessKey(), configure.getSecretKey()));
|
||||||
}
|
}
|
||||||
AutoCloseConsumerWrapper consumerWrapper = new AutoCloseConsumerWrapper();
|
|
||||||
DefaultMQPullConsumer consumer = consumerWrapper.getConsumer(rpcHook, configure.isUseTLS());
|
DefaultMQPullConsumer consumer = autoCloseConsumerWrapper.getConsumer(rpcHook, configure.isUseTLS());
|
||||||
List<MessageView> messageViewList = Lists.newArrayList();
|
List<MessageView> messageViewList = Lists.newArrayList();
|
||||||
try {
|
try {
|
||||||
String subExpression = "*";
|
String subExpression = "*";
|
||||||
@@ -262,8 +264,8 @@ public class MessageServiceImpl implements MessageService {
|
|||||||
if (isEnableAcl) {
|
if (isEnableAcl) {
|
||||||
rpcHook = new AclClientRPCHook(new SessionCredentials(configure.getAccessKey(), configure.getSecretKey()));
|
rpcHook = new AclClientRPCHook(new SessionCredentials(configure.getAccessKey(), configure.getSecretKey()));
|
||||||
}
|
}
|
||||||
AutoCloseConsumerWrapper consumerWrapper = new AutoCloseConsumerWrapper();
|
|
||||||
DefaultMQPullConsumer consumer = consumerWrapper.getConsumer(rpcHook, configure.isUseTLS());
|
DefaultMQPullConsumer consumer = autoCloseConsumerWrapper.getConsumer(rpcHook, configure.isUseTLS());
|
||||||
|
|
||||||
long total = 0;
|
long total = 0;
|
||||||
List<QueueOffsetInfo> queueOffsetInfos = new ArrayList<>();
|
List<QueueOffsetInfo> queueOffsetInfos = new ArrayList<>();
|
||||||
@@ -402,8 +404,8 @@ public class MessageServiceImpl implements MessageService {
|
|||||||
if (isEnableAcl) {
|
if (isEnableAcl) {
|
||||||
rpcHook = new AclClientRPCHook(new SessionCredentials(configure.getAccessKey(), configure.getSecretKey()));
|
rpcHook = new AclClientRPCHook(new SessionCredentials(configure.getAccessKey(), configure.getSecretKey()));
|
||||||
}
|
}
|
||||||
AutoCloseConsumerWrapper consumerWrapper = new AutoCloseConsumerWrapper();
|
|
||||||
DefaultMQPullConsumer consumer = consumerWrapper.getConsumer(rpcHook, configure.isUseTLS());
|
DefaultMQPullConsumer consumer = autoCloseConsumerWrapper.getConsumer(rpcHook, configure.isUseTLS());
|
||||||
List<MessageView> messageViews = new ArrayList<>();
|
List<MessageView> messageViews = new ArrayList<>();
|
||||||
|
|
||||||
long offset = query.getPageNum() * query.getPageSize();
|
long offset = query.getPageNum() * query.getPageSize();
|
||||||
@@ -541,9 +543,9 @@ public class MessageServiceImpl implements MessageService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public DefaultMQPullConsumer buildDefaultMQPullConsumer(RPCHook rpcHook, boolean useTLS) {
|
// public DefaultMQPullConsumer buildDefaultMQPullConsumer(RPCHook rpcHook, boolean useTLS) {
|
||||||
DefaultMQPullConsumer consumer = new DefaultMQPullConsumer(MixAll.TOOLS_CONSUMER_GROUP, rpcHook);
|
// DefaultMQPullConsumer consumer = new DefaultMQPullConsumer(MixAll.TOOLS_CONSUMER_GROUP, rpcHook);
|
||||||
consumer.setUseTLS(useTLS);
|
// consumer.setUseTLS(useTLS);
|
||||||
return consumer;
|
// return consumer;
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
@@ -81,8 +81,7 @@ public class MonitorServiceImpl implements MonitorService {
|
|||||||
private void writeDataJsonToFile(String path, String dataStr) {
|
private void writeDataJsonToFile(String path, String dataStr) {
|
||||||
try {
|
try {
|
||||||
MixAll.string2File(dataStr, path);
|
MixAll.string2File(dataStr, path);
|
||||||
}
|
} catch (Exception e) {
|
||||||
catch (Exception e) {
|
|
||||||
Throwables.throwIfUnchecked(e);
|
Throwables.throwIfUnchecked(e);
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
|
@@ -33,8 +33,7 @@ public class ProducerServiceImpl implements ProducerService {
|
|||||||
public ProducerConnection getProducerConnection(String producerGroup, String topic) {
|
public ProducerConnection getProducerConnection(String producerGroup, String topic) {
|
||||||
try {
|
try {
|
||||||
return mqAdminExt.examineProducerConnectionInfo(producerGroup, topic);
|
return mqAdminExt.examineProducerConnectionInfo(producerGroup, topic);
|
||||||
}
|
} catch (Exception e) {
|
||||||
catch (Exception e) {
|
|
||||||
Throwables.throwIfUnchecked(e);
|
Throwables.throwIfUnchecked(e);
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
|
@@ -127,7 +127,7 @@ public class TopicServiceImpl extends AbstractCommonService implements TopicServ
|
|||||||
try {
|
try {
|
||||||
TopicConfigSerializeWrapper topicConfigSerializeWrapper = mqAdminExt.getAllTopicConfig(brokerAddr.getBrokerAddrs().get(0L), 10000L);
|
TopicConfigSerializeWrapper topicConfigSerializeWrapper = mqAdminExt.getAllTopicConfig(brokerAddr.getBrokerAddrs().get(0L), 10000L);
|
||||||
for (TopicConfig topicConfig : topicConfigSerializeWrapper.getTopicConfigTable().values()) {
|
for (TopicConfig topicConfig : topicConfigSerializeWrapper.getTopicConfigTable().values()) {
|
||||||
TopicTypeMeta topicType = classifyTopicType(topicConfig.getTopicName(), topicConfigSerializeWrapper.getTopicConfigTable().get(topicConfig.getTopicName()).getAttributes(),sysTopics.getTopicList());
|
TopicTypeMeta topicType = classifyTopicType(topicConfig.getTopicName(), topicConfigSerializeWrapper.getTopicConfigTable().get(topicConfig.getTopicName()).getAttributes(), sysTopics.getTopicList());
|
||||||
if (names.contains(topicType.getTopicName())) {
|
if (names.contains(topicType.getTopicName())) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -149,7 +149,7 @@ public class TopicServiceImpl extends AbstractCommonService implements TopicServ
|
|||||||
return new TopicTypeList(names, messageTypes);
|
return new TopicTypeList(names, messageTypes);
|
||||||
}
|
}
|
||||||
|
|
||||||
private TopicTypeMeta classifyTopicType(String topicName, Map<String,String> attributes, Set<String> sysTopics) {
|
private TopicTypeMeta classifyTopicType(String topicName, Map<String, String> attributes, Set<String> sysTopics) {
|
||||||
TopicTypeMeta topicType = new TopicTypeMeta();
|
TopicTypeMeta topicType = new TopicTypeMeta();
|
||||||
topicType.setTopicName(topicName);
|
topicType.setTopicName(topicName);
|
||||||
|
|
||||||
|
@@ -17,29 +17,26 @@
|
|||||||
|
|
||||||
package org.apache.rocketmq.dashboard.service.impl;
|
package org.apache.rocketmq.dashboard.service.impl;
|
||||||
|
|
||||||
import jakarta.annotation.Resource;
|
|
||||||
import org.apache.rocketmq.auth.authentication.enums.UserType;
|
import org.apache.rocketmq.auth.authentication.enums.UserType;
|
||||||
import org.apache.rocketmq.dashboard.admin.UserMQAdminPoolManager;
|
import org.apache.rocketmq.dashboard.admin.UserMQAdminPoolManager;
|
||||||
import org.apache.rocketmq.dashboard.config.RMQConfigure;
|
|
||||||
import org.apache.rocketmq.dashboard.model.User;
|
import org.apache.rocketmq.dashboard.model.User;
|
||||||
import org.apache.rocketmq.dashboard.service.UserService;
|
import org.apache.rocketmq.dashboard.service.UserService;
|
||||||
import org.apache.rocketmq.dashboard.service.provider.UserInfoProvider;
|
import org.apache.rocketmq.dashboard.service.strategy.UserContext;
|
||||||
import org.apache.rocketmq.remoting.protocol.body.UserInfo;
|
import org.apache.rocketmq.remoting.protocol.body.UserInfo;
|
||||||
import org.apache.rocketmq.tools.admin.MQAdminExt;
|
import org.apache.rocketmq.tools.admin.MQAdminExt;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class UserServiceImpl implements UserService {
|
public class UserServiceImpl implements UserService {
|
||||||
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(UserServiceImpl.class);
|
private static final Logger log = LoggerFactory.getLogger(UserServiceImpl.class);
|
||||||
|
|
||||||
@Resource
|
|
||||||
private RMQConfigure configure;
|
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private UserInfoProvider userInfoProvider;
|
private UserContext userContext;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private UserMQAdminPoolManager userMQAdminPoolManager;
|
private UserMQAdminPoolManager userMQAdminPoolManager;
|
||||||
@@ -47,7 +44,7 @@ public class UserServiceImpl implements UserService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public User queryByName(String name) {
|
public User queryByName(String name) {
|
||||||
UserInfo userInfo = userInfoProvider.getUserInfoByUsername(name);
|
UserInfo userInfo = userContext.queryByUsername(name);
|
||||||
if (userInfo == null) {
|
if (userInfo == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,67 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.rocketmq.dashboard.service.strategy;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import org.apache.rocketmq.dashboard.service.ClusterInfoService;
|
||||||
|
import org.apache.rocketmq.remoting.protocol.body.ClusterInfo;
|
||||||
|
import org.apache.rocketmq.remoting.protocol.body.UserInfo;
|
||||||
|
import org.apache.rocketmq.remoting.protocol.route.BrokerData;
|
||||||
|
import org.apache.rocketmq.tools.admin.MQAdminExt;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class AclUserStrategy implements UserStrategy {
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(AclUserStrategy.class);
|
||||||
|
|
||||||
|
private final MQAdminExt mqAdminExt;
|
||||||
|
|
||||||
|
private final ClusterInfoService clusterInfoService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UserInfo getUserInfoByUsername(String username) {
|
||||||
|
ClusterInfo clusterInfo = clusterInfoService.get();
|
||||||
|
|
||||||
|
if (clusterInfo == null || clusterInfo.getBrokerAddrTable() == null || clusterInfo.getBrokerAddrTable().isEmpty()) {
|
||||||
|
log.warn("Cluster information is not available or has no broker addresses.");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
for (BrokerData brokerLiveInfo : clusterInfo.getBrokerAddrTable().values()) {
|
||||||
|
if (brokerLiveInfo == null || brokerLiveInfo.getBrokerAddrs() == null || brokerLiveInfo.getBrokerAddrs().isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
String brokerAddr = brokerLiveInfo.getBrokerAddrs().get(0L); // Assuming 0L is the primary address
|
||||||
|
if (brokerAddr == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
UserInfo userInfo = mqAdminExt.getUser(brokerAddr, username);
|
||||||
|
if (userInfo != null) {
|
||||||
|
return userInfo;
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("Failed to get user {} from broker {}. Trying next broker if available. Error: {}", username, brokerAddr, e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,119 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.rocketmq.dashboard.service.strategy;
|
||||||
|
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import org.apache.rocketmq.dashboard.config.RMQConfigure;
|
||||||
|
import org.apache.rocketmq.dashboard.exception.ServiceException;
|
||||||
|
import org.apache.rocketmq.dashboard.model.User;
|
||||||
|
import org.apache.rocketmq.dashboard.service.impl.AbstractFileStore;
|
||||||
|
import org.apache.rocketmq.remoting.protocol.body.UserInfo;
|
||||||
|
import org.springframework.beans.factory.InitializingBean;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.io.FileReader;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Properties;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class FileUserStrategy implements UserStrategy, InitializingBean {
|
||||||
|
@Resource
|
||||||
|
private RMQConfigure configure;
|
||||||
|
|
||||||
|
private FileBasedUserInfoStore fileBasedUserInfoStore;
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void afterPropertiesSet() throws Exception {
|
||||||
|
if (configure.isLoginRequired()) {
|
||||||
|
fileBasedUserInfoStore = new FileBasedUserInfoStore(configure);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UserInfo getUserInfoByUsername(String username) {
|
||||||
|
User user = fileBasedUserInfoStore.queryByUsernameAndPassword(username);
|
||||||
|
if (user != null) {
|
||||||
|
return UserInfo.of(user.getName(), user.getPassword(), user.getType() == 0 ? "normal" : "super");
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class FileBasedUserInfoStore extends AbstractFileStore {
|
||||||
|
private static final String FILE_NAME = "users.properties";
|
||||||
|
|
||||||
|
private static Map<String, User> userMap = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
public FileBasedUserInfoStore(RMQConfigure configure) {
|
||||||
|
super(configure, FILE_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void load(InputStream inputStream) {
|
||||||
|
Properties prop = new Properties();
|
||||||
|
try {
|
||||||
|
if (inputStream == null) {
|
||||||
|
prop.load(new FileReader(filePath));
|
||||||
|
} else {
|
||||||
|
prop.load(inputStream);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("load user.properties failed", e);
|
||||||
|
throw new ServiceException(0, String.format("Failed to load loginUserInfo property file: %s", filePath));
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, User> loadUserMap = new HashMap<>();
|
||||||
|
String[] arrs;
|
||||||
|
int role;
|
||||||
|
for (String key : prop.stringPropertyNames()) {
|
||||||
|
String v = prop.getProperty(key);
|
||||||
|
if (v == null)
|
||||||
|
continue;
|
||||||
|
arrs = v.split(",", 2);
|
||||||
|
if (arrs.length == 0) {
|
||||||
|
continue;
|
||||||
|
} else if (arrs.length == 1) {
|
||||||
|
role = 0;
|
||||||
|
} else {
|
||||||
|
role = Integer.parseInt(arrs[1].trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
loadUserMap.put(key, new User(key, arrs[0].trim(), role));
|
||||||
|
}
|
||||||
|
|
||||||
|
userMap.clear();
|
||||||
|
userMap.putAll(loadUserMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
public User queryByName(String name) {
|
||||||
|
return userMap.get(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public User queryByUsernameAndPassword(@NotNull String username) {
|
||||||
|
User user = queryByName(username);
|
||||||
|
if (user != null) {
|
||||||
|
return user.cloneOne();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.rocketmq.dashboard.service.strategy;
|
||||||
|
|
||||||
|
import jakarta.annotation.PostConstruct;
|
||||||
|
import org.apache.rocketmq.remoting.protocol.body.UserInfo;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class UserContext {
|
||||||
|
private UserStrategy userStrategy;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private Map<String, UserStrategy> userStrategies;
|
||||||
|
|
||||||
|
@Value("${rocketmq.config.authMode}")
|
||||||
|
private String authMode;
|
||||||
|
|
||||||
|
@PostConstruct
|
||||||
|
public void init() {
|
||||||
|
switch (authMode.toLowerCase()) {
|
||||||
|
case "acl":
|
||||||
|
this.userStrategy = userStrategies.get("aclUserStrategy");
|
||||||
|
break;
|
||||||
|
case "file":
|
||||||
|
default:
|
||||||
|
this.userStrategy = userStrategies.get("fileUserStrategy");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserInfo queryByUsername(String username) {
|
||||||
|
return userStrategy.getUserInfoByUsername(username);
|
||||||
|
}
|
||||||
|
}
|
@@ -15,11 +15,10 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.apache.rocketmq.dashboard.admin;
|
package org.apache.rocketmq.dashboard.service.strategy;
|
||||||
|
|
||||||
import org.apache.rocketmq.tools.admin.MQAdminExt;
|
import org.apache.rocketmq.remoting.protocol.body.UserInfo;
|
||||||
|
|
||||||
@FunctionalInterface
|
public interface UserStrategy {
|
||||||
public interface MQAdminExtCallback<T> {
|
UserInfo getUserInfoByUsername(String username);
|
||||||
T doInMQAdminExt(MQAdminExt mqAdminExt) throws Exception;
|
|
||||||
}
|
}
|
@@ -23,6 +23,7 @@ import org.apache.rocketmq.common.MixAll;
|
|||||||
import org.apache.rocketmq.remoting.RPCHook;
|
import org.apache.rocketmq.remoting.RPCHook;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
@@ -32,9 +33,10 @@ import java.util.concurrent.TimeUnit;
|
|||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
|
@Component
|
||||||
public class AutoCloseConsumerWrapper {
|
public class AutoCloseConsumerWrapper {
|
||||||
|
|
||||||
private final Logger logger = LoggerFactory.getLogger(GlobalRestfulResponseBodyAdvice.class);
|
private final Logger logger = LoggerFactory.getLogger(AutoCloseConsumerWrapper.class);
|
||||||
|
|
||||||
private static final AtomicReference<DefaultMQPullConsumer> CONSUMER_REF = new AtomicReference<>();
|
private static final AtomicReference<DefaultMQPullConsumer> CONSUMER_REF = new AtomicReference<>();
|
||||||
private final AtomicBoolean isTaskScheduled = new AtomicBoolean(false);
|
private final AtomicBoolean isTaskScheduled = new AtomicBoolean(false);
|
||||||
@@ -77,7 +79,10 @@ public class AutoCloseConsumerWrapper {
|
|||||||
|
|
||||||
protected DefaultMQPullConsumer createNewConsumer(RPCHook rpcHook, Boolean useTLS) {
|
protected DefaultMQPullConsumer createNewConsumer(RPCHook rpcHook, Boolean useTLS) {
|
||||||
return new DefaultMQPullConsumer(MixAll.TOOLS_CONSUMER_GROUP, rpcHook) {
|
return new DefaultMQPullConsumer(MixAll.TOOLS_CONSUMER_GROUP, rpcHook) {
|
||||||
{ setUseTLS(useTLS); } };
|
{
|
||||||
|
setUseTLS(useTLS);
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startIdleCheckTask() {
|
private void startIdleCheckTask() {
|
||||||
|
@@ -37,8 +37,7 @@ public class GlobalExceptionHandler {
|
|||||||
if (ex instanceof ServiceException) {
|
if (ex instanceof ServiceException) {
|
||||||
logger.error("Occur service exception: {}", ex.getMessage());
|
logger.error("Occur service exception: {}", ex.getMessage());
|
||||||
value = new JsonResult<Object>(((ServiceException) ex).getCode(), ex.getMessage());
|
value = new JsonResult<Object>(((ServiceException) ex).getCode(), ex.getMessage());
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
logger.error("op=global_exception_handler_print_error", ex);
|
logger.error("op=global_exception_handler_print_error", ex);
|
||||||
value = new JsonResult<Object>(-1, ex.getMessage() == null ? ex.toString() : ex.getMessage());
|
value = new JsonResult<Object>(-1, ex.getMessage() == null ? ex.toString() : ex.getMessage());
|
||||||
}
|
}
|
||||||
|
@@ -46,9 +46,8 @@ public class GlobalRestfulResponseBodyAdvice implements ResponseBodyAdvice<Objec
|
|||||||
}
|
}
|
||||||
JsonResult value;
|
JsonResult value;
|
||||||
if (obj instanceof JsonResult) {
|
if (obj instanceof JsonResult) {
|
||||||
value = (JsonResult)obj;
|
value = (JsonResult) obj;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
value = new JsonResult(obj);
|
value = new JsonResult(obj);
|
||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
|
@@ -87,8 +87,7 @@ public class DashboardCollectTask {
|
|||||||
CollectTaskRunnble collectTask = new CollectTaskRunnble(topic, mqAdminExt, dashboardCollectService);
|
CollectTaskRunnble collectTask = new CollectTaskRunnble(topic, mqAdminExt, dashboardCollectService);
|
||||||
collectExecutor.submit(collectTask);
|
collectExecutor.submit(collectTask);
|
||||||
}
|
}
|
||||||
}
|
} catch (Exception err) {
|
||||||
catch (Exception err) {
|
|
||||||
Throwables.throwIfUnchecked(err);
|
Throwables.throwIfUnchecked(err);
|
||||||
throw new RuntimeException(err);
|
throw new RuntimeException(err);
|
||||||
}
|
}
|
||||||
@@ -100,7 +99,6 @@ public class DashboardCollectTask {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Scheduled(cron = "0 0/1 * * * ?")
|
@Scheduled(cron = "0 0/1 * * * ?")
|
||||||
public void collectBroker() {
|
public void collectBroker() {
|
||||||
if (!rmqConfigure.isEnableDashBoardCollect()) {
|
if (!rmqConfigure.isEnableDashBoardCollect()) {
|
||||||
@@ -139,8 +137,7 @@ public class DashboardCollectTask {
|
|||||||
dashboardCollectService.getBrokerMap().put(entry.getValue(), list);
|
dashboardCollectService.getBrokerMap().put(entry.getValue(), list);
|
||||||
}
|
}
|
||||||
log.debug("Broker Collected Data in memory = {}" + JsonUtil.obj2String(dashboardCollectService.getBrokerMap().asMap()));
|
log.debug("Broker Collected Data in memory = {}" + JsonUtil.obj2String(dashboardCollectService.getBrokerMap().asMap()));
|
||||||
}
|
} catch (Exception e) {
|
||||||
catch (Exception e) {
|
|
||||||
Throwables.throwIfUnchecked(e);
|
Throwables.throwIfUnchecked(e);
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
@@ -152,12 +149,10 @@ public class DashboardCollectTask {
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
return mqAdminExt.fetchBrokerRuntimeStats(brokerAddr);
|
return mqAdminExt.fetchBrokerRuntimeStats(brokerAddr);
|
||||||
}
|
} catch (Exception e) {
|
||||||
catch (Exception e) {
|
|
||||||
try {
|
try {
|
||||||
Thread.sleep(1000);
|
Thread.sleep(1000);
|
||||||
}
|
} catch (InterruptedException e1) {
|
||||||
catch (InterruptedException e1) {
|
|
||||||
Throwables.throwIfUnchecked(e1);
|
Throwables.throwIfUnchecked(e1);
|
||||||
throw new RuntimeException(e1);
|
throw new RuntimeException(e1);
|
||||||
}
|
}
|
||||||
@@ -189,16 +184,14 @@ public class DashboardCollectTask {
|
|||||||
Map<String, List<String>> topicFileMap;
|
Map<String, List<String>> topicFileMap;
|
||||||
if (brokerFile.exists()) {
|
if (brokerFile.exists()) {
|
||||||
brokerFileMap = dashboardCollectService.jsonDataFile2map(brokerFile);
|
brokerFileMap = dashboardCollectService.jsonDataFile2map(brokerFile);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
brokerFileMap = Maps.newHashMap();
|
brokerFileMap = Maps.newHashMap();
|
||||||
Files.createParentDirs(brokerFile);
|
Files.createParentDirs(brokerFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (topicFile.exists()) {
|
if (topicFile.exists()) {
|
||||||
topicFileMap = dashboardCollectService.jsonDataFile2map(topicFile);
|
topicFileMap = dashboardCollectService.jsonDataFile2map(topicFile);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
topicFileMap = Maps.newHashMap();
|
topicFileMap = Maps.newHashMap();
|
||||||
Files.createParentDirs(topicFile);
|
Files.createParentDirs(topicFile);
|
||||||
}
|
}
|
||||||
@@ -211,8 +204,7 @@ public class DashboardCollectTask {
|
|||||||
log.debug("Broker Collected Data in memory = {}" + JsonUtil.obj2String(dashboardCollectService.getBrokerMap().asMap()));
|
log.debug("Broker Collected Data in memory = {}" + JsonUtil.obj2String(dashboardCollectService.getBrokerMap().asMap()));
|
||||||
log.debug("Topic Collected Data in memory = {}" + JsonUtil.obj2String(dashboardCollectService.getTopicMap().asMap()));
|
log.debug("Topic Collected Data in memory = {}" + JsonUtil.obj2String(dashboardCollectService.getTopicMap().asMap()));
|
||||||
|
|
||||||
}
|
} catch (IOException e) {
|
||||||
catch (IOException e) {
|
|
||||||
Throwables.throwIfUnchecked(e);
|
Throwables.throwIfUnchecked(e);
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
@@ -224,8 +216,7 @@ public class DashboardCollectTask {
|
|||||||
Map<String, List<String>> resultMap = Maps.newHashMap();
|
Map<String, List<String>> resultMap = Maps.newHashMap();
|
||||||
if (fileMap.size() == 0) {
|
if (fileMap.size() == 0) {
|
||||||
resultMap = newMap;
|
resultMap = newMap;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
for (Map.Entry<String, List<String>> entry : fileMap.entrySet()) {
|
for (Map.Entry<String, List<String>> entry : fileMap.entrySet()) {
|
||||||
List<String> oldList = entry.getValue();
|
List<String> oldList = entry.getValue();
|
||||||
List<String> newList = newMap.get(entry.getKey());
|
List<String> newList = newMap.get(entry.getKey());
|
||||||
|
@@ -38,7 +38,7 @@ public class MonitorTask {
|
|||||||
@Resource
|
@Resource
|
||||||
private ConsumerService consumerService;
|
private ConsumerService consumerService;
|
||||||
|
|
||||||
// @Scheduled(cron = "* * * * * ?")
|
// @Scheduled(cron = "* * * * * ?")
|
||||||
public void scanProblemConsumeGroup() {
|
public void scanProblemConsumeGroup() {
|
||||||
for (Map.Entry<String, ConsumerMonitorConfig> configEntry : monitorService.queryConsumerMonitorConfig().entrySet()) {
|
for (Map.Entry<String, ConsumerMonitorConfig> configEntry : monitorService.queryConsumerMonitorConfig().entrySet()) {
|
||||||
GroupConsumeInfo consumeInfo = consumerService.queryGroup(configEntry.getKey(), null);
|
GroupConsumeInfo consumeInfo = consumerService.queryGroup(configEntry.getKey(), null);
|
||||||
|
@@ -34,7 +34,7 @@ public class ExcelUtil {
|
|||||||
String sheetName, Class clazz) throws Exception {
|
String sheetName, Class clazz) throws Exception {
|
||||||
WriteCellStyle headWriteCellStyle = new WriteCellStyle();
|
WriteCellStyle headWriteCellStyle = new WriteCellStyle();
|
||||||
WriteFont writeFont = new WriteFont();
|
WriteFont writeFont = new WriteFont();
|
||||||
writeFont.setFontHeightInPoints((short)12);
|
writeFont.setFontHeightInPoints((short) 12);
|
||||||
writeFont.setFontName("Microsoft YaHei UI");
|
writeFont.setFontName("Microsoft YaHei UI");
|
||||||
headWriteCellStyle.setWriteFont(writeFont);
|
headWriteCellStyle.setWriteFont(writeFont);
|
||||||
headWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
|
headWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
|
||||||
|
@@ -55,8 +55,7 @@ public class JsonUtil {
|
|||||||
public static void writeValue(Writer writer, Object obj) {
|
public static void writeValue(Writer writer, Object obj) {
|
||||||
try {
|
try {
|
||||||
objectMapper.writeValue(writer, obj);
|
objectMapper.writeValue(writer, obj);
|
||||||
}
|
} catch (IOException e) {
|
||||||
catch (IOException e) {
|
|
||||||
Throwables.propagateIfPossible(e);
|
Throwables.propagateIfPossible(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -67,9 +66,8 @@ public class JsonUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return src instanceof String ? (String)src : objectMapper.writeValueAsString(src);
|
return src instanceof String ? (String) src : objectMapper.writeValueAsString(src);
|
||||||
}
|
} catch (Exception e) {
|
||||||
catch (Exception e) {
|
|
||||||
logger.error("Parse Object to String error src=" + src, e);
|
logger.error("Parse Object to String error src=" + src, e);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -81,9 +79,8 @@ public class JsonUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return src instanceof byte[] ? (byte[])src : objectMapper.writeValueAsBytes(src);
|
return src instanceof byte[] ? (byte[]) src : objectMapper.writeValueAsBytes(src);
|
||||||
}
|
} catch (Exception e) {
|
||||||
catch (Exception e) {
|
|
||||||
logger.error("Parse Object to byte[] error", e);
|
logger.error("Parse Object to byte[] error", e);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -95,9 +92,8 @@ public class JsonUtil {
|
|||||||
}
|
}
|
||||||
str = escapesSpecialChar(str);
|
str = escapesSpecialChar(str);
|
||||||
try {
|
try {
|
||||||
return clazz.equals(String.class) ? (T)str : objectMapper.readValue(str, clazz);
|
return clazz.equals(String.class) ? (T) str : objectMapper.readValue(str, clazz);
|
||||||
}
|
} catch (Exception e) {
|
||||||
catch (Exception e) {
|
|
||||||
logger.error("Parse String to Object error\nString: {}\nClass<T>: {}\nError: {}", str, clazz.getName(), e);
|
logger.error("Parse String to Object error\nString: {}\nClass<T>: {}\nError: {}", str, clazz.getName(), e);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -108,9 +104,8 @@ public class JsonUtil {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
return clazz.equals(byte[].class) ? (T)bytes : objectMapper.readValue(bytes, clazz);
|
return clazz.equals(byte[].class) ? (T) bytes : objectMapper.readValue(bytes, clazz);
|
||||||
}
|
} catch (Exception e) {
|
||||||
catch (Exception e) {
|
|
||||||
logger.error("Parse byte[] to Object error\nbyte[]: {}\nClass<T>: {}\nError: {}", bytes, clazz.getName(), e);
|
logger.error("Parse byte[] to Object error\nbyte[]: {}\nClass<T>: {}\nError: {}", bytes, clazz.getName(), e);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -122,9 +117,8 @@ public class JsonUtil {
|
|||||||
}
|
}
|
||||||
str = escapesSpecialChar(str);
|
str = escapesSpecialChar(str);
|
||||||
try {
|
try {
|
||||||
return (T)(typeReference.getType().equals(String.class) ? str : objectMapper.readValue(str, typeReference));
|
return (T) (typeReference.getType().equals(String.class) ? str : objectMapper.readValue(str, typeReference));
|
||||||
}
|
} catch (Exception e) {
|
||||||
catch (Exception e) {
|
|
||||||
logger.error("Parse String to Object error\nString: {}\nTypeReference<T>: {}\nError: {}", str,
|
logger.error("Parse String to Object error\nString: {}\nTypeReference<T>: {}\nError: {}", str,
|
||||||
typeReference.getType(), e);
|
typeReference.getType(), e);
|
||||||
return null;
|
return null;
|
||||||
@@ -136,10 +130,9 @@ public class JsonUtil {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
return (T)(typeReference.getType().equals(byte[].class) ? bytes : objectMapper.readValue(bytes,
|
return (T) (typeReference.getType().equals(byte[].class) ? bytes : objectMapper.readValue(bytes,
|
||||||
typeReference));
|
typeReference));
|
||||||
}
|
} catch (Exception e) {
|
||||||
catch (Exception e) {
|
|
||||||
logger.error("Parse byte[] to Object error\nbyte[]: {}\nTypeReference<T>: {}\nError: {}", bytes,
|
logger.error("Parse byte[] to Object error\nbyte[]: {}\nTypeReference<T>: {}\nError: {}", bytes,
|
||||||
typeReference.getType(), e);
|
typeReference.getType(), e);
|
||||||
return null;
|
return null;
|
||||||
|
@@ -58,6 +58,12 @@ rocketmq:
|
|||||||
ticketKey: ticket
|
ticketKey: ticket
|
||||||
# must create userInfo file: ${rocketmq.config.dataPath}/users.properties if the login is required
|
# must create userInfo file: ${rocketmq.config.dataPath}/users.properties if the login is required
|
||||||
loginRequired: false
|
loginRequired: false
|
||||||
|
# Authentication mode for RocketMQ Dashboard
|
||||||
|
# Available options:
|
||||||
|
# - 'file': Use username/password stored in a file (requires 'auth.file.path')
|
||||||
|
# - 'acl': Use credentials from ACL system (requires 'acl.access.key' and 'acl.secret.key')
|
||||||
|
# Default: file
|
||||||
|
authMode: file
|
||||||
useTLS: false
|
useTLS: false
|
||||||
proxyAddr: 127.0.0.1:8080
|
proxyAddr: 127.0.0.1:8080
|
||||||
proxyAddrs:
|
proxyAddrs:
|
||||||
|
@@ -43,8 +43,8 @@
|
|||||||
</appender>
|
</appender>
|
||||||
|
|
||||||
<root level="INFO">
|
<root level="INFO">
|
||||||
<appender-ref ref="STDOUT" />
|
<appender-ref ref="STDOUT"/>
|
||||||
<appender-ref ref="FILE" />
|
<appender-ref ref="FILE"/>
|
||||||
</root>
|
</root>
|
||||||
|
|
||||||
</configuration>
|
</configuration>
|
@@ -14,13 +14,10 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
#
|
#
|
||||||
|
|
||||||
# This file supports hot change, any change will be auto-reloaded without Dashboard restarting.
|
# This file supports hot change, any change will be auto-reloaded without Dashboard restarting.
|
||||||
# Format: a user per line, username=password[,N] #N is optional, 0 (Normal User); 1 (Admin)
|
# Format: a user per line, username=password[,N] #N is optional, 0 (Normal User); 1 (Admin)
|
||||||
|
|
||||||
# Define Admin
|
# Define Admin
|
||||||
admin=admin,1
|
super=admin,1
|
||||||
|
|
||||||
# Define Users
|
# Define Users
|
||||||
user1=user1
|
user1=user
|
||||||
user2=user2
|
user2=user
|
||||||
|
@@ -17,12 +17,20 @@
|
|||||||
|
|
||||||
package org.apache.rocketmq.dashboard;
|
package org.apache.rocketmq.dashboard;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
import org.apache.rocketmq.remoting.protocol.body.ClusterInfo;
|
||||||
import java.util.ArrayList;
|
import org.apache.rocketmq.remoting.protocol.route.BrokerData;
|
||||||
import java.util.List;
|
|
||||||
import org.mockito.internal.util.MockUtil;
|
import org.mockito.internal.util.MockUtil;
|
||||||
import org.springframework.util.ReflectionUtils;
|
import org.springframework.util.ReflectionUtils;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
public class BaseTest {
|
public class BaseTest {
|
||||||
/**
|
/**
|
||||||
* Inject the corresponding mock class automatically
|
* Inject the corresponding mock class automatically
|
||||||
@@ -69,4 +77,20 @@ public class BaseTest {
|
|||||||
ReflectionUtils.doWithFields(leafClass, fc);
|
ReflectionUtils.doWithFields(leafClass, fc);
|
||||||
return fields;
|
return fields;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected ClusterInfo getClusterInfo() {
|
||||||
|
ClusterInfo clusterInfo = new ClusterInfo();
|
||||||
|
Map<String, Set<String>> clusterAddrTable = new HashMap<>();
|
||||||
|
clusterAddrTable.put("DefaultCluster", new HashSet<>(Arrays.asList("broker-a")));
|
||||||
|
Map<String, BrokerData> brokerAddrTable = new HashMap<>();
|
||||||
|
BrokerData brokerData = new BrokerData();
|
||||||
|
brokerData.setBrokerName("broker-a");
|
||||||
|
HashMap<Long, String> brokerNameTable = new HashMap<>();
|
||||||
|
brokerNameTable.put(0L, "localhost:10911");
|
||||||
|
brokerData.setBrokerAddrs(brokerNameTable);
|
||||||
|
brokerAddrTable.put("broker-a", brokerData);
|
||||||
|
clusterInfo.setBrokerAddrTable(brokerAddrTable);
|
||||||
|
clusterInfo.setClusterAddrTable(clusterAddrTable);
|
||||||
|
return clusterInfo;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -17,26 +17,45 @@
|
|||||||
|
|
||||||
package org.apache.rocketmq.dashboard.admin;
|
package org.apache.rocketmq.dashboard.admin;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import org.apache.commons.pool2.impl.GenericObjectPool;
|
import org.apache.commons.pool2.impl.GenericObjectPool;
|
||||||
import org.apache.rocketmq.dashboard.aspect.admin.MQAdminAspect;
|
import org.apache.rocketmq.dashboard.aspect.admin.MQAdminAspect;
|
||||||
|
import org.apache.rocketmq.dashboard.config.RMQConfigure;
|
||||||
import org.apache.rocketmq.tools.admin.DefaultMQAdminExt;
|
import org.apache.rocketmq.tools.admin.DefaultMQAdminExt;
|
||||||
import org.apache.rocketmq.tools.admin.MQAdminExt;
|
import org.apache.rocketmq.tools.admin.MQAdminExt;
|
||||||
import org.aspectj.lang.ProceedingJoinPoint;
|
import org.aspectj.lang.ProceedingJoinPoint;
|
||||||
import org.aspectj.lang.reflect.MethodSignature;
|
import org.aspectj.lang.reflect.MethodSignature;
|
||||||
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.MockitoAnnotations;
|
||||||
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.mockito.Mockito.doNothing;
|
import static org.mockito.Mockito.*;
|
||||||
import static org.mockito.Mockito.mock;
|
|
||||||
import static org.mockito.Mockito.when;
|
|
||||||
|
|
||||||
public class MQAdminAspectTest {
|
public class MQAdminAspectTest {
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private RMQConfigure rmqConfigure;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
MockitoAnnotations.initMocks(this);
|
||||||
|
when(rmqConfigure.isLoginRequired()).thenReturn(false);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAroundMQAdminMethod() throws Throwable {
|
public void testAroundMQAdminMethod() throws Throwable {
|
||||||
MQAdminAspect mqAdminAspect = new MQAdminAspect();
|
MQAdminAspect mqAdminAspect = new MQAdminAspect();
|
||||||
|
Field field = mqAdminAspect.getClass().getDeclaredField("rmqConfigure");
|
||||||
|
field.setAccessible(true);
|
||||||
|
field.set(mqAdminAspect, rmqConfigure);
|
||||||
ProceedingJoinPoint joinPoint = mock(ProceedingJoinPoint.class);
|
ProceedingJoinPoint joinPoint = mock(ProceedingJoinPoint.class);
|
||||||
MethodSignature signature = mock(MethodSignature.class);
|
MethodSignature signature = mock(MethodSignature.class);
|
||||||
Method method = mock(Method.class);
|
Method method = mock(Method.class);
|
||||||
@@ -44,16 +63,39 @@ public class MQAdminAspectTest {
|
|||||||
when(joinPoint.getSignature()).thenReturn(signature);
|
when(joinPoint.getSignature()).thenReturn(signature);
|
||||||
|
|
||||||
GenericObjectPool<MQAdminExt> mqAdminExtPool = mock(GenericObjectPool.class);
|
GenericObjectPool<MQAdminExt> mqAdminExtPool = mock(GenericObjectPool.class);
|
||||||
|
// 1. Mock borrowObject() 行为:第一次抛异常,第二次返回 DefaultMQAdminExt
|
||||||
when(mqAdminExtPool.borrowObject())
|
when(mqAdminExtPool.borrowObject())
|
||||||
.thenThrow(new RuntimeException("borrowObject exception"))
|
.thenThrow(new RuntimeException("borrowObject exception"))
|
||||||
.thenReturn(new DefaultMQAdminExt());
|
.thenReturn(new DefaultMQAdminExt());
|
||||||
doNothing().doThrow(new RuntimeException("returnObject exception"))
|
|
||||||
|
// 2. Mock returnObject() 行为:第一次什么都不做,第二次抛异常
|
||||||
|
doNothing().when(mqAdminExtPool).returnObject(any());
|
||||||
|
doThrow(new RuntimeException("returnObject exception"))
|
||||||
.when(mqAdminExtPool).returnObject(any());
|
.when(mqAdminExtPool).returnObject(any());
|
||||||
Field field = mqAdminAspect.getClass().getDeclaredField("mqAdminExtPool");
|
|
||||||
|
// 3. 通过反射注入 Mock 对象
|
||||||
|
field = mqAdminAspect.getClass().getDeclaredField("mqAdminExtPool");
|
||||||
field.setAccessible(true);
|
field.setAccessible(true);
|
||||||
field.set(mqAdminAspect, mqAdminExtPool);
|
field.set(mqAdminAspect, mqAdminExtPool);
|
||||||
// exception
|
|
||||||
|
// 4. 第一次调用 aroundMQAdminMethod,预期 borrowObject() 抛异常
|
||||||
|
try {
|
||||||
mqAdminAspect.aroundMQAdminMethod(joinPoint);
|
mqAdminAspect.aroundMQAdminMethod(joinPoint);
|
||||||
|
fail("Expected RuntimeException but no exception was thrown");
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
assertEquals("borrowObject exception", e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. 第二次调用 aroundMQAdminMethod,预期 borrowObject() 成功,但 returnObject() 抛异常
|
||||||
|
try {
|
||||||
mqAdminAspect.aroundMQAdminMethod(joinPoint);
|
mqAdminAspect.aroundMQAdminMethod(joinPoint);
|
||||||
|
fail("Expected RuntimeException but no exception was thrown");
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
assertEquals("returnObject exception", e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6. 验证 borrowObject() 和 returnObject() 各调用了两次
|
||||||
|
verify(mqAdminExtPool, times(2)).borrowObject();
|
||||||
|
verify(mqAdminExtPool, times(1)).returnObject(any());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -19,27 +19,25 @@ package org.apache.rocketmq.dashboard.admin;
|
|||||||
|
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import com.google.common.collect.Sets;
|
import com.google.common.collect.Sets;
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Properties;
|
|
||||||
import java.util.Set;
|
|
||||||
import org.apache.rocketmq.client.QueryResult;
|
import org.apache.rocketmq.client.QueryResult;
|
||||||
import org.apache.rocketmq.client.exception.MQBrokerException;
|
import org.apache.rocketmq.client.exception.MQBrokerException;
|
||||||
import org.apache.rocketmq.client.impl.MQAdminImpl;
|
import org.apache.rocketmq.client.impl.MQAdminImpl;
|
||||||
import org.apache.rocketmq.client.impl.MQClientAPIImpl;
|
import org.apache.rocketmq.client.impl.MQClientAPIImpl;
|
||||||
import org.apache.rocketmq.client.impl.factory.MQClientInstance;
|
import org.apache.rocketmq.client.impl.factory.MQClientInstance;
|
||||||
import org.apache.rocketmq.common.PlainAccessConfig;
|
|
||||||
import org.apache.rocketmq.common.TopicConfig;
|
import org.apache.rocketmq.common.TopicConfig;
|
||||||
|
import org.apache.rocketmq.common.message.Message;
|
||||||
|
import org.apache.rocketmq.common.message.MessageExt;
|
||||||
|
import org.apache.rocketmq.common.message.MessageQueue;
|
||||||
|
import org.apache.rocketmq.dashboard.service.client.MQAdminExtImpl;
|
||||||
|
import org.apache.rocketmq.dashboard.service.client.MQAdminInstance;
|
||||||
|
import org.apache.rocketmq.dashboard.util.MockObjectUtil;
|
||||||
|
import org.apache.rocketmq.remoting.RemotingClient;
|
||||||
|
import org.apache.rocketmq.remoting.protocol.RemotingCommand;
|
||||||
|
import org.apache.rocketmq.remoting.protocol.RemotingSerializable;
|
||||||
|
import org.apache.rocketmq.remoting.protocol.ResponseCode;
|
||||||
import org.apache.rocketmq.remoting.protocol.admin.ConsumeStats;
|
import org.apache.rocketmq.remoting.protocol.admin.ConsumeStats;
|
||||||
import org.apache.rocketmq.remoting.protocol.admin.RollbackStats;
|
import org.apache.rocketmq.remoting.protocol.admin.RollbackStats;
|
||||||
import org.apache.rocketmq.remoting.protocol.admin.TopicStatsTable;
|
import org.apache.rocketmq.remoting.protocol.admin.TopicStatsTable;
|
||||||
import org.apache.rocketmq.common.message.MessageExt;
|
|
||||||
import org.apache.rocketmq.common.message.MessageQueue;
|
|
||||||
import org.apache.rocketmq.remoting.protocol.ResponseCode;
|
|
||||||
import org.apache.rocketmq.remoting.protocol.body.BrokerStatsData;
|
import org.apache.rocketmq.remoting.protocol.body.BrokerStatsData;
|
||||||
import org.apache.rocketmq.remoting.protocol.body.ClusterInfo;
|
import org.apache.rocketmq.remoting.protocol.body.ClusterInfo;
|
||||||
import org.apache.rocketmq.remoting.protocol.body.ConsumeMessageDirectlyResult;
|
import org.apache.rocketmq.remoting.protocol.body.ConsumeMessageDirectlyResult;
|
||||||
@@ -55,12 +53,6 @@ import org.apache.rocketmq.remoting.protocol.body.TopicConfigSerializeWrapper;
|
|||||||
import org.apache.rocketmq.remoting.protocol.body.TopicList;
|
import org.apache.rocketmq.remoting.protocol.body.TopicList;
|
||||||
import org.apache.rocketmq.remoting.protocol.route.TopicRouteData;
|
import org.apache.rocketmq.remoting.protocol.route.TopicRouteData;
|
||||||
import org.apache.rocketmq.remoting.protocol.subscription.SubscriptionGroupConfig;
|
import org.apache.rocketmq.remoting.protocol.subscription.SubscriptionGroupConfig;
|
||||||
import org.apache.rocketmq.dashboard.service.client.MQAdminExtImpl;
|
|
||||||
import org.apache.rocketmq.dashboard.service.client.MQAdminInstance;
|
|
||||||
import org.apache.rocketmq.dashboard.util.MockObjectUtil;
|
|
||||||
import org.apache.rocketmq.remoting.RemotingClient;
|
|
||||||
import org.apache.rocketmq.remoting.protocol.RemotingCommand;
|
|
||||||
import org.apache.rocketmq.remoting.protocol.RemotingSerializable;
|
|
||||||
import org.apache.rocketmq.store.stats.BrokerStatsManager;
|
import org.apache.rocketmq.store.stats.BrokerStatsManager;
|
||||||
import org.apache.rocketmq.tools.admin.DefaultMQAdminExt;
|
import org.apache.rocketmq.tools.admin.DefaultMQAdminExt;
|
||||||
import org.apache.rocketmq.tools.admin.DefaultMQAdminExtImpl;
|
import org.apache.rocketmq.tools.admin.DefaultMQAdminExtImpl;
|
||||||
@@ -74,22 +66,25 @@ import org.mockito.Mock;
|
|||||||
import org.mockito.junit.MockitoJUnitRunner;
|
import org.mockito.junit.MockitoJUnitRunner;
|
||||||
import org.springframework.test.util.ReflectionTestUtils;
|
import org.springframework.test.util.ReflectionTestUtils;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Properties;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.ConcurrentMap;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertNotNull;
|
||||||
import static org.junit.Assert.assertNull;
|
import static org.junit.Assert.assertNull;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.*;
|
||||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
|
||||||
import static org.mockito.ArgumentMatchers.anyInt;
|
|
||||||
import static org.mockito.ArgumentMatchers.anyLong;
|
|
||||||
import static org.mockito.ArgumentMatchers.anyMap;
|
|
||||||
import static org.mockito.ArgumentMatchers.anyString;
|
|
||||||
import static org.mockito.Mockito.doNothing;
|
import static org.mockito.Mockito.doNothing;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
import java.util.concurrent.ConcurrentMap;
|
|
||||||
import static org.mockito.ArgumentMatchers.eq;
|
|
||||||
|
|
||||||
@RunWith(MockitoJUnitRunner.Silent.class)
|
@RunWith(MockitoJUnitRunner.Silent.class)
|
||||||
public class MQAdminExtImplTest {
|
public class MQAdminExtImplTest {
|
||||||
@@ -154,35 +149,6 @@ public class MQAdminExtImplTest {
|
|||||||
mqAdminExtImpl.createAndUpdateTopicConfig(brokerAddr, new TopicConfig());
|
mqAdminExtImpl.createAndUpdateTopicConfig(brokerAddr, new TopicConfig());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testDeletePlainAccessConfig() throws Exception {
|
|
||||||
assertNotNull(mqAdminExtImpl);
|
|
||||||
mqAdminExtImpl.deletePlainAccessConfig(brokerAddr, "rocketmq");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testUpdateGlobalWhiteAddrConfig() throws Exception {
|
|
||||||
assertNotNull(mqAdminExtImpl);
|
|
||||||
mqAdminExtImpl.updateGlobalWhiteAddrConfig(brokerAddr, "192.168.*.*");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testCreateAndUpdatePlainAccessConfig() throws Exception {
|
|
||||||
assertNotNull(mqAdminExtImpl);
|
|
||||||
mqAdminExtImpl.createAndUpdatePlainAccessConfig(brokerAddr, new PlainAccessConfig());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testExamineBrokerClusterAclVersionInfo() throws Exception {
|
|
||||||
assertNotNull(mqAdminExtImpl);
|
|
||||||
assertNull(mqAdminExtImpl.examineBrokerClusterAclVersionInfo(brokerAddr));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testExamineBrokerClusterAclConfig() throws Exception {
|
|
||||||
assertNotNull(mqAdminExtImpl);
|
|
||||||
assertNull(mqAdminExtImpl.examineBrokerClusterAclConfig(brokerAddr));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testQueryConsumerStatus() throws Exception {
|
public void testQueryConsumerStatus() throws Exception {
|
||||||
@@ -226,18 +192,15 @@ public class MQAdminExtImplTest {
|
|||||||
public void testExamineTopicConfig() throws Exception {
|
public void testExamineTopicConfig() throws Exception {
|
||||||
assertNotNull(mqAdminExtImpl);
|
assertNotNull(mqAdminExtImpl);
|
||||||
|
|
||||||
// Create valid TopicConfigSerializeWrapper with topic_test entry
|
// Create valid TopicConfigSerializeWrapper with topictest entry
|
||||||
TopicConfigSerializeWrapper wrapper = new TopicConfigSerializeWrapper();
|
|
||||||
ConcurrentMap<String, TopicConfig> topicConfigTable = new ConcurrentHashMap<>();
|
|
||||||
TopicConfig config = new TopicConfig();
|
TopicConfig config = new TopicConfig();
|
||||||
config.setTopicName("topic_test");
|
config.setTopicName("topic_test");
|
||||||
topicConfigTable.put("topic_test", config);
|
|
||||||
wrapper.setTopicConfigTable(topicConfigTable);
|
|
||||||
|
|
||||||
// Create successful response
|
// Create successful response
|
||||||
RemotingCommand successResponse = RemotingCommand.createResponseCommand(null);
|
RemotingCommand successResponse = RemotingCommand.createResponseCommand(null);
|
||||||
successResponse.setCode(ResponseCode.SUCCESS);
|
successResponse.setCode(ResponseCode.SUCCESS);
|
||||||
successResponse.setBody(RemotingSerializable.encode(wrapper));
|
successResponse.setBody(RemotingSerializable.encode(config));
|
||||||
|
|
||||||
// Mock the remote invocation
|
// Mock the remote invocation
|
||||||
when(remotingClient.invokeSync(eq(brokerAddr), any(RemotingCommand.class), anyLong()))
|
when(remotingClient.invokeSync(eq(brokerAddr), any(RemotingCommand.class), anyLong()))
|
||||||
@@ -249,6 +212,7 @@ public class MQAdminExtImplTest {
|
|||||||
Assert.assertEquals("topic_test", topicConfig.getTopicName());
|
Assert.assertEquals("topic_test", topicConfig.getTopicName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testExamineTopicStats() throws Exception {
|
public void testExamineTopicStats() throws Exception {
|
||||||
assertNotNull(mqAdminExtImpl);
|
assertNotNull(mqAdminExtImpl);
|
||||||
@@ -257,7 +221,7 @@ public class MQAdminExtImplTest {
|
|||||||
}
|
}
|
||||||
TopicStatsTable topicStatsTable = mqAdminExtImpl.examineTopicStats("topic_test");
|
TopicStatsTable topicStatsTable = mqAdminExtImpl.examineTopicStats("topic_test");
|
||||||
Assert.assertNotNull(topicStatsTable);
|
Assert.assertNotNull(topicStatsTable);
|
||||||
Assert.assertEquals(topicStatsTable.getOffsetTable().size(), 1);
|
Assert.assertEquals(1, topicStatsTable.getOffsetTable().size());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -347,7 +311,7 @@ public class MQAdminExtImplTest {
|
|||||||
public void testGetNameServerAddressList() {
|
public void testGetNameServerAddressList() {
|
||||||
assertNotNull(mqAdminExtImpl);
|
assertNotNull(mqAdminExtImpl);
|
||||||
{
|
{
|
||||||
when(defaultMQAdminExt.getNameServerAddressList()).thenReturn(Lists.asList("127.0.0.1:9876", new String[] {"127.0.0.2:9876"}));
|
when(defaultMQAdminExt.getNameServerAddressList()).thenReturn(Lists.asList("127.0.0.1:9876", new String[]{"127.0.0.2:9876"}));
|
||||||
}
|
}
|
||||||
List<String> list = mqAdminExtImpl.getNameServerAddressList();
|
List<String> list = mqAdminExtImpl.getNameServerAddressList();
|
||||||
Assert.assertEquals(list.size(), 2);
|
Assert.assertEquals(list.size(), 2);
|
||||||
@@ -535,12 +499,10 @@ public class MQAdminExtImplTest {
|
|||||||
public void testConsumeMessageDirectly() throws Exception {
|
public void testConsumeMessageDirectly() throws Exception {
|
||||||
assertNotNull(mqAdminExtImpl);
|
assertNotNull(mqAdminExtImpl);
|
||||||
{
|
{
|
||||||
when(defaultMQAdminExt.consumeMessageDirectly(anyString(), anyString(), anyString())).thenReturn(new ConsumeMessageDirectlyResult());
|
|
||||||
when(defaultMQAdminExt.consumeMessageDirectly(anyString(), anyString(), anyString(), anyString())).thenReturn(new ConsumeMessageDirectlyResult());
|
when(defaultMQAdminExt.consumeMessageDirectly(anyString(), anyString(), anyString(), anyString())).thenReturn(new ConsumeMessageDirectlyResult());
|
||||||
}
|
}
|
||||||
ConsumeMessageDirectlyResult result1 = mqAdminExtImpl.consumeMessageDirectly("group_test", "", "7F000001ACC018B4AAC2116AF6500000");
|
|
||||||
ConsumeMessageDirectlyResult result2 = mqAdminExtImpl.consumeMessageDirectly("group_test", "", "topic_test", "7F000001ACC018B4AAC2116AF6500000");
|
ConsumeMessageDirectlyResult result2 = mqAdminExtImpl.consumeMessageDirectly("group_test", "", "topic_test", "7F000001ACC018B4AAC2116AF6500000");
|
||||||
Assert.assertNotNull(result1);
|
|
||||||
Assert.assertNotNull(result2);
|
Assert.assertNotNull(result2);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -616,15 +578,6 @@ public class MQAdminExtImplTest {
|
|||||||
Assert.assertEquals(storeTime, 1628495765398L);
|
Assert.assertEquals(storeTime, 1628495765398L);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testViewMessage() throws Exception {
|
|
||||||
assertNotNull(mqAdminExtImpl);
|
|
||||||
{
|
|
||||||
when(defaultMQAdminExt.viewMessage(anyString())).thenReturn(new MessageExt());
|
|
||||||
}
|
|
||||||
MessageExt messageExt = mqAdminExtImpl.viewMessage("7F000001ACC018B4AAC2116AF6500000");
|
|
||||||
Assert.assertNotNull(messageExt);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testQueryMessage() throws Exception {
|
public void testQueryMessage() throws Exception {
|
||||||
@@ -666,15 +619,6 @@ public class MQAdminExtImplTest {
|
|||||||
Assert.assertNotNull(timeSpans);
|
Assert.assertNotNull(timeSpans);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testViewMessage2() throws Exception {
|
|
||||||
assertNotNull(mqAdminExtImpl);
|
|
||||||
{
|
|
||||||
when(MQAdminInstance.threadLocalMqClientInstance().getMQAdminImpl()).thenReturn(mock(MQAdminImpl.class));
|
|
||||||
when(defaultMQAdminExt.viewMessage(anyString())).thenThrow(new RuntimeException("viewMessage exception"));
|
|
||||||
}
|
|
||||||
mqAdminExtImpl.viewMessage("topic_test", "7F000001ACC018B4AAC2116AF6500000");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetBrokerConfig() throws Exception {
|
public void testGetBrokerConfig() throws Exception {
|
||||||
@@ -788,7 +732,6 @@ public class MQAdminExtImplTest {
|
|||||||
@Test
|
@Test
|
||||||
public void testResumeCheckHalfMessage() throws Exception {
|
public void testResumeCheckHalfMessage() throws Exception {
|
||||||
assertNotNull(mqAdminExtImpl);
|
assertNotNull(mqAdminExtImpl);
|
||||||
Assert.assertFalse(mqAdminExtImpl.resumeCheckHalfMessage("7F000001ACC018B4AAC2116AF6500000"));
|
|
||||||
Assert.assertFalse(mqAdminExtImpl.resumeCheckHalfMessage("topic_test", "7F000001ACC018B4AAC2116AF6500000"));
|
Assert.assertFalse(mqAdminExtImpl.resumeCheckHalfMessage("topic_test", "7F000001ACC018B4AAC2116AF6500000"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -18,56 +18,41 @@
|
|||||||
package org.apache.rocketmq.dashboard.config;
|
package org.apache.rocketmq.dashboard.config;
|
||||||
|
|
||||||
import org.apache.rocketmq.dashboard.BaseTest;
|
import org.apache.rocketmq.dashboard.BaseTest;
|
||||||
import org.apache.rocketmq.dashboard.interceptor.AuthInterceptor;
|
|
||||||
import org.assertj.core.util.Lists;
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.jupiter.api.Assertions;
|
|
||||||
import org.mockito.InjectMocks;
|
|
||||||
import org.mockito.Mock;
|
|
||||||
import org.mockito.Mockito;
|
|
||||||
import org.mockito.MockitoAnnotations;
|
|
||||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
|
||||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
|
||||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
|
||||||
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class AuthWebMVCConfigurerAdapterTest extends BaseTest {
|
public class AuthWebMVCConfigurerAdapterTest extends BaseTest {
|
||||||
|
|
||||||
@InjectMocks
|
// @InjectMocks
|
||||||
private AuthWebMVCConfigurerAdapter authWebMVCConfigurerAdapter;
|
// private AuthWebMVCConfigurerAdapter authWebMVCConfigurerAdapter;
|
||||||
|
//
|
||||||
@Mock
|
// @Mock
|
||||||
private RMQConfigure configure;
|
// private RMQConfigure configure;
|
||||||
|
//
|
||||||
@Mock
|
// @Mock
|
||||||
private AuthInterceptor authInterceptor;
|
// private AuthInterceptor authInterceptor;
|
||||||
|
//
|
||||||
@Before
|
// @Before
|
||||||
public void init() throws Exception {
|
// public void init() throws Exception {
|
||||||
MockitoAnnotations.initMocks(this);
|
// MockitoAnnotations.initMocks(this);
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
|
//
|
||||||
@Test
|
// @Test
|
||||||
public void addInterceptors() {
|
// public void addInterceptors() {
|
||||||
Mockito.when(configure.isLoginRequired()).thenReturn(true);
|
// Mockito.when(configure.isLoginRequired()).thenReturn(true);
|
||||||
InterceptorRegistry registry = new InterceptorRegistry();
|
// InterceptorRegistry registry = new InterceptorRegistry();
|
||||||
Assertions.assertDoesNotThrow(() -> authWebMVCConfigurerAdapter.addInterceptors(registry));
|
// Assertions.assertDoesNotThrow(() -> authWebMVCConfigurerAdapter.addInterceptors(registry));
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
@Test
|
// @Test
|
||||||
public void addArgumentResolvers() {
|
// public void addArgumentResolvers() {
|
||||||
List<HandlerMethodArgumentResolver> argumentResolvers = Lists.newArrayList();
|
// List<HandlerMethodArgumentResolver> argumentResolvers = Lists.newArrayList();
|
||||||
authWebMVCConfigurerAdapter.addArgumentResolvers(argumentResolvers);
|
// authWebMVCConfigurerAdapter.addArgumentResolvers(argumentResolvers);
|
||||||
Assertions.assertEquals(1, argumentResolvers.size());
|
// Assertions.assertEquals(1, argumentResolvers.size());
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
@Test
|
// @Test
|
||||||
public void addViewControllers() {
|
// public void addViewControllers() {
|
||||||
ViewControllerRegistry registry = new ViewControllerRegistry(new ClassPathXmlApplicationContext());
|
// ViewControllerRegistry registry = new ViewControllerRegistry(new ClassPathXmlApplicationContext());
|
||||||
Assertions.assertDoesNotThrow(() -> authWebMVCConfigurerAdapter.addViewControllers(registry));
|
// Assertions.assertDoesNotThrow(() -> authWebMVCConfigurerAdapter.addViewControllers(registry));
|
||||||
}
|
// }
|
||||||
}
|
}
|
@@ -16,11 +16,12 @@
|
|||||||
*/
|
*/
|
||||||
package org.apache.rocketmq.dashboard.config;
|
package org.apache.rocketmq.dashboard.config;
|
||||||
|
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import org.junit.Assert;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
public class CollectExecutorConfigTest {
|
public class CollectExecutorConfigTest {
|
||||||
|
|
||||||
|
@@ -18,13 +18,14 @@
|
|||||||
package org.apache.rocketmq.dashboard.config;
|
package org.apache.rocketmq.dashboard.config;
|
||||||
|
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import java.io.File;
|
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.springframework.boot.web.server.ErrorPage;
|
import org.springframework.boot.web.server.ErrorPage;
|
||||||
import org.springframework.boot.web.server.ErrorPageRegistrar;
|
import org.springframework.boot.web.server.ErrorPageRegistrar;
|
||||||
import org.springframework.boot.web.server.ErrorPageRegistry;
|
import org.springframework.boot.web.server.ErrorPageRegistry;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
public class RMQConfigureTest {
|
public class RMQConfigureTest {
|
||||||
|
|
||||||
private RMQConfigure rmqConfigure = new RMQConfigure();
|
private RMQConfigure rmqConfigure = new RMQConfigure();
|
||||||
@@ -40,7 +41,8 @@ public class RMQConfigureTest {
|
|||||||
rmqConfigure.setLoginRequired(true);
|
rmqConfigure.setLoginRequired(true);
|
||||||
rmqConfigure.setNamesrvAddr("127.0.0.1:9876");
|
rmqConfigure.setNamesrvAddr("127.0.0.1:9876");
|
||||||
rmqConfigure.setTimeoutMillis(3000L);
|
rmqConfigure.setTimeoutMillis(3000L);
|
||||||
rmqConfigure.setNamesrvAddrs(Lists.asList("127.0.0.1:9876", new String[] {"127.0.0.2:9876"}));
|
rmqConfigure.setNamesrvAddrs(Lists.asList("127.0.0.1:9876", new String[]{"127.0.0.2:9876"}));
|
||||||
|
rmqConfigure.setAuthMode("file");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -58,6 +60,7 @@ public class RMQConfigureTest {
|
|||||||
Assert.assertEquals(rmqConfigure.getNamesrvAddr(), "127.0.0.1:9876");
|
Assert.assertEquals(rmqConfigure.getNamesrvAddr(), "127.0.0.1:9876");
|
||||||
Assert.assertEquals(rmqConfigure.getNamesrvAddrs().size(), 2);
|
Assert.assertEquals(rmqConfigure.getNamesrvAddrs().size(), 2);
|
||||||
Assert.assertEquals(rmqConfigure.getTimeoutMillis().longValue(), 3000L);
|
Assert.assertEquals(rmqConfigure.getTimeoutMillis().longValue(), 3000L);
|
||||||
|
Assert.assertEquals(rmqConfigure.getAuthMode(), "file");
|
||||||
ErrorPageRegistrar registrar = rmqConfigure.errorPageRegistrar();
|
ErrorPageRegistrar registrar = rmqConfigure.errorPageRegistrar();
|
||||||
registrar.registerErrorPages(new ErrorPageRegistry() {
|
registrar.registerErrorPages(new ErrorPageRegistry() {
|
||||||
@Override
|
@Override
|
||||||
|
@@ -16,353 +16,228 @@
|
|||||||
*/
|
*/
|
||||||
package org.apache.rocketmq.dashboard.controller;
|
package org.apache.rocketmq.dashboard.controller;
|
||||||
|
|
||||||
import com.alibaba.fastjson.JSON;
|
|
||||||
import com.google.common.collect.Lists;
|
import org.apache.rocketmq.auth.authentication.enums.UserStatus;
|
||||||
import java.util.List;
|
import org.apache.rocketmq.auth.authentication.enums.UserType;
|
||||||
import org.apache.rocketmq.common.AclConfig;
|
import org.apache.rocketmq.auth.authorization.enums.Decision;
|
||||||
import org.apache.rocketmq.common.PlainAccessConfig;
|
import org.apache.rocketmq.dashboard.model.Policy;
|
||||||
import org.apache.rocketmq.remoting.protocol.body.ClusterInfo;
|
import org.apache.rocketmq.dashboard.model.PolicyRequest;
|
||||||
import org.apache.rocketmq.dashboard.model.request.AclRequest;
|
import org.apache.rocketmq.dashboard.model.request.UserCreateRequest;
|
||||||
|
import org.apache.rocketmq.dashboard.model.request.UserInfoParam;
|
||||||
|
import org.apache.rocketmq.dashboard.model.request.UserUpdateRequest;
|
||||||
import org.apache.rocketmq.dashboard.service.impl.AclServiceImpl;
|
import org.apache.rocketmq.dashboard.service.impl.AclServiceImpl;
|
||||||
import org.apache.rocketmq.dashboard.util.MockObjectUtil;
|
import org.apache.rocketmq.dashboard.support.GlobalExceptionHandler;
|
||||||
|
import org.apache.rocketmq.remoting.protocol.body.AclInfo;
|
||||||
|
import org.apache.rocketmq.remoting.protocol.body.UserInfo;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.mockito.InjectMocks;
|
import org.mockito.InjectMocks;
|
||||||
import org.mockito.Spy;
|
import org.mockito.Mock;
|
||||||
import org.springframework.http.MediaType;
|
import org.mockito.MockitoAnnotations;
|
||||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||||
|
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import java.util.Arrays;
|
||||||
import static org.mockito.ArgumentMatchers.anyString;
|
import java.util.List;
|
||||||
import static org.mockito.Mockito.doNothing;
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.mockito.Mockito.times;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
|
||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
|
||||||
|
|
||||||
public class AclControllerTest extends BaseControllerTest {
|
public class AclControllerTest extends BaseControllerTest {
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private AclServiceImpl aclService;
|
||||||
|
|
||||||
@InjectMocks
|
@InjectMocks
|
||||||
private AclController aclController;
|
private AclController aclController;
|
||||||
|
|
||||||
@Spy
|
|
||||||
private AclServiceImpl aclService;
|
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void init() throws Exception {
|
public void init() {
|
||||||
AclConfig aclConfig = MockObjectUtil.createAclConfig();
|
MockitoAnnotations.initMocks(this);
|
||||||
when(mqAdminExt.examineBrokerClusterAclConfig(anyString())).thenReturn(aclConfig);
|
mockMvc = MockMvcBuilders.standaloneSetup(aclController).setControllerAdvice(GlobalExceptionHandler.class).build();
|
||||||
ClusterInfo clusterInfo = MockObjectUtil.createClusterInfo();
|
}
|
||||||
when(mqAdminExt.examineBrokerClusterInfo()).thenReturn(clusterInfo);
|
|
||||||
doNothing().when(mqAdminExt).createAndUpdatePlainAccessConfig(anyString(), any(PlainAccessConfig.class));
|
|
||||||
doNothing().when(mqAdminExt).deletePlainAccessConfig(anyString(), anyString());
|
@Test
|
||||||
doNothing().when(mqAdminExt).updateGlobalWhiteAddrConfig(anyString(), anyString());
|
public void testListUsers() {
|
||||||
|
// Prepare test data
|
||||||
|
String brokerAddress = "localhost:10911";
|
||||||
|
List<UserInfo> expectedUsers = Arrays.asList(
|
||||||
|
UserInfo.of("user1", "password1", "super"),
|
||||||
|
UserInfo.of("user2", "password2", "super")
|
||||||
|
);
|
||||||
|
|
||||||
|
// Mock service behavior
|
||||||
|
when(aclService.listUsers(brokerAddress)).thenReturn(expectedUsers);
|
||||||
|
|
||||||
|
// Call controller method
|
||||||
|
List<UserInfo> result = aclController.listUsers(brokerAddress);
|
||||||
|
|
||||||
|
// Verify
|
||||||
|
assertEquals(expectedUsers, result);
|
||||||
|
verify(aclService, times(1)).listUsers(brokerAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testIsEnableAcl() throws Exception {
|
public void testListUsersWithoutBrokerAddress() {
|
||||||
final String url = "/acl/enable.query";
|
// Prepare test data
|
||||||
// 1. disable acl.
|
List<UserInfo> expectedUsers = Arrays.asList(
|
||||||
requestBuilder = MockMvcRequestBuilders.get(url);
|
UserInfo.of("user1", "password1", "super")
|
||||||
perform = mockMvc.perform(requestBuilder);
|
);
|
||||||
perform.andExpect(status().isOk())
|
|
||||||
.andExpect(jsonPath("$.data").value(false));
|
|
||||||
|
|
||||||
// 2.enable acl.
|
// Mock service behavior
|
||||||
super.mockRmqConfigure();
|
when(aclService.listUsers(null)).thenReturn(expectedUsers);
|
||||||
perform = mockMvc.perform(requestBuilder);
|
// Call controller method
|
||||||
perform.andExpect(status().isOk())
|
List<UserInfo> result = aclController.listUsers(null);
|
||||||
.andExpect(jsonPath("$.data").value(true));
|
// Verify
|
||||||
|
assertEquals(expectedUsers, result);
|
||||||
|
verify(aclService, times(1)).listUsers(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetAclConfig() throws Exception {
|
public void testListAcls() {
|
||||||
final String url = "/acl/config.query";
|
// Prepare test data
|
||||||
|
String brokerAddress = "localhost:9092";
|
||||||
|
String searchParam = "user1";
|
||||||
|
Object expectedAcls = Arrays.asList(
|
||||||
|
AclInfo.of("user1", List.of("READ", "test"), List.of("TOPIC:test"), List.of("localhost:10911"), Decision.ALLOW.getName())
|
||||||
|
);
|
||||||
|
|
||||||
// 1. broker addr table is not empty.
|
// Mock service behavior
|
||||||
ClusterInfo clusterInfo = MockObjectUtil.createClusterInfo();
|
when(aclService.listAcls(brokerAddress, searchParam)).thenReturn(expectedAcls);
|
||||||
when(mqAdminExt.examineBrokerClusterInfo()).thenReturn(clusterInfo);
|
|
||||||
requestBuilder = MockMvcRequestBuilders.get(url);
|
|
||||||
perform = mockMvc.perform(requestBuilder);
|
|
||||||
perform.andExpect(status().isOk())
|
|
||||||
.andExpect(jsonPath("$.data").isMap())
|
|
||||||
.andExpect(jsonPath("$.data.globalWhiteAddrs").isNotEmpty())
|
|
||||||
.andExpect(jsonPath("$.data.plainAccessConfigs").isNotEmpty())
|
|
||||||
.andExpect(jsonPath("$.data.plainAccessConfigs[0].secretKey").isNotEmpty());
|
|
||||||
|
|
||||||
// 2. broker addr table is empty.
|
// Call controller method
|
||||||
clusterInfo.getBrokerAddrTable().clear();
|
Object result = aclController.listAcls(brokerAddress, searchParam);
|
||||||
perform = mockMvc.perform(requestBuilder);
|
|
||||||
perform.andExpect(status().isOk())
|
|
||||||
.andExpect(jsonPath("$.data").isMap())
|
|
||||||
.andExpect(jsonPath("$.data.globalWhiteAddrs").isEmpty())
|
|
||||||
.andExpect(jsonPath("$.data.plainAccessConfigs").isEmpty());
|
|
||||||
|
|
||||||
// 3. login required and user info is null.
|
// Verify
|
||||||
when(configure.isLoginRequired()).thenReturn(true);
|
assertEquals(expectedAcls, result);
|
||||||
when(mqAdminExt.examineBrokerClusterInfo()).thenReturn(MockObjectUtil.createClusterInfo());
|
verify(aclService, times(1)).listAcls(brokerAddress, searchParam);
|
||||||
perform = mockMvc.perform(requestBuilder);
|
|
||||||
perform.andExpect(status().isOk())
|
|
||||||
.andExpect(jsonPath("$.data").isMap())
|
|
||||||
.andExpect(jsonPath("$.data.globalWhiteAddrs").isNotEmpty())
|
|
||||||
.andExpect(jsonPath("$.data.plainAccessConfigs").isNotEmpty())
|
|
||||||
.andExpect(jsonPath("$.data.plainAccessConfigs[0].secretKey").isEmpty());
|
|
||||||
// 4. login required, but user is not admin. emmmm, Mockito may can not mock static method.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAddAclConfig() throws Exception {
|
public void testCreateAcl() {
|
||||||
final String url = "/acl/add.do";
|
// Prepare test data
|
||||||
PlainAccessConfig accessConfig = new PlainAccessConfig();
|
PolicyRequest request = new PolicyRequest();
|
||||||
requestBuilder = MockMvcRequestBuilders.post(url);
|
request.setBrokerAddress("localhost:9092");
|
||||||
requestBuilder.contentType(MediaType.APPLICATION_JSON_UTF8);
|
request.setSubject("user1");
|
||||||
|
request.setPolicies(List.of(
|
||||||
|
new Policy()
|
||||||
|
));
|
||||||
|
|
||||||
// 1. access key is null.
|
// Call controller method
|
||||||
requestBuilder.content(JSON.toJSONString(accessConfig));
|
Object result = aclController.createAcl(request);
|
||||||
perform = mockMvc.perform(requestBuilder);
|
|
||||||
perform.andExpect(status().isOk())
|
|
||||||
.andExpect(jsonPath("$.status").value(-1))
|
|
||||||
.andExpect(jsonPath("$.errMsg").exists());
|
|
||||||
|
|
||||||
// 2. secret key is null.
|
// Verify
|
||||||
accessConfig.setAccessKey("test-access-key");
|
assertEquals(true, result);
|
||||||
requestBuilder.content(JSON.toJSONString(accessConfig));
|
verify(aclService, times(1)).createAcl(request);
|
||||||
perform = mockMvc.perform(requestBuilder);
|
|
||||||
perform.andExpect(status().isOk())
|
|
||||||
.andExpect(jsonPath("$.status").value(-1))
|
|
||||||
.andExpect(jsonPath("$.errMsg").exists());
|
|
||||||
|
|
||||||
ClusterInfo clusterInfo = MockObjectUtil.createClusterInfo();
|
|
||||||
when(mqAdminExt.examineBrokerClusterInfo()).thenReturn(clusterInfo);
|
|
||||||
|
|
||||||
// 3. add if the access key not exist.
|
|
||||||
accessConfig.setSecretKey("12345678");
|
|
||||||
requestBuilder.content(JSON.toJSONString(accessConfig));
|
|
||||||
perform = mockMvc.perform(requestBuilder);
|
|
||||||
perform.andExpect(status().isOk())
|
|
||||||
.andExpect(jsonPath("$.status").value(0));
|
|
||||||
|
|
||||||
// 4. add failed if the access key is existed.
|
|
||||||
accessConfig.setAccessKey("rocketmq2");
|
|
||||||
requestBuilder.content(JSON.toJSONString(accessConfig));
|
|
||||||
perform = mockMvc.perform(requestBuilder);
|
|
||||||
perform.andExpect(status().isOk())
|
|
||||||
.andExpect(jsonPath("$.status").value(-1))
|
|
||||||
.andExpect(jsonPath("$.errMsg").exists());
|
|
||||||
|
|
||||||
// 5. add failed if there is no alive broker.
|
|
||||||
clusterInfo.getBrokerAddrTable().clear();
|
|
||||||
accessConfig.setAccessKey("test-access-key");
|
|
||||||
requestBuilder.content(JSON.toJSONString(accessConfig));
|
|
||||||
perform = mockMvc.perform(requestBuilder);
|
|
||||||
perform.andExpect(status().isOk())
|
|
||||||
.andExpect(jsonPath("$.status").value(-1))
|
|
||||||
.andExpect(jsonPath("$.errMsg").exists());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDeleteAclConfig() throws Exception {
|
public void testDeleteUser() {
|
||||||
final String url = "/acl/delete.do";
|
// Prepare test data
|
||||||
PlainAccessConfig accessConfig = new PlainAccessConfig();
|
String brokerAddress = "localhost:9092";
|
||||||
requestBuilder = MockMvcRequestBuilders.post(url);
|
String username = "user1";
|
||||||
requestBuilder.contentType(MediaType.APPLICATION_JSON_UTF8);
|
|
||||||
|
|
||||||
// 1. access key is null.
|
// Call controller method
|
||||||
requestBuilder.content(JSON.toJSONString(accessConfig));
|
Object result = aclController.deleteUser(brokerAddress, username);
|
||||||
perform = mockMvc.perform(requestBuilder);
|
|
||||||
perform.andExpect(status().isOk())
|
|
||||||
.andExpect(jsonPath("$.status").value(-1))
|
|
||||||
.andExpect(jsonPath("$.errMsg").exists());
|
|
||||||
|
|
||||||
// 2. access key is not null.
|
// Verify
|
||||||
accessConfig.setAccessKey("rocketmq");
|
assertEquals(true, result);
|
||||||
requestBuilder.content(JSON.toJSONString(accessConfig));
|
verify(aclService, times(1)).deleteUser(brokerAddress, username);
|
||||||
perform = mockMvc.perform(requestBuilder);
|
|
||||||
perform.andExpect(status().isOk())
|
|
||||||
.andExpect(jsonPath("$.status").value(0));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testUpdateAclConfig() throws Exception {
|
public void testDeleteUserWithoutBrokerAddress() {
|
||||||
final String url = "/acl/update.do";
|
// Prepare test data
|
||||||
PlainAccessConfig accessConfig = new PlainAccessConfig();
|
String username = "user1";
|
||||||
requestBuilder = MockMvcRequestBuilders.post(url);
|
|
||||||
requestBuilder.contentType(MediaType.APPLICATION_JSON_UTF8);
|
|
||||||
|
|
||||||
// 1. secret key is null.
|
// Call controller method
|
||||||
accessConfig.setAccessKey("rocketmq");
|
Object result = aclController.deleteUser(null, username);
|
||||||
requestBuilder.content(JSON.toJSONString(accessConfig));
|
|
||||||
perform = mockMvc.perform(requestBuilder);
|
|
||||||
perform.andExpect(status().isOk())
|
|
||||||
.andExpect(jsonPath("$.status").value(-1))
|
|
||||||
.andExpect(jsonPath("$.errMsg").exists());
|
|
||||||
|
|
||||||
// 2. update.
|
// Verify
|
||||||
accessConfig.setSecretKey("abcdefghjkl");
|
assertEquals(true, result);
|
||||||
requestBuilder.content(JSON.toJSONString(accessConfig));
|
verify(aclService, times(1)).deleteUser(null, username);
|
||||||
perform = mockMvc.perform(requestBuilder);
|
|
||||||
perform.andExpect(status().isOk())
|
|
||||||
.andExpect(jsonPath("$.status").value(0));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAddAclTopicConfig() throws Exception {
|
public void testUpdateUser() {
|
||||||
final String url = "/acl/topic/add.do";
|
// Prepare test data
|
||||||
AclRequest request = new AclRequest();
|
UserUpdateRequest request = new UserUpdateRequest();
|
||||||
request.setConfig(createDefaultPlainAccessConfig());
|
request.setBrokerAddress("localhost:9092");
|
||||||
|
request.setUserInfo(new UserInfoParam("user1", "newPassword", UserStatus.ENABLE.getName(), UserType.SUPER.getName()));
|
||||||
|
|
||||||
// 1. if not exist.
|
// Call controller method
|
||||||
request.setTopicPerm("test_topic=PUB");
|
Object result = aclController.updateUser(request);
|
||||||
requestBuilder = MockMvcRequestBuilders.post(url);
|
|
||||||
requestBuilder.contentType(MediaType.APPLICATION_JSON_UTF8);
|
|
||||||
requestBuilder.content(JSON.toJSONString(request));
|
|
||||||
perform = mockMvc.perform(requestBuilder);
|
|
||||||
perform.andExpect(status().isOk())
|
|
||||||
.andExpect(jsonPath("$.status").value(0));
|
|
||||||
|
|
||||||
// 2. if exist.
|
// Verify
|
||||||
request.setTopicPerm("topicA=PUB");
|
assertEquals(true, result);
|
||||||
requestBuilder.content(JSON.toJSONString(request));
|
verify(aclService, times(1)).updateUser(request.getBrokerAddress(), request.getUserInfo());
|
||||||
perform = mockMvc.perform(requestBuilder);
|
|
||||||
perform.andExpect(status().isOk())
|
|
||||||
.andExpect(jsonPath("$.status").value(0));
|
|
||||||
|
|
||||||
// 3. if access key not exist.
|
|
||||||
request.getConfig().setAccessKey("test_access_key123");
|
|
||||||
requestBuilder.content(JSON.toJSONString(request));
|
|
||||||
perform = mockMvc.perform(requestBuilder);
|
|
||||||
perform.andExpect(status().isOk())
|
|
||||||
.andExpect(jsonPath("$.status").value(0));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAddAclGroupConfig() throws Exception {
|
public void testCreateUser() {
|
||||||
final String url = "/acl/group/add.do";
|
// Prepare test data
|
||||||
AclRequest request = new AclRequest();
|
UserCreateRequest request = new UserCreateRequest();
|
||||||
request.setConfig(createDefaultPlainAccessConfig());
|
request.setBrokerAddress("localhost:9092");
|
||||||
|
request.setUserInfo(new UserInfoParam("user1", "newPassword", UserStatus.ENABLE.getName(), UserType.SUPER.getName()));
|
||||||
|
|
||||||
// 1. if not exist.
|
// Call controller method
|
||||||
request.setGroupPerm("test_consumer=PUB|SUB");
|
Object result = aclController.createUser(request);
|
||||||
requestBuilder = MockMvcRequestBuilders.post(url);
|
|
||||||
requestBuilder.contentType(MediaType.APPLICATION_JSON_UTF8);
|
|
||||||
requestBuilder.content(JSON.toJSONString(request));
|
|
||||||
perform = mockMvc.perform(requestBuilder);
|
|
||||||
perform.andExpect(status().isOk())
|
|
||||||
.andExpect(jsonPath("$.status").value(0));
|
|
||||||
|
|
||||||
// 2. if exist.
|
// Verify
|
||||||
request.setGroupPerm("groupA=PUB|SUB");
|
assertEquals(true, result);
|
||||||
requestBuilder.content(JSON.toJSONString(request));
|
verify(aclService, times(1)).createUser(request.getBrokerAddress(), request.getUserInfo());
|
||||||
perform = mockMvc.perform(requestBuilder);
|
|
||||||
perform.andExpect(status().isOk())
|
|
||||||
.andExpect(jsonPath("$.status").value(0));
|
|
||||||
|
|
||||||
// 3. if access key not exist.
|
|
||||||
request.getConfig().setAccessKey("test_access_key123");
|
|
||||||
requestBuilder.content(JSON.toJSONString(request));
|
|
||||||
perform = mockMvc.perform(requestBuilder);
|
|
||||||
perform.andExpect(status().isOk())
|
|
||||||
.andExpect(jsonPath("$.status").value(0));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDeletePermConfig() throws Exception {
|
public void testDeleteAcl() {
|
||||||
final String url = "/acl/perm/delete.do";
|
// Prepare test data
|
||||||
AclRequest request = new AclRequest();
|
String brokerAddress = "localhost:9092";
|
||||||
request.setConfig(createDefaultPlainAccessConfig());
|
String subject = "user1";
|
||||||
requestBuilder = MockMvcRequestBuilders.post(url);
|
String resource = "TOPIC:test";
|
||||||
requestBuilder.contentType(MediaType.APPLICATION_JSON_UTF8);
|
|
||||||
requestBuilder.content(JSON.toJSONString(request));
|
|
||||||
perform = mockMvc.perform(requestBuilder);
|
|
||||||
perform.andExpect(status().isOk())
|
|
||||||
.andExpect(jsonPath("$.status").value(0));
|
|
||||||
|
|
||||||
// if access key not exist.
|
// Call controller method
|
||||||
request.getConfig().setAccessKey("test_access_key123");
|
Object result = aclController.deleteAcl(brokerAddress, subject, resource);
|
||||||
requestBuilder.content(JSON.toJSONString(request));
|
|
||||||
perform = mockMvc.perform(requestBuilder);
|
// Verify
|
||||||
perform.andExpect(status().isOk())
|
assertEquals(true, result);
|
||||||
.andExpect(jsonPath("$.status").value(0));
|
verify(aclService, times(1)).deleteAcl(brokerAddress, subject, resource);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSyncConfig() throws Exception {
|
public void testDeleteAclWithoutBrokerAddressAndResource() {
|
||||||
final String url = "/acl/sync.do";
|
// Prepare test data
|
||||||
requestBuilder = MockMvcRequestBuilders.post(url);
|
String subject = "user1";
|
||||||
requestBuilder.contentType(MediaType.APPLICATION_JSON_UTF8);
|
|
||||||
requestBuilder.content(JSON.toJSONString(createDefaultPlainAccessConfig()));
|
// Call controller method
|
||||||
perform = mockMvc.perform(requestBuilder);
|
Object result = aclController.deleteAcl(null, subject, null);
|
||||||
perform.andExpect(status().isOk())
|
|
||||||
.andExpect(jsonPath("$.status").value(0));
|
// Verify
|
||||||
|
assertEquals(true, result);
|
||||||
|
verify(aclService, times(1)).deleteAcl(null, subject, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAddWhiteList() throws Exception {
|
public void testUpdateAcl() {
|
||||||
final String url = "/acl/white/list/add.do";
|
// Prepare test data
|
||||||
List<String> whiteList = Lists.newArrayList("192.168.0.1");
|
PolicyRequest request = new PolicyRequest();
|
||||||
|
request.setBrokerAddress("localhost:9092");
|
||||||
|
request.setSubject("user1");
|
||||||
|
request.setPolicies(List.of(
|
||||||
|
new Policy()
|
||||||
|
));
|
||||||
|
|
||||||
// 1. if global white list is not null.
|
// Call controller method
|
||||||
requestBuilder = MockMvcRequestBuilders.post(url);
|
Object result = aclController.updateAcl(request);
|
||||||
requestBuilder.contentType(MediaType.APPLICATION_JSON_UTF8);
|
|
||||||
requestBuilder.content(JSON.toJSONString(whiteList));
|
|
||||||
perform = mockMvc.perform(requestBuilder);
|
|
||||||
perform.andExpect(status().isOk())
|
|
||||||
.andExpect(jsonPath("$.status").value(0));
|
|
||||||
|
|
||||||
// 2. if global white list is null.
|
// Verify
|
||||||
AclConfig aclConfig = MockObjectUtil.createAclConfig();
|
assertEquals(true, result);
|
||||||
aclConfig.setGlobalWhiteAddrs(null);
|
verify(aclService, times(1)).updateAcl(request);
|
||||||
when(mqAdminExt.examineBrokerClusterAclConfig(anyString())).thenReturn(aclConfig);
|
|
||||||
perform = mockMvc.perform(requestBuilder);
|
|
||||||
perform.andExpect(status().isOk())
|
|
||||||
.andExpect(jsonPath("$.status").value(0));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Override
|
||||||
public void testDeleteWhiteAddr() throws Exception {
|
protected Object getTestController() {
|
||||||
final String url = "/acl/white/list/delete.do";
|
|
||||||
requestBuilder = MockMvcRequestBuilders.delete(url);
|
|
||||||
requestBuilder.param("request", "localhost");
|
|
||||||
perform = mockMvc.perform(requestBuilder);
|
|
||||||
perform.andExpect(status().isOk())
|
|
||||||
.andExpect(jsonPath("$.status").value(0));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testSynchronizeWhiteList() throws Exception {
|
|
||||||
final String url = "/acl/white/list/sync.do";
|
|
||||||
List<String> whiteList = Lists.newArrayList();
|
|
||||||
|
|
||||||
// 1. if white list for syncing is empty.
|
|
||||||
requestBuilder = MockMvcRequestBuilders.post(url);
|
|
||||||
requestBuilder.contentType(MediaType.APPLICATION_JSON_UTF8);
|
|
||||||
requestBuilder.content(JSON.toJSONString(whiteList));
|
|
||||||
perform = mockMvc.perform(requestBuilder);
|
|
||||||
perform.andExpect(status().isOk())
|
|
||||||
.andExpect(jsonPath("$.status").value(-1))
|
|
||||||
.andExpect(jsonPath("$.errMsg").exists());
|
|
||||||
|
|
||||||
// 2. if white list for syncing is not empty.
|
|
||||||
whiteList.add("localhost");
|
|
||||||
requestBuilder.content(JSON.toJSONString(whiteList));
|
|
||||||
perform = mockMvc.perform(requestBuilder);
|
|
||||||
perform.andExpect(status().isOk())
|
|
||||||
.andExpect(jsonPath("$.status").value(0));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override protected Object getTestController() {
|
|
||||||
return aclController;
|
return aclController;
|
||||||
}
|
}
|
||||||
|
|
||||||
private PlainAccessConfig createDefaultPlainAccessConfig() {
|
|
||||||
PlainAccessConfig config = new PlainAccessConfig();
|
|
||||||
config.setAdmin(false);
|
|
||||||
config.setAccessKey("rocketmq");
|
|
||||||
config.setSecretKey("123456789");
|
|
||||||
config.setDefaultGroupPerm("SUB");
|
|
||||||
config.setDefaultTopicPerm("DENY");
|
|
||||||
config.setTopicPerms(Lists.newArrayList("topicA=DENY", "topicB=PUB|SUB"));
|
|
||||||
config.setGroupPerms(Lists.newArrayList("groupA=DENY", "groupB=PUB|SUB"));
|
|
||||||
|
|
||||||
return config;
|
|
||||||
}
|
|
||||||
}
|
}
|
@@ -23,6 +23,8 @@ import org.apache.rocketmq.dashboard.config.RMQConfigure;
|
|||||||
import org.apache.rocketmq.dashboard.support.GlobalExceptionHandler;
|
import org.apache.rocketmq.dashboard.support.GlobalExceptionHandler;
|
||||||
import org.apache.rocketmq.dashboard.support.GlobalRestfulResponseBodyAdvice;
|
import org.apache.rocketmq.dashboard.support.GlobalRestfulResponseBodyAdvice;
|
||||||
import org.apache.rocketmq.dashboard.util.MyPrintingResultHandler;
|
import org.apache.rocketmq.dashboard.util.MyPrintingResultHandler;
|
||||||
|
import org.apache.rocketmq.remoting.protocol.body.ClusterInfo;
|
||||||
|
import org.apache.rocketmq.remoting.protocol.route.BrokerData;
|
||||||
import org.apache.rocketmq.tools.admin.MQAdminExt;
|
import org.apache.rocketmq.tools.admin.MQAdminExt;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
@@ -32,6 +34,12 @@ import org.springframework.test.web.servlet.ResultActions;
|
|||||||
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
|
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
|
||||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||||
|
@@ -16,17 +16,18 @@
|
|||||||
*/
|
*/
|
||||||
package org.apache.rocketmq.dashboard.controller;
|
package org.apache.rocketmq.dashboard.controller;
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Properties;
|
|
||||||
import org.apache.rocketmq.remoting.protocol.body.ClusterInfo;
|
|
||||||
import org.apache.rocketmq.remoting.protocol.body.KVTable;
|
|
||||||
import org.apache.rocketmq.dashboard.service.impl.ClusterServiceImpl;
|
import org.apache.rocketmq.dashboard.service.impl.ClusterServiceImpl;
|
||||||
import org.apache.rocketmq.dashboard.util.MockObjectUtil;
|
import org.apache.rocketmq.dashboard.util.MockObjectUtil;
|
||||||
|
import org.apache.rocketmq.remoting.protocol.body.ClusterInfo;
|
||||||
|
import org.apache.rocketmq.remoting.protocol.body.KVTable;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.mockito.InjectMocks;
|
import org.mockito.InjectMocks;
|
||||||
import org.mockito.Spy;
|
import org.mockito.Spy;
|
||||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
import static org.mockito.ArgumentMatchers.anyString;
|
import static org.mockito.ArgumentMatchers.anyString;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||||
|
@@ -19,44 +19,43 @@ package org.apache.rocketmq.dashboard.controller;
|
|||||||
|
|
||||||
import com.alibaba.fastjson.JSON;
|
import com.alibaba.fastjson.JSON;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import org.apache.rocketmq.client.exception.MQClientException;
|
import org.apache.rocketmq.client.exception.MQClientException;
|
||||||
|
import org.apache.rocketmq.common.message.MessageQueue;
|
||||||
|
import org.apache.rocketmq.dashboard.model.QueueStatInfo;
|
||||||
|
import org.apache.rocketmq.dashboard.model.TopicConsumerInfo;
|
||||||
|
import org.apache.rocketmq.dashboard.model.request.ConsumerConfigInfo;
|
||||||
|
import org.apache.rocketmq.dashboard.model.request.DeleteSubGroupRequest;
|
||||||
|
import org.apache.rocketmq.dashboard.model.request.ResetOffsetRequest;
|
||||||
|
import org.apache.rocketmq.dashboard.service.ClusterInfoService;
|
||||||
|
import org.apache.rocketmq.dashboard.service.impl.ConsumerServiceImpl;
|
||||||
|
import org.apache.rocketmq.dashboard.util.MockObjectUtil;
|
||||||
|
import org.apache.rocketmq.remoting.protocol.ResponseCode;
|
||||||
import org.apache.rocketmq.remoting.protocol.admin.ConsumeStats;
|
import org.apache.rocketmq.remoting.protocol.admin.ConsumeStats;
|
||||||
import org.apache.rocketmq.remoting.protocol.admin.RollbackStats;
|
import org.apache.rocketmq.remoting.protocol.admin.RollbackStats;
|
||||||
import org.apache.rocketmq.common.message.MessageQueue;
|
|
||||||
import org.apache.rocketmq.remoting.protocol.ResponseCode;
|
|
||||||
import org.apache.rocketmq.remoting.protocol.body.ClusterInfo;
|
import org.apache.rocketmq.remoting.protocol.body.ClusterInfo;
|
||||||
|
import org.apache.rocketmq.remoting.protocol.body.Connection;
|
||||||
import org.apache.rocketmq.remoting.protocol.body.ConsumerConnection;
|
import org.apache.rocketmq.remoting.protocol.body.ConsumerConnection;
|
||||||
import org.apache.rocketmq.remoting.protocol.body.ConsumerRunningInfo;
|
import org.apache.rocketmq.remoting.protocol.body.ConsumerRunningInfo;
|
||||||
import org.apache.rocketmq.remoting.protocol.body.SubscriptionGroupWrapper;
|
import org.apache.rocketmq.remoting.protocol.body.SubscriptionGroupWrapper;
|
||||||
import org.apache.rocketmq.remoting.protocol.heartbeat.ConsumeType;
|
import org.apache.rocketmq.remoting.protocol.heartbeat.ConsumeType;
|
||||||
import org.apache.rocketmq.remoting.protocol.heartbeat.MessageModel;
|
import org.apache.rocketmq.remoting.protocol.heartbeat.MessageModel;
|
||||||
import org.apache.rocketmq.remoting.protocol.subscription.SubscriptionGroupConfig;
|
import org.apache.rocketmq.remoting.protocol.subscription.SubscriptionGroupConfig;
|
||||||
import org.apache.rocketmq.dashboard.model.request.ConsumerConfigInfo;
|
|
||||||
import org.apache.rocketmq.dashboard.model.request.DeleteSubGroupRequest;
|
|
||||||
import org.apache.rocketmq.dashboard.model.request.ResetOffsetRequest;
|
|
||||||
import org.apache.rocketmq.dashboard.service.impl.ConsumerServiceImpl;
|
|
||||||
import org.apache.rocketmq.dashboard.util.MockObjectUtil;
|
|
||||||
import org.apache.rocketmq.dashboard.model.TopicConsumerInfo;
|
|
||||||
import org.apache.rocketmq.dashboard.model.QueueStatInfo;
|
|
||||||
import org.apache.rocketmq.remoting.protocol.body.Connection;
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.mockito.InjectMocks;
|
import org.mockito.InjectMocks;
|
||||||
|
import org.mockito.Mock;
|
||||||
import org.mockito.Spy;
|
import org.mockito.Spy;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import static org.hamcrest.Matchers.hasSize;
|
import static org.hamcrest.Matchers.hasSize;
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.*;
|
||||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
|
||||||
import static org.mockito.ArgumentMatchers.anyLong;
|
|
||||||
import static org.mockito.ArgumentMatchers.anyString;
|
|
||||||
import static org.mockito.ArgumentMatchers.isNull;
|
|
||||||
import static org.mockito.Mockito.doNothing;
|
import static org.mockito.Mockito.doNothing;
|
||||||
import static org.mockito.Mockito.doReturn;
|
import static org.mockito.Mockito.doReturn;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
@@ -71,12 +70,18 @@ public class ConsumerControllerTest extends BaseControllerTest {
|
|||||||
@Spy
|
@Spy
|
||||||
private ConsumerServiceImpl consumerService;
|
private ConsumerServiceImpl consumerService;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private ClusterInfoService clusterInfoService;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void init() throws Exception {
|
public void init() throws Exception {
|
||||||
|
// 2. mock ClusterInfo data
|
||||||
|
ClusterInfo mockClusterInfo = getClusterInfo();
|
||||||
|
when(clusterInfoService.get()).thenReturn(mockClusterInfo);
|
||||||
consumerService.afterPropertiesSet();
|
consumerService.afterPropertiesSet();
|
||||||
super.mockRmqConfigure();
|
super.mockRmqConfigure();
|
||||||
ClusterInfo clusterInfo = MockObjectUtil.createClusterInfo();
|
// ClusterInfo clusterInfo = MockObjectUtil.createClusterInfo();
|
||||||
when(mqAdminExt.examineBrokerClusterInfo()).thenReturn(clusterInfo);
|
// when(mqAdminExt.examineBrokerClusterInfo()).thenReturn(clusterInfo);
|
||||||
SubscriptionGroupWrapper wrapper = MockObjectUtil.createSubscriptionGroupWrapper();
|
SubscriptionGroupWrapper wrapper = MockObjectUtil.createSubscriptionGroupWrapper();
|
||||||
when(mqAdminExt.getAllSubscriptionGroup(anyString(), anyLong())).thenReturn(wrapper);
|
when(mqAdminExt.getAllSubscriptionGroup(anyString(), anyLong())).thenReturn(wrapper);
|
||||||
ConsumeStats stats = MockObjectUtil.createConsumeStats();
|
ConsumeStats stats = MockObjectUtil.createConsumeStats();
|
||||||
@@ -109,6 +114,7 @@ public class ConsumerControllerTest extends BaseControllerTest {
|
|||||||
@Test
|
@Test
|
||||||
public void testGroupQuery() throws Exception {
|
public void testGroupQuery() throws Exception {
|
||||||
final String url = "/consumer/group.query";
|
final String url = "/consumer/group.query";
|
||||||
|
|
||||||
requestBuilder = MockMvcRequestBuilders.get(url);
|
requestBuilder = MockMvcRequestBuilders.get(url);
|
||||||
requestBuilder.param("consumerGroup", "group_test");
|
requestBuilder.param("consumerGroup", "group_test");
|
||||||
perform = mockMvc.perform(requestBuilder);
|
perform = mockMvc.perform(requestBuilder);
|
||||||
@@ -177,6 +183,10 @@ public class ConsumerControllerTest extends BaseControllerTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testExamineSubscriptionGroupConfig() throws Exception {
|
public void testExamineSubscriptionGroupConfig() throws Exception {
|
||||||
|
ClusterInfo mockClusterInfo = getClusterInfo();
|
||||||
|
{
|
||||||
|
when(clusterInfoService.get()).thenReturn(mockClusterInfo);
|
||||||
|
}
|
||||||
final String url = "/consumer/examineSubscriptionGroupConfig.query";
|
final String url = "/consumer/examineSubscriptionGroupConfig.query";
|
||||||
requestBuilder = MockMvcRequestBuilders.get(url);
|
requestBuilder = MockMvcRequestBuilders.get(url);
|
||||||
requestBuilder.param("consumerGroup", "group_test");
|
requestBuilder.param("consumerGroup", "group_test");
|
||||||
@@ -188,7 +198,9 @@ public class ConsumerControllerTest extends BaseControllerTest {
|
|||||||
@Test
|
@Test
|
||||||
public void testDelete() throws Exception {
|
public void testDelete() throws Exception {
|
||||||
final String url = "/consumer/deleteSubGroup.do";
|
final String url = "/consumer/deleteSubGroup.do";
|
||||||
|
ClusterInfo mockClusterInfo = getClusterInfo();
|
||||||
{
|
{
|
||||||
|
when(clusterInfoService.get()).thenReturn(mockClusterInfo);
|
||||||
doNothing().when(mqAdminExt).deleteSubscriptionGroup(any(), anyString());
|
doNothing().when(mqAdminExt).deleteSubscriptionGroup(any(), anyString());
|
||||||
doNothing().when(mqAdminExt).deleteTopicInBroker(any(), anyString());
|
doNothing().when(mqAdminExt).deleteTopicInBroker(any(), anyString());
|
||||||
doNothing().when(mqAdminExt).deleteTopicInNameServer(any(), anyString());
|
doNothing().when(mqAdminExt).deleteTopicInNameServer(any(), anyString());
|
||||||
@@ -214,7 +226,6 @@ public class ConsumerControllerTest extends BaseControllerTest {
|
|||||||
requestBuilder.content(JSON.toJSONString(consumerConfigInfo));
|
requestBuilder.content(JSON.toJSONString(consumerConfigInfo));
|
||||||
perform = mockMvc.perform(requestBuilder);
|
perform = mockMvc.perform(requestBuilder);
|
||||||
performErrorExpect(perform);
|
performErrorExpect(perform);
|
||||||
|
|
||||||
{
|
{
|
||||||
doNothing().when(mqAdminExt).createAndUpdateSubscriptionGroupConfig(anyString(), any());
|
doNothing().when(mqAdminExt).createAndUpdateSubscriptionGroupConfig(anyString(), any());
|
||||||
}
|
}
|
||||||
@@ -233,6 +244,7 @@ public class ConsumerControllerTest extends BaseControllerTest {
|
|||||||
.andExpect(jsonPath("$.data").value(true));
|
.andExpect(jsonPath("$.data").value(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testQueryConsumerByTopic() throws Exception {
|
public void testQueryConsumerByTopic() throws Exception {
|
||||||
// Prepare test data
|
// Prepare test data
|
||||||
@@ -313,7 +325,8 @@ public class ConsumerControllerTest extends BaseControllerTest {
|
|||||||
.andExpect(jsonPath("$.data.jstack").value("test"));
|
.andExpect(jsonPath("$.data.jstack").value("test"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override protected Object getTestController() {
|
@Override
|
||||||
|
protected Object getTestController() {
|
||||||
return consumerController;
|
return consumerController;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -18,14 +18,6 @@ package org.apache.rocketmq.dashboard.controller;
|
|||||||
|
|
||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
import com.google.common.io.Files;
|
import com.google.common.io.Files;
|
||||||
import java.io.File;
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.text.DateFormat;
|
|
||||||
import java.text.SimpleDateFormat;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import org.apache.rocketmq.dashboard.service.impl.DashboardCollectServiceImpl;
|
import org.apache.rocketmq.dashboard.service.impl.DashboardCollectServiceImpl;
|
||||||
import org.apache.rocketmq.dashboard.service.impl.DashboardServiceImpl;
|
import org.apache.rocketmq.dashboard.service.impl.DashboardServiceImpl;
|
||||||
import org.apache.rocketmq.dashboard.util.JsonUtil;
|
import org.apache.rocketmq.dashboard.util.JsonUtil;
|
||||||
@@ -36,6 +28,15 @@ import org.mockito.InjectMocks;
|
|||||||
import org.mockito.Spy;
|
import org.mockito.Spy;
|
||||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.text.DateFormat;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import static org.hamcrest.Matchers.hasKey;
|
import static org.hamcrest.Matchers.hasKey;
|
||||||
import static org.hamcrest.Matchers.hasSize;
|
import static org.hamcrest.Matchers.hasSize;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
@@ -18,13 +18,8 @@ package org.apache.rocketmq.dashboard.controller;
|
|||||||
|
|
||||||
import com.alibaba.fastjson.JSON;
|
import com.alibaba.fastjson.JSON;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import java.util.List;
|
|
||||||
import org.apache.rocketmq.client.exception.MQClientException;
|
import org.apache.rocketmq.client.exception.MQClientException;
|
||||||
import org.apache.rocketmq.common.MixAll;
|
import org.apache.rocketmq.common.MixAll;
|
||||||
import org.apache.rocketmq.remoting.protocol.ResponseCode;
|
|
||||||
import org.apache.rocketmq.remoting.protocol.body.CMResult;
|
|
||||||
import org.apache.rocketmq.remoting.protocol.body.ConsumeMessageDirectlyResult;
|
|
||||||
import org.apache.rocketmq.remoting.protocol.route.TopicRouteData;
|
|
||||||
import org.apache.rocketmq.dashboard.model.DlqMessageRequest;
|
import org.apache.rocketmq.dashboard.model.DlqMessageRequest;
|
||||||
import org.apache.rocketmq.dashboard.model.MessagePage;
|
import org.apache.rocketmq.dashboard.model.MessagePage;
|
||||||
import org.apache.rocketmq.dashboard.model.MessageView;
|
import org.apache.rocketmq.dashboard.model.MessageView;
|
||||||
@@ -32,6 +27,10 @@ import org.apache.rocketmq.dashboard.model.request.MessageQuery;
|
|||||||
import org.apache.rocketmq.dashboard.service.impl.DlqMessageServiceImpl;
|
import org.apache.rocketmq.dashboard.service.impl.DlqMessageServiceImpl;
|
||||||
import org.apache.rocketmq.dashboard.service.impl.MessageServiceImpl;
|
import org.apache.rocketmq.dashboard.service.impl.MessageServiceImpl;
|
||||||
import org.apache.rocketmq.dashboard.util.MockObjectUtil;
|
import org.apache.rocketmq.dashboard.util.MockObjectUtil;
|
||||||
|
import org.apache.rocketmq.remoting.protocol.ResponseCode;
|
||||||
|
import org.apache.rocketmq.remoting.protocol.body.CMResult;
|
||||||
|
import org.apache.rocketmq.remoting.protocol.body.ConsumeMessageDirectlyResult;
|
||||||
|
import org.apache.rocketmq.remoting.protocol.route.TopicRouteData;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.mockito.InjectMocks;
|
import org.mockito.InjectMocks;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
@@ -41,6 +40,8 @@ import org.springframework.data.domain.PageRequest;
|
|||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import static org.hamcrest.Matchers.hasSize;
|
import static org.hamcrest.Matchers.hasSize;
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
@@ -172,7 +173,8 @@ public class DlqMessageControllerTest extends BaseControllerTest {
|
|||||||
.andExpect(content().contentType("application/vnd.ms-excel;charset=utf-8"));
|
.andExpect(content().contentType("application/vnd.ms-excel;charset=utf-8"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override protected Object getTestController() {
|
@Override
|
||||||
|
protected Object getTestController() {
|
||||||
return dlqMessageController;
|
return dlqMessageController;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -16,20 +16,26 @@
|
|||||||
*/
|
*/
|
||||||
package org.apache.rocketmq.dashboard.controller;
|
package org.apache.rocketmq.dashboard.controller;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import org.apache.rocketmq.dashboard.model.User;
|
import org.apache.rocketmq.dashboard.model.User;
|
||||||
import org.apache.rocketmq.dashboard.service.impl.UserServiceImpl;
|
import org.apache.rocketmq.dashboard.service.impl.UserServiceImpl;
|
||||||
|
import org.apache.rocketmq.dashboard.service.strategy.UserContext;
|
||||||
|
import org.apache.rocketmq.dashboard.service.strategy.UserStrategy;
|
||||||
|
import org.apache.rocketmq.dashboard.support.GlobalExceptionHandler;
|
||||||
import org.apache.rocketmq.dashboard.util.WebUtil;
|
import org.apache.rocketmq.dashboard.util.WebUtil;
|
||||||
import org.junit.Assert;
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.mockito.InjectMocks;
|
import org.mockito.InjectMocks;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.MockitoAnnotations;
|
||||||
import org.mockito.Spy;
|
import org.mockito.Spy;
|
||||||
import org.springframework.test.util.ReflectionTestUtils;
|
|
||||||
import org.springframework.util.ReflectionUtils;
|
|
||||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||||
|
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||||
|
import org.springframework.util.ReflectionUtils;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||||
|
|
||||||
@@ -38,19 +44,27 @@ public class LoginControllerTest extends BaseControllerTest {
|
|||||||
@InjectMocks
|
@InjectMocks
|
||||||
private LoginController loginController;
|
private LoginController loginController;
|
||||||
|
|
||||||
@Spy
|
@Mock
|
||||||
private UserServiceImpl userService;
|
private UserServiceImpl userService;
|
||||||
|
|
||||||
|
@Spy
|
||||||
|
private UserContext userContext;
|
||||||
|
|
||||||
|
@Spy
|
||||||
|
private UserStrategy userStrategy;
|
||||||
|
|
||||||
private String contextPath = "/rocketmq-console";
|
private String contextPath = "/rocketmq-console";
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void init() {
|
public void init() {
|
||||||
|
MockitoAnnotations.initMocks(this);
|
||||||
super.mockRmqConfigure();
|
super.mockRmqConfigure();
|
||||||
when(configure.isLoginRequired()).thenReturn(true);
|
when(configure.isLoginRequired()).thenReturn(true);
|
||||||
when(configure.getRocketMqDashboardDataPath()).thenReturn("");
|
when(configure.getRocketMqDashboardDataPath()).thenReturn("");
|
||||||
Field contextPathField = ReflectionUtils.findField(LoginController.class, "contextPath");
|
Field contextPathField = ReflectionUtils.findField(LoginController.class, "contextPath");
|
||||||
ReflectionUtils.makeAccessible(contextPathField);
|
ReflectionUtils.makeAccessible(contextPathField);
|
||||||
ReflectionUtils.setField(contextPathField, loginController, contextPath);
|
ReflectionUtils.setField(contextPathField, loginController, contextPath);
|
||||||
|
mockMvc = MockMvcBuilders.standaloneSetup(loginController).setControllerAdvice(GlobalExceptionHandler.class).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -60,57 +74,56 @@ public class LoginControllerTest extends BaseControllerTest {
|
|||||||
requestBuilder.sessionAttr(WebUtil.USER_NAME, "admin");
|
requestBuilder.sessionAttr(WebUtil.USER_NAME, "admin");
|
||||||
perform = mockMvc.perform(requestBuilder);
|
perform = mockMvc.perform(requestBuilder);
|
||||||
perform.andExpect(status().isOk())
|
perform.andExpect(status().isOk())
|
||||||
.andExpect(jsonPath("$.data.logined").value(true))
|
.andExpect(jsonPath("$.logined").value(true))
|
||||||
.andExpect(jsonPath("$.data.loginRequired").value(true));
|
.andExpect(jsonPath("$.loginRequired").value(true));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testLogin() throws Exception {
|
public void testLogin() throws Exception {
|
||||||
final String url = "/login/login.do";
|
final String url = "/login/login.do";
|
||||||
final String username = "admin";
|
final String username = "admin";
|
||||||
final String rightPwd = "admin";
|
final String rightPwd = "admin";
|
||||||
final String wrongPwd = "rocketmq";
|
final String wrongPwd = "rocketmq";
|
||||||
{
|
|
||||||
UserServiceImpl.FileBasedUserInfoStore store
|
// 模拟 userService.queryByName 方法返回一个用户
|
||||||
= new UserServiceImpl.FileBasedUserInfoStore(configure);
|
User user = new User("admin", "admin", 1);
|
||||||
User user = store.queryByName(username);
|
user.setPassword(rightPwd);
|
||||||
Assert.assertNotNull(user);
|
|
||||||
Assert.assertEquals(user.getPassword(), rightPwd);
|
|
||||||
ReflectionTestUtils.setField(userService, "fileBasedUserInfoStore", store);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 1、login fail
|
// 1、login fail
|
||||||
requestBuilder = MockMvcRequestBuilders.post(url);
|
perform = mockMvc.perform(post(url)
|
||||||
requestBuilder.param("username", username)
|
.param("username", username)
|
||||||
.param("password", wrongPwd);
|
.param("password", wrongPwd));
|
||||||
perform = mockMvc.perform(requestBuilder);
|
|
||||||
perform.andExpect(status().isOk())
|
perform.andExpect(status().isOk())
|
||||||
.andExpect(jsonPath("$.data").doesNotExist())
|
.andExpect(jsonPath("$.data").doesNotExist())
|
||||||
.andExpect(jsonPath("$.status").value(-1))
|
.andExpect(jsonPath("$.status").value(-1))
|
||||||
.andExpect(jsonPath("$.errMsg").value("Bad username or password!"));
|
.andExpect(jsonPath("$.errMsg").value("Bad username or password!"));
|
||||||
|
|
||||||
// 2、login success
|
when(userService.queryByUsernameAndPassword(username, rightPwd)).thenReturn(user);
|
||||||
requestBuilder = MockMvcRequestBuilders.post(url);
|
|
||||||
requestBuilder.param("username", username)
|
// 2、login success
|
||||||
.param("password", rightPwd);
|
perform = mockMvc.perform(post(url)
|
||||||
perform = mockMvc.perform(requestBuilder);
|
.param("username", username)
|
||||||
perform.andExpect(status().isOk())
|
.param("password", rightPwd));
|
||||||
.andExpect(jsonPath("$.data.contextPath").value(contextPath));
|
perform.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.contextPath").value(contextPath));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testLogout() throws Exception {
|
public void testLogout() throws Exception {
|
||||||
final String url = "/login/logout.do";
|
final String url = "/login/logout.do";
|
||||||
requestBuilder = MockMvcRequestBuilders.post(url);
|
requestBuilder = post(url);
|
||||||
requestBuilder.sessionAttr(WebUtil.USER_NAME, "root");
|
requestBuilder.sessionAttr(WebUtil.USER_NAME, "root");
|
||||||
perform = mockMvc.perform(requestBuilder);
|
perform = mockMvc.perform(requestBuilder);
|
||||||
perform.andExpect(status().isOk())
|
perform.andExpect(status().isOk())
|
||||||
.andExpect(jsonPath("$.data").value(contextPath));
|
.andExpect(jsonPath("$.data").value(contextPath));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override protected Object getTestController() {
|
@Override
|
||||||
|
protected Object getTestController() {
|
||||||
return loginController;
|
return loginController;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -18,11 +18,6 @@ package org.apache.rocketmq.dashboard.controller;
|
|||||||
|
|
||||||
import com.alibaba.fastjson.JSON;
|
import com.alibaba.fastjson.JSON;
|
||||||
import com.google.common.cache.Cache;
|
import com.google.common.cache.Cache;
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
import org.apache.rocketmq.client.QueryResult;
|
import org.apache.rocketmq.client.QueryResult;
|
||||||
import org.apache.rocketmq.client.consumer.DefaultMQPullConsumer;
|
import org.apache.rocketmq.client.consumer.DefaultMQPullConsumer;
|
||||||
import org.apache.rocketmq.client.consumer.PullResult;
|
import org.apache.rocketmq.client.consumer.PullResult;
|
||||||
@@ -32,28 +27,33 @@ import org.apache.rocketmq.client.exception.MQClientException;
|
|||||||
import org.apache.rocketmq.common.message.MessageClientIDSetter;
|
import org.apache.rocketmq.common.message.MessageClientIDSetter;
|
||||||
import org.apache.rocketmq.common.message.MessageExt;
|
import org.apache.rocketmq.common.message.MessageExt;
|
||||||
import org.apache.rocketmq.common.message.MessageQueue;
|
import org.apache.rocketmq.common.message.MessageQueue;
|
||||||
import org.apache.rocketmq.remoting.protocol.body.CMResult;
|
|
||||||
import org.apache.rocketmq.remoting.protocol.body.ConsumeMessageDirectlyResult;
|
|
||||||
import org.apache.rocketmq.remoting.protocol.body.ConsumerConnection;
|
|
||||||
import org.apache.rocketmq.dashboard.model.QueueOffsetInfo;
|
import org.apache.rocketmq.dashboard.model.QueueOffsetInfo;
|
||||||
import org.apache.rocketmq.dashboard.model.request.MessageQuery;
|
import org.apache.rocketmq.dashboard.model.request.MessageQuery;
|
||||||
import org.apache.rocketmq.dashboard.service.impl.MessageServiceImpl;
|
import org.apache.rocketmq.dashboard.service.impl.MessageServiceImpl;
|
||||||
|
import org.apache.rocketmq.dashboard.support.AutoCloseConsumerWrapper;
|
||||||
import org.apache.rocketmq.dashboard.util.MockObjectUtil;
|
import org.apache.rocketmq.dashboard.util.MockObjectUtil;
|
||||||
|
import org.apache.rocketmq.remoting.RPCHook;
|
||||||
|
import org.apache.rocketmq.remoting.protocol.body.CMResult;
|
||||||
|
import org.apache.rocketmq.remoting.protocol.body.ConsumeMessageDirectlyResult;
|
||||||
|
import org.apache.rocketmq.remoting.protocol.body.ConsumerConnection;
|
||||||
import org.apache.rocketmq.tools.admin.api.MessageTrack;
|
import org.apache.rocketmq.tools.admin.api.MessageTrack;
|
||||||
import org.apache.rocketmq.tools.admin.api.TrackType;
|
import org.apache.rocketmq.tools.admin.api.TrackType;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.mockito.InjectMocks;
|
import org.mockito.InjectMocks;
|
||||||
|
import org.mockito.Mock;
|
||||||
import org.mockito.Spy;
|
import org.mockito.Spy;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import static org.hamcrest.Matchers.hasSize;
|
import static org.hamcrest.Matchers.hasSize;
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.*;
|
||||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
|
||||||
import static org.mockito.ArgumentMatchers.anyInt;
|
|
||||||
import static org.mockito.ArgumentMatchers.anyLong;
|
|
||||||
import static org.mockito.ArgumentMatchers.anyString;
|
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||||
@@ -71,6 +71,9 @@ public class MessageControllerTest extends BaseControllerTest {
|
|||||||
|
|
||||||
private DefaultMQPullConsumer defaultMQPullConsumer;
|
private DefaultMQPullConsumer defaultMQPullConsumer;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private AutoCloseConsumerWrapper autoCloseConsumerWrapper;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void init() throws Exception {
|
public void init() throws Exception {
|
||||||
super.mockRmqConfigure();
|
super.mockRmqConfigure();
|
||||||
@@ -89,7 +92,6 @@ public class MessageControllerTest extends BaseControllerTest {
|
|||||||
when(pullResult.getNextBeginOffset()).thenReturn(Long.MAX_VALUE);
|
when(pullResult.getNextBeginOffset()).thenReturn(Long.MAX_VALUE);
|
||||||
when(pullResult.getPullStatus()).thenReturn(PullStatus.FOUND);
|
when(pullResult.getPullStatus()).thenReturn(PullStatus.FOUND);
|
||||||
when(pullResult.getMsgFoundList()).thenReturn(wrappers);
|
when(pullResult.getMsgFoundList()).thenReturn(wrappers);
|
||||||
when(messageService.buildDefaultMQPullConsumer(any(), anyBoolean())).thenReturn(defaultMQPullConsumer);
|
|
||||||
|
|
||||||
// Ensure searchOffset returns values that make sense for the test times
|
// Ensure searchOffset returns values that make sense for the test times
|
||||||
when(defaultMQPullConsumer.searchOffset(any(MessageQueue.class), anyLong())).thenAnswer(invocation -> {
|
when(defaultMQPullConsumer.searchOffset(any(MessageQueue.class), anyLong())).thenAnswer(invocation -> {
|
||||||
@@ -115,6 +117,7 @@ public class MessageControllerTest extends BaseControllerTest {
|
|||||||
// Override the previous mock to ensure the test finds messages
|
// Override the previous mock to ensure the test finds messages
|
||||||
when(defaultMQPullConsumer.pull(any(MessageQueue.class), anyString(), anyLong(), anyInt()))
|
when(defaultMQPullConsumer.pull(any(MessageQueue.class), anyString(), anyLong(), anyInt()))
|
||||||
.thenReturn(pullResultWithMessages);
|
.thenReturn(pullResultWithMessages);
|
||||||
|
when(autoCloseConsumerWrapper.getConsumer(any(RPCHook.class), anyBoolean())).thenReturn(defaultMQPullConsumer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -269,7 +272,8 @@ public class MessageControllerTest extends BaseControllerTest {
|
|||||||
.andExpect(jsonPath("$.data.consumeResult").value(CMResult.CR_LATER.name()));
|
.andExpect(jsonPath("$.data.consumeResult").value(CMResult.CR_LATER.name()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override protected Object getTestController() {
|
@Override
|
||||||
|
protected Object getTestController() {
|
||||||
return messageController;
|
return messageController;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -16,8 +16,6 @@
|
|||||||
*/
|
*/
|
||||||
package org.apache.rocketmq.dashboard.controller;
|
package org.apache.rocketmq.dashboard.controller;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import org.apache.rocketmq.client.QueryResult;
|
import org.apache.rocketmq.client.QueryResult;
|
||||||
import org.apache.rocketmq.client.exception.MQClientException;
|
import org.apache.rocketmq.client.exception.MQClientException;
|
||||||
import org.apache.rocketmq.client.trace.TraceType;
|
import org.apache.rocketmq.client.trace.TraceType;
|
||||||
@@ -33,6 +31,9 @@ import org.mockito.InjectMocks;
|
|||||||
import org.mockito.Spy;
|
import org.mockito.Spy;
|
||||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import static org.hamcrest.Matchers.hasSize;
|
import static org.hamcrest.Matchers.hasSize;
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.mockito.ArgumentMatchers.anyInt;
|
import static org.mockito.ArgumentMatchers.anyInt;
|
||||||
@@ -135,7 +136,8 @@ public class MessageTraceControllerTest extends BaseControllerTest {
|
|||||||
.andExpect(jsonPath("$.data.messageTraceViews", hasSize(4)));
|
.andExpect(jsonPath("$.data.messageTraceViews", hasSize(4)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override protected Object getTestController() {
|
@Override
|
||||||
|
protected Object getTestController() {
|
||||||
return messageTraceController;
|
return messageTraceController;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -17,9 +17,6 @@
|
|||||||
package org.apache.rocketmq.dashboard.controller;
|
package org.apache.rocketmq.dashboard.controller;
|
||||||
|
|
||||||
import com.fasterxml.jackson.core.type.TypeReference;
|
import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
import java.io.File;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
import org.apache.rocketmq.common.MixAll;
|
import org.apache.rocketmq.common.MixAll;
|
||||||
import org.apache.rocketmq.dashboard.model.ConsumerMonitorConfig;
|
import org.apache.rocketmq.dashboard.model.ConsumerMonitorConfig;
|
||||||
import org.apache.rocketmq.dashboard.service.impl.MonitorServiceImpl;
|
import org.apache.rocketmq.dashboard.service.impl.MonitorServiceImpl;
|
||||||
@@ -33,6 +30,10 @@ import org.mockito.Spy;
|
|||||||
import org.springframework.test.util.ReflectionTestUtils;
|
import org.springframework.test.util.ReflectionTestUtils;
|
||||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||||
@@ -135,7 +136,8 @@ public class MonitorControllerTest extends BaseControllerTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override protected Object getTestController() {
|
@Override
|
||||||
|
protected Object getTestController() {
|
||||||
return monitorController;
|
return monitorController;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -44,7 +44,8 @@ public class NamesvrControllerTest extends BaseControllerTest {
|
|||||||
Assert.assertEquals(namesrvAddr, "127.0.0.1:9876");
|
Assert.assertEquals(namesrvAddr, "127.0.0.1:9876");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override protected Object getTestController() {
|
@Override
|
||||||
|
protected Object getTestController() {
|
||||||
return namesvrController;
|
return namesvrController;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user