mirror of
https://github.com/apache/rocketmq-dashboard.git
synced 2025-09-10 11:40:01 +08:00
Compare commits
4 Commits
8564296440
...
a5138eb0d8
Author | SHA1 | Date | |
---|---|---|---|
|
a5138eb0d8 | ||
|
b43c7abe52 | ||
|
bfd0e26737 | ||
|
31d8086db3 |
@@ -536,22 +536,6 @@ const remoteApi = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
refreshTopicList: async () => {
|
|
||||||
try {
|
|
||||||
const response = await remoteApi._fetch(remoteApi.buildUrl("/topic/refresh"), {
|
|
||||||
method: 'POST'
|
|
||||||
});
|
|
||||||
const result = await response.json();
|
|
||||||
if (result.status === 0 && result.data === true) {
|
|
||||||
return remoteApi.queryTopicList();
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error refreshing topic list:", error);
|
|
||||||
return { status: 1, errMsg: "Failed to refresh topic list" };
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
deleteTopic: async (topic) => {
|
deleteTopic: async (topic) => {
|
||||||
try {
|
try {
|
||||||
const url = remoteApi.buildUrl(`/topic/deleteTopic.do?topic=${encodeURIComponent(topic)}`);
|
const url = remoteApi.buildUrl(`/topic/deleteTopic.do?topic=${encodeURIComponent(topic)}`);
|
||||||
|
@@ -15,7 +15,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { useState } from 'react';
|
import React, {useEffect, useState} from 'react';
|
||||||
import { Layout, Menu, Dropdown, Button, Drawer, Grid, Space } from 'antd';
|
import { Layout, Menu, Dropdown, Button, Drawer, Grid, Space } from 'antd';
|
||||||
import {GlobalOutlined, DownOutlined, UserOutlined, MenuOutlined, BgColorsOutlined} from '@ant-design/icons';
|
import {GlobalOutlined, DownOutlined, UserOutlined, MenuOutlined, BgColorsOutlined} from '@ant-design/icons';
|
||||||
import { useLocation, useNavigate } from 'react-router-dom';
|
import { useLocation, useNavigate } from 'react-router-dom';
|
||||||
@@ -26,13 +26,13 @@ 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 = ({ username, 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 [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
|
||||||
@@ -46,10 +46,10 @@ const Navbar = ({ username, rmqVersion = true, showAcl = true}) => {
|
|||||||
const onLogout = () => {
|
const onLogout = () => {
|
||||||
remoteApi.logout().then(res => {
|
remoteApi.logout().then(res => {
|
||||||
if (res.status === 0) {
|
if (res.status === 0) {
|
||||||
window.sessionStorage.removeItem("username");
|
window.localStorage.removeItem("username");
|
||||||
window.sessionStorage.removeItem("userRole");
|
window.localStorage.removeItem("userRole");
|
||||||
window.sessionStorage.removeItem("token");
|
window.localStorage.removeItem("token");
|
||||||
window.sessionStorage.removeItem("rmqVersion");
|
window.localStorage.removeItem("rmqVersion");
|
||||||
navigate('/login');
|
navigate('/login');
|
||||||
} else {
|
} else {
|
||||||
console.error('Logout failed:', res.message)
|
console.error('Logout failed:', res.message)
|
||||||
@@ -59,6 +59,15 @@ const Navbar = ({ username, rmqVersion = true, showAcl = true}) => {
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const storedUsername = window.localStorage.getItem("username");
|
||||||
|
if (storedUsername) {
|
||||||
|
setUserName(storedUsername);
|
||||||
|
}else {
|
||||||
|
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>
|
||||||
@@ -66,13 +75,6 @@ const Navbar = ({ username, rmqVersion = true, showAcl = true}) => {
|
|||||||
</Menu>
|
</Menu>
|
||||||
);
|
);
|
||||||
|
|
||||||
const versionMenu = (
|
|
||||||
<Menu onClick={({ key }) => alert(t.version.switchVersion + ': ' + key)}>
|
|
||||||
<Menu.Item key="5">RocketMQ v5</Menu.Item>
|
|
||||||
<Menu.Item key="4">RocketMQ v4</Menu.Item>
|
|
||||||
</Menu>
|
|
||||||
);
|
|
||||||
|
|
||||||
const userMenu = (
|
const userMenu = (
|
||||||
<Menu>
|
<Menu>
|
||||||
<Menu.Item key="logout" onClick={onLogout}>{t.LOGOUT}</Menu.Item>
|
<Menu.Item key="logout" onClick={onLogout}>{t.LOGOUT}</Menu.Item>
|
||||||
@@ -159,12 +161,12 @@ const Navbar = ({ username, rmqVersion = true, showAcl = true}) => {
|
|||||||
</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>
|
||||||
|
@@ -15,7 +15,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Button, Checkbox, Form, Input, message, Modal} from "antd";
|
import {Button, Checkbox, Form, Input, Modal} from "antd";
|
||||||
import React, {useEffect} from "react";
|
import React, {useEffect} from "react";
|
||||||
import {remoteApi} from "../../api/remoteApi/remoteApi";
|
import {remoteApi} from "../../api/remoteApi/remoteApi";
|
||||||
|
|
||||||
@@ -26,6 +26,7 @@ const SendTopicMessageDialog = ({
|
|||||||
setSendResultData,
|
setSendResultData,
|
||||||
setIsSendResultModalVisible,
|
setIsSendResultModalVisible,
|
||||||
setIsSendTopicMessageModalVisible,
|
setIsSendTopicMessageModalVisible,
|
||||||
|
message,
|
||||||
t,
|
t,
|
||||||
}) => {
|
}) => {
|
||||||
const [form] = Form.useForm();
|
const [form] = Form.useForm();
|
||||||
@@ -46,8 +47,8 @@ const SendTopicMessageDialog = ({
|
|||||||
|
|
||||||
const handleSendTopicMessage = async () => {
|
const handleSendTopicMessage = async () => {
|
||||||
try {
|
try {
|
||||||
const values = await form.validateFields(); // 👈 从表单获取最新值
|
const values = await form.validateFields();
|
||||||
const result = await remoteApi.sendTopicMessage(values); // 👈 用表单数据发送
|
const result = await remoteApi.sendTopicMessage(values);
|
||||||
if (result.status === 0) {
|
if (result.status === 0) {
|
||||||
setSendResultData(result.data);
|
setSendResultData(result.data);
|
||||||
setIsSendResultModalVisible(true);
|
setIsSendResultModalVisible(true);
|
||||||
|
@@ -278,7 +278,7 @@ export const translations = {
|
|||||||
"PLEASE_ADD_RESOURCE": "请添加资源!",
|
"PLEASE_ADD_RESOURCE": "请添加资源!",
|
||||||
"ENTER_IP_HINT": "请输入 IP 地址,按回车键添加,支持 IPv4、IPv6 和 CIDR",
|
"ENTER_IP_HINT": "请输入 IP 地址,按回车键添加,支持 IPv4、IPv6 和 CIDR",
|
||||||
"PLEASE_ENTER_DECISION": "请输入决策!",
|
"PLEASE_ENTER_DECISION": "请输入决策!",
|
||||||
|
"MENU": "菜单",
|
||||||
},
|
},
|
||||||
en: {
|
en: {
|
||||||
"DEFAULT": "Default",
|
"DEFAULT": "Default",
|
||||||
@@ -535,6 +535,8 @@ export const translations = {
|
|||||||
"PLEASE_ADD_RESOURCE": "Please add resource!",
|
"PLEASE_ADD_RESOURCE": "Please add resource!",
|
||||||
"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",
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@@ -488,7 +488,7 @@ const Acl = () => {
|
|||||||
dataIndex: 'userStatus',
|
dataIndex: 'userStatus',
|
||||||
key: 'userStatus',
|
key: 'userStatus',
|
||||||
render: (status) => (
|
render: (status) => (
|
||||||
<Tag color={status=== 'enable' ? 'red' : 'green'}>{status}</Tag>
|
<Tag color={status=== 'enable' ? 'green' : 'red'}>{status}</Tag>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@@ -30,8 +30,8 @@ const Login = () => {
|
|||||||
remoteApi.login(username, password).then((res) => {
|
remoteApi.login(username, password).then((res) => {
|
||||||
if (res.status === 0) {
|
if (res.status === 0) {
|
||||||
messageApi.success('登录成功');
|
messageApi.success('登录成功');
|
||||||
window.sessionStorage.setItem("username", res.data.loginUserName);
|
window.localStorage.setItem("username", res.data.loginUserName);
|
||||||
window.sessionStorage.setItem("userrole", res.data.loginUserRole);
|
window.localStorage.setItem("userrole", res.data.loginUserRole);
|
||||||
window.location.href = '/';
|
window.location.href = '/';
|
||||||
} else {
|
} else {
|
||||||
messageApi.error(res.message || '登录失败,请检查用户名和密码');
|
messageApi.error(res.message || '登录失败,请检查用户名和密码');
|
||||||
|
@@ -172,28 +172,7 @@ const DeployHistoryList = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const refreshTopicList = async () => {
|
|
||||||
setLoading(true);
|
|
||||||
try {
|
|
||||||
const result = await remoteApi.refreshTopicList();
|
|
||||||
if (result.status === 0) {
|
|
||||||
setAllTopicList(result.data.topicNameList);
|
|
||||||
setAllMessageTypeList(result.data.messageTypeList);
|
|
||||||
setPaginationConf(prev => ({
|
|
||||||
...prev,
|
|
||||||
total: result.data.topicNameList.length
|
|
||||||
}));
|
|
||||||
messageApi.success(t.REFRESHING_TOPIC_LIST);
|
|
||||||
} else {
|
|
||||||
messageApi.error(result.errMsg);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error refreshing topic list:", error);
|
|
||||||
messageApi.error("Failed to refresh topic list");
|
|
||||||
} finally {
|
|
||||||
setLoading(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const filterList = (currentPage) => {
|
const filterList = (currentPage) => {
|
||||||
const lowExceptStr = filterStr.toLowerCase();
|
const lowExceptStr = filterStr.toLowerCase();
|
||||||
@@ -298,7 +277,7 @@ const DeployHistoryList = () => {
|
|||||||
messageApi.success(t.TOPIC_OPERATION_SUCCESS);
|
messageApi.success(t.TOPIC_OPERATION_SUCCESS);
|
||||||
closeAddUpdateDialog();
|
closeAddUpdateDialog();
|
||||||
if(!isUpdateMode) {
|
if(!isUpdateMode) {
|
||||||
refreshTopicList();
|
await getTopicList()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
messageApi.error(result.errMsg);
|
messageApi.error(result.errMsg);
|
||||||
@@ -316,7 +295,7 @@ const DeployHistoryList = () => {
|
|||||||
if (result.status === 0) {
|
if (result.status === 0) {
|
||||||
messageApi.success(`${t.TOPIC} [${topicToDelete}] ${t.DELETED_SUCCESSFULLY}`);
|
messageApi.success(`${t.TOPIC} [${topicToDelete}] ${t.DELETED_SUCCESSFULLY}`);
|
||||||
setAllTopicList(allTopicList.filter(topic => topic !== topicToDelete));
|
setAllTopicList(allTopicList.filter(topic => topic !== topicToDelete));
|
||||||
await refreshTopicList()
|
await getTopicList()
|
||||||
} else {
|
} else {
|
||||||
messageApi.error(result.errMsg);
|
messageApi.error(result.errMsg);
|
||||||
}
|
}
|
||||||
@@ -614,7 +593,7 @@ const DeployHistoryList = () => {
|
|||||||
</Form.Item>
|
</Form.Item>
|
||||||
)}
|
)}
|
||||||
<Form.Item>
|
<Form.Item>
|
||||||
<Button type="primary" onClick={refreshTopicList}>
|
<Button type="primary" onClick={getTopicList}>
|
||||||
{t.REFRESH}
|
{t.REFRESH}
|
||||||
</Button>
|
</Button>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
@@ -714,6 +693,7 @@ const DeployHistoryList = () => {
|
|||||||
setIsSendResultModalVisible={setIsSendResultModalVisible}
|
setIsSendResultModalVisible={setIsSendResultModalVisible}
|
||||||
setIsSendTopicMessageModalVisible={setIsSendTopicMessageModalVisible}
|
setIsSendTopicMessageModalVisible={setIsSendTopicMessageModalVisible}
|
||||||
sendTopicMessageData={sendTopicMessageData}
|
sendTopicMessageData={sendTopicMessageData}
|
||||||
|
message={messageApi}
|
||||||
t={t}
|
t={t}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -71,7 +71,7 @@ const AppRouter = () => {
|
|||||||
return (
|
return (
|
||||||
<Layout style={{minHeight: '100vh'}}>
|
<Layout style={{minHeight: '100vh'}}>
|
||||||
<Header style={{padding: 0, height: 'auto', lineHeight: 'normal'}}>
|
<Header style={{padding: 0, height: 'auto', lineHeight: 'normal'}}>
|
||||||
<Navbar username={window.sessionStorage.getItem("username")}/>
|
<Navbar/>
|
||||||
</Header>
|
</Header>
|
||||||
|
|
||||||
<Content style={{padding: '24px'}}>
|
<Content style={{padding: '24px'}}>
|
||||||
|
46
pom.xml
46
pom.xml
@@ -84,6 +84,7 @@
|
|||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
<maven.compiler.source>17</maven.compiler.source>
|
<maven.compiler.source>17</maven.compiler.source>
|
||||||
<maven.compiler.target>17</maven.compiler.target>
|
<maven.compiler.target>17</maven.compiler.target>
|
||||||
|
<maven.test.skip>false</maven.test.skip>
|
||||||
<guava.version>29.0-jre</guava.version>
|
<guava.version>29.0-jre</guava.version>
|
||||||
<commons-digester.version>2.1</commons-digester.version>
|
<commons-digester.version>2.1</commons-digester.version>
|
||||||
<commons-lang.version>2.6</commons-lang.version>
|
<commons-lang.version>2.6</commons-lang.version>
|
||||||
@@ -310,13 +311,13 @@
|
|||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||||
<version>${spring.boot.version}</version>
|
<version>${spring.boot.version}</version>
|
||||||
<!-- <executions>-->
|
<executions>
|
||||||
<!-- <execution>-->
|
<execution>
|
||||||
<!-- <goals>-->
|
<goals>
|
||||||
<!-- <goal>repackage</goal>-->
|
<goal>repackage</goal>
|
||||||
<!-- </goals>-->
|
</goals>
|
||||||
<!-- </execution>-->
|
</execution>
|
||||||
<!-- </executions>-->
|
</executions>
|
||||||
</plugin>
|
</plugin>
|
||||||
<!-- Use dockerfile-maven instead, https://github.com/spotify/dockerfile-maven -->
|
<!-- Use dockerfile-maven instead, https://github.com/spotify/dockerfile-maven -->
|
||||||
<plugin>
|
<plugin>
|
||||||
@@ -412,11 +413,11 @@
|
|||||||
<exclude>docs/**</exclude>
|
<exclude>docs/**</exclude>
|
||||||
<exclude>src/main/resources/static/vendor/**</exclude>
|
<exclude>src/main/resources/static/vendor/**</exclude>
|
||||||
<exclude>src/main/resources/static/src/data/**</exclude>
|
<exclude>src/main/resources/static/src/data/**</exclude>
|
||||||
<exclude>frontend/node_modules/**</exclude>
|
<exclude>frontend-new/node_modules/**</exclude>
|
||||||
<exclude>frontend/build/**</exclude>
|
<exclude>frontend-new/build/**</exclude>
|
||||||
<exclude>frontend/**.json</exclude>
|
<exclude>frontend-new/**.json</exclude>
|
||||||
<exclude>frontend/**.lock</exclude>
|
<exclude>frontend-new/**.lock</exclude>
|
||||||
<exclude>frontend/public/manifest.json</exclude>
|
<exclude>frontend-new/public/manifest.json</exclude>
|
||||||
<exclude>package-lock.json</exclude>
|
<exclude>package-lock.json</exclude>
|
||||||
</excludes>
|
</excludes>
|
||||||
</configuration>
|
</configuration>
|
||||||
@@ -431,32 +432,31 @@
|
|||||||
</configuration>
|
</configuration>
|
||||||
<executions>
|
<executions>
|
||||||
<execution>
|
<execution>
|
||||||
<id>install node and yarn</id>
|
<id>install node </id>
|
||||||
<goals>
|
<goals>
|
||||||
<goal>install-node-and-yarn</goal>
|
<goal>install-node-and-npm</goal>
|
||||||
</goals>
|
</goals>
|
||||||
<configuration>
|
<configuration>
|
||||||
<nodeVersion>v16.2.0</nodeVersion>
|
<nodeVersion>v18.2.0</nodeVersion>
|
||||||
<yarnVersion>v1.22.10</yarnVersion>
|
|
||||||
</configuration>
|
</configuration>
|
||||||
</execution>
|
</execution>
|
||||||
|
|
||||||
<execution>
|
<execution>
|
||||||
<id>yarn install</id>
|
<id>npm install</id>
|
||||||
<goals>
|
<goals>
|
||||||
<goal>yarn</goal>
|
<goal>npm</goal>
|
||||||
</goals>
|
</goals>
|
||||||
<configuration>
|
<configuration>
|
||||||
<arguments>install</arguments>
|
<arguments>install --legacy-peer-deps</arguments>
|
||||||
</configuration>
|
</configuration>
|
||||||
</execution>
|
</execution>
|
||||||
<execution>
|
<execution>
|
||||||
<id>yarn build</id>
|
<id>npm build</id>
|
||||||
<goals>
|
<goals>
|
||||||
<goal>yarn</goal>
|
<goal>npm</goal>
|
||||||
</goals>
|
</goals>
|
||||||
<configuration>
|
<configuration>
|
||||||
<arguments>build</arguments>
|
<arguments>run build</arguments>
|
||||||
</configuration>
|
</configuration>
|
||||||
</execution>
|
</execution>
|
||||||
</executions>
|
</executions>
|
||||||
@@ -469,7 +469,7 @@
|
|||||||
<configuration>
|
<configuration>
|
||||||
<target>
|
<target>
|
||||||
<copy todir="${project.build.directory}/classes/public">
|
<copy todir="${project.build.directory}/classes/public">
|
||||||
<fileset dir="${project.basedir}/frontend/build" />
|
<fileset dir="${project.basedir}/frontend-new/build" />
|
||||||
</copy>
|
</copy>
|
||||||
</target>
|
</target>
|
||||||
</configuration>
|
</configuration>
|
||||||
|
@@ -55,11 +55,6 @@ public class TopicController {
|
|||||||
return topicService.fetchAllTopicList(skipSysProcess, skipRetryAndDlq);
|
return topicService.fetchAllTopicList(skipSysProcess, skipRetryAndDlq);
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequestMapping(value = "/refresh", method = {RequestMethod.POST})
|
|
||||||
@ResponseBody
|
|
||||||
public Object refresh() {
|
|
||||||
return topicService.refreshTopicList();
|
|
||||||
}
|
|
||||||
|
|
||||||
@RequestMapping(value = "/list.queryTopicType", method = RequestMethod.GET)
|
@RequestMapping(value = "/list.queryTopicType", method = RequestMethod.GET)
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
|
@@ -19,6 +19,7 @@ package org.apache.rocketmq.dashboard.permisssion;
|
|||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import org.apache.rocketmq.dashboard.config.RMQConfigure;
|
import org.apache.rocketmq.dashboard.config.RMQConfigure;
|
||||||
|
import org.apache.rocketmq.dashboard.exception.ServiceException;
|
||||||
import org.apache.rocketmq.dashboard.model.UserInfo;
|
import org.apache.rocketmq.dashboard.model.UserInfo;
|
||||||
import org.apache.rocketmq.dashboard.service.PermissionService;
|
import org.apache.rocketmq.dashboard.service.PermissionService;
|
||||||
import org.apache.rocketmq.dashboard.util.WebUtil;
|
import org.apache.rocketmq.dashboard.util.WebUtil;
|
||||||
@@ -55,13 +56,13 @@ public class PermissionAspect {
|
|||||||
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
|
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
|
||||||
String url = request.getRequestURI();
|
String url = request.getRequestURI();
|
||||||
UserInfo userInfo = (UserInfo) request.getSession().getAttribute(WebUtil.USER_INFO);
|
UserInfo userInfo = (UserInfo) request.getSession().getAttribute(WebUtil.USER_INFO);
|
||||||
// if (userInfo == null || userInfo.getUser() == null) {
|
if (userInfo == null || userInfo.getUser() == null) {
|
||||||
// throw new ServiceException(-1, "user not login");
|
throw new ServiceException(-1, "user not login");
|
||||||
// }
|
}
|
||||||
// boolean checkResult = permissionService.checkUrlAvailable(userInfo, url);
|
boolean checkResult = permissionService.checkUrlAvailable(userInfo, url);
|
||||||
// if (!checkResult) {
|
if (!checkResult) {
|
||||||
// throw new ServiceException(-1, "no permission");
|
throw new ServiceException(-1, "no permission");
|
||||||
// }
|
}
|
||||||
}
|
}
|
||||||
return joinPoint.proceed();
|
return joinPoint.proceed();
|
||||||
}
|
}
|
||||||
|
@@ -17,8 +17,8 @@
|
|||||||
package org.apache.rocketmq.dashboard.permisssion;
|
package org.apache.rocketmq.dashboard.permisssion;
|
||||||
|
|
||||||
public enum UserRoleEnum {
|
public enum UserRoleEnum {
|
||||||
ADMIN(1, "admin"),
|
SUPER(1, "Super"),
|
||||||
ORDINARY(0, "ordinary");
|
NORMAL(2, "Normal");
|
||||||
|
|
||||||
private int roleType;
|
private int roleType;
|
||||||
private String roleName;
|
private String roleName;
|
||||||
|
@@ -54,5 +54,4 @@ public interface TopicService {
|
|||||||
|
|
||||||
SendResult sendTopicMessageRequest(SendTopicMessageRequest sendTopicMessageRequest);
|
SendResult sendTopicMessageRequest(SendTopicMessageRequest sendTopicMessageRequest);
|
||||||
|
|
||||||
boolean refreshTopicList();
|
|
||||||
}
|
}
|
||||||
|
@@ -63,10 +63,12 @@ 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.body.UserInfo;
|
import org.apache.rocketmq.remoting.protocol.body.UserInfo;
|
||||||
import org.apache.rocketmq.remoting.protocol.header.ExportRocksDBConfigToJsonRequestHeader;
|
import org.apache.rocketmq.remoting.protocol.header.ExportRocksDBConfigToJsonRequestHeader;
|
||||||
|
import org.apache.rocketmq.remoting.protocol.header.GetTopicConfigRequestHeader;
|
||||||
import org.apache.rocketmq.remoting.protocol.header.controller.ElectMasterResponseHeader;
|
import org.apache.rocketmq.remoting.protocol.header.controller.ElectMasterResponseHeader;
|
||||||
import org.apache.rocketmq.remoting.protocol.header.controller.GetMetaDataResponseHeader;
|
import org.apache.rocketmq.remoting.protocol.header.controller.GetMetaDataResponseHeader;
|
||||||
import org.apache.rocketmq.remoting.protocol.heartbeat.SubscriptionData;
|
import org.apache.rocketmq.remoting.protocol.heartbeat.SubscriptionData;
|
||||||
import org.apache.rocketmq.remoting.protocol.route.TopicRouteData;
|
import org.apache.rocketmq.remoting.protocol.route.TopicRouteData;
|
||||||
|
import org.apache.rocketmq.remoting.protocol.statictopic.TopicConfigAndQueueMapping;
|
||||||
import org.apache.rocketmq.remoting.protocol.statictopic.TopicQueueMappingDetail;
|
import org.apache.rocketmq.remoting.protocol.statictopic.TopicQueueMappingDetail;
|
||||||
import org.apache.rocketmq.remoting.protocol.subscription.GroupForbidden;
|
import org.apache.rocketmq.remoting.protocol.subscription.GroupForbidden;
|
||||||
import org.apache.rocketmq.remoting.protocol.subscription.SubscriptionGroupConfig;
|
import org.apache.rocketmq.remoting.protocol.subscription.SubscriptionGroupConfig;
|
||||||
@@ -84,8 +86,6 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
import java.util.concurrent.ConcurrentMap;
|
|
||||||
|
|
||||||
import static org.apache.rocketmq.remoting.protocol.RemotingSerializable.decode;
|
import static org.apache.rocketmq.remoting.protocol.RemotingSerializable.decode;
|
||||||
|
|
||||||
@@ -93,14 +93,9 @@ import static org.apache.rocketmq.remoting.protocol.RemotingSerializable.decode;
|
|||||||
public class MQAdminExtImpl implements MQAdminExt {
|
public class MQAdminExtImpl implements MQAdminExt {
|
||||||
private Logger logger = LoggerFactory.getLogger(MQAdminExtImpl.class);
|
private Logger logger = LoggerFactory.getLogger(MQAdminExtImpl.class);
|
||||||
|
|
||||||
private static final ConcurrentMap<String, TopicConfigSerializeWrapper> TOPIC_CONFIG_CACHE = new ConcurrentHashMap<>();
|
|
||||||
|
|
||||||
public MQAdminExtImpl() {}
|
public MQAdminExtImpl() {}
|
||||||
|
|
||||||
public static void clearTopicConfigCache() {
|
|
||||||
TOPIC_CONFIG_CACHE.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateBrokerConfig(String brokerAddr, Properties properties)
|
public void updateBrokerConfig(String brokerAddr, Properties properties)
|
||||||
@@ -157,15 +152,11 @@ public class MQAdminExtImpl implements MQAdminExt {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TopicConfig examineTopicConfig(String addr, String topic) throws MQBrokerException {
|
public TopicConfig examineTopicConfig(String addr, String topic) throws MQBrokerException {
|
||||||
TopicConfigSerializeWrapper cachedWrapper = TOPIC_CONFIG_CACHE.get(addr);
|
|
||||||
|
|
||||||
if (cachedWrapper != null && cachedWrapper.getTopicConfigTable().containsKey(topic)) {
|
|
||||||
return cachedWrapper.getTopicConfigTable().get(topic);
|
|
||||||
}
|
|
||||||
|
|
||||||
RemotingClient remotingClient = MQAdminInstance.threadLocalRemotingClient();
|
RemotingClient remotingClient = MQAdminInstance.threadLocalRemotingClient();
|
||||||
RemotingCommand request = RemotingCommand.createRequestCommand(RequestCode.GET_ALL_TOPIC_CONFIG, null);
|
GetTopicConfigRequestHeader header = new GetTopicConfigRequestHeader();
|
||||||
RemotingCommand response = null;
|
header.setTopic(topic);
|
||||||
|
RemotingCommand request = RemotingCommand.createRequestCommand(RequestCode.GET_TOPIC_CONFIG, header);
|
||||||
|
RemotingCommand response;
|
||||||
try {
|
try {
|
||||||
response = remotingClient.invokeSync(addr, request, 3000);
|
response = remotingClient.invokeSync(addr, request, 3000);
|
||||||
} catch (Exception err) {
|
} catch (Exception err) {
|
||||||
@@ -174,11 +165,11 @@ public class MQAdminExtImpl implements MQAdminExt {
|
|||||||
}
|
}
|
||||||
switch (response.getCode()) {
|
switch (response.getCode()) {
|
||||||
case ResponseCode.SUCCESS: {
|
case ResponseCode.SUCCESS: {
|
||||||
TopicConfigSerializeWrapper topicConfigSerializeWrapper =
|
TopicConfigAndQueueMapping topicConfigAndQueueMapping = decode(response.getBody(), TopicConfigAndQueueMapping.class);
|
||||||
decode(response.getBody(), TopicConfigSerializeWrapper.class);
|
if (topicConfigAndQueueMapping == null) {
|
||||||
|
throw new MQBrokerException(ResponseCode.TOPIC_NOT_EXIST, "Topic not exist: " + topic);
|
||||||
TOPIC_CONFIG_CACHE.put(addr, topicConfigSerializeWrapper);
|
}
|
||||||
return topicConfigSerializeWrapper.getTopicConfigTable().get(topic);
|
return topicConfigAndQueueMapping;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
throw new MQBrokerException(response.getCode(), response.getRemark());
|
throw new MQBrokerException(response.getCode(), response.getRemark());
|
||||||
|
@@ -56,6 +56,7 @@ public class LoginServiceImpl implements LoginService {
|
|||||||
if (username != null) {
|
if (username != null) {
|
||||||
UserInfo userInfo = userInfoProvider.getUserInfoByUsername(username);
|
UserInfo userInfo = userInfoProvider.getUserInfoByUsername(username);
|
||||||
if (userInfo == null) {
|
if (userInfo == null) {
|
||||||
|
auth(request, response);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
UserInfoContext.set(WebUtil.USER_NAME, userInfo);
|
UserInfoContext.set(WebUtil.USER_NAME, userInfo);
|
||||||
|
@@ -33,8 +33,9 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
import static org.apache.rocketmq.dashboard.permisssion.UserRoleEnum.ADMIN;
|
import static org.apache.rocketmq.dashboard.permisssion.UserRoleEnum.NORMAL;
|
||||||
import static org.apache.rocketmq.dashboard.permisssion.UserRoleEnum.ORDINARY;
|
import static org.apache.rocketmq.dashboard.permisssion.UserRoleEnum.SUPER;
|
||||||
|
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class PermissionServiceImpl implements PermissionService, InitializingBean {
|
public class PermissionServiceImpl implements PermissionService, InitializingBean {
|
||||||
@@ -55,10 +56,10 @@ public class PermissionServiceImpl implements PermissionService, InitializingBea
|
|||||||
public boolean checkUrlAvailable(UserInfo userInfo, String url) {
|
public boolean checkUrlAvailable(UserInfo userInfo, String url) {
|
||||||
int type = userInfo.getUser().getType();
|
int type = userInfo.getUser().getType();
|
||||||
// if it is admin, it could access all resources
|
// if it is admin, it could access all resources
|
||||||
if (type == ADMIN.getRoleType()) {
|
if (type == SUPER.getRoleType()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
String loginUserRole = ORDINARY.getRoleName();
|
String loginUserRole = NORMAL.getRoleName();
|
||||||
Map<String, List<String>> rolePerms = PermissionFileStore.rolePerms;
|
Map<String, List<String>> rolePerms = PermissionFileStore.rolePerms;
|
||||||
List<String> perms = rolePerms.get(loginUserRole);
|
List<String> perms = rolePerms.get(loginUserRole);
|
||||||
for (String perm : perms) {
|
for (String perm : perms) {
|
||||||
|
@@ -45,7 +45,6 @@ import org.apache.rocketmq.dashboard.model.request.TopicTypeMeta;
|
|||||||
import org.apache.rocketmq.dashboard.service.AbstractCommonService;
|
import org.apache.rocketmq.dashboard.service.AbstractCommonService;
|
||||||
import org.apache.rocketmq.dashboard.service.ClusterInfoService;
|
import org.apache.rocketmq.dashboard.service.ClusterInfoService;
|
||||||
import org.apache.rocketmq.dashboard.service.TopicService;
|
import org.apache.rocketmq.dashboard.service.TopicService;
|
||||||
import org.apache.rocketmq.dashboard.service.client.MQAdminExtImpl;
|
|
||||||
import org.apache.rocketmq.dashboard.support.GlobalExceptionHandler;
|
import org.apache.rocketmq.dashboard.support.GlobalExceptionHandler;
|
||||||
import org.apache.rocketmq.remoting.RPCHook;
|
import org.apache.rocketmq.remoting.RPCHook;
|
||||||
import org.apache.rocketmq.remoting.protocol.admin.TopicStatsTable;
|
import org.apache.rocketmq.remoting.protocol.admin.TopicStatsTable;
|
||||||
@@ -71,7 +70,6 @@ import java.util.Map;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.ArrayBlockingQueue;
|
import java.util.concurrent.ArrayBlockingQueue;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.ConcurrentMap;
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@@ -85,9 +83,6 @@ public class TopicServiceImpl extends AbstractCommonService implements TopicServ
|
|||||||
@Autowired
|
@Autowired
|
||||||
private ClusterInfoService clusterInfoService;
|
private ClusterInfoService clusterInfoService;
|
||||||
|
|
||||||
private final ConcurrentMap<String, TopicRouteData> routeCache = new ConcurrentHashMap<>();
|
|
||||||
private final Object cacheLock = new Object();
|
|
||||||
|
|
||||||
private transient DefaultMQProducer systemTopicProducer;
|
private transient DefaultMQProducer systemTopicProducer;
|
||||||
|
|
||||||
private final Object producerLock = new Object();
|
private final Object producerLock = new Object();
|
||||||
@@ -195,24 +190,11 @@ public class TopicServiceImpl extends AbstractCommonService implements TopicServ
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TopicRouteData route(String topic) {
|
public TopicRouteData route(String topic) {
|
||||||
TopicRouteData cachedData = routeCache.get(topic);
|
try {
|
||||||
if (cachedData != null) {
|
return mqAdminExt.examineTopicRouteInfo(topic);
|
||||||
return cachedData;
|
} catch (Exception ex) {
|
||||||
}
|
Throwables.throwIfUnchecked(ex);
|
||||||
|
throw new RuntimeException(ex);
|
||||||
synchronized (cacheLock) {
|
|
||||||
cachedData = routeCache.get(topic);
|
|
||||||
if (cachedData != null) {
|
|
||||||
return cachedData;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
TopicRouteData freshData = mqAdminExt.examineTopicRouteInfo(topic);
|
|
||||||
routeCache.put(topic, freshData);
|
|
||||||
return freshData;
|
|
||||||
} catch (Exception ex) {
|
|
||||||
Throwables.throwIfUnchecked(ex);
|
|
||||||
throw new RuntimeException(ex);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -228,7 +210,6 @@ public class TopicServiceImpl extends AbstractCommonService implements TopicServ
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void createOrUpdate(TopicConfigInfo topicCreateOrUpdateRequest) {
|
public void createOrUpdate(TopicConfigInfo topicCreateOrUpdateRequest) {
|
||||||
MQAdminExtImpl.clearTopicConfigCache();
|
|
||||||
TopicConfig topicConfig = new TopicConfig();
|
TopicConfig topicConfig = new TopicConfig();
|
||||||
BeanUtils.copyProperties(topicCreateOrUpdateRequest, topicConfig);
|
BeanUtils.copyProperties(topicCreateOrUpdateRequest, topicConfig);
|
||||||
String messageType = topicCreateOrUpdateRequest.getMessageType();
|
String messageType = topicCreateOrUpdateRequest.getMessageType();
|
||||||
@@ -455,13 +436,6 @@ public class TopicServiceImpl extends AbstractCommonService implements TopicServ
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean refreshTopicList() {
|
|
||||||
routeCache.clear();
|
|
||||||
clusterInfoService.refresh();
|
|
||||||
MQAdminExtImpl.clearTopicConfigCache();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void waitSendTraceFinish(DefaultMQProducer producer, boolean traceEnabled) {
|
private void waitSendTraceFinish(DefaultMQProducer producer, boolean traceEnabled) {
|
||||||
if (!traceEnabled) {
|
if (!traceEnabled) {
|
||||||
|
@@ -24,13 +24,12 @@ 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.provider.UserInfoProvider;
|
||||||
import org.apache.rocketmq.logging.org.slf4j.Logger;
|
|
||||||
import org.apache.rocketmq.logging.org.slf4j.LoggerFactory;
|
|
||||||
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.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
@Service
|
@Service
|
||||||
public class UserServiceImpl implements UserService {
|
public class UserServiceImpl implements UserService {
|
||||||
|
|
||||||
|
@@ -17,15 +17,14 @@
|
|||||||
|
|
||||||
package org.apache.rocketmq.dashboard.service.provider;
|
package org.apache.rocketmq.dashboard.service.provider;
|
||||||
import org.apache.rocketmq.dashboard.service.ClusterInfoService;
|
import org.apache.rocketmq.dashboard.service.ClusterInfoService;
|
||||||
import org.apache.rocketmq.logging.org.slf4j.Logger;
|
|
||||||
import org.apache.rocketmq.logging.org.slf4j.LoggerFactory;
|
|
||||||
import org.apache.rocketmq.remoting.protocol.body.ClusterInfo;
|
import org.apache.rocketmq.remoting.protocol.body.ClusterInfo;
|
||||||
import org.apache.rocketmq.remoting.protocol.body.UserInfo;
|
import org.apache.rocketmq.remoting.protocol.body.UserInfo;
|
||||||
import org.apache.rocketmq.remoting.protocol.route.BrokerData;
|
import org.apache.rocketmq.remoting.protocol.route.BrokerData;
|
||||||
import org.apache.rocketmq.tools.admin.MQAdminExt;
|
import org.apache.rocketmq.tools.admin.MQAdminExt;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class UserInfoProviderImpl implements UserInfoProvider {
|
public class UserInfoProviderImpl implements UserInfoProvider {
|
||||||
|
@@ -22,12 +22,13 @@
|
|||||||
# **: Matches 0 or more characters.
|
# **: Matches 0 or more characters.
|
||||||
|
|
||||||
rolePerms:
|
rolePerms:
|
||||||
ordinary:
|
Normal:
|
||||||
- /rocketmq/*.query
|
- /rocketmq/*.query
|
||||||
- /ops/*.query
|
- /ops/*.query
|
||||||
- /dashboard/*.query
|
- /dashboard/*.query
|
||||||
- /topic/*.query
|
- /topic/*.query
|
||||||
- /topic/sendTopicMessage.do
|
- /topic/sendTopicMessage.do
|
||||||
|
- /topic/list.queryTopicType
|
||||||
- /producer/*.query
|
- /producer/*.query
|
||||||
- /message/*.query
|
- /message/*.query
|
||||||
- /messageTrace/*.query
|
- /messageTrace/*.query
|
||||||
|
Reference in New Issue
Block a user