mirror of
https://github.com/apache/rocketmq-dashboard.git
synced 2025-09-11 03:49:06 +08:00
Compare commits
71 Commits
init
...
dependabot
Author | SHA1 | Date | |
---|---|---|---|
|
94dda468f9 | ||
|
86bdb06364 | ||
|
7a54427d9c | ||
|
fc9781e6fc | ||
|
ea8834bacd | ||
|
63d9da7429 | ||
|
daa181ccfd | ||
|
f5b9bbb9a7 | ||
|
6de5d51661 | ||
|
4269879d93 | ||
|
0fc7ca904d | ||
|
636ba51bc3 | ||
|
8c5d8747a7 | ||
|
3f02992e13 | ||
|
78b85ebdb6 | ||
|
653332d488 | ||
|
d2da9ca41f | ||
|
6da21f480e | ||
|
e281b74456 | ||
|
529501c007 | ||
|
560b56e9dc | ||
|
8acd94c89a | ||
|
2d8bf001b3 | ||
|
1caeb4ce31 | ||
|
749e6f15f7 | ||
|
2524ca668a | ||
|
6054c057c8 | ||
|
e660c7d874 | ||
|
a5a3659c04 | ||
|
2160e23b91 | ||
|
ef7b97e96a | ||
|
d73d9b2793 | ||
|
a2234f8517 | ||
|
4b2b61e394 | ||
|
d5fed12773 | ||
|
cc30bb2373 | ||
|
d0583b73c3 | ||
|
193a66e5c2 | ||
|
9e9762ef44 | ||
|
8b12e8e7d7 | ||
|
f6d4e18b89 | ||
|
a5d7cce483 | ||
|
58336d951a | ||
|
dc67c660ff | ||
|
f4b759dba6 | ||
|
ef09ceb736 | ||
|
91794b5a9c | ||
|
d120610686 | ||
|
ef3a27d642 | ||
|
0614bc499e | ||
|
b0a2c8ff41 | ||
|
d5588b1fd0 | ||
|
3fd2223cb5 | ||
|
975449e268 | ||
|
5b2a027cd8 | ||
|
25129169d5 | ||
|
a1490d8377 | ||
|
38e97a4c53 | ||
|
818cd8d5d1 | ||
|
c9d4cd1b72 | ||
|
635e0ce4d0 | ||
|
ef25e37ca6 | ||
|
b1752d6dd6 | ||
|
0f5a5e1dda | ||
|
417f34130e | ||
|
a977542a0f | ||
|
51ed6117c2 | ||
|
bcfd10b65a | ||
|
1a751091b3 | ||
|
62a4cafea0 | ||
|
39d9db8a48 |
11
.asf.yaml
11
.asf.yaml
@@ -5,3 +5,14 @@ github:
|
||||
issues: true
|
||||
# Enable wiki
|
||||
wiki: true
|
||||
enabled_merge_buttons:
|
||||
squash: true
|
||||
merge: false
|
||||
rebase: false
|
||||
|
||||
notifications:
|
||||
commits: commits@rocketmq.apache.org
|
||||
issues: commits@rocketmq.apache.org
|
||||
pullrequests: commits@rocketmq.apache.org
|
||||
jobs: commits@rocketmq.apache.org
|
||||
discussions: dev@rocketmq.apache.org
|
||||
|
4
.gitignore
vendored
4
.gitignore
vendored
@@ -1,3 +1,7 @@
|
||||
/.idea/
|
||||
/target/
|
||||
*.iml
|
||||
.classpath
|
||||
.project
|
||||
.factorypath
|
||||
.settings/
|
||||
|
@@ -14,9 +14,11 @@ jdk:
|
||||
- oraclejdk11
|
||||
|
||||
script:
|
||||
- travis_retry mvn -B clean
|
||||
- travis_retry mvn -B clean apache-rat:check
|
||||
- travis_retry mvn -B package findbugs:findbugs jacoco:report coveralls:report
|
||||
|
||||
# - travis_retry mvn -B package findbugs:findbugs coveralls:report
|
||||
#after_success:
|
||||
# - mvn sonar:sonar
|
||||
after_success:
|
||||
- bash <(curl -s https://codecov.io/bash) -t 996e4d8e-8a00-49f2-b649-38c03716d690 || echo 'Codecov failed to upload'
|
||||
|
60
LICENSE
60
LICENSE
@@ -15,7 +15,7 @@
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (properties) the power, direct or indirect, to cause the
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
@@ -357,38 +357,16 @@ SOFTWARE.
|
||||
|
||||
echarts
|
||||
----------------------------------
|
||||
https://github.com/ecomfe/echarts
|
||||
https://github.com/apache/echarts
|
||||
|
||||
Copyright (c) 2017, Baidu Inc.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
The views and conclusions contained in the software and documentation are those
|
||||
of the authors and should not be interpreted as representing official policies,
|
||||
either expressed or implied, of the FreeBSD Project.
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
html5-boilerplate
|
||||
------------------------------------------
|
||||
https://github.com/h5bp/html5-boilerplate
|
||||
h5bp/html5-boilerplate is licensed under the MIT License
|
||||
Copyright (c) HTML5 Boilerplate
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
@@ -409,23 +387,6 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
Font-Awesome
|
||||
----------------------
|
||||
https://github.com/FortAwesome/Font-Awesome
|
||||
|
||||
License
|
||||
|
||||
The Font Awesome font is licensed under the SIL OFL 1.1:
|
||||
http://scripts.sil.org/OFL
|
||||
Font Awesome CSS, LESS, and Sass files are licensed under the MIT License:
|
||||
https://opensource.org/licenses/mit-license.html
|
||||
The Font Awesome documentation is licensed under the CC BY 3.0 License:
|
||||
http://creativecommons.org/licenses/by/3.0/
|
||||
Attribution is no longer required as of Font Awesome 3.0, but much appreciated:
|
||||
Font Awesome by Dave Gandy - http://fontawesome.io
|
||||
Full details: http://fontawesome.io/license/
|
||||
|
||||
|
||||
jquery
|
||||
---------------------
|
||||
Copyright JS Foundation and other contributors, https://js.foundation/
|
||||
@@ -551,6 +512,15 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
pagination
|
||||
---------------------
|
||||
https://github.com/317482454/tm.pagination
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
|
||||
bootstrap-validator
|
||||
---------------------------
|
||||
|
4
NOTICE
4
NOTICE
@@ -1,5 +1,5 @@
|
||||
Apache RocketMQ Dashboard
|
||||
Copyright 2016-2017 The Apache Software Foundation
|
||||
Apache RocketMQ
|
||||
Copyright 2016-2022 The Apache Software Foundation
|
||||
|
||||
This product includes software developed at
|
||||
The Apache Software Foundation (http://www.apache.org/).
|
59
README.md
59
README.md
@@ -1,52 +1,50 @@
|
||||
## RocketMQ Dashboard [](https://travis-ci.com/github/apache/rocketmq-dashboard)[](https://coveralls.io/github/apache/rocketmq-dashboard?branch=master)
|
||||
## [Apache RocketMQ](https://github.com/apache/rocketmq) Dashboard
|
||||
[](https://travis-ci.com/github/apache/rocketmq-dashboard) [](https://coveralls.io/github/apache/rocketmq-dashboard?branch=master)
|
||||
[](https://www.apache.org/licenses/LICENSE-2.0.html)
|
||||
[](https://codecov.io/gh/apache/rocketmq-dashboard)
|
||||
[](http://isitmaintained.com/project/apache/rocketmq-dashboard "Average time to resolve an issue")
|
||||
[](http://isitmaintained.com/project/apache/rocketmq-dashboard "Percentage of issues still open")
|
||||
[](https://twitter.com/intent/follow?screen_name=ApacheRocketMQ)
|
||||
## How To Install
|
||||
## Quick Start
|
||||
|
||||
### With Docker
|
||||
### Run with docker
|
||||
|
||||
* get docker image
|
||||
#### Pull from [docker hub(rocketmq-dashboard)](https://hub.docker.com/r/apacherocketmq/rocketmq-dashboard/tags)
|
||||
|
||||
```
|
||||
mvn clean package -Dmaven.test.skip=true docker:build
|
||||
```shell
|
||||
docker pull apacherocketmq/rocketmq-dashboard:latest
|
||||
```
|
||||
|
||||
or
|
||||
#### Run it (use your own `rocketmq.namesrv.addr` and `port`)
|
||||
|
||||
```
|
||||
docker pull apacherocketmq/rocketmq-console
|
||||
```shell
|
||||
docker run -d --name rocketmq-dashboard -e "JAVA_OPTS=-Drocketmq.namesrv.addr=127.0.0.1:9876" -p 8080:8080 -t apacherocketmq/rocketmq-dashboard:latest
|
||||
```
|
||||
|
||||
> currently the newest available docker image is apacherocketmq/rocketmq-console:2.0.0
|
||||
### Run with source code
|
||||
|
||||
|
||||
* run it (change namesvrAddr and port yourself)
|
||||
#### Prerequisite
|
||||
1. 64bit OS, Linux/Unix/Mac is recommended;
|
||||
2. 64bit JDK 1.8+;
|
||||
3. Maven 3.2.x;
|
||||
|
||||
```
|
||||
docker run -e "JAVA_OPTS=-Drocketmq.namesrv.addr=127.0.0.1:9876 -Dcom.rocketmq.sendMessageWithVIPChannel=false" -p 8080:8080 -t apacherocketmq/rocketmq-console-ng
|
||||
```
|
||||
#### Maven spring-boot run
|
||||
|
||||
or
|
||||
|
||||
```
|
||||
docker run -e "JAVA_OPTS=-Drocketmq.namesrv.addr=127.0.0.1:9876 -Dcom.rocketmq.sendMessageWithVIPChannel=false" -p 8080:8080 -t apacherocketmq/rocketmq-console-ng:2.0.0
|
||||
```
|
||||
|
||||
### Without Docker
|
||||
require java 1.8+
|
||||
```
|
||||
```shell
|
||||
mvn spring-boot:run
|
||||
```
|
||||
or
|
||||
```
|
||||
|
||||
#### Maven build and run
|
||||
|
||||
```shell
|
||||
mvn clean package -Dmaven.test.skip=true
|
||||
java -jar target/rocketmq-dashboard-2.0.0.jar
|
||||
java -jar target/rocketmq-dashboard-1.0.1-SNAPSHOT.jar
|
||||
```
|
||||
|
||||
#### Tips
|
||||
* if you download package slow,you can change maven's mirror(maven's settings.xml)
|
||||
* If you download the package slowly, you can change maven's mirror(maven's settings.xml)
|
||||
|
||||
```
|
||||
<mirrors>
|
||||
@@ -59,18 +57,17 @@ java -jar target/rocketmq-dashboard-2.0.0.jar
|
||||
</mirrors>
|
||||
```
|
||||
|
||||
* if you use the rocketmq < 3.5.8,please add -Dcom.rocketmq.sendMessageWithVIPChannel=false when you start rocketmq-dashboard(or you can change it in ops page)
|
||||
* change the rocketmq.config.namesrvAddr in resource/application.properties.(or you can change it in ops page)
|
||||
* Change the rocketmq.config.namesrvAddr in resource/application.properties.(or you can change it in ops page)
|
||||
|
||||
## UserGuide
|
||||
|
||||
[English](https://github.com/apache/rocketmq-dashboard/blob/master/doc/1_0_0/UserGuide_EN.md)
|
||||
[English](https://github.com/apache/rocketmq-dashboard/blob/master/docs/1_0_0/UserGuide_EN.md)
|
||||
|
||||
[中文](https://github.com/apache/rocketmq-dashboard/blob/master/doc/1_0_0/UserGuide_CN.md)
|
||||
[中文](https://github.com/apache/rocketmq-dashboard/blob/master/docs/1_0_0/UserGuide_CN.md)
|
||||
|
||||
## Contributing
|
||||
|
||||
We are always very happy to have contributions, whether for trivial cleanups or big new features. Please see the RocketMQ main website to read [details](http://rocketmq.apache.org/docs/how-to-contribute/).
|
||||
We are always very happy to have contributions, whether for trivial cleanups or big new features. Please see the RocketMQ main website to read the [details](http://rocketmq.apache.org/docs/how-to-contribute/).
|
||||
|
||||
## License
|
||||
[Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.html) Copyright (C) Apache Software Foundation
|
||||
|
@@ -91,7 +91,7 @@ server.port=8443
|
||||
## 登录访问Dashboard
|
||||
在访问Dashboard时支持按用户名和密码登录控制台,在操作完成后登出。需要做如下的设置:
|
||||
|
||||
* 1.在Spring配置文件resources/application.properties中修改 开启登录功能
|
||||
* 1.在Spring配置文件resources/application.properties中修改rocketmq.config.loginRequired=true开启登录功能
|
||||
```$xslt
|
||||
# 开启登录功能
|
||||
rocketmq.config.loginRequired=true
|
||||
@@ -112,4 +112,41 @@ admin=admin,1
|
||||
user1=user1
|
||||
user2=user2
|
||||
```
|
||||
* 3. 启动控制台则开启了登录功能
|
||||
* 3.启动控制台则开启了登录功能
|
||||
|
||||
## 权限检验
|
||||
如果用户访问console时开启了登录功能,会按照登录的角色对访问的接口进行权限控制。
|
||||
* 1.在Spring配置文件resources/application.properties中修改rocketmq.config.loginRequired=true开启登录功能
|
||||
```$xslt
|
||||
# 开启登录功能
|
||||
rocketmq.config.loginRequired=true
|
||||
|
||||
# Dashboard文件目录,登录用户配置文件所在目录
|
||||
rocketmq.config.dataPath=/tmp/rocketmq-console/data
|
||||
```
|
||||
* 2.确保${rocketmq.config.dataPath}定义的目录存在,并且该目录下创建访问权限配置文件"role-permission.yml",
|
||||
如果该目录下不存在此文件,则默认使用resources/role-permission.yml文件。该文件保存了普通用户角色所有能访问的接口地址。
|
||||
role-permission.yml文件格式为:
|
||||
```$xslt
|
||||
# 该文件支持热修改,即添加和修改用户时,不需要重新启动console
|
||||
# 格式,如果增加和删除接口权限,直接在列表中增加和删除接口地址即可。
|
||||
# 接口路径配置支持通配符
|
||||
# * 表示匹配0或多个不是/的字符
|
||||
# ** 表示匹配0或多个任意字符
|
||||
# ? 表示匹配1个任意字符
|
||||
|
||||
rolePerms:
|
||||
# 普通用户
|
||||
ordinary:
|
||||
- /rocketmq/nsaddr
|
||||
- /ops/*
|
||||
- /dashboard/**
|
||||
- /topic/*.query
|
||||
- /topic/sendTopicMessage.do
|
||||
- /producer/*.query
|
||||
- /message/*
|
||||
- /messageTrace/*
|
||||
- /monitor/*
|
||||
....
|
||||
```
|
||||
* 3.前端页面显示上,为了更好区分普通用户和admin用户权限,关于资源的删除、更新等操作按钮不对普通用户角色显示,如果要执行资源相关操作,需要退出使用admin角色登录。
|
@@ -115,4 +115,44 @@ admin=admin,1
|
||||
user1=user1
|
||||
user2=user2
|
||||
```
|
||||
* 3. Restart Dashboard Application after above configuration setting well.
|
||||
* 3.Restart Console Application after above configuration setting well.
|
||||
|
||||
|
||||
## Permission Control
|
||||
If the login function is enabled when a user accesses the Console, the user controls the access permission of the interface based on the login role.
|
||||
|
||||
* 1.Turn on the property in resources/application.properties.
|
||||
```$xslt
|
||||
# open the login func
|
||||
rocketmq.config.loginRequired=true
|
||||
|
||||
# Directory of ashboard & login user configure file
|
||||
rocketmq.config.dataPath=/tmp/rocketmq-console/data
|
||||
```
|
||||
* 2.Make sure the directory defined in property ${rocketmq.config.dataPath} exists and the permission control file "role-permission.yml" is created under it.
|
||||
The console system will use the resources/role-permission.yml by default if a customized file is not found。
|
||||
|
||||
The format in the content of role-permission.yml:
|
||||
```$xslt
|
||||
# This file supports hot change, any change will be auto-reloaded without Console restarting.
|
||||
# Format: To add or delete interface permissions, add or delete interface addresses from the list.
|
||||
# the interface paths can be configured with wildcard characters.
|
||||
# ?: Matches 1 characters.
|
||||
# *: Matches 0 or more characters that are not /.
|
||||
# **: Matches 0 or more characters.
|
||||
|
||||
rolePerms:
|
||||
# ordinary user
|
||||
ordinary:
|
||||
- /rocketmq/nsaddr
|
||||
- /ops/*
|
||||
- /dashboard/**
|
||||
- /topic/*.query
|
||||
- /topic/sendTopicMessage.do
|
||||
- /producer/*.query
|
||||
- /message/*
|
||||
- /messageTrace/*
|
||||
- /monitor/*
|
||||
....
|
||||
```
|
||||
* 3.On the front page, operation buttons such as deleting and updating resources are not displayed for common users in order to better distinguish the rights of common users and admin users. If need to operate related resources, log out and use the admin role to log in
|
23
frontend/.gitignore
vendored
Normal file
23
frontend/.gitignore
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
16635
frontend/package-lock.json
generated
Normal file
16635
frontend/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
40
frontend/package.json
Normal file
40
frontend/package.json
Normal file
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"name": "frontend",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@testing-library/jest-dom": "^5.11.4",
|
||||
"@testing-library/react": "^11.1.0",
|
||||
"@testing-library/user-event": "^12.1.10",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-json-view": "^1.21.3",
|
||||
"react-scripts": "4.0.3",
|
||||
"web-vitals": "^1.0.1"
|
||||
},
|
||||
"proxy": "http://localhost:8080",
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
"build": "react-scripts build",
|
||||
"test": "react-scripts test",
|
||||
"eject": "react-scripts eject"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": [
|
||||
"react-app",
|
||||
"react-app/jest"
|
||||
]
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
],
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
}
|
||||
}
|
BIN
frontend/public/favicon.ico
Normal file
BIN
frontend/public/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.8 KiB |
59
frontend/public/index.html
Normal file
59
frontend/public/index.html
Normal file
@@ -0,0 +1,59 @@
|
||||
<!--
|
||||
~ 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.
|
||||
-->
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta
|
||||
name="description"
|
||||
content="Web site created using create-react-app"
|
||||
/>
|
||||
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
||||
<!--
|
||||
manifest.json provides metadata used when your web app is installed on a
|
||||
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
||||
-->
|
||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
||||
<!--
|
||||
Notice the use of %PUBLIC_URL% in the tags above.
|
||||
It will be replaced with the URL of the `public` folder during the build.
|
||||
Only files inside the `public` folder can be referenced from the HTML.
|
||||
|
||||
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
||||
work correctly both with client-side routing and a non-root public URL.
|
||||
Learn how to configure a non-root public URL by running `npm run build`.
|
||||
-->
|
||||
<title>React App</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
<!--
|
||||
This HTML file is a template.
|
||||
If you open it directly in the browser, you will see an empty page.
|
||||
|
||||
You can add webfonts, meta tags, or analytics to this file.
|
||||
The build step will place the bundled scripts into the <body> tag.
|
||||
|
||||
To begin the development, run `npm start` or `yarn start`.
|
||||
To create a production bundle, use `npm run build` or `yarn build`.
|
||||
-->
|
||||
</body>
|
||||
</html>
|
BIN
frontend/public/logo192.png
Normal file
BIN
frontend/public/logo192.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.2 KiB |
BIN
frontend/public/logo512.png
Normal file
BIN
frontend/public/logo512.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 9.4 KiB |
25
frontend/public/manifest.json
Normal file
25
frontend/public/manifest.json
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"short_name": "React App",
|
||||
"name": "Create React App Sample",
|
||||
"icons": [
|
||||
{
|
||||
"src": "favicon.ico",
|
||||
"sizes": "64x64 32x32 24x24 16x16",
|
||||
"type": "image/x-icon"
|
||||
},
|
||||
{
|
||||
"src": "logo192.png",
|
||||
"type": "image/png",
|
||||
"sizes": "192x192"
|
||||
},
|
||||
{
|
||||
"src": "logo512.png",
|
||||
"type": "image/png",
|
||||
"sizes": "512x512"
|
||||
}
|
||||
],
|
||||
"start_url": ".",
|
||||
"display": "standalone",
|
||||
"theme_color": "#000000",
|
||||
"background_color": "#ffffff"
|
||||
}
|
54
frontend/src/App.css
Normal file
54
frontend/src/App.css
Normal file
@@ -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.
|
||||
*/
|
||||
.App {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.App-logo {
|
||||
height: 40vmin;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
.App-logo {
|
||||
animation: App-logo-spin infinite 20s linear;
|
||||
}
|
||||
}
|
||||
|
||||
.App-header {
|
||||
background-color: #282c34;
|
||||
min-height: 40vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: calc(10px + 2vmin);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.App-link {
|
||||
color: #61dafb;
|
||||
}
|
||||
|
||||
@keyframes App-logo-spin {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
49
frontend/src/App.js
Normal file
49
frontend/src/App.js
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import React, {useState, useEffect} from 'react';
|
||||
import logo from './logo.svg';
|
||||
import './App.css';
|
||||
|
||||
function App() {
|
||||
const [message, setMessage] = useState("");
|
||||
|
||||
useEffect(() => {
|
||||
fetch('cluster/list.query')
|
||||
.then(response => response.text())
|
||||
.then(message => {
|
||||
setMessage(message);
|
||||
});
|
||||
}, [])
|
||||
|
||||
|
||||
return (
|
||||
<div className="App">
|
||||
<header className="App-header">
|
||||
<img src={logo} className="App-logo" alt="logo" height="60"/>
|
||||
<p>
|
||||
Edit <code>src/App.js</code> and save to reload.
|
||||
</p>
|
||||
</header>
|
||||
<h1>ClusterInfo</h1>
|
||||
<p>
|
||||
{message}
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
24
frontend/src/App.test.js
Normal file
24
frontend/src/App.test.js
Normal file
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import App from './App';
|
||||
|
||||
test('renders learn react link', () => {
|
||||
render(<App />);
|
||||
const linkElement = screen.getByText(/learn react/i);
|
||||
expect(linkElement).toBeInTheDocument();
|
||||
});
|
29
frontend/src/index.css
Normal file
29
frontend/src/index.css
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
||||
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
||||
sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
|
||||
monospace;
|
||||
}
|
33
frontend/src/index.js
Normal file
33
frontend/src/index.js
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import './index.css';
|
||||
import App from './App';
|
||||
import reportWebVitals from './reportWebVitals';
|
||||
|
||||
ReactDOM.render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>,
|
||||
document.getElementById('root')
|
||||
);
|
||||
|
||||
// If you want to start measuring performance in your app, pass a function
|
||||
// to log results (for example: reportWebVitals(console.log))
|
||||
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
|
||||
reportWebVitals();
|
17
frontend/src/logo.svg
Normal file
17
frontend/src/logo.svg
Normal file
@@ -0,0 +1,17 @@
|
||||
<!--
|
||||
~ 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.
|
||||
-->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 841.9 595.3"><g fill="#61DAFB"><path d="M666.3 296.5c0-32.5-40.7-63.3-103.1-82.4 14.4-63.6 8-114.2-20.2-130.4-6.5-3.8-14.1-5.6-22.4-5.6v22.3c4.6 0 8.3.9 11.4 2.6 13.6 7.8 19.5 37.5 14.9 75.7-1.1 9.4-2.9 19.3-5.1 29.4-19.6-4.8-41-8.5-63.5-10.9-13.5-18.5-27.5-35.3-41.6-50 32.6-30.3 63.2-46.9 84-46.9V78c-27.5 0-63.5 19.6-99.9 53.6-36.4-33.8-72.4-53.2-99.9-53.2v22.3c20.7 0 51.4 16.5 84 46.6-14 14.7-28 31.4-41.3 49.9-22.6 2.4-44 6.1-63.6 11-2.3-10-4-19.7-5.2-29-4.7-38.2 1.1-67.9 14.6-75.8 3-1.8 6.9-2.6 11.5-2.6V78.5c-8.4 0-16 1.8-22.6 5.6-28.1 16.2-34.4 66.7-19.9 130.1-62.2 19.2-102.7 49.9-102.7 82.3 0 32.5 40.7 63.3 103.1 82.4-14.4 63.6-8 114.2 20.2 130.4 6.5 3.8 14.1 5.6 22.5 5.6 27.5 0 63.5-19.6 99.9-53.6 36.4 33.8 72.4 53.2 99.9 53.2 8.4 0 16-1.8 22.6-5.6 28.1-16.2 34.4-66.7 19.9-130.1 62-19.1 102.5-49.9 102.5-82.3zm-130.2-66.7c-3.7 12.9-8.3 26.2-13.5 39.5-4.1-8-8.4-16-13.1-24-4.6-8-9.5-15.8-14.4-23.4 14.2 2.1 27.9 4.7 41 7.9zm-45.8 106.5c-7.8 13.5-15.8 26.3-24.1 38.2-14.9 1.3-30 2-45.2 2-15.1 0-30.2-.7-45-1.9-8.3-11.9-16.4-24.6-24.2-38-7.6-13.1-14.5-26.4-20.8-39.8 6.2-13.4 13.2-26.8 20.7-39.9 7.8-13.5 15.8-26.3 24.1-38.2 14.9-1.3 30-2 45.2-2 15.1 0 30.2.7 45 1.9 8.3 11.9 16.4 24.6 24.2 38 7.6 13.1 14.5 26.4 20.8 39.8-6.3 13.4-13.2 26.8-20.7 39.9zm32.3-13c5.4 13.4 10 26.8 13.8 39.8-13.1 3.2-26.9 5.9-41.2 8 4.9-7.7 9.8-15.6 14.4-23.7 4.6-8 8.9-16.1 13-24.1zM421.2 430c-9.3-9.6-18.6-20.3-27.8-32 9 .4 18.2.7 27.5.7 9.4 0 18.7-.2 27.8-.7-9 11.7-18.3 22.4-27.5 32zm-74.4-58.9c-14.2-2.1-27.9-4.7-41-7.9 3.7-12.9 8.3-26.2 13.5-39.5 4.1 8 8.4 16 13.1 24 4.7 8 9.5 15.8 14.4 23.4zM420.7 163c9.3 9.6 18.6 20.3 27.8 32-9-.4-18.2-.7-27.5-.7-9.4 0-18.7.2-27.8.7 9-11.7 18.3-22.4 27.5-32zm-74 58.9c-4.9 7.7-9.8 15.6-14.4 23.7-4.6 8-8.9 16-13 24-5.4-13.4-10-26.8-13.8-39.8 13.1-3.1 26.9-5.8 41.2-7.9zm-90.5 125.2c-35.4-15.1-58.3-34.9-58.3-50.6 0-15.7 22.9-35.6 58.3-50.6 8.6-3.7 18-7 27.7-10.1 5.7 19.6 13.2 40 22.5 60.9-9.2 20.8-16.6 41.1-22.2 60.6-9.9-3.1-19.3-6.5-28-10.2zM310 490c-13.6-7.8-19.5-37.5-14.9-75.7 1.1-9.4 2.9-19.3 5.1-29.4 19.6 4.8 41 8.5 63.5 10.9 13.5 18.5 27.5 35.3 41.6 50-32.6 30.3-63.2 46.9-84 46.9-4.5-.1-8.3-1-11.3-2.7zm237.2-76.2c4.7 38.2-1.1 67.9-14.6 75.8-3 1.8-6.9 2.6-11.5 2.6-20.7 0-51.4-16.5-84-46.6 14-14.7 28-31.4 41.3-49.9 22.6-2.4 44-6.1 63.6-11 2.3 10.1 4.1 19.8 5.2 29.1zm38.5-66.7c-8.6 3.7-18 7-27.7 10.1-5.7-19.6-13.2-40-22.5-60.9 9.2-20.8 16.6-41.1 22.2-60.6 9.9 3.1 19.3 6.5 28.1 10.2 35.4 15.1 58.3 34.9 58.3 50.6-.1 15.7-23 35.6-58.4 50.6zM320.8 78.4z"/><circle cx="420.9" cy="296.5" r="45.7"/><path d="M520.5 78.1z"/></g></svg>
|
After Width: | Height: | Size: 3.4 KiB |
29
frontend/src/reportWebVitals.js
Normal file
29
frontend/src/reportWebVitals.js
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
const reportWebVitals = onPerfEntry => {
|
||||
if (onPerfEntry && onPerfEntry instanceof Function) {
|
||||
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
|
||||
getCLS(onPerfEntry);
|
||||
getFID(onPerfEntry);
|
||||
getFCP(onPerfEntry);
|
||||
getLCP(onPerfEntry);
|
||||
getTTFB(onPerfEntry);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export default reportWebVitals;
|
21
frontend/src/setupTests.js
Normal file
21
frontend/src/setupTests.js
Normal file
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
// jest-dom adds custom jest matchers for asserting on DOM nodes.
|
||||
// allows you to do things like:
|
||||
// expect(element).toHaveTextContent(/react/i)
|
||||
// learn more: https://github.com/testing-library/jest-dom
|
||||
import '@testing-library/jest-dom';
|
11595
frontend/yarn.lock
Normal file
11595
frontend/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
6
package-lock.json
generated
Normal file
6
package-lock.json
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "rocketmq-dashboard",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {}
|
||||
}
|
178
pom.xml
178
pom.xml
@@ -1,7 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<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">
|
||||
<!--
|
||||
~ 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.
|
||||
-->
|
||||
|
||||
<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>
|
||||
<groupId>org.apache</groupId>
|
||||
@@ -10,12 +25,19 @@
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.apache</groupId>
|
||||
<groupId>org.apache.rocketmq</groupId>
|
||||
<artifactId>rocketmq-dashboard</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>2.0.0</version>
|
||||
<version>1.0.1-SNAPSHOT</version>
|
||||
<name>rocketmq-dashboard</name>
|
||||
|
||||
<scm>
|
||||
<url>git@github.com:apache/rocketmq-dashboard.git</url>
|
||||
<connection>scm:git:git@github.com:apache/rocketmq-dashboard.git</connection>
|
||||
<developerConnection>scm:git:git@github.com:apache/rocketmq-dashboard.git</developerConnection>
|
||||
<tag>1.0.0</tag>
|
||||
</scm>
|
||||
|
||||
<mailingLists>
|
||||
<mailingList>
|
||||
<name>Development List</name>
|
||||
@@ -45,6 +67,11 @@
|
||||
</developer>
|
||||
</developers>
|
||||
|
||||
<organization>
|
||||
<name>Apache Software Foundation</name>
|
||||
<url>http://www.apache.org</url>
|
||||
</organization>
|
||||
|
||||
<licenses>
|
||||
<license>
|
||||
<name>Apache License, Version 2.0</name>
|
||||
@@ -63,15 +90,24 @@
|
||||
<commons-lang.version>2.6</commons-lang.version>
|
||||
<commons-io.version>2.4</commons-io.version>
|
||||
<commons-cli.version>1.2</commons-cli.version>
|
||||
<rocketmq.version>4.9.0</rocketmq.version>
|
||||
<commons-collections.version>3.2.2</commons-collections.version>
|
||||
<rocketmq.version>4.9.3</rocketmq.version>
|
||||
<surefire.version>2.19.1</surefire.version>
|
||||
<aspectj.version>1.9.6</aspectj.version>
|
||||
<lombok.version>1.18.12</lombok.version>
|
||||
<main.basedir>${basedir}/../..</main.basedir>
|
||||
<docker.image.prefix>apacherocketmq</docker.image.prefix>
|
||||
<spring.boot.version>2.2.2.RELEASE</spring.boot.version>
|
||||
<spring.boot.version>2.6.0</spring.boot.version>
|
||||
<mockito-inline.version>3.3.3</mockito-inline.version>
|
||||
<jaxb-api.version>2.3.1</jaxb-api.version>
|
||||
<commons-pool2.version>2.4.3</commons-pool2.version>
|
||||
<easyexcel.version>2.2.10</easyexcel.version>
|
||||
<asm.version>4.2</asm.version>
|
||||
<junit.version>4.12</junit.version>
|
||||
<snakeyaml.version>1.30</snakeyaml.version>
|
||||
<cglib.version>2.2.2</cglib.version>
|
||||
<joor.version>0.9.6</joor.version>
|
||||
<bcpkix-jdk15on.version>1.68</bcpkix-jdk15on.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
@@ -95,11 +131,22 @@
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<version>${spring.boot.version}</version>
|
||||
<scope>test</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-validation</artifactId>
|
||||
<version>${spring.boot.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-collections</groupId>
|
||||
<artifactId>commons-collections</artifactId>
|
||||
<version>3.2.2</version>
|
||||
<version>${commons-collections.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.rocketmq</groupId>
|
||||
@@ -173,17 +220,17 @@
|
||||
<dependency>
|
||||
<groupId>cglib</groupId>
|
||||
<artifactId>cglib</artifactId>
|
||||
<version>2.2.2</version>
|
||||
<version>${cglib.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jooq</groupId>
|
||||
<artifactId>joor</artifactId>
|
||||
<version>0.9.6</version>
|
||||
<version>${joor.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcpkix-jdk15on</artifactId>
|
||||
<version>1.68</version>
|
||||
<version>${bcpkix-jdk15on.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.xml.bind</groupId>
|
||||
@@ -201,6 +248,33 @@
|
||||
<version>${mockito-inline.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-pool2</artifactId>
|
||||
<version>${commons-pool2.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>easyexcel</artifactId>
|
||||
<version>${easyexcel.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.ow2.asm</groupId>
|
||||
<artifactId>asm</artifactId>
|
||||
<version>${asm.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>${junit.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.yaml</groupId>
|
||||
<artifactId>snakeyaml</artifactId>
|
||||
<version>${snakeyaml.version}</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
@@ -314,7 +388,87 @@
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.rat</groupId>
|
||||
<artifactId>apache-rat-plugin</artifactId>
|
||||
<version>0.12</version>
|
||||
<configuration>
|
||||
<excludes>
|
||||
<exclude>.gitignore</exclude>
|
||||
<exclude>.travis.yml</exclude>
|
||||
<exclude>.asf.yaml</exclude>
|
||||
<exclude>README.md</exclude>
|
||||
<exclude>.github/**</exclude>
|
||||
<exclude>docs/**</exclude>
|
||||
<exclude>src/main/resources/static/vendor/**</exclude>
|
||||
<exclude>src/main/resources/static/src/data/**</exclude>
|
||||
<exclude>frontend/node_modules/**</exclude>
|
||||
<exclude>frontend/build/**</exclude>
|
||||
<exclude>frontend/**.json</exclude>
|
||||
<exclude>frontend/**.lock</exclude>
|
||||
<exclude>frontend/public/manifest.json</exclude>
|
||||
<exclude>package-lock.json</exclude>
|
||||
</excludes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>com.github.eirslett</groupId>
|
||||
<artifactId>frontend-maven-plugin</artifactId>
|
||||
<version>1.11.3</version>
|
||||
<configuration>
|
||||
<workingDirectory>frontend</workingDirectory>
|
||||
<installDirectory>target</installDirectory>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>install node and yarn</id>
|
||||
<goals>
|
||||
<goal>install-node-and-yarn</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<nodeVersion>v16.2.0</nodeVersion>
|
||||
<yarnVersion>v1.22.10</yarnVersion>
|
||||
</configuration>
|
||||
</execution>
|
||||
|
||||
<execution>
|
||||
<id>yarn install</id>
|
||||
<goals>
|
||||
<goal>yarn</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<arguments>install</arguments>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>yarn build</id>
|
||||
<goals>
|
||||
<goal>yarn</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<arguments>build</arguments>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-antrun-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>generate-resources</phase>
|
||||
<configuration>
|
||||
<target>
|
||||
<copy todir="${project.build.directory}/classes/public">
|
||||
<fileset dir="${project.basedir}/frontend/build"/>
|
||||
</copy>
|
||||
</target>
|
||||
</configuration>
|
||||
<goals>
|
||||
<goal>run</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
|
@@ -1,3 +1,20 @@
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
FROM java:8
|
||||
VOLUME /tmp
|
||||
ADD rocketmq-dashboard-*.jar rocketmq-dashboard.jar
|
||||
|
@@ -19,11 +19,14 @@ package org.apache.rocketmq.dashboard;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.web.servlet.ServletComponentScan;
|
||||
import org.springframework.context.annotation.EnableMBeanExport;
|
||||
import org.springframework.jmx.support.RegistrationPolicy;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableScheduling
|
||||
@ServletComponentScan
|
||||
@EnableMBeanExport(registration = RegistrationPolicy.IGNORE_EXISTING)
|
||||
public class App {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* 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.admin;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.rocketmq.acl.common.AclClientRPCHook;
|
||||
import org.apache.rocketmq.acl.common.SessionCredentials;
|
||||
import org.apache.rocketmq.dashboard.config.RMQConfigure;
|
||||
import org.apache.rocketmq.remoting.RPCHook;
|
||||
import org.apache.rocketmq.tools.admin.DefaultMQAdminExt;
|
||||
import org.apache.rocketmq.tools.admin.MQAdminExt;
|
||||
|
||||
@Slf4j
|
||||
public class MQAdminFactory {
|
||||
private RMQConfigure rmqConfigure;
|
||||
|
||||
public MQAdminFactory(RMQConfigure rmqConfigure) {
|
||||
this.rmqConfigure = rmqConfigure;
|
||||
}
|
||||
|
||||
private final AtomicLong adminIndex = new AtomicLong(0);
|
||||
|
||||
public MQAdminExt getInstance() throws Exception {
|
||||
RPCHook rpcHook = null;
|
||||
final String accessKey = rmqConfigure.getAccessKey();
|
||||
final String secretKey = rmqConfigure.getSecretKey();
|
||||
boolean isEnableAcl = StringUtils.isNotEmpty(accessKey) && StringUtils.isNotEmpty(secretKey);
|
||||
if (isEnableAcl) {
|
||||
rpcHook = new AclClientRPCHook(new SessionCredentials(accessKey, secretKey));
|
||||
}
|
||||
DefaultMQAdminExt mqAdminExt = null;
|
||||
if (rmqConfigure.getTimeoutMillis() == null) {
|
||||
mqAdminExt = new DefaultMQAdminExt(rpcHook);
|
||||
} else {
|
||||
mqAdminExt = new DefaultMQAdminExt(rpcHook, rmqConfigure.getTimeoutMillis());
|
||||
}
|
||||
mqAdminExt.setAdminExtGroup(mqAdminExt.getAdminExtGroup() + "_" + adminIndex.getAndIncrement());
|
||||
mqAdminExt.setVipChannelEnabled(Boolean.parseBoolean(rmqConfigure.getIsVIPChannel()));
|
||||
mqAdminExt.setUseTLS(rmqConfigure.isUseTLS());
|
||||
mqAdminExt.setInstanceName(Long.toString(System.currentTimeMillis()));
|
||||
mqAdminExt.start();
|
||||
log.info("create MQAdmin instance {} success.", mqAdminExt);
|
||||
return mqAdminExt;
|
||||
}
|
||||
}
|
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.rocketmq.dashboard.admin;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.collections.MapUtils;
|
||||
import org.apache.commons.pool2.PooledObject;
|
||||
import org.apache.commons.pool2.PooledObjectFactory;
|
||||
import org.apache.commons.pool2.impl.DefaultPooledObject;
|
||||
import org.apache.rocketmq.common.protocol.body.ClusterInfo;
|
||||
import org.apache.rocketmq.tools.admin.MQAdminExt;
|
||||
|
||||
@Slf4j
|
||||
public class MQAdminPooledObjectFactory implements PooledObjectFactory<MQAdminExt> {
|
||||
|
||||
private MQAdminFactory mqAdminFactory;
|
||||
|
||||
@Override
|
||||
public PooledObject<MQAdminExt> makeObject() throws Exception {
|
||||
DefaultPooledObject<MQAdminExt> pooledObject = new DefaultPooledObject<>(
|
||||
mqAdminFactory.getInstance());
|
||||
return pooledObject;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroyObject(PooledObject<MQAdminExt> p) {
|
||||
MQAdminExt mqAdmin = p.getObject();
|
||||
if (mqAdmin != null) {
|
||||
try {
|
||||
mqAdmin.shutdown();
|
||||
} catch (Exception e) {
|
||||
log.warn("MQAdminExt shutdown err", e);
|
||||
}
|
||||
}
|
||||
log.info("destroy object {}", p.getObject());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validateObject(PooledObject<MQAdminExt> p) {
|
||||
MQAdminExt mqAdmin = p.getObject();
|
||||
ClusterInfo clusterInfo = null;
|
||||
try {
|
||||
clusterInfo = mqAdmin.examineBrokerClusterInfo();
|
||||
} catch (Exception e) {
|
||||
log.warn("validate object {} err", p.getObject(), e);
|
||||
}
|
||||
if (clusterInfo == null || MapUtils.isEmpty(clusterInfo.getBrokerAddrTable())) {
|
||||
log.warn("validateObject failed, clusterInfo = {}", clusterInfo);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void activateObject(PooledObject<MQAdminExt> p) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void passivateObject(PooledObject<MQAdminExt> p) {
|
||||
}
|
||||
|
||||
public void setMqAdminFactory(MQAdminFactory mqAdminFactory) {
|
||||
this.mqAdminFactory = mqAdminFactory;
|
||||
}
|
||||
}
|
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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.admin;
|
||||
|
||||
import org.apache.commons.pool2.impl.GenericObjectPool;
|
||||
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
|
||||
import org.apache.rocketmq.dashboard.config.RMQConfigure;
|
||||
import org.apache.rocketmq.tools.admin.MQAdminExt;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
public class MqAdminExtObjectPool {
|
||||
|
||||
@Autowired
|
||||
private RMQConfigure rmqConfigure;
|
||||
|
||||
@Bean
|
||||
public GenericObjectPool<MQAdminExt> mqAdminExtPool() {
|
||||
GenericObjectPoolConfig genericObjectPoolConfig = new GenericObjectPoolConfig();
|
||||
genericObjectPoolConfig.setTestWhileIdle(true);
|
||||
genericObjectPoolConfig.setMaxWaitMillis(10000);
|
||||
genericObjectPoolConfig.setTimeBetweenEvictionRunsMillis(20000);
|
||||
MQAdminPooledObjectFactory mqAdminPooledObjectFactory = new MQAdminPooledObjectFactory();
|
||||
MQAdminFactory mqAdminFactory = new MQAdminFactory(rmqConfigure);
|
||||
mqAdminPooledObjectFactory.setMqAdminFactory(mqAdminFactory);
|
||||
GenericObjectPool<MQAdminExt> genericObjectPool = new GenericObjectPool<MQAdminExt>(
|
||||
mqAdminPooledObjectFactory,
|
||||
genericObjectPoolConfig);
|
||||
return genericObjectPool;
|
||||
}
|
||||
}
|
@@ -16,28 +16,24 @@
|
||||
*/
|
||||
package org.apache.rocketmq.dashboard.aspect.admin;
|
||||
|
||||
import org.apache.rocketmq.dashboard.aspect.admin.annotation.MultiMQAdminCmdMethod;
|
||||
import org.apache.rocketmq.dashboard.config.RMQConfigure;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.pool2.impl.GenericObjectPool;
|
||||
import org.apache.rocketmq.dashboard.service.client.MQAdminInstance;
|
||||
import org.apache.rocketmq.tools.admin.MQAdminExt;
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.annotation.Around;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.aspectj.lang.annotation.Pointcut;
|
||||
import org.aspectj.lang.reflect.MethodSignature;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
@Aspect
|
||||
@Service
|
||||
@Slf4j
|
||||
public class MQAdminAspect {
|
||||
private Logger logger = LoggerFactory.getLogger(MQAdminAspect.class);
|
||||
|
||||
@Autowired
|
||||
private RMQConfigure rmqConfigure;
|
||||
private GenericObjectPool<MQAdminExt> mqAdminExtPool;
|
||||
|
||||
public MQAdminAspect() {
|
||||
}
|
||||
@@ -47,30 +43,16 @@ public class MQAdminAspect {
|
||||
|
||||
}
|
||||
|
||||
@Pointcut("@annotation(org.apache.rocketmq.dashboard.aspect.admin.annotation.MultiMQAdminCmdMethod)")
|
||||
public void multiMQAdminMethodPointCut() {
|
||||
|
||||
}
|
||||
|
||||
@Around(value = "mQAdminMethodPointCut() || multiMQAdminMethodPointCut()")
|
||||
@Around(value = "mQAdminMethodPointCut()")
|
||||
public Object aroundMQAdminMethod(ProceedingJoinPoint joinPoint) throws Throwable {
|
||||
long start = System.currentTimeMillis();
|
||||
Object obj = null;
|
||||
try {
|
||||
MethodSignature signature = (MethodSignature)joinPoint.getSignature();
|
||||
Method method = signature.getMethod();
|
||||
MultiMQAdminCmdMethod multiMQAdminCmdMethod = method.getAnnotation(MultiMQAdminCmdMethod.class);
|
||||
if (multiMQAdminCmdMethod != null && multiMQAdminCmdMethod.timeoutMillis() > 0) {
|
||||
MQAdminInstance.initMQAdminInstance(multiMQAdminCmdMethod.timeoutMillis(),rmqConfigure.getAccessKey(),rmqConfigure.getSecretKey(), rmqConfigure.isUseTLS());
|
||||
}
|
||||
else {
|
||||
MQAdminInstance.initMQAdminInstance(0,rmqConfigure.getAccessKey(),rmqConfigure.getSecretKey(), rmqConfigure.isUseTLS());
|
||||
}
|
||||
MQAdminInstance.createMQAdmin(mqAdminExtPool);
|
||||
obj = joinPoint.proceed();
|
||||
}
|
||||
finally {
|
||||
MQAdminInstance.destroyMQAdminInstance();
|
||||
logger.debug("op=look method={} cost={}", joinPoint.getSignature().getName(), System.currentTimeMillis() - start);
|
||||
} finally {
|
||||
MQAdminInstance.returnMQAdmin(mqAdminExtPool);
|
||||
log.debug("op=look method={} cost={}", joinPoint.getSignature().getName(), System.currentTimeMillis() - start);
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
@@ -30,6 +30,7 @@ import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
import org.springframework.web.multipart.support.MissingServletRequestPartException;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
@@ -52,6 +53,7 @@ public class AuthWebMVCConfigurerAdapter extends WebMvcConfigurerAdapter {
|
||||
"/cluster/**",
|
||||
"/consumer/**",
|
||||
"/dashboard/**",
|
||||
"/dlqMessage/**",
|
||||
"/message/**",
|
||||
"/messageTrace/**",
|
||||
"/monitor/**",
|
||||
@@ -59,7 +61,8 @@ public class AuthWebMVCConfigurerAdapter extends WebMvcConfigurerAdapter {
|
||||
"/ops/**",
|
||||
"/producer/**",
|
||||
"/test/**",
|
||||
"/topic/**");
|
||||
"/topic/**",
|
||||
"/acl/**");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,4 +89,9 @@ public class AuthWebMVCConfigurerAdapter extends WebMvcConfigurerAdapter {
|
||||
|
||||
super.addArgumentResolvers(argumentResolvers); //REVIEW ME
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addViewControllers(ViewControllerRegistry registry) {
|
||||
registry.addViewController("*.htm").setViewName("forward:/app.html");
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* 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.config;
|
||||
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.LinkedBlockingDeque;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
@ConfigurationProperties(prefix = "threadpool.config")
|
||||
@Data
|
||||
public class CollectExecutorConfig {
|
||||
private int coreSize = 20;
|
||||
private int maxSize = 20;
|
||||
private long keepAliveTime = 3000L;
|
||||
private int queueSize = 1000;
|
||||
|
||||
@Bean(name = "collectExecutor")
|
||||
public ExecutorService collectExecutor(CollectExecutorConfig collectExecutorConfig) {
|
||||
ExecutorService collectExecutor = new ThreadPoolExecutor(
|
||||
collectExecutorConfig.getCoreSize(),
|
||||
collectExecutorConfig.getMaxSize(),
|
||||
collectExecutorConfig.getKeepAliveTime(),
|
||||
TimeUnit.MILLISECONDS,
|
||||
new LinkedBlockingDeque<>(collectExecutorConfig.getQueueSize()),
|
||||
new ThreadFactory() {
|
||||
private final AtomicLong threadIndex = new AtomicLong(0);
|
||||
|
||||
@Override
|
||||
public Thread newThread(Runnable r) {
|
||||
return new Thread(r, "collectTopicThread_" + this.threadIndex.incrementAndGet());
|
||||
}
|
||||
},
|
||||
new ThreadPoolExecutor.DiscardOldestPolicy()
|
||||
);
|
||||
return collectExecutor;
|
||||
}
|
||||
}
|
@@ -16,9 +16,10 @@
|
||||
*/
|
||||
package org.apache.rocketmq.dashboard.config;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.rocketmq.common.MixAll;
|
||||
import org.apache.rocketmq.common.topic.TopicValidator;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
@@ -30,6 +31,7 @@ import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
|
||||
import static org.apache.rocketmq.client.ClientConfig.SEND_MESSAGE_WITH_VIP_CHANNEL_PROPERTY;
|
||||
|
||||
@@ -48,8 +50,6 @@ public class RMQConfigure {
|
||||
|
||||
private boolean enableDashBoardCollect;
|
||||
|
||||
private String msgTrackTopicName;
|
||||
|
||||
private boolean loginRequired = false;
|
||||
|
||||
private String accessKey;
|
||||
@@ -58,6 +58,10 @@ public class RMQConfigure {
|
||||
|
||||
private boolean useTLS = false;
|
||||
|
||||
private Long timeoutMillis;
|
||||
|
||||
private List<String> namesrvAddrs = new ArrayList<>();
|
||||
|
||||
public String getAccessKey() {
|
||||
return accessKey;
|
||||
}
|
||||
@@ -78,6 +82,17 @@ public class RMQConfigure {
|
||||
return namesrvAddr;
|
||||
}
|
||||
|
||||
public List<String> getNamesrvAddrs() {
|
||||
return namesrvAddrs;
|
||||
}
|
||||
|
||||
public void setNamesrvAddrs(List<String> namesrvAddrs) {
|
||||
this.namesrvAddrs = namesrvAddrs;
|
||||
if (CollectionUtils.isNotEmpty(namesrvAddrs)) {
|
||||
this.setNamesrvAddr(namesrvAddrs.get(0));
|
||||
}
|
||||
}
|
||||
|
||||
public void setNamesrvAddr(String namesrvAddr) {
|
||||
if (StringUtils.isNotBlank(namesrvAddr)) {
|
||||
this.namesrvAddr = namesrvAddr;
|
||||
@@ -121,17 +136,6 @@ public class RMQConfigure {
|
||||
this.enableDashBoardCollect = Boolean.valueOf(enableDashBoardCollect);
|
||||
}
|
||||
|
||||
public String getMsgTrackTopicNameOrDefault() {
|
||||
if (StringUtils.isEmpty(msgTrackTopicName)) {
|
||||
return TopicValidator.RMQ_SYS_TRACE_TOPIC;
|
||||
}
|
||||
return msgTrackTopicName;
|
||||
}
|
||||
|
||||
public void setMsgTrackTopicName(String msgTrackTopicName) {
|
||||
this.msgTrackTopicName = msgTrackTopicName;
|
||||
}
|
||||
|
||||
public boolean isLoginRequired() {
|
||||
return loginRequired;
|
||||
}
|
||||
@@ -148,6 +152,14 @@ public class RMQConfigure {
|
||||
this.useTLS = useTLS;
|
||||
}
|
||||
|
||||
public Long getTimeoutMillis() {
|
||||
return timeoutMillis;
|
||||
}
|
||||
|
||||
public void setTimeoutMillis(Long timeoutMillis) {
|
||||
this.timeoutMillis = timeoutMillis;
|
||||
}
|
||||
|
||||
// Error Page process logic, move to a central configure later
|
||||
@Bean
|
||||
public ErrorPageRegistrar errorPageRegistrar() {
|
||||
|
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
* 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.controller;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import java.util.List;
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.rocketmq.common.AclConfig;
|
||||
import org.apache.rocketmq.common.PlainAccessConfig;
|
||||
import org.apache.rocketmq.dashboard.config.RMQConfigure;
|
||||
import org.apache.rocketmq.dashboard.model.User;
|
||||
import org.apache.rocketmq.dashboard.model.UserInfo;
|
||||
import org.apache.rocketmq.dashboard.model.request.AclRequest;
|
||||
import org.apache.rocketmq.dashboard.permisssion.Permission;
|
||||
import org.apache.rocketmq.dashboard.service.AclService;
|
||||
import org.apache.rocketmq.dashboard.support.JsonResult;
|
||||
import org.apache.rocketmq.dashboard.util.WebUtil;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/acl")
|
||||
@Permission
|
||||
public class AclController {
|
||||
|
||||
@Resource
|
||||
private AclService aclService;
|
||||
|
||||
@Resource
|
||||
private RMQConfigure configure;
|
||||
|
||||
@GetMapping("/enable.query")
|
||||
public Object isEnableAcl() {
|
||||
return new JsonResult<>(configure.isACLEnabled());
|
||||
}
|
||||
|
||||
@GetMapping("/config.query")
|
||||
public AclConfig getAclConfig(HttpServletRequest request) {
|
||||
if (!configure.isLoginRequired()) {
|
||||
return aclService.getAclConfig(false);
|
||||
}
|
||||
UserInfo userInfo = (UserInfo) WebUtil.getValueFromSession(request, WebUtil.USER_INFO);
|
||||
// if user info is null but reach here, must exclude secret key for safety.
|
||||
return aclService.getAclConfig(userInfo == null || userInfo.getUser().getType() != User.ADMIN);
|
||||
}
|
||||
|
||||
@PostMapping("/add.do")
|
||||
public Object addAclConfig(@RequestBody PlainAccessConfig config) {
|
||||
Preconditions.checkArgument(StringUtils.isNotEmpty(config.getAccessKey()), "accessKey is null");
|
||||
Preconditions.checkArgument(StringUtils.isNotEmpty(config.getSecretKey()), "secretKey is null");
|
||||
aclService.addAclConfig(config);
|
||||
return true;
|
||||
}
|
||||
|
||||
@PostMapping("/delete.do")
|
||||
public Object deleteAclConfig(@RequestBody PlainAccessConfig config) {
|
||||
Preconditions.checkArgument(StringUtils.isNotEmpty(config.getAccessKey()), "accessKey is null");
|
||||
aclService.deleteAclConfig(config);
|
||||
return true;
|
||||
}
|
||||
|
||||
@PostMapping("/update.do")
|
||||
public Object updateAclConfig(@RequestBody PlainAccessConfig config) {
|
||||
Preconditions.checkArgument(StringUtils.isNotEmpty(config.getSecretKey()), "secretKey is null");
|
||||
aclService.updateAclConfig(config);
|
||||
return true;
|
||||
}
|
||||
|
||||
@PostMapping("/topic/add.do")
|
||||
public Object addAclTopicConfig(@RequestBody AclRequest request) {
|
||||
Preconditions.checkArgument(StringUtils.isNotEmpty(request.getConfig().getAccessKey()), "accessKey is null");
|
||||
Preconditions.checkArgument(StringUtils.isNotEmpty(request.getConfig().getSecretKey()), "secretKey is null");
|
||||
Preconditions.checkArgument(CollectionUtils.isNotEmpty(request.getConfig().getTopicPerms()), "topic perms is null");
|
||||
Preconditions.checkArgument(StringUtils.isNotEmpty(request.getTopicPerm()), "topic perm is null");
|
||||
aclService.addOrUpdateAclTopicConfig(request);
|
||||
return true;
|
||||
}
|
||||
|
||||
@PostMapping("/group/add.do")
|
||||
public Object addAclGroupConfig(@RequestBody AclRequest request) {
|
||||
Preconditions.checkArgument(StringUtils.isNotEmpty(request.getConfig().getAccessKey()), "accessKey is null");
|
||||
Preconditions.checkArgument(StringUtils.isNotEmpty(request.getConfig().getSecretKey()), "secretKey is null");
|
||||
Preconditions.checkArgument(CollectionUtils.isNotEmpty(request.getConfig().getGroupPerms()), "group perms is null");
|
||||
Preconditions.checkArgument(StringUtils.isNotEmpty(request.getGroupPerm()), "group perm is null");
|
||||
aclService.addOrUpdateAclGroupConfig(request);
|
||||
return true;
|
||||
}
|
||||
|
||||
@PostMapping("/perm/delete.do")
|
||||
public Object deletePermConfig(@RequestBody AclRequest request) {
|
||||
Preconditions.checkArgument(StringUtils.isNotEmpty(request.getConfig().getAccessKey()), "accessKey is null");
|
||||
Preconditions.checkArgument(StringUtils.isNotEmpty(request.getConfig().getSecretKey()), "secretKey is null");
|
||||
aclService.deletePermConfig(request);
|
||||
return true;
|
||||
}
|
||||
|
||||
@PostMapping("/sync.do")
|
||||
public Object syncConfig(@RequestBody PlainAccessConfig config) {
|
||||
Preconditions.checkArgument(StringUtils.isNotEmpty(config.getAccessKey()), "accessKey is null");
|
||||
Preconditions.checkArgument(StringUtils.isNotEmpty(config.getSecretKey()), "secretKey is null");
|
||||
aclService.syncData(config);
|
||||
return true;
|
||||
}
|
||||
|
||||
@PostMapping("/white/list/add.do")
|
||||
public Object addWhiteList(@RequestBody List<String> whiteList) {
|
||||
Preconditions.checkArgument(CollectionUtils.isNotEmpty(whiteList), "white list is null");
|
||||
aclService.addWhiteList(whiteList);
|
||||
return true;
|
||||
}
|
||||
|
||||
@DeleteMapping("/white/list/delete.do")
|
||||
public Object deleteWhiteAddr(@RequestParam String request) {
|
||||
aclService.deleteWhiteAddr(request);
|
||||
return true;
|
||||
}
|
||||
|
||||
@PostMapping("/white/list/sync.do")
|
||||
public Object synchronizeWhiteList(@RequestBody List<String> whiteList) {
|
||||
Preconditions.checkArgument(CollectionUtils.isNotEmpty(whiteList), "white list is null");
|
||||
aclService.synchronizeWhiteList(whiteList);
|
||||
return true;
|
||||
}
|
||||
}
|
@@ -16,6 +16,7 @@
|
||||
*/
|
||||
package org.apache.rocketmq.dashboard.controller;
|
||||
|
||||
import org.apache.rocketmq.dashboard.permisssion.Permission;
|
||||
import org.apache.rocketmq.dashboard.service.ClusterService;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
@@ -27,6 +28,7 @@ import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
@Controller
|
||||
@RequestMapping("/cluster")
|
||||
@Permission
|
||||
public class ClusterController {
|
||||
|
||||
@Resource
|
||||
|
@@ -24,6 +24,7 @@ import org.apache.rocketmq.dashboard.model.ConnectionInfo;
|
||||
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.permisssion.Permission;
|
||||
import org.apache.rocketmq.dashboard.service.ConsumerService;
|
||||
import org.apache.rocketmq.dashboard.util.JsonUtil;
|
||||
import org.slf4j.Logger;
|
||||
@@ -37,6 +38,7 @@ import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
@Controller
|
||||
@RequestMapping("/consumer")
|
||||
@Permission
|
||||
public class ConsumerController {
|
||||
private Logger logger = LoggerFactory.getLogger(ConsumerController.class);
|
||||
|
||||
@@ -45,8 +47,8 @@ public class ConsumerController {
|
||||
|
||||
@RequestMapping(value = "/groupList.query")
|
||||
@ResponseBody
|
||||
public Object list() {
|
||||
return consumerService.queryGroupList();
|
||||
public Object list(@RequestParam(value = "skipSysGroup", required = false) boolean skipSysGroup) {
|
||||
return consumerService.queryGroupList(skipSysGroup);
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/group.query")
|
||||
|
@@ -20,6 +20,7 @@ package org.apache.rocketmq.dashboard.controller;
|
||||
import javax.annotation.Resource;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import org.apache.rocketmq.dashboard.permisssion.Permission;
|
||||
import org.apache.rocketmq.dashboard.service.DashboardService;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
@@ -29,6 +30,7 @@ import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
@Controller
|
||||
@RequestMapping("/dashboard")
|
||||
@Permission
|
||||
public class DashboardController {
|
||||
|
||||
@Resource
|
||||
@@ -49,7 +51,7 @@ public class DashboardController {
|
||||
return dashboardService.queryTopicData(date,topicName);
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/topicCurrent", method = RequestMethod.GET)
|
||||
@RequestMapping(value = "/topicCurrent.query", method = RequestMethod.GET)
|
||||
@ResponseBody
|
||||
public Object topicCurrent() {
|
||||
return dashboardService.queryTopicCurrentData();
|
||||
|
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* 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.controller;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.rocketmq.common.MixAll;
|
||||
import org.apache.rocketmq.common.message.MessageExt;
|
||||
import org.apache.rocketmq.dashboard.exception.ServiceException;
|
||||
import org.apache.rocketmq.dashboard.model.DlqMessageExcelModel;
|
||||
import org.apache.rocketmq.dashboard.model.DlqMessageRequest;
|
||||
import org.apache.rocketmq.dashboard.model.request.MessageQuery;
|
||||
import org.apache.rocketmq.dashboard.permisssion.Permission;
|
||||
import org.apache.rocketmq.dashboard.service.DlqMessageService;
|
||||
import org.apache.rocketmq.dashboard.util.ExcelUtil;
|
||||
import org.apache.rocketmq.tools.admin.MQAdminExt;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
@Controller
|
||||
@RequestMapping("/dlqMessage")
|
||||
@Permission
|
||||
@Slf4j
|
||||
public class DlqMessageController {
|
||||
|
||||
@Resource
|
||||
private DlqMessageService dlqMessageService;
|
||||
|
||||
@Resource
|
||||
private MQAdminExt mqAdminExt;
|
||||
|
||||
@RequestMapping(value = "/queryDlqMessageByConsumerGroup.query", method = RequestMethod.POST)
|
||||
@ResponseBody
|
||||
public Object queryDlqMessageByConsumerGroup(@RequestBody MessageQuery query) {
|
||||
return dlqMessageService.queryDlqMessageByPage(query);
|
||||
}
|
||||
|
||||
@GetMapping(value = "/exportDlqMessage.do")
|
||||
public void exportDlqMessage(HttpServletResponse response, @RequestParam String consumerGroup,
|
||||
@RequestParam String msgId) {
|
||||
MessageExt messageExt = null;
|
||||
try {
|
||||
String topic = MixAll.DLQ_GROUP_TOPIC_PREFIX + consumerGroup;
|
||||
messageExt = mqAdminExt.viewMessage(topic, msgId);
|
||||
} catch (Exception e) {
|
||||
throw new ServiceException(-1, String.format("Failed to query message by Id: %s", msgId));
|
||||
}
|
||||
DlqMessageExcelModel excelModel = new DlqMessageExcelModel(messageExt);
|
||||
try {
|
||||
ExcelUtil.writeExcel(response, Lists.newArrayList(excelModel), "dlq", "dlq", DlqMessageExcelModel.class);
|
||||
} catch (Exception e) {
|
||||
throw new ServiceException(-1, String.format("export dlq message failed!"));
|
||||
}
|
||||
}
|
||||
|
||||
@PostMapping(value = "/batchResendDlqMessage.do")
|
||||
@ResponseBody
|
||||
public Object batchResendDlqMessage(@RequestBody List<DlqMessageRequest> dlqMessages) {
|
||||
return dlqMessageService.batchResendDlqMessage(dlqMessages);
|
||||
}
|
||||
|
||||
@PostMapping(value = "/batchExportDlqMessage.do")
|
||||
public void batchExportDlqMessage(HttpServletResponse response, @RequestBody List<DlqMessageRequest> dlqMessages) {
|
||||
List<DlqMessageExcelModel> dlqMessageExcelModelList = new ArrayList<>(dlqMessages.size());
|
||||
for (DlqMessageRequest dlqMessage : dlqMessages) {
|
||||
DlqMessageExcelModel excelModel = new DlqMessageExcelModel();
|
||||
try {
|
||||
String topic = MixAll.DLQ_GROUP_TOPIC_PREFIX + dlqMessage.getConsumerGroup();
|
||||
MessageExt messageExt = mqAdminExt.viewMessage(topic, dlqMessage.getMsgId());
|
||||
excelModel = new DlqMessageExcelModel(messageExt);
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to query message by Id:{}", dlqMessage.getMsgId(), e);
|
||||
excelModel.setMsgId(dlqMessage.getMsgId());
|
||||
excelModel.setException(e.getMessage());
|
||||
}
|
||||
dlqMessageExcelModelList.add(excelModel);
|
||||
}
|
||||
try {
|
||||
ExcelUtil.writeExcel(response, dlqMessageExcelModelList, "dlqs", "dlqs", DlqMessageExcelModel.class);
|
||||
} catch (Exception e) {
|
||||
throw new ServiceException(-1, String.format("export dlq message failed!"));
|
||||
}
|
||||
}
|
||||
}
|
@@ -19,6 +19,7 @@ package org.apache.rocketmq.dashboard.controller;
|
||||
|
||||
import org.apache.rocketmq.dashboard.config.RMQConfigure;
|
||||
import org.apache.rocketmq.dashboard.model.LoginInfo;
|
||||
import org.apache.rocketmq.dashboard.model.LoginResult;
|
||||
import org.apache.rocketmq.dashboard.model.User;
|
||||
import org.apache.rocketmq.dashboard.model.UserInfo;
|
||||
import org.apache.rocketmq.dashboard.service.UserService;
|
||||
@@ -65,7 +66,7 @@ public class LoginController {
|
||||
|
||||
@RequestMapping(value = "/login.do", method = RequestMethod.POST)
|
||||
@ResponseBody
|
||||
public JsonResult<String> login(@RequestParam("username") String username,
|
||||
public Object login(@RequestParam("username") String username,
|
||||
@RequestParam(value = "password") String password,
|
||||
HttpServletRequest request,
|
||||
HttpServletResponse response) throws Exception {
|
||||
@@ -80,7 +81,8 @@ public class LoginController {
|
||||
WebUtil.setSessionValue(request, WebUtil.USER_INFO, userInfo);
|
||||
WebUtil.setSessionValue(request, WebUtil.USER_NAME, username);
|
||||
userInfo.setSessionId(WebUtil.getSessionId(request));
|
||||
return new JsonResult<>(contextPath);
|
||||
LoginResult result = new LoginResult(username, user.getType(), contextPath);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -22,6 +22,7 @@ import org.apache.rocketmq.common.protocol.body.ConsumeMessageDirectlyResult;
|
||||
import org.apache.rocketmq.dashboard.model.MessagePage;
|
||||
import org.apache.rocketmq.dashboard.model.MessageView;
|
||||
import org.apache.rocketmq.dashboard.model.request.MessageQuery;
|
||||
import org.apache.rocketmq.dashboard.permisssion.Permission;
|
||||
import org.apache.rocketmq.dashboard.service.MessageService;
|
||||
import org.apache.rocketmq.dashboard.util.JsonUtil;
|
||||
import org.apache.rocketmq.tools.admin.api.MessageTrack;
|
||||
@@ -41,6 +42,7 @@ import java.util.Map;
|
||||
|
||||
@Controller
|
||||
@RequestMapping("/message")
|
||||
@Permission
|
||||
public class MessageController {
|
||||
private Logger logger = LoggerFactory.getLogger(MessageController.class);
|
||||
@Resource
|
||||
|
@@ -26,6 +26,7 @@ import javax.annotation.Resource;
|
||||
import org.apache.rocketmq.common.Pair;
|
||||
import org.apache.rocketmq.dashboard.model.MessageView;
|
||||
import org.apache.rocketmq.dashboard.model.trace.MessageTraceGraph;
|
||||
import org.apache.rocketmq.dashboard.permisssion.Permission;
|
||||
import org.apache.rocketmq.dashboard.service.MessageService;
|
||||
import org.apache.rocketmq.dashboard.service.MessageTraceService;
|
||||
import org.apache.rocketmq.tools.admin.api.MessageTrack;
|
||||
@@ -37,6 +38,7 @@ import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
@Controller
|
||||
@RequestMapping("/messageTrace")
|
||||
@Permission
|
||||
public class MessageTraceController {
|
||||
|
||||
@Resource
|
||||
@@ -62,7 +64,8 @@ public class MessageTraceController {
|
||||
|
||||
@RequestMapping(value = "/viewMessageTraceGraph.query", method = RequestMethod.GET)
|
||||
@ResponseBody
|
||||
public MessageTraceGraph viewMessageTraceGraph(@RequestParam String msgId) {
|
||||
return messageTraceService.queryMessageTraceGraph(msgId);
|
||||
public MessageTraceGraph viewMessageTraceGraph(@RequestParam String msgId,
|
||||
@RequestParam(required = false) String traceTopic) {
|
||||
return messageTraceService.queryMessageTraceGraph(msgId, traceTopic);
|
||||
}
|
||||
}
|
||||
|
@@ -18,6 +18,7 @@ package org.apache.rocketmq.dashboard.controller;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import org.apache.rocketmq.dashboard.model.ConsumerMonitorConfig;
|
||||
import org.apache.rocketmq.dashboard.permisssion.Permission;
|
||||
import org.apache.rocketmq.dashboard.service.MonitorService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -29,6 +30,7 @@ import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
@Controller
|
||||
@RequestMapping("/monitor")
|
||||
@Permission
|
||||
public class MonitorController {
|
||||
|
||||
private Logger logger = LoggerFactory.getLogger(MonitorController.class);
|
||||
|
@@ -18,6 +18,7 @@ package org.apache.rocketmq.dashboard.controller;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import org.apache.rocketmq.dashboard.aspect.admin.annotation.OriginalControllerReturnValue;
|
||||
import org.apache.rocketmq.dashboard.permisssion.Permission;
|
||||
import org.apache.rocketmq.dashboard.service.OpsService;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
@@ -26,11 +27,12 @@ import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
@Controller
|
||||
@RequestMapping("/rocketmq")
|
||||
@Permission
|
||||
public class NamesvrController {
|
||||
@Resource
|
||||
private OpsService opsService;
|
||||
|
||||
@RequestMapping(value = "/nsaddr", method = RequestMethod.GET)
|
||||
@RequestMapping(value = "/nsaddr.query", method = RequestMethod.GET)
|
||||
@ResponseBody
|
||||
@OriginalControllerReturnValue
|
||||
public Object nsaddr() {
|
||||
|
@@ -16,7 +16,10 @@
|
||||
*/
|
||||
package org.apache.rocketmq.dashboard.controller;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import javax.annotation.Resource;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.rocketmq.dashboard.permisssion.Permission;
|
||||
import org.apache.rocketmq.dashboard.service.OpsService;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
@@ -26,6 +29,7 @@ import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
@Controller
|
||||
@RequestMapping("/ops")
|
||||
@Permission
|
||||
public class OpsController {
|
||||
|
||||
@Resource
|
||||
@@ -44,6 +48,15 @@ public class OpsController {
|
||||
return true;
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/addNameSvrAddr.do", method = RequestMethod.POST)
|
||||
@ResponseBody
|
||||
public Object addNameSvrAddr(@RequestParam String newNamesrvAddr) {
|
||||
Preconditions.checkArgument(StringUtils.isNotEmpty(newNamesrvAddr),
|
||||
"namesrvAddr can not be blank");
|
||||
opsService.addNameSvrAddr(newNamesrvAddr);
|
||||
return true;
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/updateIsVIPChannel.do", method = RequestMethod.POST)
|
||||
@ResponseBody
|
||||
public Object updateIsVIPChannel(@RequestParam String useVIPChannel) {
|
||||
@@ -51,14 +64,12 @@ public class OpsController {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@RequestMapping(value = "/rocketMqStatus.query", method = RequestMethod.GET)
|
||||
@ResponseBody
|
||||
public Object clusterStatus() {
|
||||
return opsService.rocketMqStatusCheck();
|
||||
}
|
||||
|
||||
|
||||
@RequestMapping(value = "/updateUseTLS.do", method = RequestMethod.POST)
|
||||
@ResponseBody
|
||||
public Object updateUseTLS(@RequestParam String useTLS) {
|
||||
|
@@ -19,6 +19,7 @@ package org.apache.rocketmq.dashboard.controller;
|
||||
import javax.annotation.Resource;
|
||||
import org.apache.rocketmq.common.protocol.body.ProducerConnection;
|
||||
import org.apache.rocketmq.dashboard.model.ConnectionInfo;
|
||||
import org.apache.rocketmq.dashboard.permisssion.Permission;
|
||||
import org.apache.rocketmq.dashboard.service.ProducerService;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
@@ -28,6 +29,7 @@ import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
@Controller
|
||||
@RequestMapping("/producer")
|
||||
@Permission
|
||||
public class ProducerController {
|
||||
|
||||
@Resource
|
||||
|
@@ -17,6 +17,7 @@
|
||||
package org.apache.rocketmq.dashboard.controller;
|
||||
|
||||
import org.apache.rocketmq.client.exception.MQClientException;
|
||||
import org.apache.rocketmq.dashboard.permisssion.Permission;
|
||||
import org.apache.rocketmq.remoting.exception.RemotingException;
|
||||
import org.apache.rocketmq.dashboard.model.request.SendTopicMessageRequest;
|
||||
import org.apache.rocketmq.dashboard.model.request.TopicConfigInfo;
|
||||
@@ -38,6 +39,7 @@ import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
@Controller
|
||||
@RequestMapping("/topic")
|
||||
@Permission
|
||||
public class TopicController {
|
||||
private Logger logger = LoggerFactory.getLogger(TopicController.class);
|
||||
|
||||
@@ -49,13 +51,9 @@ public class TopicController {
|
||||
|
||||
@RequestMapping(value = "/list.query", method = RequestMethod.GET)
|
||||
@ResponseBody
|
||||
public Object list(@RequestParam(value = "skipSysProcess", required = false) String skipSysProcess)
|
||||
throws MQClientException, RemotingException, InterruptedException {
|
||||
boolean flag = false;
|
||||
if ("true".equals(skipSysProcess)) {
|
||||
flag = true;
|
||||
}
|
||||
return topicService.fetchAllTopicList(flag);
|
||||
public Object list(@RequestParam(value = "skipSysProcess", required = false) boolean skipSysProcess,
|
||||
@RequestParam(value = "skipRetryAndDlq", required = false) boolean skipRetryAndDlq) {
|
||||
return topicService.fetchAllTopicList(skipSysProcess, skipRetryAndDlq);
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/stats.query", method = RequestMethod.GET)
|
||||
|
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* 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.model;
|
||||
|
||||
import com.alibaba.excel.annotation.ExcelProperty;
|
||||
import com.alibaba.excel.annotation.write.style.ColumnWidth;
|
||||
import com.alibaba.excel.metadata.BaseRowModel;
|
||||
import com.alibaba.excel.util.DateUtils;
|
||||
import com.google.common.base.Charsets;
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.apache.rocketmq.common.message.MessageExt;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public class DlqMessageExcelModel extends BaseRowModel implements Serializable {
|
||||
|
||||
@ExcelProperty(value = "topic", index = 0)
|
||||
@ColumnWidth(value = 15)
|
||||
private String topic;
|
||||
|
||||
@ExcelProperty(value = "msgId", index = 1)
|
||||
@ColumnWidth(value = 15)
|
||||
private String msgId;
|
||||
|
||||
@ExcelProperty(value = "bornHost", index = 2)
|
||||
@ColumnWidth(value = 15)
|
||||
private String bornHost;
|
||||
|
||||
@ExcelProperty(value = "bornTimestamp", index = 3)
|
||||
@ColumnWidth(value = 25)
|
||||
private String bornTimestamp;
|
||||
|
||||
@ExcelProperty(value = "storeTimestamp", index = 4)
|
||||
@ColumnWidth(value = 25)
|
||||
private String storeTimestamp;
|
||||
|
||||
@ExcelProperty(value = "reconsumeTimes", index = 5)
|
||||
@ColumnWidth(value = 25)
|
||||
private int reconsumeTimes;
|
||||
|
||||
@ExcelProperty(value = "properties", index = 6)
|
||||
@ColumnWidth(value = 20)
|
||||
private String properties;
|
||||
|
||||
@ExcelProperty(value = "messageBody", index = 7)
|
||||
@ColumnWidth(value = 20)
|
||||
private String messageBody;
|
||||
|
||||
@ExcelProperty(value = "bodyCRC", index = 8)
|
||||
@ColumnWidth(value = 15)
|
||||
private int bodyCRC;
|
||||
|
||||
@ExcelProperty(value = "exception", index = 9)
|
||||
@ColumnWidth(value = 30)
|
||||
private String exception;
|
||||
|
||||
public DlqMessageExcelModel(MessageExt messageExt) {
|
||||
this.topic = messageExt.getTopic();
|
||||
this.msgId = messageExt.getMsgId();
|
||||
this.bornHost = messageExt.getBornHostString();
|
||||
this.bornTimestamp = DateUtils.format(new Date(messageExt.getBornTimestamp()), DateUtils.DATE_FORMAT_19);
|
||||
this.storeTimestamp = DateUtils.format(new Date(messageExt.getStoreTimestamp()), DateUtils.DATE_FORMAT_19);
|
||||
this.reconsumeTimes = messageExt.getReconsumeTimes();
|
||||
this.properties = messageExt.getProperties().toString();
|
||||
this.messageBody = new String(messageExt.getBody(), Charsets.UTF_8);
|
||||
this.bodyCRC = messageExt.getBodyCRC();
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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.model;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class DlqMessageRequest {
|
||||
|
||||
private String topicName;
|
||||
|
||||
private String consumerGroup;
|
||||
|
||||
private String msgId;
|
||||
|
||||
private String clientId;
|
||||
}
|
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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.model;
|
||||
|
||||
import lombok.Data;
|
||||
import org.apache.rocketmq.common.protocol.body.CMResult;
|
||||
import org.apache.rocketmq.common.protocol.body.ConsumeMessageDirectlyResult;
|
||||
|
||||
@Data
|
||||
public class DlqMessageResendResult {
|
||||
private CMResult consumeResult;
|
||||
private String remark;
|
||||
private String msgId;
|
||||
|
||||
public DlqMessageResendResult(ConsumeMessageDirectlyResult consumeMessageDirectlyResult, String msgId) {
|
||||
this.consumeResult = consumeMessageDirectlyResult.getConsumeResult();
|
||||
this.remark = consumeMessageDirectlyResult.getRemark();
|
||||
this.msgId = msgId;
|
||||
}
|
||||
}
|
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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.model;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class LoginResult {
|
||||
|
||||
private String loginUserName;
|
||||
|
||||
/**
|
||||
* 0: normal 1: admin
|
||||
*/
|
||||
private int loginUserRole;
|
||||
|
||||
private String contextPath;
|
||||
}
|
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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.model.request;
|
||||
|
||||
import lombok.Data;
|
||||
import org.apache.rocketmq.common.PlainAccessConfig;
|
||||
|
||||
@Data
|
||||
public class AclRequest {
|
||||
|
||||
private PlainAccessConfig config;
|
||||
|
||||
private String topicPerm;
|
||||
|
||||
private String groupPerm;
|
||||
}
|
@@ -14,18 +14,14 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.rocketmq.dashboard.permisssion;
|
||||
|
||||
package org.apache.rocketmq.dashboard.aspect.admin.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Target({ElementType.METHOD})
|
||||
@Target({ElementType.TYPE, ElementType.METHOD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface MultiMQAdminCmdMethod {
|
||||
long timeoutMillis() default 0;
|
||||
public @interface Permission {
|
||||
}
|
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 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.permisssion;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
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.service.PermissionService;
|
||||
import org.apache.rocketmq.dashboard.util.WebUtil;
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.annotation.Around;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.aspectj.lang.annotation.Pointcut;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
|
||||
@Aspect
|
||||
@Component
|
||||
public class PermissionAspect {
|
||||
|
||||
@Resource
|
||||
private RMQConfigure configure;
|
||||
|
||||
@Resource
|
||||
private PermissionService permissionService;
|
||||
|
||||
/**
|
||||
* @Permission can be applied to the Controller class to implement Permission verification on all methods in the class
|
||||
* can also be applied to methods in a class for fine control
|
||||
*/
|
||||
@Pointcut("@annotation(org.apache.rocketmq.dashboard.permisssion.Permission) || @within(org.apache.rocketmq.dashboard.permisssion.Permission)")
|
||||
private void permission() {
|
||||
|
||||
}
|
||||
|
||||
@Around("permission()")
|
||||
public Object checkPermission(ProceedingJoinPoint joinPoint) throws Throwable {
|
||||
if (configure.isLoginRequired()) {
|
||||
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
|
||||
String url = request.getRequestURI();
|
||||
UserInfo userInfo = (UserInfo) request.getSession().getAttribute(WebUtil.USER_INFO);
|
||||
if (userInfo == null || userInfo.getUser() == null) {
|
||||
throw new ServiceException(-1, "user not login");
|
||||
}
|
||||
boolean checkResult = permissionService.checkUrlAvailable(userInfo, url);
|
||||
if (!checkResult) {
|
||||
throw new ServiceException(-1, "no permission");
|
||||
}
|
||||
}
|
||||
return joinPoint.proceed();
|
||||
}
|
||||
}
|
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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.permisssion;
|
||||
|
||||
public enum UserRoleEnum {
|
||||
ADMIN(1, "admin"),
|
||||
ORDINARY(0, "ordinary");
|
||||
|
||||
private int roleType;
|
||||
private String roleName;
|
||||
|
||||
UserRoleEnum(int roleType, String roleName) {
|
||||
this.roleType = roleType;
|
||||
this.roleName = roleName;
|
||||
}
|
||||
|
||||
public int getRoleType() {
|
||||
return roleType;
|
||||
}
|
||||
|
||||
public String getRoleName() {
|
||||
return roleName;
|
||||
}
|
||||
}
|
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import java.util.List;
|
||||
import org.apache.rocketmq.common.AclConfig;
|
||||
import org.apache.rocketmq.common.PlainAccessConfig;
|
||||
import org.apache.rocketmq.dashboard.model.request.AclRequest;
|
||||
|
||||
public interface AclService {
|
||||
|
||||
AclConfig getAclConfig(boolean excludeSecretKey);
|
||||
|
||||
void addAclConfig(PlainAccessConfig config);
|
||||
|
||||
void deleteAclConfig(PlainAccessConfig config);
|
||||
|
||||
void updateAclConfig(PlainAccessConfig config);
|
||||
|
||||
void addOrUpdateAclTopicConfig(AclRequest request);
|
||||
|
||||
void addOrUpdateAclGroupConfig(AclRequest request);
|
||||
|
||||
void deletePermConfig(AclRequest request);
|
||||
|
||||
void syncData(PlainAccessConfig config);
|
||||
|
||||
void addWhiteList(List<String> whiteList);
|
||||
|
||||
void deleteWhiteAddr(String addr);
|
||||
|
||||
void synchronizeWhiteList(List<String> whiteList);
|
||||
}
|
@@ -31,7 +31,7 @@ import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public interface ConsumerService {
|
||||
List<GroupConsumeInfo> queryGroupList();
|
||||
List<GroupConsumeInfo> queryGroupList(boolean skipSysGroup);
|
||||
|
||||
GroupConsumeInfo queryGroup(String consumerGroup);
|
||||
|
||||
|
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import java.util.List;
|
||||
import org.apache.rocketmq.dashboard.model.DlqMessageResendResult;
|
||||
import org.apache.rocketmq.dashboard.model.DlqMessageRequest;
|
||||
import org.apache.rocketmq.dashboard.model.MessagePage;
|
||||
import org.apache.rocketmq.dashboard.model.request.MessageQuery;
|
||||
|
||||
public interface DlqMessageService {
|
||||
|
||||
MessagePage queryDlqMessageByPage(MessageQuery query);
|
||||
|
||||
List<DlqMessageResendResult> batchResendDlqMessage(List<DlqMessageRequest> dlqMessages);
|
||||
}
|
@@ -27,5 +27,5 @@ public interface MessageTraceService {
|
||||
|
||||
List<MessageTraceView> queryMessageTraceByTopicAndKey(final String topic, final String key);
|
||||
|
||||
MessageTraceGraph queryMessageTraceGraph(final String key);
|
||||
MessageTraceGraph queryMessageTraceGraph(final String key, final String traceTopic);
|
||||
}
|
||||
|
@@ -31,4 +31,6 @@ public interface OpsService {
|
||||
boolean updateIsVIPChannel(String useVIPChannel);
|
||||
|
||||
boolean updateUseTLS(boolean useTLS);
|
||||
|
||||
void addNameSvrAddr(String namesrvAddr);
|
||||
}
|
||||
|
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import org.apache.rocketmq.dashboard.model.UserInfo;
|
||||
|
||||
public interface PermissionService {
|
||||
|
||||
boolean checkUrlAvailable(UserInfo userInfo, String url);
|
||||
}
|
@@ -29,7 +29,7 @@ import org.apache.rocketmq.dashboard.model.request.TopicConfigInfo;
|
||||
import java.util.List;
|
||||
|
||||
public interface TopicService {
|
||||
TopicList fetchAllTopicList(boolean skipSysProcess);
|
||||
TopicList fetchAllTopicList(boolean skipSysProcess, boolean skipRetryAndDlq);
|
||||
|
||||
TopicStatsTable stats(String topic);
|
||||
|
||||
|
@@ -91,29 +91,34 @@ public class MQAdminExtImpl implements MQAdminExt {
|
||||
MQAdminInstance.threadLocalMQAdminExt().createAndUpdateTopicConfig(addr, config);
|
||||
}
|
||||
|
||||
@Override public void createAndUpdatePlainAccessConfig(String addr,
|
||||
@Override
|
||||
public void createAndUpdatePlainAccessConfig(String addr,
|
||||
PlainAccessConfig plainAccessConfig) throws RemotingException, MQBrokerException, InterruptedException, MQClientException {
|
||||
|
||||
MQAdminInstance.threadLocalMQAdminExt().createAndUpdatePlainAccessConfig(addr, plainAccessConfig);
|
||||
}
|
||||
|
||||
@Override public void deletePlainAccessConfig(String addr,
|
||||
@Override
|
||||
public void deletePlainAccessConfig(String addr,
|
||||
String accessKey) throws RemotingException, MQBrokerException, InterruptedException, MQClientException {
|
||||
|
||||
MQAdminInstance.threadLocalMQAdminExt().deletePlainAccessConfig(addr, accessKey);
|
||||
}
|
||||
|
||||
@Override public void updateGlobalWhiteAddrConfig(String addr,
|
||||
@Override
|
||||
public void updateGlobalWhiteAddrConfig(String addr,
|
||||
String globalWhiteAddrs) throws RemotingException, MQBrokerException, InterruptedException, MQClientException {
|
||||
|
||||
MQAdminInstance.threadLocalMQAdminExt().updateGlobalWhiteAddrConfig(addr, globalWhiteAddrs);
|
||||
}
|
||||
|
||||
@Override public ClusterAclVersionInfo examineBrokerClusterAclVersionInfo(
|
||||
@Override
|
||||
public ClusterAclVersionInfo examineBrokerClusterAclVersionInfo(
|
||||
String addr) throws RemotingException, MQBrokerException, InterruptedException, MQClientException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override public AclConfig examineBrokerClusterAclConfig(
|
||||
@Override
|
||||
public AclConfig examineBrokerClusterAclConfig(
|
||||
String addr) throws RemotingException, MQBrokerException, InterruptedException, MQClientException {
|
||||
return null;
|
||||
return MQAdminInstance.threadLocalMQAdminExt().examineBrokerClusterAclConfig(addr);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -235,6 +240,12 @@ public class MQAdminExtImpl implements MQAdminExt {
|
||||
return MQAdminInstance.threadLocalMQAdminExt().wipeWritePermOfBroker(namesrvAddr, brokerName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int addWritePermOfBroker(String namesrvAddr,
|
||||
String brokerName) throws RemotingCommandException, RemotingConnectException, RemotingSendRequestException, RemotingTimeoutException, InterruptedException, MQClientException {
|
||||
return MQAdminInstance.threadLocalMQAdminExt().addWritePermOfBroker(namesrvAddr, brokerName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putKVConfig(String namespace, String key, String value) {
|
||||
MQAdminInstance.threadLocalMQAdminExt().putKVConfig(namespace, key, value);
|
||||
@@ -272,8 +283,9 @@ public class MQAdminExtImpl implements MQAdminExt {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteSubscriptionGroup(String addr, String groupName, boolean removeOffset) throws RemotingException, MQBrokerException, InterruptedException, MQClientException {
|
||||
throw new UnsupportedOperationException();
|
||||
public void deleteSubscriptionGroup(String addr, String groupName, boolean removeOffset)
|
||||
throws RemotingException, MQBrokerException, InterruptedException, MQClientException {
|
||||
MQAdminInstance.threadLocalMQAdminExt().deleteSubscriptionGroup(addr, groupName, removeOffset);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -511,9 +523,21 @@ public class MQAdminExtImpl implements MQAdminExt {
|
||||
}
|
||||
|
||||
@Override
|
||||
public TopicConfigSerializeWrapper getAllTopicGroup(String brokerAddr,
|
||||
public SubscriptionGroupWrapper getUserSubscriptionGroup(String brokerAddr,
|
||||
long timeoutMillis) throws InterruptedException, RemotingTimeoutException, RemotingSendRequestException, RemotingConnectException, MQBrokerException {
|
||||
return MQAdminInstance.threadLocalMQAdminExt().getAllTopicGroup(brokerAddr, timeoutMillis);
|
||||
return MQAdminInstance.threadLocalMQAdminExt().getUserSubscriptionGroup(brokerAddr, timeoutMillis);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TopicConfigSerializeWrapper getAllTopicConfig(String brokerAddr,
|
||||
long timeoutMillis) throws InterruptedException, RemotingTimeoutException, RemotingSendRequestException, RemotingConnectException, MQBrokerException {
|
||||
return MQAdminInstance.threadLocalMQAdminExt().getAllTopicConfig(brokerAddr, timeoutMillis);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TopicConfigSerializeWrapper getUserTopicConfig(String brokerAddr, boolean specialTopic,
|
||||
long timeoutMillis) throws InterruptedException, RemotingException, MQBrokerException, MQClientException {
|
||||
return MQAdminInstance.threadLocalMQAdminExt().getUserTopicConfig(brokerAddr, specialTopic, timeoutMillis);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -16,29 +16,27 @@
|
||||
*/
|
||||
package org.apache.rocketmq.dashboard.service.client;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.rocketmq.acl.common.AclClientRPCHook;
|
||||
import org.apache.rocketmq.acl.common.SessionCredentials;
|
||||
import org.apache.rocketmq.client.exception.MQClientException;
|
||||
import org.apache.commons.pool2.impl.GenericObjectPool;
|
||||
import org.apache.rocketmq.client.impl.MQClientAPIImpl;
|
||||
import org.apache.rocketmq.client.impl.factory.MQClientInstance;
|
||||
import org.apache.rocketmq.remoting.RPCHook;
|
||||
import org.apache.rocketmq.remoting.RemotingClient;
|
||||
import org.apache.rocketmq.tools.admin.DefaultMQAdminExt;
|
||||
import org.apache.rocketmq.tools.admin.DefaultMQAdminExtImpl;
|
||||
import org.apache.rocketmq.tools.admin.MQAdminExt;
|
||||
import org.joor.Reflect;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class MQAdminInstance {
|
||||
private static final ThreadLocal<DefaultMQAdminExt> MQ_ADMIN_EXT_THREAD_LOCAL = new ThreadLocal<DefaultMQAdminExt>();
|
||||
private static final ThreadLocal<Integer> INIT_COUNTER = new ThreadLocal<Integer>();
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(MQAdminInstance.class);
|
||||
private static final ThreadLocal<MQAdminExt> MQ_ADMIN_EXT_THREAD_LOCAL = new ThreadLocal<>();
|
||||
|
||||
public static MQAdminExt threadLocalMQAdminExt() {
|
||||
DefaultMQAdminExt defaultMQAdminExt = MQ_ADMIN_EXT_THREAD_LOCAL.get();
|
||||
if (defaultMQAdminExt == null) {
|
||||
MQAdminExt mqAdminExt = MQ_ADMIN_EXT_THREAD_LOCAL.get();
|
||||
if (mqAdminExt == null) {
|
||||
throw new IllegalStateException("defaultMQAdminExt should be init before you get this");
|
||||
}
|
||||
return defaultMQAdminExt;
|
||||
return mqAdminExt;
|
||||
}
|
||||
|
||||
public static RemotingClient threadLocalRemotingClient() {
|
||||
@@ -51,44 +49,27 @@ public class MQAdminInstance {
|
||||
DefaultMQAdminExtImpl defaultMQAdminExtImpl = Reflect.on(MQAdminInstance.threadLocalMQAdminExt()).get("defaultMQAdminExtImpl");
|
||||
return Reflect.on(defaultMQAdminExtImpl).get("mqClientInstance");
|
||||
}
|
||||
public static void initMQAdminInstance(long timeoutMillis,String accessKey,String secretKey, boolean useTLS) throws MQClientException {
|
||||
Integer nowCount = INIT_COUNTER.get();
|
||||
if (nowCount == null) {
|
||||
RPCHook rpcHook = null;
|
||||
boolean isEnableAcl = !StringUtils.isEmpty(accessKey) && !StringUtils.isEmpty(secretKey);
|
||||
if (isEnableAcl) {
|
||||
rpcHook = new AclClientRPCHook(new SessionCredentials(accessKey, secretKey));
|
||||
|
||||
public static void createMQAdmin(GenericObjectPool<MQAdminExt> mqAdminExtPool) {
|
||||
try {
|
||||
// Get the mqAdmin instance from the object pool
|
||||
MQAdminExt mqAdminExt = mqAdminExtPool.borrowObject();
|
||||
MQ_ADMIN_EXT_THREAD_LOCAL.set(mqAdminExt);
|
||||
} catch (Exception e) {
|
||||
LOGGER.error("get mqAdmin from pool error", e);
|
||||
}
|
||||
DefaultMQAdminExt defaultMQAdminExt;
|
||||
if (timeoutMillis > 0) {
|
||||
defaultMQAdminExt = new DefaultMQAdminExt(rpcHook,timeoutMillis);
|
||||
}
|
||||
else {
|
||||
defaultMQAdminExt = new DefaultMQAdminExt(rpcHook);
|
||||
}
|
||||
defaultMQAdminExt.setUseTLS(useTLS);
|
||||
defaultMQAdminExt.setInstanceName(Long.toString(System.currentTimeMillis()));
|
||||
defaultMQAdminExt.start();
|
||||
MQ_ADMIN_EXT_THREAD_LOCAL.set(defaultMQAdminExt);
|
||||
INIT_COUNTER.set(1);
|
||||
}
|
||||
else {
|
||||
INIT_COUNTER.set(nowCount + 1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static void destroyMQAdminInstance() {
|
||||
Integer nowCount = INIT_COUNTER.get() - 1;
|
||||
if (nowCount > 0) {
|
||||
INIT_COUNTER.set(nowCount);
|
||||
return;
|
||||
}
|
||||
public static void returnMQAdmin(GenericObjectPool<MQAdminExt> mqAdminExtPool) {
|
||||
MQAdminExt mqAdminExt = MQ_ADMIN_EXT_THREAD_LOCAL.get();
|
||||
if (mqAdminExt != null) {
|
||||
mqAdminExt.shutdown();
|
||||
MQ_ADMIN_EXT_THREAD_LOCAL.remove();
|
||||
INIT_COUNTER.remove();
|
||||
try {
|
||||
// After execution, return the mqAdmin instance to the object pool
|
||||
mqAdminExtPool.returnObject(mqAdminExt);
|
||||
} catch (Exception e) {
|
||||
LOGGER.error("return mqAdmin to pool error", e);
|
||||
}
|
||||
}
|
||||
MQ_ADMIN_EXT_THREAD_LOCAL.remove();
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* 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.impl;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import org.apache.rocketmq.dashboard.config.RMQConfigure;
|
||||
import org.apache.rocketmq.srvutil.FileWatchService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public abstract class AbstractFileStore {
|
||||
public final Logger log = LoggerFactory.getLogger(this.getClass());
|
||||
|
||||
public String filePath;
|
||||
|
||||
public AbstractFileStore(RMQConfigure configure, String fileName) {
|
||||
filePath = configure.getRocketMqDashboardDataPath() + File.separator + fileName;
|
||||
if (!new File(filePath).exists()) {
|
||||
// Use the default path
|
||||
InputStream inputStream = getClass().getResourceAsStream("/" + fileName);
|
||||
if (inputStream == null) {
|
||||
log.error(String.format("Can not found the file %s in Spring Boot jar", fileName));
|
||||
System.exit(1);
|
||||
} else {
|
||||
try {
|
||||
load(inputStream);
|
||||
} catch (Exception e) {
|
||||
log.error("fail to load file {}", filePath, e);
|
||||
} finally {
|
||||
try {
|
||||
inputStream.close();
|
||||
} catch (IOException e) {
|
||||
log.error("inputStream close exception", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log.info(String.format("configure file is %s", filePath));
|
||||
load();
|
||||
watch();
|
||||
}
|
||||
}
|
||||
|
||||
abstract void load(InputStream inputStream);
|
||||
|
||||
private void load() {
|
||||
load(null);
|
||||
}
|
||||
|
||||
private boolean watch() {
|
||||
try {
|
||||
FileWatchService fileWatchService = new FileWatchService(new String[] {filePath}, new FileWatchService.Listener() {
|
||||
@Override
|
||||
public void onChanged(String path) {
|
||||
log.info("The file changed, reload the context");
|
||||
load();
|
||||
}
|
||||
});
|
||||
fileWatchService.start();
|
||||
log.info("Succeed to start FileWatcherService");
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to start FileWatcherService", e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
@@ -0,0 +1,359 @@
|
||||
/*
|
||||
* 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.impl;
|
||||
|
||||
import com.google.common.base.Throwables;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.rocketmq.client.exception.MQBrokerException;
|
||||
import org.apache.rocketmq.client.exception.MQClientException;
|
||||
import org.apache.rocketmq.common.AclConfig;
|
||||
import org.apache.rocketmq.common.MixAll;
|
||||
import org.apache.rocketmq.common.PlainAccessConfig;
|
||||
import org.apache.rocketmq.common.protocol.body.ClusterInfo;
|
||||
import org.apache.rocketmq.common.protocol.route.BrokerData;
|
||||
import org.apache.rocketmq.dashboard.model.request.AclRequest;
|
||||
import org.apache.rocketmq.dashboard.service.AbstractCommonService;
|
||||
import org.apache.rocketmq.dashboard.service.AclService;
|
||||
import org.apache.rocketmq.remoting.exception.RemotingConnectException;
|
||||
import org.apache.rocketmq.remoting.exception.RemotingException;
|
||||
import org.apache.rocketmq.remoting.exception.RemotingSendRequestException;
|
||||
import org.apache.rocketmq.remoting.exception.RemotingTimeoutException;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
@Slf4j
|
||||
public class AclServiceImpl extends AbstractCommonService implements AclService {
|
||||
|
||||
@Override
|
||||
public AclConfig getAclConfig(boolean excludeSecretKey) {
|
||||
try {
|
||||
Optional<String> addr = getMasterSet().stream().findFirst();
|
||||
if (addr.isPresent()) {
|
||||
if (!excludeSecretKey) {
|
||||
return mqAdminExt.examineBrokerClusterAclConfig(addr.get());
|
||||
} else {
|
||||
AclConfig aclConfig = mqAdminExt.examineBrokerClusterAclConfig(addr.get());
|
||||
if (CollectionUtils.isNotEmpty(aclConfig.getPlainAccessConfigs())) {
|
||||
aclConfig.getPlainAccessConfigs().forEach(pac -> pac.setSecretKey(null));
|
||||
}
|
||||
return aclConfig;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("getAclConfig error.", e);
|
||||
throw Throwables.propagate(e);
|
||||
}
|
||||
AclConfig aclConfig = new AclConfig();
|
||||
aclConfig.setGlobalWhiteAddrs(Collections.emptyList());
|
||||
aclConfig.setPlainAccessConfigs(Collections.emptyList());
|
||||
return aclConfig;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addAclConfig(PlainAccessConfig config) {
|
||||
try {
|
||||
Set<String> masterSet = getMasterSet();
|
||||
|
||||
if (masterSet.isEmpty()) {
|
||||
throw new IllegalStateException("broker addr list is empty");
|
||||
}
|
||||
// check to see if account is exists
|
||||
for (String addr : masterSet) {
|
||||
AclConfig aclConfig = mqAdminExt.examineBrokerClusterAclConfig(addr);
|
||||
List<PlainAccessConfig> plainAccessConfigs = aclConfig.getPlainAccessConfigs();
|
||||
for (PlainAccessConfig pac : plainAccessConfigs) {
|
||||
if (pac.getAccessKey().equals(config.getAccessKey())) {
|
||||
throw new IllegalArgumentException(String.format("broker: %s, exist accessKey: %s", addr, config.getAccessKey()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// all broker
|
||||
for (String addr : getBrokerAddrs()) {
|
||||
mqAdminExt.createAndUpdatePlainAccessConfig(addr, config);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw Throwables.propagate(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteAclConfig(PlainAccessConfig config) {
|
||||
try {
|
||||
for (String addr : getBrokerAddrs()) {
|
||||
log.info("Start to delete acl [{}] from broker [{}]", config.getAccessKey(), addr);
|
||||
if (isExistAccessKey(config.getAccessKey(), addr)) {
|
||||
mqAdminExt.deletePlainAccessConfig(addr, config.getAccessKey());
|
||||
}
|
||||
log.info("Delete acl [{}] from broker [{}] complete", config.getAccessKey(), addr);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw Throwables.propagate(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateAclConfig(PlainAccessConfig config) {
|
||||
try {
|
||||
for (String addr : getBrokerAddrs()) {
|
||||
AclConfig aclConfig = mqAdminExt.examineBrokerClusterAclConfig(addr);
|
||||
if (aclConfig.getPlainAccessConfigs() != null) {
|
||||
PlainAccessConfig remoteConfig = null;
|
||||
for (PlainAccessConfig pac : aclConfig.getPlainAccessConfigs()) {
|
||||
if (pac.getAccessKey().equals(config.getAccessKey())) {
|
||||
remoteConfig = pac;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (remoteConfig != null) {
|
||||
remoteConfig.setSecretKey(config.getSecretKey());
|
||||
remoteConfig.setAdmin(config.isAdmin());
|
||||
config = remoteConfig;
|
||||
}
|
||||
}
|
||||
mqAdminExt.createAndUpdatePlainAccessConfig(addr, config);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw Throwables.propagate(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addOrUpdateAclTopicConfig(AclRequest request) {
|
||||
try {
|
||||
PlainAccessConfig addConfig = request.getConfig();
|
||||
for (String addr : getBrokerAddrs()) {
|
||||
AclConfig aclConfig = mqAdminExt.examineBrokerClusterAclConfig(addr);
|
||||
PlainAccessConfig remoteConfig = null;
|
||||
if (aclConfig.getPlainAccessConfigs() != null) {
|
||||
for (PlainAccessConfig config : aclConfig.getPlainAccessConfigs()) {
|
||||
if (config.getAccessKey().equals(addConfig.getAccessKey())) {
|
||||
remoteConfig = config;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (remoteConfig == null) {
|
||||
// Maybe the broker no acl config of the access key, therefore add it;
|
||||
mqAdminExt.createAndUpdatePlainAccessConfig(addr, addConfig);
|
||||
} else {
|
||||
if (remoteConfig.getTopicPerms() == null) {
|
||||
remoteConfig.setTopicPerms(new ArrayList<>());
|
||||
}
|
||||
removeExist(remoteConfig.getTopicPerms(), request.getTopicPerm().split("=")[0]);
|
||||
remoteConfig.getTopicPerms().add(request.getTopicPerm());
|
||||
mqAdminExt.createAndUpdatePlainAccessConfig(addr, remoteConfig);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw Throwables.propagate(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addOrUpdateAclGroupConfig(AclRequest request) {
|
||||
try {
|
||||
PlainAccessConfig addConfig = request.getConfig();
|
||||
for (String addr : getBrokerAddrs()) {
|
||||
AclConfig aclConfig = mqAdminExt.examineBrokerClusterAclConfig(addr);
|
||||
PlainAccessConfig remoteConfig = null;
|
||||
if (aclConfig.getPlainAccessConfigs() != null) {
|
||||
for (PlainAccessConfig config : aclConfig.getPlainAccessConfigs()) {
|
||||
if (config.getAccessKey().equals(addConfig.getAccessKey())) {
|
||||
remoteConfig = config;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (remoteConfig == null) {
|
||||
// May be the broker no acl config of the access key, therefore add it;
|
||||
mqAdminExt.createAndUpdatePlainAccessConfig(addr, addConfig);
|
||||
} else {
|
||||
if (remoteConfig.getGroupPerms() == null) {
|
||||
remoteConfig.setGroupPerms(new ArrayList<>());
|
||||
}
|
||||
removeExist(remoteConfig.getGroupPerms(), request.getGroupPerm().split("=")[0]);
|
||||
remoteConfig.getGroupPerms().add(request.getGroupPerm());
|
||||
mqAdminExt.createAndUpdatePlainAccessConfig(addr, remoteConfig);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw Throwables.propagate(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deletePermConfig(AclRequest request) {
|
||||
try {
|
||||
PlainAccessConfig deleteConfig = request.getConfig();
|
||||
|
||||
String topic = StringUtils.isNotEmpty(request.getTopicPerm()) ? request.getTopicPerm().split("=")[0] : null;
|
||||
String group = StringUtils.isNotEmpty(request.getGroupPerm()) ? request.getGroupPerm().split("=")[0] : null;
|
||||
if (deleteConfig.getTopicPerms() != null && topic != null) {
|
||||
removeExist(deleteConfig.getTopicPerms(), topic);
|
||||
}
|
||||
if (deleteConfig.getGroupPerms() != null && group != null) {
|
||||
removeExist(deleteConfig.getGroupPerms(), group);
|
||||
}
|
||||
|
||||
for (String addr : getBrokerAddrs()) {
|
||||
AclConfig aclConfig = mqAdminExt.examineBrokerClusterAclConfig(addr);
|
||||
PlainAccessConfig remoteConfig = null;
|
||||
if (aclConfig.getPlainAccessConfigs() != null) {
|
||||
for (PlainAccessConfig config : aclConfig.getPlainAccessConfigs()) {
|
||||
if (config.getAccessKey().equals(deleteConfig.getAccessKey())) {
|
||||
remoteConfig = config;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (remoteConfig == null) {
|
||||
// Maybe the broker no acl config of the access key, therefore add it;
|
||||
mqAdminExt.createAndUpdatePlainAccessConfig(addr, deleteConfig);
|
||||
} else {
|
||||
if (remoteConfig.getTopicPerms() != null && topic != null) {
|
||||
removeExist(remoteConfig.getTopicPerms(), topic);
|
||||
}
|
||||
if (remoteConfig.getGroupPerms() != null && group != null) {
|
||||
removeExist(remoteConfig.getGroupPerms(), group);
|
||||
}
|
||||
mqAdminExt.createAndUpdatePlainAccessConfig(addr, remoteConfig);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw Throwables.propagate(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void syncData(PlainAccessConfig config) {
|
||||
try {
|
||||
for (String addr : getBrokerAddrs()) {
|
||||
mqAdminExt.createAndUpdatePlainAccessConfig(addr, config);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw Throwables.propagate(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addWhiteList(List<String> whiteList) {
|
||||
if (whiteList == null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
for (String addr : getBrokerAddrs()) {
|
||||
AclConfig aclConfig = mqAdminExt.examineBrokerClusterAclConfig(addr);
|
||||
if (aclConfig.getGlobalWhiteAddrs() != null) {
|
||||
aclConfig.setGlobalWhiteAddrs(Stream.of(whiteList, aclConfig.getGlobalWhiteAddrs()).flatMap(Collection::stream).distinct().collect(Collectors.toList()));
|
||||
} else {
|
||||
aclConfig.setGlobalWhiteAddrs(whiteList);
|
||||
}
|
||||
mqAdminExt.updateGlobalWhiteAddrConfig(addr, StringUtils.join(aclConfig.getGlobalWhiteAddrs(), ","));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw Throwables.propagate(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteWhiteAddr(String deleteAddr) {
|
||||
try {
|
||||
for (String addr : getBrokerAddrs()) {
|
||||
AclConfig aclConfig = mqAdminExt.examineBrokerClusterAclConfig(addr);
|
||||
if (aclConfig.getGlobalWhiteAddrs() == null || aclConfig.getGlobalWhiteAddrs().isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
aclConfig.getGlobalWhiteAddrs().remove(deleteAddr);
|
||||
mqAdminExt.updateGlobalWhiteAddrConfig(addr, StringUtils.join(aclConfig.getGlobalWhiteAddrs(), ","));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw Throwables.propagate(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void synchronizeWhiteList(List<String> whiteList) {
|
||||
if (whiteList == null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
for (String addr : getBrokerAddrs()) {
|
||||
mqAdminExt.updateGlobalWhiteAddrConfig(addr, StringUtils.join(whiteList, ","));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw Throwables.propagate(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void removeExist(List<String> list, String name) {
|
||||
Iterator<String> iterator = list.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
String v = iterator.next();
|
||||
String cmp = v.split("=")[0];
|
||||
if (cmp.equals(name)) {
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isExistAccessKey(String accessKey,
|
||||
String addr) throws InterruptedException, RemotingException, MQClientException, MQBrokerException {
|
||||
AclConfig aclConfig = mqAdminExt.examineBrokerClusterAclConfig(addr);
|
||||
List<PlainAccessConfig> plainAccessConfigs = aclConfig.getPlainAccessConfigs();
|
||||
if (plainAccessConfigs == null || plainAccessConfigs.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
for (PlainAccessConfig config : plainAccessConfigs) {
|
||||
if (accessKey.equals(config.getAccessKey())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private Set<BrokerData> getBrokerDataSet() throws InterruptedException, RemotingConnectException, RemotingTimeoutException, RemotingSendRequestException, MQBrokerException {
|
||||
ClusterInfo clusterInfo = mqAdminExt.examineBrokerClusterInfo();
|
||||
Map<String, BrokerData> brokerDataMap = clusterInfo.getBrokerAddrTable();
|
||||
return new HashSet<>(brokerDataMap.values());
|
||||
}
|
||||
|
||||
private Set<String> getMasterSet() throws InterruptedException, MQBrokerException, RemotingTimeoutException, RemotingSendRequestException, RemotingConnectException {
|
||||
return getBrokerDataSet().stream().map(data -> data.getBrokerAddrs().get(MixAll.MASTER_ID)).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
private Set<String> getBrokerAddrs() throws InterruptedException, MQBrokerException, RemotingTimeoutException, RemotingSendRequestException, RemotingConnectException {
|
||||
Set<String> brokerAddrs = new HashSet<>();
|
||||
getBrokerDataSet().forEach(data -> brokerAddrs.addAll(data.getBrokerAddrs().values()));
|
||||
return brokerAddrs;
|
||||
}
|
||||
}
|
@@ -23,14 +23,27 @@ import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.RejectedExecutionHandler;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.annotation.Resource;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.rocketmq.client.exception.MQClientException;
|
||||
import org.apache.rocketmq.common.MQVersion;
|
||||
import org.apache.rocketmq.common.MixAll;
|
||||
import org.apache.rocketmq.common.admin.ConsumeStats;
|
||||
import org.apache.rocketmq.common.admin.RollbackStats;
|
||||
import org.apache.rocketmq.common.message.MessageQueue;
|
||||
@@ -43,7 +56,8 @@ import org.apache.rocketmq.common.protocol.body.GroupList;
|
||||
import org.apache.rocketmq.common.protocol.body.SubscriptionGroupWrapper;
|
||||
import org.apache.rocketmq.common.protocol.route.BrokerData;
|
||||
import org.apache.rocketmq.common.subscription.SubscriptionGroupConfig;
|
||||
import org.apache.rocketmq.dashboard.aspect.admin.annotation.MultiMQAdminCmdMethod;
|
||||
import org.apache.rocketmq.common.utils.ThreadUtils;
|
||||
import org.apache.rocketmq.dashboard.config.RMQConfigure;
|
||||
import org.apache.rocketmq.dashboard.model.ConsumerGroupRollBackStat;
|
||||
import org.apache.rocketmq.dashboard.model.GroupConsumeInfo;
|
||||
import org.apache.rocketmq.dashboard.model.QueueStatInfo;
|
||||
@@ -55,17 +69,59 @@ import org.apache.rocketmq.dashboard.service.AbstractCommonService;
|
||||
import org.apache.rocketmq.dashboard.service.ConsumerService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import static com.google.common.base.Throwables.propagate;
|
||||
|
||||
@Service
|
||||
public class ConsumerServiceImpl extends AbstractCommonService implements ConsumerService {
|
||||
public class ConsumerServiceImpl extends AbstractCommonService implements ConsumerService, InitializingBean, DisposableBean {
|
||||
private Logger logger = LoggerFactory.getLogger(ConsumerServiceImpl.class);
|
||||
|
||||
@Resource
|
||||
private RMQConfigure configure;
|
||||
|
||||
private static final Set<String> SYSTEM_GROUP_SET = new HashSet<>();
|
||||
|
||||
private ExecutorService executorService;
|
||||
|
||||
@Override
|
||||
@MultiMQAdminCmdMethod
|
||||
public List<GroupConsumeInfo> queryGroupList() {
|
||||
public void afterPropertiesSet() {
|
||||
Runtime runtime = Runtime.getRuntime();
|
||||
int corePoolSize = Math.max(10, runtime.availableProcessors() * 2);
|
||||
int maximumPoolSize = Math.max(20, runtime.availableProcessors() * 2);
|
||||
ThreadFactory threadFactory = new ThreadFactory() {
|
||||
private final AtomicLong threadIndex = new AtomicLong(0);
|
||||
|
||||
@Override
|
||||
public Thread newThread(Runnable r) {
|
||||
return new Thread(r, "QueryGroup_" + this.threadIndex.incrementAndGet());
|
||||
}
|
||||
};
|
||||
RejectedExecutionHandler handler = new ThreadPoolExecutor.DiscardOldestPolicy();
|
||||
this.executorService = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, 60L, TimeUnit.SECONDS,
|
||||
new LinkedBlockingQueue<>(5000), threadFactory, handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
ThreadUtils.shutdownGracefully(executorService, 10L, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
static {
|
||||
SYSTEM_GROUP_SET.add(MixAll.TOOLS_CONSUMER_GROUP);
|
||||
SYSTEM_GROUP_SET.add(MixAll.FILTERSRV_CONSUMER_GROUP);
|
||||
SYSTEM_GROUP_SET.add(MixAll.SELF_TEST_CONSUMER_GROUP);
|
||||
SYSTEM_GROUP_SET.add(MixAll.ONS_HTTP_PROXY_GROUP);
|
||||
SYSTEM_GROUP_SET.add(MixAll.CID_ONSAPI_PULL_GROUP);
|
||||
SYSTEM_GROUP_SET.add(MixAll.CID_ONSAPI_PERMISSION_GROUP);
|
||||
SYSTEM_GROUP_SET.add(MixAll.CID_ONSAPI_OWNER_GROUP);
|
||||
SYSTEM_GROUP_SET.add(MixAll.CID_SYS_RMQ_TRANS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<GroupConsumeInfo> queryGroupList(boolean skipSysGroup) {
|
||||
Set<String> consumerGroupSet = Sets.newHashSet();
|
||||
try {
|
||||
ClusterInfo clusterInfo = mqAdminExt.examineBrokerClusterInfo();
|
||||
@@ -77,16 +133,39 @@ public class ConsumerServiceImpl extends AbstractCommonService implements Consum
|
||||
catch (Exception err) {
|
||||
throw Throwables.propagate(err);
|
||||
}
|
||||
List<GroupConsumeInfo> groupConsumeInfoList = Lists.newArrayList();
|
||||
List<GroupConsumeInfo> groupConsumeInfoList = Collections.synchronizedList(Lists.newArrayList());
|
||||
CountDownLatch countDownLatch = new CountDownLatch(consumerGroupSet.size());
|
||||
for (String consumerGroup : consumerGroupSet) {
|
||||
groupConsumeInfoList.add(queryGroup(consumerGroup));
|
||||
executorService.submit(() -> {
|
||||
try {
|
||||
GroupConsumeInfo consumeInfo = queryGroup(consumerGroup);
|
||||
groupConsumeInfoList.add(consumeInfo);
|
||||
} catch (Exception e) {
|
||||
logger.error("queryGroup exception, consumerGroup: {}", consumerGroup, e);
|
||||
} finally {
|
||||
countDownLatch.countDown();
|
||||
}
|
||||
});
|
||||
}
|
||||
try {
|
||||
countDownLatch.await(30, TimeUnit.SECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
logger.error("query consumerGroup countDownLatch await Exception", e);
|
||||
}
|
||||
|
||||
if (!skipSysGroup) {
|
||||
groupConsumeInfoList.stream().map(group -> {
|
||||
if (SYSTEM_GROUP_SET.contains(group.getGroup())) {
|
||||
group.setGroup(String.format("%s%s", "%SYS%", group.getGroup()));
|
||||
}
|
||||
return group;
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
Collections.sort(groupConsumeInfoList);
|
||||
return groupConsumeInfoList;
|
||||
}
|
||||
|
||||
@Override
|
||||
@MultiMQAdminCmdMethod
|
||||
public GroupConsumeInfo queryGroup(String consumerGroup) {
|
||||
GroupConsumeInfo groupConsumeInfo = new GroupConsumeInfo();
|
||||
try {
|
||||
@@ -133,7 +212,6 @@ public class ConsumerServiceImpl extends AbstractCommonService implements Consum
|
||||
}
|
||||
|
||||
@Override
|
||||
@MultiMQAdminCmdMethod
|
||||
public List<TopicConsumerInfo> queryConsumeStatsList(final String topic, String groupName) {
|
||||
ConsumeStats consumeStats = null;
|
||||
try {
|
||||
@@ -184,7 +262,6 @@ public class ConsumerServiceImpl extends AbstractCommonService implements Consum
|
||||
}
|
||||
|
||||
@Override
|
||||
@MultiMQAdminCmdMethod
|
||||
public Map<String /*groupName*/, TopicConsumerInfo> queryConsumeStatsListByTopicName(String topic) {
|
||||
Map<String, TopicConsumerInfo> group2ConsumerInfoMap = Maps.newHashMap();
|
||||
try {
|
||||
@@ -206,7 +283,6 @@ public class ConsumerServiceImpl extends AbstractCommonService implements Consum
|
||||
}
|
||||
|
||||
@Override
|
||||
@MultiMQAdminCmdMethod
|
||||
public Map<String, ConsumerGroupRollBackStat> resetOffset(ResetOffsetRequest resetOffsetRequest) {
|
||||
Map<String, ConsumerGroupRollBackStat> groupRollbackStats = Maps.newHashMap();
|
||||
for (String consumerGroup : resetOffsetRequest.getConsumerGroupList()) {
|
||||
@@ -251,7 +327,6 @@ public class ConsumerServiceImpl extends AbstractCommonService implements Consum
|
||||
}
|
||||
|
||||
@Override
|
||||
@MultiMQAdminCmdMethod
|
||||
public List<ConsumerConfigInfo> examineSubscriptionGroupConfig(String group) {
|
||||
List<ConsumerConfigInfo> consumerConfigInfoList = Lists.newArrayList();
|
||||
try {
|
||||
@@ -272,13 +347,22 @@ public class ConsumerServiceImpl extends AbstractCommonService implements Consum
|
||||
}
|
||||
|
||||
@Override
|
||||
@MultiMQAdminCmdMethod
|
||||
public boolean deleteSubGroup(DeleteSubGroupRequest deleteSubGroupRequest) {
|
||||
Set<String> brokerSet = this.fetchBrokerNameSetBySubscriptionGroup(deleteSubGroupRequest.getGroupName());
|
||||
List<String> brokerList = deleteSubGroupRequest.getBrokerNameList();
|
||||
boolean deleteInNsFlag = false;
|
||||
// If the list of brokers passed in by the request contains the list of brokers that the consumer is in, delete RETRY and DLQ topic in namesrv
|
||||
if (brokerList.containsAll(brokerSet)) {
|
||||
deleteInNsFlag = true;
|
||||
}
|
||||
try {
|
||||
ClusterInfo clusterInfo = mqAdminExt.examineBrokerClusterInfo();
|
||||
for (String brokerName : deleteSubGroupRequest.getBrokerNameList()) {
|
||||
logger.info("addr={} groupName={}", clusterInfo.getBrokerAddrTable().get(brokerName).selectBrokerAddr(), deleteSubGroupRequest.getGroupName());
|
||||
mqAdminExt.deleteSubscriptionGroup(clusterInfo.getBrokerAddrTable().get(brokerName).selectBrokerAddr(), deleteSubGroupRequest.getGroupName());
|
||||
mqAdminExt.deleteSubscriptionGroup(clusterInfo.getBrokerAddrTable().get(brokerName).selectBrokerAddr(), deleteSubGroupRequest.getGroupName(), true);
|
||||
// Delete %RETRY%+Group and %DLQ%+Group in broker and namesrv
|
||||
deleteResources(MixAll.RETRY_GROUP_TOPIC_PREFIX + deleteSubGroupRequest.getGroupName(), brokerName, clusterInfo, deleteInNsFlag);
|
||||
deleteResources(MixAll.DLQ_GROUP_TOPIC_PREFIX + deleteSubGroupRequest.getGroupName(), brokerName, clusterInfo, deleteInNsFlag);
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
@@ -287,6 +371,18 @@ public class ConsumerServiceImpl extends AbstractCommonService implements Consum
|
||||
return true;
|
||||
}
|
||||
|
||||
private void deleteResources(String topic, String brokerName, ClusterInfo clusterInfo, boolean deleteInNsFlag) throws Exception {
|
||||
mqAdminExt.deleteTopicInBroker(Sets.newHashSet(clusterInfo.getBrokerAddrTable().get(brokerName).selectBrokerAddr()), topic);
|
||||
Set<String> nameServerSet = null;
|
||||
if (StringUtils.isNotBlank(configure.getNamesrvAddr())) {
|
||||
String[] ns = configure.getNamesrvAddr().split(";");
|
||||
nameServerSet = new HashSet<>(Arrays.asList(ns));
|
||||
}
|
||||
if (deleteInNsFlag) {
|
||||
mqAdminExt.deleteTopicInNameServer(nameServerSet, topic);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean createAndUpdateSubscriptionGroupConfig(ConsumerConfigInfo consumerConfigInfo) {
|
||||
try {
|
||||
@@ -303,7 +399,6 @@ public class ConsumerServiceImpl extends AbstractCommonService implements Consum
|
||||
}
|
||||
|
||||
@Override
|
||||
@MultiMQAdminCmdMethod
|
||||
public Set<String> fetchBrokerNameSetBySubscriptionGroup(String group) {
|
||||
Set<String> brokerNameSet = Sets.newHashSet();
|
||||
try {
|
||||
|
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* 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.impl;
|
||||
|
||||
import com.google.common.base.Throwables;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import javax.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.rocketmq.client.exception.MQClientException;
|
||||
import org.apache.rocketmq.common.MixAll;
|
||||
import org.apache.rocketmq.common.protocol.ResponseCode;
|
||||
import org.apache.rocketmq.common.protocol.body.ConsumeMessageDirectlyResult;
|
||||
import org.apache.rocketmq.dashboard.model.DlqMessageResendResult;
|
||||
import org.apache.rocketmq.dashboard.model.DlqMessageRequest;
|
||||
import org.apache.rocketmq.dashboard.model.MessagePage;
|
||||
import org.apache.rocketmq.dashboard.model.MessageView;
|
||||
import org.apache.rocketmq.dashboard.model.request.MessageQuery;
|
||||
import org.apache.rocketmq.dashboard.service.DlqMessageService;
|
||||
import org.apache.rocketmq.dashboard.service.MessageService;
|
||||
import org.apache.rocketmq.tools.admin.MQAdminExt;
|
||||
import org.springframework.data.domain.PageImpl;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
@Slf4j
|
||||
public class DlqMessageServiceImpl implements DlqMessageService {
|
||||
|
||||
@Resource
|
||||
private MQAdminExt mqAdminExt;
|
||||
|
||||
@Resource
|
||||
private MessageService messageService;
|
||||
|
||||
@Override
|
||||
public MessagePage queryDlqMessageByPage(MessageQuery query) {
|
||||
List<MessageView> messageViews = new ArrayList<>();
|
||||
PageRequest page = PageRequest.of(query.getPageNum(), query.getPageSize());
|
||||
String topic = query.getTopic();
|
||||
try {
|
||||
mqAdminExt.examineTopicRouteInfo(topic);
|
||||
} catch (MQClientException e) {
|
||||
// If the %DLQ%Group does not exist, the message returns null
|
||||
if (topic.startsWith(MixAll.DLQ_GROUP_TOPIC_PREFIX)
|
||||
&& e.getResponseCode() == ResponseCode.TOPIC_NOT_EXIST) {
|
||||
return new MessagePage(new PageImpl<>(messageViews, page, 0), query.getTaskId());
|
||||
} else {
|
||||
throw Throwables.propagate(e);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw Throwables.propagate(e);
|
||||
}
|
||||
return messageService.queryMessageByPage(query);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DlqMessageResendResult> batchResendDlqMessage(List<DlqMessageRequest> dlqMessages) {
|
||||
List<DlqMessageResendResult> batchResendResults = new LinkedList<>();
|
||||
for (DlqMessageRequest dlqMessage : dlqMessages) {
|
||||
ConsumeMessageDirectlyResult result = messageService.consumeMessageDirectly(dlqMessage.getTopicName(),
|
||||
dlqMessage.getMsgId(), dlqMessage.getConsumerGroup(),
|
||||
dlqMessage.getClientId());
|
||||
DlqMessageResendResult resendResult = new DlqMessageResendResult(result, dlqMessage.getMsgId());
|
||||
batchResendResults.add(resendResult);
|
||||
}
|
||||
return batchResendResults;
|
||||
}
|
||||
}
|
@@ -31,6 +31,7 @@ import org.apache.rocketmq.acl.common.SessionCredentials;
|
||||
import org.apache.rocketmq.client.consumer.DefaultMQPullConsumer;
|
||||
import org.apache.rocketmq.client.consumer.PullResult;
|
||||
import org.apache.rocketmq.client.consumer.PullStatus;
|
||||
import org.apache.rocketmq.client.exception.MQClientException;
|
||||
import org.apache.rocketmq.common.MixAll;
|
||||
import org.apache.rocketmq.common.Pair;
|
||||
import org.apache.rocketmq.common.message.MessageClientIDSetter;
|
||||
@@ -111,6 +112,9 @@ public class MessageServiceImpl implements MessageService {
|
||||
}
|
||||
});
|
||||
} catch (Exception err) {
|
||||
if (err instanceof MQClientException) {
|
||||
throw new ServiceException(-1, ((MQClientException) err).getErrorMessage());
|
||||
}
|
||||
throw Throwables.propagate(err);
|
||||
}
|
||||
}
|
||||
@@ -325,7 +329,7 @@ public class MessageServiceImpl implements MessageService {
|
||||
List<MessageExt> msgFoundList = pullResult.getMsgFoundList();
|
||||
for (int i = msgFoundList.size() - 1; i >= 0; i--) {
|
||||
MessageExt messageExt = msgFoundList.get(i);
|
||||
if (messageExt.getStoreTimestamp() < query.getBegin()) {
|
||||
if (messageExt.getStoreTimestamp() > query.getEnd()) {
|
||||
end--;
|
||||
} else {
|
||||
hasIllegalOffset = false;
|
||||
|
@@ -20,8 +20,6 @@ package org.apache.rocketmq.dashboard.service.impl;
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import com.google.common.base.Throwables;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -33,7 +31,9 @@ import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.rocketmq.client.trace.TraceType;
|
||||
import org.apache.rocketmq.common.Pair;
|
||||
import org.apache.rocketmq.common.message.MessageExt;
|
||||
import org.apache.rocketmq.common.topic.TopicValidator;
|
||||
import org.apache.rocketmq.dashboard.config.RMQConfigure;
|
||||
import org.apache.rocketmq.dashboard.exception.ServiceException;
|
||||
import org.apache.rocketmq.dashboard.model.MessageTraceView;
|
||||
import org.apache.rocketmq.dashboard.model.trace.ProducerNode;
|
||||
import org.apache.rocketmq.dashboard.model.trace.MessageTraceGraph;
|
||||
@@ -65,7 +65,7 @@ public class MessageTraceServiceImpl implements MessageTraceService {
|
||||
|
||||
@Override
|
||||
public List<MessageTraceView> queryMessageTraceKey(String key) {
|
||||
String queryTopic = configure.getMsgTrackTopicNameOrDefault();
|
||||
String queryTopic = TopicValidator.RMQ_SYS_TRACE_TOPIC;
|
||||
logger.info("query data topic name is:{}", queryTopic);
|
||||
return queryMessageTraceByTopicAndKey(queryTopic, key);
|
||||
}
|
||||
@@ -81,13 +81,16 @@ public class MessageTraceServiceImpl implements MessageTraceService {
|
||||
}
|
||||
return messageTraceViews;
|
||||
} catch (Exception err) {
|
||||
throw Throwables.propagate(err);
|
||||
throw new ServiceException(-1, String.format("Failed to query message trace by msgId %s", key));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageTraceGraph queryMessageTraceGraph(String key) {
|
||||
List<MessageTraceView> messageTraceViews = queryMessageTraceKey(key);
|
||||
public MessageTraceGraph queryMessageTraceGraph(String key, String topic) {
|
||||
if (StringUtils.isEmpty(topic)) {
|
||||
topic = TopicValidator.RMQ_SYS_TRACE_TOPIC;
|
||||
}
|
||||
List<MessageTraceView> messageTraceViews = queryMessageTraceByTopicAndKey(topic, key);
|
||||
return buildMessageTraceGraph(messageTraceViews);
|
||||
}
|
||||
|
||||
|
@@ -16,16 +16,18 @@
|
||||
*/
|
||||
package org.apache.rocketmq.dashboard.service.impl;
|
||||
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.collect.Maps;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.annotation.Resource;
|
||||
import org.apache.commons.pool2.impl.GenericObjectPool;
|
||||
import org.apache.rocketmq.dashboard.config.RMQConfigure;
|
||||
import org.apache.rocketmq.dashboard.service.AbstractCommonService;
|
||||
import org.apache.rocketmq.dashboard.service.OpsService;
|
||||
import org.apache.rocketmq.dashboard.service.checker.CheckerType;
|
||||
import org.apache.rocketmq.dashboard.service.checker.RocketMqChecker;
|
||||
import org.apache.rocketmq.tools.admin.MQAdminExt;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
@@ -34,13 +36,17 @@ public class OpsServiceImpl extends AbstractCommonService implements OpsService
|
||||
@Resource
|
||||
private RMQConfigure configure;
|
||||
|
||||
@Autowired
|
||||
private GenericObjectPool<MQAdminExt> mqAdminExtPool;
|
||||
|
||||
@Resource
|
||||
private List<RocketMqChecker> rocketMqCheckerList;
|
||||
|
||||
@Override
|
||||
public Map<String, Object> homePageInfo() {
|
||||
Map<String, Object> homePageInfoMap = Maps.newHashMap();
|
||||
homePageInfoMap.put("namesvrAddrList", Splitter.on(";").splitToList(configure.getNamesrvAddr()));
|
||||
homePageInfoMap.put("currentNamesrv", configure.getNamesrvAddr());
|
||||
homePageInfoMap.put("namesvrAddrList", configure.getNamesrvAddrs());
|
||||
homePageInfoMap.put("useVIPChannel", Boolean.valueOf(configure.getIsVIPChannel()));
|
||||
homePageInfoMap.put("useTLS", configure.isUseTLS());
|
||||
return homePageInfoMap;
|
||||
@@ -49,6 +55,8 @@ public class OpsServiceImpl extends AbstractCommonService implements OpsService
|
||||
@Override
|
||||
public void updateNameSvrAddrList(String nameSvrAddrList) {
|
||||
configure.setNamesrvAddr(nameSvrAddrList);
|
||||
// when update namesrvAddr, clean the mqAdminExt objects pool.
|
||||
mqAdminExtPool.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -65,14 +73,26 @@ public class OpsServiceImpl extends AbstractCommonService implements OpsService
|
||||
return checkResultMap;
|
||||
}
|
||||
|
||||
@Override public boolean updateIsVIPChannel(String useVIPChannel) {
|
||||
@Override
|
||||
public boolean updateIsVIPChannel(String useVIPChannel) {
|
||||
configure.setIsVIPChannel(useVIPChannel);
|
||||
mqAdminExtPool.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updateUseTLS(boolean useTLS) {
|
||||
configure.setUseTLS(useTLS);
|
||||
mqAdminExtPool.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addNameSvrAddr(String namesrvAddr) {
|
||||
List<String> namesrvAddrs = configure.getNamesrvAddrs();
|
||||
if (namesrvAddrs != null && !namesrvAddrs.contains(namesrvAddr)) {
|
||||
namesrvAddrs.add(namesrvAddr);
|
||||
}
|
||||
configure.setNamesrvAddrs(namesrvAddrs);
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* 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.impl;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import java.io.FileReader;
|
||||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import javax.annotation.Resource;
|
||||
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.service.PermissionService;
|
||||
import org.apache.rocketmq.dashboard.util.MatcherUtil;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
|
||||
import static org.apache.rocketmq.dashboard.permisssion.UserRoleEnum.ADMIN;
|
||||
import static org.apache.rocketmq.dashboard.permisssion.UserRoleEnum.ORDINARY;
|
||||
|
||||
@Service
|
||||
public class PermissionServiceImpl implements PermissionService, InitializingBean {
|
||||
|
||||
@Resource
|
||||
private RMQConfigure configure;
|
||||
|
||||
private PermissionFileStore permissionFileStore;
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
if (configure.isLoginRequired()) {
|
||||
permissionFileStore = new PermissionFileStore(configure);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkUrlAvailable(UserInfo userInfo, String url) {
|
||||
int type = userInfo.getUser().getType();
|
||||
// if it is admin, it could access all resources
|
||||
if (type == ADMIN.getRoleType()) {
|
||||
return true;
|
||||
}
|
||||
String loginUserRole = ORDINARY.getRoleName();
|
||||
Map<String, List<String>> rolePerms = PermissionFileStore.rolePerms;
|
||||
List<String> perms = rolePerms.get(loginUserRole);
|
||||
for (String perm : perms) {
|
||||
if (MatcherUtil.match(perm, url)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static class PermissionFileStore extends AbstractFileStore {
|
||||
private static final String FILE_NAME = "role-permission.yml";
|
||||
|
||||
private static Map<String/**role**/, List<String>/**accessUrls**/> rolePerms = new ConcurrentHashMap<>();
|
||||
|
||||
public PermissionFileStore(RMQConfigure configure) {
|
||||
super(configure, FILE_NAME);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load(InputStream inputStream) {
|
||||
Yaml yaml = new Yaml();
|
||||
JSONObject rolePermsData = null;
|
||||
try {
|
||||
if (inputStream == null) {
|
||||
rolePermsData = yaml.loadAs(new FileReader(filePath), JSONObject.class);
|
||||
} else {
|
||||
rolePermsData = yaml.loadAs(inputStream, JSONObject.class);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("load user-permission.yml failed", e);
|
||||
throw new ServiceException(0, String.format("Failed to load rolePermission property file: %s", filePath));
|
||||
}
|
||||
rolePerms.clear();
|
||||
rolePerms.putAll(rolePermsData.getObject("rolePerms", Map.class));
|
||||
}
|
||||
}
|
||||
}
|
@@ -20,6 +20,7 @@ package org.apache.rocketmq.dashboard.service.impl;
|
||||
import com.google.common.base.Throwables;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Sets;
|
||||
import java.util.stream.Collectors;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.rocketmq.acl.common.AclClientRPCHook;
|
||||
import org.apache.rocketmq.acl.common.SessionCredentials;
|
||||
@@ -36,6 +37,7 @@ import org.apache.rocketmq.common.protocol.body.GroupList;
|
||||
import org.apache.rocketmq.common.protocol.body.TopicList;
|
||||
import org.apache.rocketmq.common.protocol.route.BrokerData;
|
||||
import org.apache.rocketmq.common.protocol.route.TopicRouteData;
|
||||
import org.apache.rocketmq.common.topic.TopicValidator;
|
||||
import org.apache.rocketmq.dashboard.config.RMQConfigure;
|
||||
import org.apache.rocketmq.dashboard.model.request.SendTopicMessageRequest;
|
||||
import org.apache.rocketmq.dashboard.model.request.TopicConfigInfo;
|
||||
@@ -61,23 +63,23 @@ public class TopicServiceImpl extends AbstractCommonService implements TopicServ
|
||||
private RMQConfigure configure;
|
||||
|
||||
@Override
|
||||
public TopicList fetchAllTopicList(boolean skipSysProcess) {
|
||||
public TopicList fetchAllTopicList(boolean skipSysProcess, boolean skipRetryAndDlq) {
|
||||
try {
|
||||
TopicList allTopics = mqAdminExt.fetchAllTopicList();
|
||||
if (skipSysProcess) {
|
||||
return allTopics;
|
||||
}
|
||||
|
||||
TopicList sysTopics = getSystemTopicList();
|
||||
Set<String> topics = new HashSet<>();
|
||||
|
||||
for (String topic : allTopics.getTopicList()) {
|
||||
if (sysTopics.getTopicList().contains(topic)) {
|
||||
topics.add(String.format("%s%s", "%SYS%", topic));
|
||||
} else {
|
||||
topics.add(topic);
|
||||
Set<String> topics =
|
||||
allTopics.getTopicList().stream().map(topic -> {
|
||||
if (!skipSysProcess && sysTopics.getTopicList().contains(topic)) {
|
||||
topic = String.format("%s%s", "%SYS%", topic);
|
||||
}
|
||||
return topic;
|
||||
}).filter(topic -> {
|
||||
if (skipRetryAndDlq) {
|
||||
return !(topic.startsWith(MixAll.RETRY_GROUP_TOPIC_PREFIX)
|
||||
|| topic.startsWith(MixAll.DLQ_GROUP_TOPIC_PREFIX));
|
||||
}
|
||||
return true;
|
||||
}).collect(Collectors.toSet());
|
||||
allTopics.getTopicList().clear();
|
||||
allTopics.getTopicList().addAll(topics);
|
||||
return allTopics;
|
||||
@@ -133,10 +135,10 @@ public class TopicServiceImpl extends AbstractCommonService implements TopicServ
|
||||
ClusterInfo clusterInfo = null;
|
||||
try {
|
||||
clusterInfo = mqAdminExt.examineBrokerClusterInfo();
|
||||
return mqAdminExt.examineTopicConfig(clusterInfo.getBrokerAddrTable().get(brokerName).selectBrokerAddr(), topic);
|
||||
} catch (Exception e) {
|
||||
throw Throwables.propagate(e);
|
||||
}
|
||||
return mqAdminExt.examineTopicConfig(clusterInfo.getBrokerAddrTable().get(brokerName).selectBrokerAddr(), topic);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -209,7 +211,7 @@ public class TopicServiceImpl extends AbstractCommonService implements TopicServ
|
||||
}
|
||||
|
||||
public DefaultMQProducer buildDefaultMQProducer(String producerGroup, RPCHook rpcHook, boolean traceEnabled) {
|
||||
DefaultMQProducer defaultMQProducer = new DefaultMQProducer(producerGroup, rpcHook, traceEnabled, configure.getMsgTrackTopicNameOrDefault());
|
||||
DefaultMQProducer defaultMQProducer = new DefaultMQProducer(producerGroup, rpcHook, traceEnabled, TopicValidator.RMQ_SYS_TRACE_TOPIC);
|
||||
defaultMQProducer.setUseTLS(configure.isUseTLS());
|
||||
return defaultMQProducer;
|
||||
}
|
||||
|
@@ -21,15 +21,11 @@ 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.UserService;
|
||||
import org.apache.rocketmq.srvutil.FileWatchService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.InputStream;
|
||||
import java.util.HashMap;
|
||||
@@ -40,9 +36,9 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
@Service
|
||||
public class UserServiceImpl implements UserService, InitializingBean {
|
||||
@Resource
|
||||
RMQConfigure configure;
|
||||
private RMQConfigure configure;
|
||||
|
||||
FileBasedUserInfoStore fileBasedUserInfoStore;
|
||||
private FileBasedUserInfoStore fileBasedUserInfoStore;
|
||||
|
||||
@Override
|
||||
public User queryByName(String name) {
|
||||
@@ -61,40 +57,17 @@ public class UserServiceImpl implements UserService, InitializingBean {
|
||||
}
|
||||
}
|
||||
|
||||
public static class FileBasedUserInfoStore {
|
||||
private final Logger log = LoggerFactory.getLogger(this.getClass());
|
||||
public static class FileBasedUserInfoStore extends AbstractFileStore {
|
||||
private static final String FILE_NAME = "users.properties";
|
||||
|
||||
private String filePath;
|
||||
private final Map<String, User> userMap = new ConcurrentHashMap<>();
|
||||
|
||||
private static Map<String, User> userMap = new ConcurrentHashMap<>();
|
||||
|
||||
public FileBasedUserInfoStore(RMQConfigure configure) {
|
||||
filePath = configure.getRocketMqDashboardDataPath() + File.separator + FILE_NAME;
|
||||
if (!new File(filePath).exists()) {
|
||||
//Use the default path
|
||||
InputStream inputStream = getClass().getResourceAsStream("/" + FILE_NAME);
|
||||
if (inputStream == null) {
|
||||
log.error(String.format("Can not found the file %s in Spring Boot jar", FILE_NAME));
|
||||
System.out.printf(String.format("Can not found file %s in Spring Boot jar or %s, stop the dashboard starting",
|
||||
FILE_NAME, configure.getRocketMqDashboardDataPath()));
|
||||
System.exit(1);
|
||||
} else {
|
||||
load(inputStream);
|
||||
}
|
||||
} else {
|
||||
log.info(String.format("Login Users configure file is %s", filePath));
|
||||
load();
|
||||
watch();
|
||||
}
|
||||
super(configure, FILE_NAME);
|
||||
}
|
||||
|
||||
private void load() {
|
||||
load(null);
|
||||
}
|
||||
|
||||
private void load(InputStream inputStream) {
|
||||
|
||||
@Override
|
||||
public void load(InputStream inputStream) {
|
||||
Properties prop = new Properties();
|
||||
try {
|
||||
if (inputStream == null) {
|
||||
@@ -112,7 +85,8 @@ public class UserServiceImpl implements UserService, InitializingBean {
|
||||
int role;
|
||||
for (String key : prop.stringPropertyNames()) {
|
||||
String v = prop.getProperty(key);
|
||||
if (v == null) continue;
|
||||
if (v == null)
|
||||
continue;
|
||||
arrs = v.split(",", 2);
|
||||
if (arrs.length == 0) {
|
||||
continue;
|
||||
@@ -125,30 +99,10 @@ public class UserServiceImpl implements UserService, InitializingBean {
|
||||
loadUserMap.put(key, new User(key, arrs[0].trim(), role));
|
||||
}
|
||||
|
||||
|
||||
userMap.clear();
|
||||
userMap.putAll(loadUserMap);
|
||||
}
|
||||
|
||||
private boolean watch() {
|
||||
try {
|
||||
FileWatchService fileWatchService = new FileWatchService(new String[]{filePath}, new FileWatchService.Listener() {
|
||||
@Override
|
||||
public void onChanged(String path) {
|
||||
log.info("The loginUserInfo property file changed, reload the context");
|
||||
load();
|
||||
}
|
||||
});
|
||||
fileWatchService.start();
|
||||
log.info("Succeed to start LoginUserWatcherService");
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to start LoginUserWatcherService", e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public User queryByName(String name) {
|
||||
return userMap.get(name);
|
||||
}
|
||||
@@ -158,7 +112,6 @@ public class UserServiceImpl implements UserService, InitializingBean {
|
||||
if (user != null && password.equals(user.getPassword())) {
|
||||
return user.cloneOne();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@@ -34,11 +34,12 @@ public class GlobalExceptionHandler {
|
||||
public JsonResult<Object> jsonErrorHandler(HttpServletRequest req, Exception ex) throws Exception {
|
||||
JsonResult<Object> value = null;
|
||||
if (ex != null) {
|
||||
logger.error("op=global_exception_handler_print_error", ex);
|
||||
if (ex instanceof ServiceException) {
|
||||
logger.error("Occur service exception: {}", ex.getMessage());
|
||||
value = new JsonResult<Object>(((ServiceException) ex).getCode(), ex.getMessage());
|
||||
}
|
||||
else {
|
||||
logger.error("op=global_exception_handler_print_error", ex);
|
||||
value = new JsonResult<Object>(-1, ex.getMessage() == null ? ex.toString() : ex.getMessage());
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* 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.task;
|
||||
|
||||
import com.google.common.base.Throwables;
|
||||
import com.google.common.collect.Lists;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.rocketmq.common.MixAll;
|
||||
import org.apache.rocketmq.common.protocol.body.BrokerStatsData;
|
||||
import org.apache.rocketmq.common.protocol.body.GroupList;
|
||||
import org.apache.rocketmq.common.protocol.route.BrokerData;
|
||||
import org.apache.rocketmq.common.protocol.route.TopicRouteData;
|
||||
import org.apache.rocketmq.dashboard.service.DashboardCollectService;
|
||||
import org.apache.rocketmq.store.stats.BrokerStatsManager;
|
||||
import org.apache.rocketmq.tools.admin.MQAdminExt;
|
||||
import org.apache.rocketmq.tools.command.stats.StatsAllSubCommand;
|
||||
|
||||
@Slf4j
|
||||
public class CollectTaskRunnble implements Runnable {
|
||||
|
||||
private String topic;
|
||||
|
||||
private MQAdminExt mqAdminExt;
|
||||
|
||||
private DashboardCollectService dashboardCollectService;
|
||||
|
||||
public CollectTaskRunnble(String topic, MQAdminExt mqAdminExt,
|
||||
DashboardCollectService dashboardCollectService) {
|
||||
this.topic = topic;
|
||||
this.mqAdminExt = mqAdminExt;
|
||||
this.dashboardCollectService = dashboardCollectService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
Date date = new Date();
|
||||
try {
|
||||
TopicRouteData topicRouteData = mqAdminExt.examineTopicRouteInfo(topic);
|
||||
GroupList groupList = mqAdminExt.queryTopicConsumeByWho(topic);
|
||||
double inTPS = 0;
|
||||
long inMsgCntToday = 0;
|
||||
double outTPS = 0;
|
||||
long outMsgCntToday = 0;
|
||||
for (BrokerData bd : topicRouteData.getBrokerDatas()) {
|
||||
String masterAddr = bd.getBrokerAddrs().get(MixAll.MASTER_ID);
|
||||
if (masterAddr != null) {
|
||||
try {
|
||||
BrokerStatsData bsd = mqAdminExt.viewBrokerStatsData(masterAddr, BrokerStatsManager.TOPIC_PUT_NUMS, topic);
|
||||
inTPS += bsd.getStatsMinute().getTps();
|
||||
inMsgCntToday += StatsAllSubCommand.compute24HourSum(bsd);
|
||||
} catch (Exception e) {
|
||||
log.warn("Exception caught: mqAdminExt get broker stats data TOPIC_PUT_NUMS failed, topic [{}]", topic, e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (groupList != null && !groupList.getGroupList().isEmpty()) {
|
||||
for (String group : groupList.getGroupList()) {
|
||||
for (BrokerData bd : topicRouteData.getBrokerDatas()) {
|
||||
String masterAddr = bd.getBrokerAddrs().get(MixAll.MASTER_ID);
|
||||
if (masterAddr != null) {
|
||||
try {
|
||||
String statsKey = String.format("%s@%s", topic, group);
|
||||
BrokerStatsData bsd = mqAdminExt.viewBrokerStatsData(masterAddr, BrokerStatsManager.GROUP_GET_NUMS, statsKey);
|
||||
outTPS += bsd.getStatsMinute().getTps();
|
||||
outMsgCntToday += StatsAllSubCommand.compute24HourSum(bsd);
|
||||
} catch (Exception e) {
|
||||
log.warn("Exception caught: mqAdminExt get broker stats data GROUP_GET_NUMS failed, topic [{}], group [{}]", topic, group, e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<String> list;
|
||||
try {
|
||||
list = dashboardCollectService.getTopicMap().get(topic);
|
||||
} catch (ExecutionException e) {
|
||||
throw Throwables.propagate(e);
|
||||
}
|
||||
if (null == list) {
|
||||
list = Lists.newArrayList();
|
||||
}
|
||||
|
||||
list.add(date.getTime() + "," + new BigDecimal(inTPS).setScale(5, BigDecimal.ROUND_HALF_UP) + "," + inMsgCntToday + "," + new BigDecimal(outTPS).setScale(5, BigDecimal.ROUND_HALF_UP) + "," + outMsgCntToday);
|
||||
dashboardCollectService.getTopicMap().put(topic, list);
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to collect topic: {} data", topic, e);
|
||||
}
|
||||
}
|
||||
}
|
@@ -16,18 +16,6 @@
|
||||
*/
|
||||
package org.apache.rocketmq.dashboard.task;
|
||||
|
||||
import com.google.common.base.Stopwatch;
|
||||
import org.apache.rocketmq.common.protocol.body.ClusterInfo;
|
||||
import org.apache.rocketmq.common.protocol.body.GroupList;
|
||||
import org.apache.rocketmq.common.protocol.body.KVTable;
|
||||
import org.apache.rocketmq.common.protocol.body.TopicList;
|
||||
import org.apache.rocketmq.common.protocol.route.BrokerData;
|
||||
import org.apache.rocketmq.common.protocol.route.TopicRouteData;
|
||||
import org.apache.rocketmq.common.topic.TopicValidator;
|
||||
import org.apache.rocketmq.dashboard.aspect.admin.annotation.MultiMQAdminCmdMethod;
|
||||
import org.apache.rocketmq.store.stats.BrokerStatsManager;
|
||||
import org.apache.rocketmq.tools.admin.MQAdminExt;
|
||||
import org.apache.rocketmq.tools.command.stats.StatsAllSubCommand;
|
||||
import com.google.common.base.Throwables;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import com.google.common.collect.Lists;
|
||||
@@ -43,13 +31,18 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import javax.annotation.Resource;
|
||||
import org.apache.rocketmq.common.MixAll;
|
||||
import org.apache.rocketmq.common.protocol.body.BrokerStatsData;
|
||||
import org.apache.rocketmq.common.protocol.body.ClusterInfo;
|
||||
import org.apache.rocketmq.common.protocol.body.KVTable;
|
||||
import org.apache.rocketmq.common.protocol.body.TopicList;
|
||||
import org.apache.rocketmq.common.protocol.route.BrokerData;
|
||||
import org.apache.rocketmq.common.topic.TopicValidator;
|
||||
import org.apache.rocketmq.dashboard.config.RMQConfigure;
|
||||
import org.apache.rocketmq.dashboard.service.DashboardCollectService;
|
||||
import org.apache.rocketmq.dashboard.util.JsonUtil;
|
||||
import org.apache.rocketmq.tools.admin.MQAdminExt;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
@@ -68,15 +61,14 @@ public class DashboardCollectTask {
|
||||
|
||||
private final static Logger log = LoggerFactory.getLogger(DashboardCollectTask.class);
|
||||
|
||||
@Resource
|
||||
private ExecutorService collectExecutor;
|
||||
|
||||
@Scheduled(cron = "30 0/1 * * * ?")
|
||||
@MultiMQAdminCmdMethod(timeoutMillis = 5000)
|
||||
public void collectTopic() {
|
||||
if (!rmqConfigure.isEnableDashBoardCollect()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Date date = new Date();
|
||||
Stopwatch stopwatch = Stopwatch.createUnstarted();
|
||||
try {
|
||||
TopicList topicList = mqAdminExt.fetchAllTopicList();
|
||||
Set<String> topicSet = topicList.getTopicList();
|
||||
@@ -87,77 +79,9 @@ public class DashboardCollectTask {
|
||||
|| TopicValidator.isSystemTopic(topic)) {
|
||||
continue;
|
||||
}
|
||||
TopicRouteData topicRouteData = mqAdminExt.examineTopicRouteInfo(topic);
|
||||
|
||||
GroupList groupList = mqAdminExt.queryTopicConsumeByWho(topic);
|
||||
|
||||
double inTPS = 0;
|
||||
|
||||
long inMsgCntToday = 0;
|
||||
|
||||
double outTPS = 0;
|
||||
|
||||
long outMsgCntToday = 0;
|
||||
|
||||
for (BrokerData bd : topicRouteData.getBrokerDatas()) {
|
||||
String masterAddr = bd.getBrokerAddrs().get(MixAll.MASTER_ID);
|
||||
if (masterAddr != null) {
|
||||
try {
|
||||
stopwatch.start();
|
||||
log.info("start time: {}", stopwatch.toString());
|
||||
BrokerStatsData bsd = mqAdminExt.viewBrokerStatsData(masterAddr, BrokerStatsManager.TOPIC_PUT_NUMS, topic);
|
||||
stopwatch.stop();
|
||||
log.info("stop time : {}", stopwatch.toString());
|
||||
|
||||
inTPS += bsd.getStatsMinute().getTps();
|
||||
inMsgCntToday += StatsAllSubCommand.compute24HourSum(bsd);
|
||||
CollectTaskRunnble collectTask = new CollectTaskRunnble(topic, mqAdminExt, dashboardCollectService);
|
||||
collectExecutor.submit(collectTask);
|
||||
}
|
||||
catch (Exception e) {
|
||||
stopwatch.reset();
|
||||
log.warn("Exception caught: mqAdminExt get broker stats data TOPIC_PUT_NUMS failed");
|
||||
log.warn("Response [{}] ", e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (groupList != null && !groupList.getGroupList().isEmpty()) {
|
||||
|
||||
for (String group : groupList.getGroupList()) {
|
||||
for (BrokerData bd : topicRouteData.getBrokerDatas()) {
|
||||
String masterAddr = bd.getBrokerAddrs().get(MixAll.MASTER_ID);
|
||||
if (masterAddr != null) {
|
||||
try {
|
||||
String statsKey = String.format("%s@%s", topic, group);
|
||||
BrokerStatsData bsd = mqAdminExt.viewBrokerStatsData(masterAddr, BrokerStatsManager.GROUP_GET_NUMS, statsKey);
|
||||
outTPS += bsd.getStatsMinute().getTps();
|
||||
outMsgCntToday += StatsAllSubCommand.compute24HourSum(bsd);
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.warn("Exception caught: mqAdminExt get broker stats data GROUP_GET_NUMS failed");
|
||||
log.warn("Response [{}] ", e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<String> list;
|
||||
try {
|
||||
list = dashboardCollectService.getTopicMap().get(topic);
|
||||
}
|
||||
catch (ExecutionException e) {
|
||||
throw Throwables.propagate(e);
|
||||
}
|
||||
if (null == list) {
|
||||
list = Lists.newArrayList();
|
||||
}
|
||||
|
||||
list.add(date.getTime() + "," + new BigDecimal(inTPS).setScale(5, BigDecimal.ROUND_HALF_UP) + "," + inMsgCntToday + "," + new BigDecimal(outTPS).setScale(5, BigDecimal.ROUND_HALF_UP) + "," + outMsgCntToday);
|
||||
dashboardCollectService.getTopicMap().put(topic, list);
|
||||
|
||||
}
|
||||
|
||||
log.debug("Topic Collected Data in memory = {}" + JsonUtil.obj2String(dashboardCollectService.getTopicMap().asMap()));
|
||||
}
|
||||
catch (Exception err) {
|
||||
throw Throwables.propagate(err);
|
||||
@@ -330,6 +254,7 @@ public class DashboardCollectTask {
|
||||
for (Map.Entry<String, Set<String>> entry : clusterTable.entrySet()) {
|
||||
String clusterName = entry.getKey();
|
||||
TopicValidator.addSystemTopic(clusterName);
|
||||
TopicValidator.addSystemTopic(MixAll.getReplyTopic(clusterName));
|
||||
Set<String> brokerNames = entry.getValue();
|
||||
for (String brokerName : brokerNames) {
|
||||
TopicValidator.addSystemTopic(brokerName);
|
||||
|
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* 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.util;
|
||||
|
||||
import com.alibaba.excel.EasyExcel;
|
||||
import com.alibaba.excel.support.ExcelTypeEnum;
|
||||
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
|
||||
import com.alibaba.excel.write.metadata.style.WriteFont;
|
||||
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
|
||||
import java.io.OutputStream;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.List;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import org.apache.poi.ss.usermodel.HorizontalAlignment;
|
||||
|
||||
public class ExcelUtil {
|
||||
|
||||
public static void writeExcel(HttpServletResponse response, List<? extends Object> data, String fileName,
|
||||
String sheetName, Class clazz) throws Exception {
|
||||
WriteCellStyle headWriteCellStyle = new WriteCellStyle();
|
||||
WriteFont writeFont = new WriteFont();
|
||||
writeFont.setFontHeightInPoints((short)12);
|
||||
writeFont.setFontName("Microsoft YaHei UI");
|
||||
headWriteCellStyle.setWriteFont(writeFont);
|
||||
headWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
|
||||
|
||||
WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
|
||||
contentWriteCellStyle.setWriteFont(writeFont);
|
||||
contentWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
|
||||
HorizontalCellStyleStrategy horizontalCellStyleStrategy = new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle);
|
||||
EasyExcel.write(getOutputStream(fileName, response), clazz)
|
||||
.excelType(ExcelTypeEnum.XLSX).sheet(sheetName).registerWriteHandler(horizontalCellStyleStrategy).doWrite(data);
|
||||
}
|
||||
|
||||
private static OutputStream getOutputStream(String fileName, HttpServletResponse response) throws Exception {
|
||||
fileName = URLEncoder.encode(fileName, "UTF-8");
|
||||
response.setContentType("application/vnd.ms-excel");
|
||||
response.setCharacterEncoding("utf8");
|
||||
response.setHeader("Content-Disposition", "attachment;filename=" + fileName + ".xlsx");
|
||||
return response.getOutputStream();
|
||||
}
|
||||
}
|
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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.util;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class MatcherUtil {
|
||||
|
||||
public static boolean match(String accessUrl, String reqPath) {
|
||||
String regPath = getRegPath(accessUrl);
|
||||
return Pattern.compile(regPath).matcher(reqPath).matches();
|
||||
}
|
||||
|
||||
private static String getRegPath(String path) {
|
||||
char[] chars = path.toCharArray();
|
||||
int len = chars.length;
|
||||
StringBuilder sb = new StringBuilder();
|
||||
boolean preX = false;
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (chars[i] == '*') {
|
||||
if (preX) {
|
||||
sb.append(".*");
|
||||
preX = false;
|
||||
} else if (i + 1 == len) {
|
||||
sb.append("[^/]*");
|
||||
} else {
|
||||
preX = true;
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
if (preX) {
|
||||
sb.append("[^/]*");
|
||||
preX = false;
|
||||
}
|
||||
if (chars[i] == '?') {
|
||||
sb.append('.');
|
||||
} else {
|
||||
sb.append(chars[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
@@ -1,35 +0,0 @@
|
||||
server.address=0.0.0.0
|
||||
server.port=8080
|
||||
|
||||
### SSL setting
|
||||
#server.ssl.key-store=classpath:rmqcngkeystore.jks
|
||||
#server.ssl.key-store-password=rocketmq
|
||||
#server.ssl.keyStoreType=PKCS12
|
||||
#server.ssl.keyAlias=rmqcngkey
|
||||
|
||||
#spring.application.index=true
|
||||
spring.application.name=rocketmq-dashboard
|
||||
spring.http.encoding.charset=UTF-8
|
||||
spring.http.encoding.enabled=true
|
||||
spring.http.encoding.force=true
|
||||
logging.level.root=INFO
|
||||
logging.config=classpath:logback.xml
|
||||
#if this value is empty,use env value rocketmq.config.namesrvAddr NAMESRV_ADDR | now, you can set it in ops page.default localhost:9876
|
||||
rocketmq.config.namesrvAddr=
|
||||
#if you use rocketmq version < 3.5.8, rocketmq.config.isVIPChannel should be false.default true
|
||||
rocketmq.config.isVIPChannel=
|
||||
#rocketmq-console's data path:dashboard/monitor
|
||||
rocketmq.config.dataPath=/tmp/rocketmq-console/data
|
||||
#set it false if you don't want use dashboard.default true
|
||||
rocketmq.config.enableDashBoardCollect=true
|
||||
#set the message track trace topic if you don't want use the default one
|
||||
rocketmq.config.msgTrackTopicName=
|
||||
rocketmq.config.ticketKey=ticket
|
||||
|
||||
#Must create userInfo file: ${rocketmq.config.dataPath}/users.properties if the login is required
|
||||
rocketmq.config.loginRequired=false
|
||||
|
||||
#set the accessKey and secretKey if you used acl
|
||||
#rocketmq.config.accessKey=
|
||||
#rocketmq.config.secretKey=
|
||||
rocketmq.config.useTLS=false
|
69
src/main/resources/application.yml
Normal file
69
src/main/resources/application.yml
Normal file
@@ -0,0 +1,69 @@
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
server:
|
||||
port: 8080
|
||||
servlet:
|
||||
encoding:
|
||||
charset: UTF-8
|
||||
enabled: true
|
||||
force: true
|
||||
## SSL setting
|
||||
# ssl:
|
||||
# key-store: classpath:rmqcngkeystore.jks
|
||||
# key-store-password: rocketmq
|
||||
# key-store-type: PKCS12
|
||||
# key-alias: rmqcngkey
|
||||
|
||||
spring:
|
||||
application:
|
||||
name: rocketmq-dashboard
|
||||
|
||||
logging:
|
||||
config: classpath:logback.xml
|
||||
|
||||
rocketmq:
|
||||
config:
|
||||
# if this value is empty,use env value rocketmq.config.namesrvAddr NAMESRV_ADDR | now, default localhost:9876
|
||||
# configure multiple namesrv addresses to manage multiple different clusters
|
||||
namesrvAddrs:
|
||||
- 127.0.0.1:9876
|
||||
- 127.0.0.2:9876
|
||||
# if you use rocketmq version < 3.5.8, rocketmq.config.isVIPChannel should be false.default true
|
||||
isVIPChannel:
|
||||
# timeout for mqadminExt, default 5000ms
|
||||
timeoutMillis:
|
||||
# rocketmq-console's data path:dashboard/monitor
|
||||
dataPath: /tmp/rocketmq-console/data
|
||||
# set it false if you don't want use dashboard.default true
|
||||
enableDashBoardCollect: true
|
||||
# set the message track trace topic if you don't want use the default one
|
||||
msgTrackTopicName:
|
||||
ticketKey: ticket
|
||||
# must create userInfo file: ${rocketmq.config.dataPath}/users.properties if the login is required
|
||||
loginRequired: false
|
||||
useTLS: false
|
||||
# set the accessKey and secretKey if you used acl
|
||||
accessKey: # if version > 4.4.0
|
||||
secretKey: # if version > 4.4.0
|
||||
|
||||
threadpool:
|
||||
config:
|
||||
coreSize: 10
|
||||
maxSize: 10
|
||||
keepAliveTime: 3000
|
||||
queueSize: 5000
|
@@ -1,17 +1,34 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ 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.
|
||||
-->
|
||||
|
||||
<configuration>
|
||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder charset="UTF-8">
|
||||
<pattern>[%d{yyyy-MM-dd HH:mm:ss.SSS}] %5p %m%n</pattern>
|
||||
<pattern>[%d{yyyy-MM-dd HH:mm:ss.SSS}] %p %t - %m%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<appender name="FILE"
|
||||
class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>${user.home}/logs/consolelogs/rocketmq-console.log</file>
|
||||
<file>${user.home}/logs/dashboardlogs/rocketmq-dashboard.log</file>
|
||||
<append>true</append>
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<fileNamePattern>${user.home}/logs/consolelogs/rocketmq-console-%d{yyyy-MM-dd}.%i.log
|
||||
<fileNamePattern>${user.home}/logs/dashboardlogs/rocketmq-dashboard-%d{yyyy-MM-dd}.%i.log
|
||||
</fileNamePattern>
|
||||
<timeBasedFileNamingAndTriggeringPolicy
|
||||
class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
|
||||
@@ -20,7 +37,7 @@
|
||||
<MaxHistory>10</MaxHistory>
|
||||
</rollingPolicy>
|
||||
<encoder>
|
||||
<pattern>[%d{yyyy-MM-dd HH:mm:ss.SSS}] %5p %m%n</pattern>
|
||||
<pattern>[%d{yyyy-MM-dd HH:mm:ss.SSS}] %p %t - %m%n</pattern>
|
||||
<charset class="java.nio.charset.Charset">UTF-8</charset>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
40
src/main/resources/role-permission.yml
Normal file
40
src/main/resources/role-permission.yml
Normal file
@@ -0,0 +1,40 @@
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
# This file supports hot change, any change will be auto-reloaded without Console restarting.
|
||||
# the interface paths can be configured with wildcard characters.
|
||||
# ?: Matches 1 characters.
|
||||
# *: Matches 0 or more characters that are not /.
|
||||
# **: Matches 0 or more characters.
|
||||
|
||||
rolePerms:
|
||||
ordinary:
|
||||
- /rocketmq/*.query
|
||||
- /ops/*.query
|
||||
- /dashboard/*.query
|
||||
- /topic/*.query
|
||||
- /topic/sendTopicMessage.do
|
||||
- /producer/*.query
|
||||
- /message/*.query
|
||||
- /messageTrace/*.query
|
||||
- /monitor/*.query
|
||||
- /consumer/*.query
|
||||
- /cluster/*.query
|
||||
- /dlqMessage/*.query
|
||||
- /dlqMessage/exportDlqMessage.do
|
||||
- /dlqMessage/batchResendDlqMessage.do
|
||||
- /acl/*.query
|
@@ -41,8 +41,6 @@
|
||||
<link rel="stylesheet" href="vendor/ng-dialog/ngDialog-theme-default.css">
|
||||
<link rel="stylesheet" href="vendor/dropdown/jquery.dropdown.css">
|
||||
<link rel="stylesheet" href="vendor/datatimepicker/bootstrap-datetimepicker.min.css">
|
||||
<link rel="stylesheet" href="vendor/font-awesome-4.7.0/css/font-awesome.min.css">
|
||||
<link rel="stylesheet" href="vendor/font-awesome-4.7.0/fonts/fontawesome-webfont.svg">
|
||||
<link rel="stylesheet" type="text/css" href="vendor/chosen/chosen.css"/>
|
||||
<link rel="stylesheet" type="text/css" href="vendor/chosen/chosen-spinner.css"/>
|
||||
<link rel="stylesheet" type="text/css" href="vendor/angular-material/angular-material.min.css"/>
|
||||
@@ -109,10 +107,12 @@
|
||||
<script type="text/javascript" src="src/consumer.js?timestamp=6"></script>
|
||||
<script type="text/javascript" src="src/producer.js"></script>
|
||||
<script type="text/javascript" src="src/message.js"></script>
|
||||
<script type="text/javascript" src="src/dlqMessage.js"></script>
|
||||
<script type="text/javascript" src="src/messageTrace.js"></script>
|
||||
<script type="text/javascript" src="src/ops.js?timestamp=7"></script>
|
||||
<script type="text/javascript" src="src/remoteApi/remoteApi.js"></script>
|
||||
<script type="text/javascript" src="vendor/preLoading/main.js"></script>
|
||||
<script type="text/javascript" src="src/login.js"></script>
|
||||
<script type="text/javascript" src="src/acl.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
540
src/main/resources/static/src/acl.js
Normal file
540
src/main/resources/static/src/acl.js
Normal file
@@ -0,0 +1,540 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
var module = app;
|
||||
|
||||
module.controller('aclController', ['$scope', 'ngDialog', '$http', 'Notification', '$window', function ($scope, ngDialog, $http, Notification, $window) {
|
||||
$scope.paginationConf = {
|
||||
currentPage: 1,
|
||||
totalItems: 0,
|
||||
itemsPerPage: 10,
|
||||
pagesLength: 15,
|
||||
perPageOptions: [10],
|
||||
rememberPerPage: 'perPageItems',
|
||||
onChange: function () {
|
||||
$scope.showPlainAccessConfigs(this.currentPage, this.totalItems);
|
||||
}
|
||||
};
|
||||
|
||||
$scope.plainAccessConfigs = [];
|
||||
$scope.allPlainAccessConfigs = [];
|
||||
$scope.globalWhiteAddrs = [];
|
||||
$scope.allGlobalWhiteAddrs = [];
|
||||
$scope.userRole = $window.sessionStorage.getItem("userrole");
|
||||
$scope.writeOperationEnabled = $scope.userRole == null ? true : ($scope.userRole == 1 ? true : false);
|
||||
$scope.showSecretKeyType = {};
|
||||
|
||||
$scope.refreshPlainAccessConfigs = function () {
|
||||
$http({
|
||||
method: "GET",
|
||||
url: "acl/config.query",
|
||||
params: {}
|
||||
}).success(function (resp) {
|
||||
|
||||
// globalWhiteAddrs
|
||||
// plainAccessConfigs
|
||||
if (resp.status == 0) {
|
||||
$scope.allPlainAccessConfigs = resp.data.plainAccessConfigs;
|
||||
$scope.allGlobalWhiteAddrs = resp.data.globalWhiteAddrs;
|
||||
$scope.showSecretKeyType = {};
|
||||
$scope.allPlainAccessConfigs.forEach(e => $scope.showSecretKeyType[e.accessKey] = {
|
||||
type: 'password',
|
||||
action: 'SHOW'
|
||||
});
|
||||
$scope.showPlainAccessConfigs(1, $scope.allPlainAccessConfigs.length);
|
||||
} else {
|
||||
Notification.error({message: resp.errMsg, delay: 2000});
|
||||
}
|
||||
});
|
||||
}
|
||||
$scope.refreshPlainAccessConfigs();
|
||||
$scope.filterStr = "";
|
||||
$scope.$watch('filterStr', function () {
|
||||
$scope.paginationConf.currentPage = 1;
|
||||
$scope.filterList(1);
|
||||
});
|
||||
|
||||
$scope.filterList = function (currentPage) {
|
||||
var lowExceptStr = $scope.filterStr.toLowerCase();
|
||||
var canShowList = [];
|
||||
|
||||
$scope.allPlainAccessConfigs.forEach(function (element) {
|
||||
if (element.accessKey.toLowerCase().indexOf(lowExceptStr) != -1) {
|
||||
canShowList.push(element);
|
||||
}
|
||||
});
|
||||
$scope.paginationConf.totalItems = canShowList.length;
|
||||
var perPage = $scope.paginationConf.itemsPerPage;
|
||||
var from = (currentPage - 1) * perPage;
|
||||
var to = (from + perPage) > canShowList.length ? canShowList.length : from + perPage;
|
||||
$scope.plainAccessConfigs = canShowList.slice(from, to);
|
||||
};
|
||||
|
||||
$scope.showPlainAccessConfigs = function (currentPage, totalItem) {
|
||||
var perPage = $scope.paginationConf.itemsPerPage;
|
||||
var from = (currentPage - 1) * perPage;
|
||||
var to = (from + perPage) > totalItem ? totalItem : from + perPage;
|
||||
$scope.plainAccessConfigs = $scope.allPlainAccessConfigs.slice(from, to);
|
||||
$scope.paginationConf.totalItems = totalItem;
|
||||
$scope.filterList($scope.paginationConf.currentPage)
|
||||
};
|
||||
|
||||
|
||||
// add acl account
|
||||
$scope.openAddDialog = function () {
|
||||
var request = {};
|
||||
request.accessKey = '';
|
||||
request.secretKey = '';
|
||||
request.admin = false;
|
||||
request.defaultTopicPerm = 'DENY';
|
||||
request.defaultGroupPerm = 'SUB';
|
||||
ngDialog.open({
|
||||
preCloseCallback: function (value) {
|
||||
$scope.refreshPlainAccessConfigs();
|
||||
},
|
||||
template: 'addAclAccountDialog',
|
||||
controller: 'addAclAccountDialogController',
|
||||
data: request
|
||||
});
|
||||
}
|
||||
|
||||
$scope.deleteAclConfig = function (accessKey) {
|
||||
$http({
|
||||
method: "POST",
|
||||
url: "acl/delete.do",
|
||||
data: {accessKey: accessKey}
|
||||
}).success(function (resp) {
|
||||
if (resp.status == 0) {
|
||||
Notification.info({message: "success!", delay: 2000});
|
||||
$scope.refreshPlainAccessConfigs();
|
||||
} else {
|
||||
Notification.error({message: resp.errMsg, delay: 2000});
|
||||
}
|
||||
});
|
||||
}
|
||||
$scope.openUpdateDialog = function (request) {
|
||||
ngDialog.open({
|
||||
preCloseCallback: function (value) {
|
||||
$scope.refreshPlainAccessConfigs();
|
||||
},
|
||||
template: 'updateAclAccountDialog',
|
||||
controller: 'updateAclAccountDialogController',
|
||||
data: request
|
||||
});
|
||||
}
|
||||
|
||||
// add acl topic permission
|
||||
$scope.openAddTopicDialog = function (request) {
|
||||
$.extend(request, {pub: true, sub: true, deny: false})
|
||||
ngDialog.open({
|
||||
preCloseCallback: function (value) {
|
||||
$scope.refreshPlainAccessConfigs();
|
||||
},
|
||||
template: 'addAclTopicDialog',
|
||||
controller: 'addAclTopicDialogController',
|
||||
data: request
|
||||
});
|
||||
}
|
||||
|
||||
// update acl topic permission
|
||||
$scope.openUpdateTopicDialog = function (request, topic) {
|
||||
var perm = {pub: false, sub: false, deny: false};
|
||||
var topicInfo = topic.split('=');
|
||||
$.each(topicInfo[1].split('|'), function (i, e) {
|
||||
switch (e) {
|
||||
case 'PUB':
|
||||
perm.pub = true;
|
||||
break;
|
||||
case 'SUB':
|
||||
perm.sub = true;
|
||||
break;
|
||||
case 'DENY':
|
||||
perm.deny = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
$.extend(request, perm, {topic: topicInfo[0]});
|
||||
ngDialog.open({
|
||||
preCloseCallback: function (value) {
|
||||
$scope.refreshPlainAccessConfigs();
|
||||
},
|
||||
template: 'updateAclTopicDialog',
|
||||
controller: 'updateAclTopicDialogController',
|
||||
data: request
|
||||
});
|
||||
}
|
||||
|
||||
// add acl group permission
|
||||
$scope.openAddGroupDialog = function (request) {
|
||||
$.extend(request, {pub: true, sub: true, deny: false})
|
||||
ngDialog.open({
|
||||
preCloseCallback: function (value) {
|
||||
$scope.refreshPlainAccessConfigs();
|
||||
},
|
||||
template: 'addAclGroupDialog',
|
||||
controller: 'addAclGroupDialogController',
|
||||
data: request
|
||||
});
|
||||
}
|
||||
|
||||
// update acl group permission
|
||||
$scope.openUpdateGroupDialog = function (request, group) {
|
||||
var perm = {pub: false, sub: false, deny: false};
|
||||
var groupInfo = group.split('=');
|
||||
$.each(groupInfo[1].split('|'), function (i, e) {
|
||||
switch (e) {
|
||||
case 'PUB':
|
||||
perm.pub = true;
|
||||
break;
|
||||
case 'SUB':
|
||||
perm.sub = true;
|
||||
break;
|
||||
case 'DENY':
|
||||
perm.deny = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
$.extend(request, perm, {group: groupInfo[0]});
|
||||
ngDialog.open({
|
||||
preCloseCallback: function (value) {
|
||||
$scope.refreshPlainAccessConfigs();
|
||||
},
|
||||
template: 'updateAclGroupDialog',
|
||||
controller: 'updateAclGroupDialogController',
|
||||
data: request
|
||||
});
|
||||
}
|
||||
|
||||
$scope.deletePermConfig = function (config, name, type) {
|
||||
var request = {config: config};
|
||||
switch (type) {
|
||||
case 'topic':
|
||||
request.topicPerm = name;
|
||||
break;
|
||||
case 'group':
|
||||
request.groupPerm = name;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
$http({
|
||||
method: "POST",
|
||||
url: "acl/perm/delete.do",
|
||||
data: request
|
||||
}).success(function (resp) {
|
||||
if (resp.status == 0) {
|
||||
Notification.info({message: "success!", delay: 2000});
|
||||
$scope.refreshPlainAccessConfigs();
|
||||
} else {
|
||||
Notification.error({message: resp.errMsg, delay: 2000});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$scope.synchronizeData = function (request) {
|
||||
$http({
|
||||
method: "POST",
|
||||
url: "acl/sync.do",
|
||||
data: request
|
||||
}).success(function (resp) {
|
||||
if (resp.status == 0) {
|
||||
Notification.info({message: "success!", delay: 2000});
|
||||
$scope.refreshPlainAccessConfigs();
|
||||
} else {
|
||||
Notification.error({message: resp.errMsg, delay: 2000});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$scope.openAddAddrDialog = function () {
|
||||
ngDialog.open({
|
||||
preCloseCallback: function (value) {
|
||||
$scope.refreshPlainAccessConfigs();
|
||||
},
|
||||
template: 'addWhiteListDialog',
|
||||
controller: 'addWhiteListDialogController'
|
||||
});
|
||||
}
|
||||
|
||||
$scope.deleteGlobalWhiteAddr = function (request) {
|
||||
$http({
|
||||
method: "DELETE",
|
||||
url: "acl/white/list/delete.do?request=" + request
|
||||
}).success(function (resp) {
|
||||
if (resp.status == 0) {
|
||||
Notification.info({message: "success!", delay: 2000});
|
||||
$scope.refreshPlainAccessConfigs();
|
||||
} else {
|
||||
Notification.error({message: resp.errMsg, delay: 2000});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$scope.synchronizeWhiteList = function (request) {
|
||||
$http({
|
||||
method: "POST",
|
||||
url: "acl/white/list/sync.do",
|
||||
data: request
|
||||
}).success(function (resp) {
|
||||
if (resp.status == 0) {
|
||||
Notification.info({message: "success!", delay: 2000});
|
||||
$scope.refreshPlainAccessConfigs();
|
||||
} else {
|
||||
Notification.error({message: resp.errMsg, delay: 2000});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$scope.switchSecretKeyType = function (accessKey) {
|
||||
if ($scope.showSecretKeyType[accessKey].type == 'password') {
|
||||
$scope.showSecretKeyType[accessKey] = {type: 'text', action: 'HIDE'};
|
||||
} else {
|
||||
$scope.showSecretKeyType[accessKey] = {type: 'password', action: 'SHOW'};
|
||||
}
|
||||
}
|
||||
}]);
|
||||
|
||||
module.controller('addAclAccountDialogController', ['$scope', 'ngDialog', '$http', 'Notification', function ($scope, ngDialog, $http, Notification) {
|
||||
$scope.addRequest = function (requestItem) {
|
||||
$http({
|
||||
method: "POST",
|
||||
url: "acl/add.do",
|
||||
data: requestItem
|
||||
}).success(function (resp) {
|
||||
if (resp.status == 0) {
|
||||
Notification.info({message: "success!", delay: 2000});
|
||||
} else {
|
||||
Notification.error({message: resp.errMsg, delay: 2000});
|
||||
}
|
||||
});
|
||||
}
|
||||
}]
|
||||
);
|
||||
|
||||
module.controller('updateAclAccountDialogController', ['$scope', 'ngDialog', '$http', 'Notification', function ($scope, ngDialog, $http, Notification) {
|
||||
$scope.updateAclAccountRequest = function (requestItem) {
|
||||
$http({
|
||||
method: "POST",
|
||||
url: "acl/update.do",
|
||||
data: requestItem
|
||||
}).success(function (resp) {
|
||||
if (resp.status == 0) {
|
||||
Notification.info({message: "success!", delay: 2000});
|
||||
} else {
|
||||
Notification.error({message: resp.errMsg, delay: 2000});
|
||||
}
|
||||
});
|
||||
}
|
||||
}]
|
||||
);
|
||||
|
||||
module.controller('addAclTopicDialogController', ['$scope', 'ngDialog', '$http', 'Notification', function ($scope, ngDialog, $http, Notification) {
|
||||
$scope.updateAclAccountRequest = function (requestItem) {
|
||||
if ((requestItem.deny && requestItem.sub) || (requestItem.deny && requestItem.pub)) {
|
||||
alert("Forbid deny && pub/sub.");
|
||||
return false;
|
||||
}
|
||||
if (!requestItem.topic) {
|
||||
alert("topic is null");
|
||||
return false;
|
||||
}
|
||||
//var request = requestItem.originalData;
|
||||
var originalData = $.extend(true, {}, requestItem.originalData);
|
||||
if (!originalData.topicPerms) {
|
||||
originalData.topicPerms = new Array();
|
||||
}
|
||||
var topicPerm = concatPerm(requestItem.topic, requestItem.pub ? 0x01 : 0, requestItem.sub ? 0x02 : 0, requestItem.deny ? 0x04 : 0);
|
||||
originalData.topicPerms.push(topicPerm);
|
||||
var request = {topicPerm: topicPerm, config: originalData};
|
||||
$http({
|
||||
method: "POST",
|
||||
url: "acl/topic/add.do",
|
||||
data: request
|
||||
}).success(function (resp) {
|
||||
if (resp.status == 0) {
|
||||
Notification.info({message: "success!", delay: 2000});
|
||||
} else {
|
||||
Notification.error({message: resp.errMsg, delay: 2000});
|
||||
}
|
||||
});
|
||||
}
|
||||
}]
|
||||
);
|
||||
|
||||
module.controller('updateAclTopicDialogController', ['$scope', 'ngDialog', '$http', 'Notification', function ($scope, ngDialog, $http, Notification) {
|
||||
$scope.updateAclAccountRequest = function (requestItem) {
|
||||
if ((requestItem.deny && requestItem.sub) || (requestItem.deny && requestItem.pub)) {
|
||||
alert("Forbid deny && pub/sub.");
|
||||
return false;
|
||||
}
|
||||
var originalData = $.extend(true, {}, requestItem.originalData);
|
||||
if (!originalData.topicPerms) {
|
||||
originalData.topicPerms = new Array();
|
||||
}
|
||||
var topicPerm = concatPerm(requestItem.topic, requestItem.pub ? 0x01 : 0, requestItem.sub ? 0x02 : 0, requestItem.deny ? 0x04 : 0);
|
||||
|
||||
for (var i = 0; i < originalData.topicPerms.length; i++) {
|
||||
if (originalData.topicPerms[i].split('=')[0] == requestItem.topic) {
|
||||
originalData.topicPerms[i] = topicPerm;
|
||||
}
|
||||
}
|
||||
var request = {topicPerm: topicPerm, config: originalData};
|
||||
$http({
|
||||
method: "POST",
|
||||
url: "acl/topic/add.do",
|
||||
data: request
|
||||
}).success(function (resp) {
|
||||
if (resp.status == 0) {
|
||||
Notification.info({message: "success!", delay: 2000});
|
||||
} else {
|
||||
Notification.error({message: resp.errMsg, delay: 2000});
|
||||
}
|
||||
});
|
||||
}
|
||||
}]
|
||||
);
|
||||
|
||||
module.controller('addAclGroupDialogController', ['$scope', 'ngDialog', '$http', 'Notification', function ($scope, ngDialog, $http, Notification) {
|
||||
$scope.updateAclAccountRequest = function (requestItem) {
|
||||
if ((requestItem.deny && requestItem.sub) || (requestItem.deny && requestItem.pub)) {
|
||||
alert("Forbid deny && pub/sub.");
|
||||
return false;
|
||||
}
|
||||
//var request = requestItem.originalData;
|
||||
var originalData = $.extend(true, {}, requestItem.originalData);
|
||||
if (!originalData.groupPerms) {
|
||||
originalData.groupPerms = new Array();
|
||||
}
|
||||
var groupPerm = concatPerm(requestItem.group, requestItem.pub ? 0x01 : 0, requestItem.sub ? 0x02 : 0, requestItem.deny ? 0x04 : 0);
|
||||
originalData.groupPerms.push(groupPerm);
|
||||
var request = {groupPerm: groupPerm, config: originalData};
|
||||
$http({
|
||||
method: "POST",
|
||||
url: "acl/group/add.do",
|
||||
data: request
|
||||
}).success(function (resp) {
|
||||
if (resp.status == 0) {
|
||||
Notification.info({message: "success!", delay: 2000});
|
||||
} else {
|
||||
Notification.error({message: resp.errMsg, delay: 2000});
|
||||
}
|
||||
});
|
||||
}
|
||||
}]
|
||||
);
|
||||
|
||||
module.controller('updateAclGroupDialogController', ['$scope', 'ngDialog', '$http', 'Notification', function ($scope, ngDialog, $http, Notification) {
|
||||
$scope.updateAclAccountRequest = function (requestItem) {
|
||||
if ((requestItem.deny && requestItem.sub) || (requestItem.deny && requestItem.pub)) {
|
||||
alert("Forbid deny && pub/sub.");
|
||||
return false;
|
||||
}
|
||||
var originalData = $.extend(true, {}, requestItem.originalData);
|
||||
if (!originalData.groupPerms) {
|
||||
originalData.groupPerms = new Array();
|
||||
}
|
||||
var groupPerm = concatPerm(requestItem.group, requestItem.pub ? 0x01 : 0, requestItem.sub ? 0x02 : 0, requestItem.deny ? 0x04 : 0);
|
||||
|
||||
for (var i = 0; i < originalData.groupPerms.length; i++) {
|
||||
if (originalData.groupPerms[i].split('=')[0] == requestItem.group) {
|
||||
originalData.groupPerms[i] = groupPerm;
|
||||
}
|
||||
}
|
||||
var request = {groupPerm: groupPerm, config: originalData};
|
||||
$http({
|
||||
method: "POST",
|
||||
url: "acl/group/add.do",
|
||||
data: request
|
||||
}).success(function (resp) {
|
||||
if (resp.status == 0) {
|
||||
Notification.info({message: "success!", delay: 2000});
|
||||
} else {
|
||||
Notification.error({message: resp.errMsg, delay: 2000});
|
||||
}
|
||||
});
|
||||
}
|
||||
}]
|
||||
);
|
||||
|
||||
/**
|
||||
*
|
||||
* pub: 0x01, sub: 0x02, deny: 0x04
|
||||
*/
|
||||
function concatPerm(name, pub, sub, deny) {
|
||||
var perm = '';
|
||||
|
||||
switch (pub | sub | deny) {
|
||||
case 0x01:
|
||||
perm = 'PUB';
|
||||
break;
|
||||
case 0x02:
|
||||
perm = 'SUB';
|
||||
break;
|
||||
case 0x03:
|
||||
perm = 'PUB|SUB';
|
||||
break;
|
||||
case 0x04:
|
||||
perm = 'DENY';
|
||||
break;
|
||||
default:
|
||||
perm = 'DENY';
|
||||
break;
|
||||
}
|
||||
|
||||
return name + '=' + perm;
|
||||
}
|
||||
|
||||
module.controller('addWhiteListDialogController', ['$scope', 'ngDialog', '$http', 'Notification', function ($scope, ngDialog, $http, Notification) {
|
||||
$scope.addWhiteListRequest = function (requestItem) {
|
||||
$http({
|
||||
method: "POST",
|
||||
url: "acl/white/list/add.do",
|
||||
data: requestItem.split(',')
|
||||
}).success(function (resp) {
|
||||
if (resp.status == 0) {
|
||||
Notification.info({message: "success!", delay: 2000});
|
||||
} else {
|
||||
Notification.error({message: resp.errMsg, delay: 2000});
|
||||
}
|
||||
});
|
||||
}
|
||||
}]
|
||||
);
|
||||
|
||||
module.controller('aclBelongItemDialogController', ['$scope', 'ngDialog', '$http', 'Notification', function ($scope, ngDialog, $http, Notification) {
|
||||
$scope.postBelongItemRequest = function (topicRequestItem) {
|
||||
topicRequestItem.type = 1
|
||||
$http({
|
||||
method: "POST",
|
||||
url: "acl/belong/item/add.do",
|
||||
data: topicRequestItem
|
||||
}).success(function (resp) {
|
||||
if (resp.status == 0) {
|
||||
Notification.info({message: "success!", delay: 2000});
|
||||
} else {
|
||||
Notification.error({message: resp.errMsg, delay: 2000});
|
||||
}
|
||||
});
|
||||
}
|
||||
}]
|
||||
);
|
@@ -49,6 +49,15 @@ var app = angular.module('app', [
|
||||
}
|
||||
console.log('initFlag0='+ initFlag + ' loginFlag0==='+loginFlag);
|
||||
|
||||
$http({
|
||||
method: "GET",
|
||||
url: "acl/enable.query"
|
||||
}).success(function (resp) {
|
||||
if (resp && resp.status == 0) {
|
||||
$rootScope.show = resp.data;
|
||||
}
|
||||
});
|
||||
|
||||
$rootScope.$on('$locationChangeStart', function (event, next, current) {
|
||||
// redirect to login page if not logged in and trying to access a restricted page
|
||||
init(function(resp){
|
||||
@@ -195,12 +204,18 @@ app.config(['$routeProvider', '$httpProvider','$cookiesProvider','getDictNamePro
|
||||
}).when('/message', {
|
||||
templateUrl: 'view/pages/message.html',
|
||||
controller:'messageController'
|
||||
}).when('/dlqMessage', {
|
||||
templateUrl: 'view/pages/dlqMessage.html',
|
||||
controller:'dlqMessageController'
|
||||
}).when('/messageTrace', {
|
||||
templateUrl: 'view/pages/messageTrace.html',
|
||||
controller:'messageTraceController'
|
||||
}).when('/ops', {
|
||||
templateUrl: 'view/pages/ops.html',
|
||||
controller:'opsController'
|
||||
}).when('/acl', {
|
||||
templateUrl: 'view/pages/acl.html',
|
||||
controller: 'aclController'
|
||||
}).when('/404', {
|
||||
templateUrl: 'view/pages/404.html'
|
||||
}).otherwise('/404');
|
||||
|
@@ -17,7 +17,7 @@
|
||||
|
||||
var module = app;
|
||||
|
||||
module.controller('consumerController', ['$scope', 'ngDialog', '$http','Notification',function ($scope, ngDialog, $http,Notification) {
|
||||
module.controller('consumerController', ['$scope', 'ngDialog', '$http', 'Notification', '$window', function ($scope, ngDialog, $http, Notification, $window) {
|
||||
$scope.paginationConf = {
|
||||
currentPage: 1,
|
||||
totalItems: 0,
|
||||
@@ -26,34 +26,46 @@ module.controller('consumerController', ['$scope', 'ngDialog', '$http','Notifica
|
||||
perPageOptions: [10],
|
||||
rememberPerPage: 'perPageItems',
|
||||
onChange: function () {
|
||||
$scope.showConsumerGroupList(this.currentPage,this.totalItems);
|
||||
$scope.showConsumerGroupList(this.currentPage, this.totalItems);
|
||||
}
|
||||
};
|
||||
$scope.sortKey = null;
|
||||
$scope.sortOrder=1;
|
||||
$scope.sortOrder = 1;
|
||||
$scope.intervalProcessSwitch = false;
|
||||
$scope.intervalProcess = null;
|
||||
$scope.allConsumerGrouopList = [];
|
||||
$scope.consumerGroupShowList = [];
|
||||
$scope.sortByKey = function (key) {
|
||||
$scope.paginationConf.currentPage=1;
|
||||
$scope.paginationConf.currentPage = 1;
|
||||
$scope.sortOrder = -$scope.sortOrder;
|
||||
$scope.sortKey = key;
|
||||
$scope.doSort();
|
||||
};
|
||||
$scope.userRole = $window.sessionStorage.getItem("userrole");
|
||||
$scope.writeOperationEnabled = $scope.userRole == null ? true : ($scope.userRole == 1 ? true : false);
|
||||
$scope.filterNormal = true;
|
||||
$scope.filterSystem = false;
|
||||
|
||||
$scope.doSort = function (){// todo how to change this fe's code ? (it's dirty)
|
||||
if($scope.sortKey == 'diffTotal'){
|
||||
$scope.allConsumerGrouopList.sort(function(a,b) {return (a.diffTotal > b.diffTotal) ? $scope.sortOrder : ((b.diffTotal > a.diffTotal) ? -$scope.sortOrder : 0);} );
|
||||
$scope.doSort = function () {// todo how to change this fe's code ? (it's dirty)
|
||||
if ($scope.sortKey == 'diffTotal') {
|
||||
$scope.allConsumerGrouopList.sort(function (a, b) {
|
||||
return (a.diffTotal > b.diffTotal) ? $scope.sortOrder : ((b.diffTotal > a.diffTotal) ? -$scope.sortOrder : 0);
|
||||
});
|
||||
}
|
||||
if($scope.sortKey == 'group'){
|
||||
$scope.allConsumerGrouopList.sort(function(a,b) {return (a.group > b.group) ? $scope.sortOrder : ((b.group > a.group) ? -$scope.sortOrder : 0);} );
|
||||
if ($scope.sortKey == 'group') {
|
||||
$scope.allConsumerGrouopList.sort(function (a, b) {
|
||||
return (a.group > b.group) ? $scope.sortOrder : ((b.group > a.group) ? -$scope.sortOrder : 0);
|
||||
});
|
||||
}
|
||||
if($scope.sortKey == 'count'){
|
||||
$scope.allConsumerGrouopList.sort(function(a,b) {return (a.count > b.count) ? $scope.sortOrder : ((b.count > a.count) ? -$scope.sortOrder : 0);} );
|
||||
if ($scope.sortKey == 'count') {
|
||||
$scope.allConsumerGrouopList.sort(function (a, b) {
|
||||
return (a.count > b.count) ? $scope.sortOrder : ((b.count > a.count) ? -$scope.sortOrder : 0);
|
||||
});
|
||||
}
|
||||
if($scope.sortKey == 'consumeTps'){
|
||||
$scope.allConsumerGrouopList.sort(function(a,b) {return (a.consumeTps > b.consumeTps) ? $scope.sortOrder : ((b.consumeTps > a.consumeTps) ? -$scope.sortOrder : 0);} );
|
||||
if ($scope.sortKey == 'consumeTps') {
|
||||
$scope.allConsumerGrouopList.sort(function (a, b) {
|
||||
return (a.consumeTps > b.consumeTps) ? $scope.sortOrder : ((b.consumeTps > a.consumeTps) ? -$scope.sortOrder : 0);
|
||||
});
|
||||
}
|
||||
$scope.filterList($scope.paginationConf.currentPage)
|
||||
};
|
||||
@@ -65,30 +77,30 @@ module.controller('consumerController', ['$scope', 'ngDialog', '$http','Notifica
|
||||
method: "GET",
|
||||
url: "consumer/groupList.query"
|
||||
}).success(function (resp) {
|
||||
if(resp.status ==0){
|
||||
if (resp.status == 0) {
|
||||
$scope.allConsumerGrouopList = resp.data;
|
||||
console.log($scope.allConsumerGrouopList);
|
||||
console.log(JSON.stringify(resp));
|
||||
$scope.showConsumerGroupList($scope.paginationConf.currentPage,$scope.allConsumerGrouopList.length);
|
||||
$scope.showConsumerGroupList($scope.paginationConf.currentPage, $scope.allConsumerGrouopList.length);
|
||||
|
||||
//Hide loader
|
||||
$('#loaderConsumer').addClass("hide-myloader");
|
||||
}else {
|
||||
} else {
|
||||
Notification.error({message: resp.errMsg, delay: 2000});
|
||||
}
|
||||
});
|
||||
};
|
||||
$scope.monitor = function(consumerGroupName){
|
||||
$scope.monitor = function (consumerGroupName) {
|
||||
$http({
|
||||
method: "GET",
|
||||
url: "monitor/consumerMonitorConfigByGroupName.query",
|
||||
params:{consumeGroupName:consumerGroupName}
|
||||
params: {consumeGroupName: consumerGroupName}
|
||||
}).success(function (resp) {
|
||||
// if(resp.status ==0){
|
||||
ngDialog.open({
|
||||
template: 'consumerMonitorDialog',
|
||||
controller: 'consumerMonitorDialogController',
|
||||
data:{consumerGroupName:consumerGroupName,data:resp.data}
|
||||
data: {consumerGroupName: consumerGroupName, data: resp.data}
|
||||
});
|
||||
// }else {
|
||||
// Notification.error({message: resp.errMsg, delay: 2000});
|
||||
@@ -109,35 +121,59 @@ module.controller('consumerController', ['$scope', 'ngDialog', '$http','Notifica
|
||||
|
||||
|
||||
$scope.refreshConsumerData();
|
||||
$scope.filterStr="";
|
||||
$scope.$watch('filterStr', function() {
|
||||
$scope.paginationConf.currentPage=1;
|
||||
$scope.filterStr = "";
|
||||
$scope.$watch('filterStr', function () {
|
||||
$scope.paginationConf.currentPage = 1;
|
||||
$scope.filterList(1)
|
||||
});
|
||||
|
||||
$scope.$watch('filterNormal', function () {
|
||||
$scope.filterList(1);
|
||||
});
|
||||
|
||||
$scope.$watch('filterSystem', function () {
|
||||
$scope.filterList(1);
|
||||
});
|
||||
|
||||
$scope.filterByType = function (str) {
|
||||
if ($scope.filterSystem) {
|
||||
if (str.startsWith("%S")) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
if ($scope.filterNormal) {
|
||||
if (str.startsWith("%") == false) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
$scope.filterList = function (currentPage) {
|
||||
var lowExceptStr = $scope.filterStr.toLowerCase();
|
||||
var canShowList = [];
|
||||
$scope.allConsumerGrouopList.forEach(function(element) {
|
||||
$scope.allConsumerGrouopList.forEach(function (element) {
|
||||
console.log(element)
|
||||
if (element.group.toLowerCase().indexOf(lowExceptStr) != -1){
|
||||
if ($scope.filterByType(element.group)) {
|
||||
if (element.group.toLowerCase().indexOf(lowExceptStr) != -1) {
|
||||
canShowList.push(element);
|
||||
}
|
||||
}
|
||||
});
|
||||
$scope.paginationConf.totalItems =canShowList.length;
|
||||
$scope.paginationConf.totalItems = canShowList.length;
|
||||
var perPage = $scope.paginationConf.itemsPerPage;
|
||||
var from = (currentPage - 1) * perPage;
|
||||
var to = (from + perPage)>canShowList.length?canShowList.length:from + perPage;
|
||||
var to = (from + perPage) > canShowList.length ? canShowList.length : from + perPage;
|
||||
$scope.consumerGroupShowList = canShowList.slice(from, to);
|
||||
};
|
||||
|
||||
|
||||
$scope.showConsumerGroupList = function (currentPage,totalItem) {
|
||||
$scope.showConsumerGroupList = function (currentPage, totalItem) {
|
||||
var perPage = $scope.paginationConf.itemsPerPage;
|
||||
var from = (currentPage - 1) * perPage;
|
||||
var to = (from + perPage)>totalItem?totalItem:from + perPage;
|
||||
var to = (from + perPage) > totalItem ? totalItem : from + perPage;
|
||||
$scope.consumerGroupShowList = $scope.allConsumerGrouopList.slice(from, to);
|
||||
$scope.paginationConf.totalItems = totalItem ;
|
||||
$scope.paginationConf.totalItems = totalItem;
|
||||
console.log($scope.consumerGroupShowList)
|
||||
console.log($scope.paginationConf.totalItems)
|
||||
$scope.doSort()
|
||||
@@ -145,9 +181,9 @@ module.controller('consumerController', ['$scope', 'ngDialog', '$http','Notifica
|
||||
$scope.openAddDialog = function () {
|
||||
$scope.openCreateOrUpdateDialog(null);
|
||||
};
|
||||
$scope.openCreateOrUpdateDialog = function(request){
|
||||
$scope.openCreateOrUpdateDialog = function (request) {
|
||||
var bIsUpdate = true;
|
||||
if(request == null){
|
||||
if (request == null) {
|
||||
request = [{
|
||||
brokerNameList: [],
|
||||
subscriptionGroupConfig: {
|
||||
@@ -168,123 +204,126 @@ module.controller('consumerController', ['$scope', 'ngDialog', '$http','Notifica
|
||||
method: "GET",
|
||||
url: "cluster/list.query"
|
||||
}).success(function (resp) {
|
||||
if(resp.status ==0){
|
||||
if (resp.status == 0) {
|
||||
console.log(resp);
|
||||
ngDialog.open({
|
||||
preCloseCallback: function(value) {
|
||||
preCloseCallback: function (value) {
|
||||
// Refresh topic list
|
||||
$scope.refreshConsumerData();
|
||||
},
|
||||
template: 'consumerModifyDialog',
|
||||
controller: 'consumerModifyDialogController',
|
||||
data:{
|
||||
consumerRequestList:request,
|
||||
allClusterNameList:Object.keys(resp.data.clusterInfo.clusterAddrTable),
|
||||
allBrokerNameList:Object.keys(resp.data.brokerServer),
|
||||
bIsUpdate:bIsUpdate
|
||||
data: {
|
||||
consumerRequestList: request,
|
||||
allClusterNameList: Object.keys(resp.data.clusterInfo.clusterAddrTable),
|
||||
allBrokerNameList: Object.keys(resp.data.brokerServer),
|
||||
bIsUpdate: bIsUpdate,
|
||||
writeOperationEnabled: $scope.writeOperationEnabled
|
||||
}
|
||||
});
|
||||
}else {
|
||||
} else {
|
||||
Notification.error({message: resp.errMsg, delay: 2000});
|
||||
}
|
||||
});
|
||||
};
|
||||
$scope.detail = function(consumerGroupName){
|
||||
$scope.detail = function (consumerGroupName) {
|
||||
$http({
|
||||
method: "GET",
|
||||
url: "consumer/queryTopicByConsumer.query",
|
||||
params:{consumerGroup:consumerGroupName}
|
||||
params: {consumerGroup: consumerGroupName}
|
||||
}).success(function (resp) {
|
||||
if(resp.status ==0){
|
||||
if (resp.status == 0) {
|
||||
console.log(resp);
|
||||
ngDialog.open({
|
||||
template: 'consumerTopicViewDialog',
|
||||
controller: 'consumerTopicViewDialogController',
|
||||
data:{consumerGroupName:consumerGroupName,data:resp.data}
|
||||
data: {consumerGroupName: consumerGroupName, data: resp.data}
|
||||
});
|
||||
}else {
|
||||
} else {
|
||||
Notification.error({message: resp.errMsg, delay: 2000});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$scope.client = function(consumerGroupName){
|
||||
$scope.client = function (consumerGroupName) {
|
||||
$http({
|
||||
method: "GET",
|
||||
url: "consumer/consumerConnection.query",
|
||||
params:{consumerGroup:consumerGroupName}
|
||||
params: {consumerGroup: consumerGroupName}
|
||||
}).success(function (resp) {
|
||||
if(resp.status ==0){
|
||||
if (resp.status == 0) {
|
||||
console.log(resp);
|
||||
ngDialog.open({
|
||||
template: 'clientInfoDialog',
|
||||
// controller: 'addTopicDialogController',
|
||||
data:{data:resp.data,consumerGroupName:consumerGroupName}
|
||||
data: {data: resp.data, consumerGroupName: consumerGroupName}
|
||||
});
|
||||
}else {
|
||||
} else {
|
||||
Notification.error({message: resp.errMsg, delay: 2000});
|
||||
}
|
||||
});
|
||||
};
|
||||
$scope.updateConfigDialog = function(consumerGroupName){
|
||||
$scope.updateConfigDialog = function (consumerGroupName) {
|
||||
$http({
|
||||
method: "GET",
|
||||
url: "consumer/examineSubscriptionGroupConfig.query",
|
||||
params:{consumerGroup:consumerGroupName}
|
||||
params: {consumerGroup: consumerGroupName}
|
||||
}).success(function (resp) {
|
||||
if(resp.status ==0){
|
||||
if (resp.status == 0) {
|
||||
console.log(resp);
|
||||
$scope.openCreateOrUpdateDialog(resp.data);
|
||||
}else {
|
||||
} else {
|
||||
Notification.error({message: resp.errMsg, delay: 2000});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
};
|
||||
$scope.delete = function(consumerGroupName){
|
||||
$scope.delete = function (consumerGroupName) {
|
||||
$http({
|
||||
method: "GET",
|
||||
url: "consumer/fetchBrokerNameList.query",
|
||||
params:{
|
||||
consumerGroup:consumerGroupName
|
||||
params: {
|
||||
consumerGroup: consumerGroupName
|
||||
}
|
||||
}).success(function (resp) {
|
||||
if(resp.status ==0){
|
||||
if (resp.status == 0) {
|
||||
console.log(resp);
|
||||
|
||||
ngDialog.open({
|
||||
preCloseCallback: function(value) {
|
||||
preCloseCallback: function (value) {
|
||||
// Refresh topic list
|
||||
$scope.refreshConsumerData();
|
||||
},
|
||||
template: 'deleteConsumerDialog',
|
||||
controller: 'deleteConsumerDialogController',
|
||||
data:{
|
||||
data: {
|
||||
// allClusterList:Object.keys(resp.data.clusterInfo.clusterAddrTable),
|
||||
allBrokerNameList:resp.data,
|
||||
consumerGroupName:consumerGroupName
|
||||
allBrokerNameList: resp.data,
|
||||
consumerGroupName: consumerGroupName
|
||||
}
|
||||
});
|
||||
}else {
|
||||
} else {
|
||||
Notification.error({message: resp.errMsg, delay: 2000});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}])
|
||||
module.controller('consumerMonitorDialogController', function ($scope, ngDialog, $http,Notification) {
|
||||
module.controller('consumerMonitorDialogController', function ($scope, ngDialog, $http, Notification) {
|
||||
$scope.createOrUpdateConsumerMonitor = function () {
|
||||
$http({
|
||||
method: "POST",
|
||||
url: "monitor/createOrUpdateConsumerMonitor.do",
|
||||
params:{consumeGroupName:$scope.ngDialogData.consumerGroupName,
|
||||
minCount:$scope.ngDialogData.data.minCount,
|
||||
maxDiffTotal:$scope.ngDialogData.data.maxDiffTotal}
|
||||
params: {
|
||||
consumeGroupName: $scope.ngDialogData.consumerGroupName,
|
||||
minCount: $scope.ngDialogData.data.minCount,
|
||||
maxDiffTotal: $scope.ngDialogData.data.maxDiffTotal
|
||||
}
|
||||
}).success(function (resp) {
|
||||
if(resp.status ==0){
|
||||
if (resp.status == 0) {
|
||||
Notification.info({message: "update success!", delay: 2000});
|
||||
}else {
|
||||
} else {
|
||||
Notification.error({message: resp.errMsg, delay: 2000});
|
||||
}
|
||||
});
|
||||
@@ -293,7 +332,7 @@ module.controller('consumerMonitorDialogController', function ($scope, ngDialog,
|
||||
);
|
||||
|
||||
|
||||
module.controller('deleteConsumerDialogController', ['$scope', 'ngDialog', '$http','Notification',function ($scope, ngDialog, $http,Notification) {
|
||||
module.controller('deleteConsumerDialogController', ['$scope', 'ngDialog', '$http', 'Notification', function ($scope, ngDialog, $http, Notification) {
|
||||
$scope.selectedClusterList = [];
|
||||
$scope.selectedBrokerNameList = [];
|
||||
$scope.delete = function () {
|
||||
@@ -303,13 +342,15 @@ module.controller('deleteConsumerDialogController', ['$scope', 'ngDialog', '$htt
|
||||
$http({
|
||||
method: "POST",
|
||||
url: "consumer/deleteSubGroup.do",
|
||||
data:{groupName:$scope.ngDialogData.consumerGroupName,
|
||||
brokerNameList:$scope.selectedBrokerNameList}
|
||||
data: {
|
||||
groupName: $scope.ngDialogData.consumerGroupName,
|
||||
brokerNameList: $scope.selectedBrokerNameList
|
||||
}
|
||||
}).success(function (resp) {
|
||||
if(resp.status ==0){
|
||||
if (resp.status == 0) {
|
||||
Notification.info({message: "delete success!", delay: 2000});
|
||||
ngDialog.close(this);
|
||||
}else {
|
||||
} else {
|
||||
Notification.error({message: resp.errMsg, delay: 2000});
|
||||
}
|
||||
});
|
||||
@@ -317,19 +358,19 @@ module.controller('deleteConsumerDialogController', ['$scope', 'ngDialog', '$htt
|
||||
}]
|
||||
);
|
||||
|
||||
module.controller('consumerModifyDialogController', ['$scope', 'ngDialog', '$http','Notification',function ($scope, ngDialog, $http,Notification) {
|
||||
module.controller('consumerModifyDialogController', ['$scope', 'ngDialog', '$http', 'Notification', function ($scope, ngDialog, $http, Notification) {
|
||||
$scope.postConsumerRequest = function (consumerRequest) {
|
||||
var request = JSON.parse(JSON.stringify(consumerRequest));
|
||||
console.log(request);
|
||||
$http({
|
||||
method: "POST",
|
||||
url: "consumer/createOrUpdate.do",
|
||||
data:request
|
||||
data: request
|
||||
}).success(function (resp) {
|
||||
if(resp.status ==0){
|
||||
if (resp.status == 0) {
|
||||
Notification.info({message: "update success!", delay: 2000});
|
||||
ngDialog.close(this);
|
||||
}else {
|
||||
} else {
|
||||
Notification.error({message: resp.errMsg, delay: 2000});
|
||||
}
|
||||
});
|
||||
@@ -351,8 +392,10 @@ module.controller('consumerTopicViewDialogController', ['$scope', 'ngDialog', '$
|
||||
if (resp.status == 0) {
|
||||
ngDialog.open({
|
||||
template: 'consumerClientDialog',
|
||||
data:{consumerClientInfo:resp.data,
|
||||
clientId:clientId}
|
||||
data: {
|
||||
consumerClientInfo: resp.data,
|
||||
clientId: clientId
|
||||
}
|
||||
});
|
||||
} else {
|
||||
Notification.error({message: resp.errMsg, delay: 2000});
|
||||
@@ -361,6 +404,3 @@ module.controller('consumerTopicViewDialogController', ['$scope', 'ngDialog', '$
|
||||
};
|
||||
}]
|
||||
);
|
||||
|
||||
|
||||
|
||||
|
297
src/main/resources/static/src/dlqMessage.js
Normal file
297
src/main/resources/static/src/dlqMessage.js
Normal file
@@ -0,0 +1,297 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
var module = app;
|
||||
const SYS_GROUP_TOPIC_PREFIX = "%SYS%";
|
||||
module.controller('dlqMessageController', ['$scope', 'ngDialog', '$http', 'Notification', function ($scope, ngDialog, $http, Notification) {
|
||||
$scope.allConsumerGroupList = [];
|
||||
$scope.selectedConsumerGroup = [];
|
||||
$scope.messageId = "";
|
||||
$scope.queryDlqMessageByConsumerGroupResult = [];
|
||||
$scope.queryDlqMessageByMessageIdResult = {};
|
||||
$http({
|
||||
method: "GET",
|
||||
url: "consumer/groupList.query"
|
||||
}).success(function (resp) {
|
||||
if (resp.status == 0) {
|
||||
for (const consumerGroup of resp.data) {
|
||||
if (!consumerGroup.group.startsWith(SYS_GROUP_TOPIC_PREFIX)) {
|
||||
$scope.allConsumerGroupList.push(consumerGroup.group);
|
||||
}
|
||||
}
|
||||
$scope.allConsumerGroupList.sort();
|
||||
} else {
|
||||
Notification.error({message: resp.errMsg, delay: 2000});
|
||||
}
|
||||
});
|
||||
$scope.timepickerBegin = moment().subtract(3, 'hour').format('YYYY-MM-DD HH:mm');
|
||||
$scope.timepickerEnd = moment().format('YYYY-MM-DD HH:mm');
|
||||
$scope.timepickerOptions = {format: 'YYYY-MM-DD HH:mm', showClear: true};
|
||||
|
||||
$scope.taskId = "";
|
||||
|
||||
$scope.paginationConf = {
|
||||
currentPage: 1,
|
||||
totalItems: 0,
|
||||
itemsPerPage: 20,
|
||||
pagesLength: 15,
|
||||
perPageOptions: [10],
|
||||
rememberPerPage: 'perPageItems',
|
||||
onChange: function () {
|
||||
$scope.queryDlqMessageByConsumerGroup()
|
||||
}
|
||||
};
|
||||
|
||||
$scope.queryDlqMessageByConsumerGroup = function () {
|
||||
$("#noMsgTip").css("display", "none");
|
||||
if ($scope.timepickerEnd < $scope.timepickerBegin) {
|
||||
Notification.error({message: "endTime is later than beginTime!", delay: 2000});
|
||||
return
|
||||
}
|
||||
if ($scope.selectedConsumerGroup === [] || (typeof $scope.selectedConsumerGroup) == "object") {
|
||||
return
|
||||
}
|
||||
$http({
|
||||
method: "POST",
|
||||
url: "dlqMessage/queryDlqMessageByConsumerGroup.query",
|
||||
data: {
|
||||
topic: DLQ_GROUP_TOPIC_PREFIX + $scope.selectedConsumerGroup,
|
||||
begin: $scope.timepickerBegin.valueOf(),
|
||||
end: $scope.timepickerEnd.valueOf(),
|
||||
pageNum: $scope.paginationConf.currentPage,
|
||||
pageSize: $scope.paginationConf.itemsPerPage,
|
||||
taskId: $scope.taskId
|
||||
}
|
||||
}).success(function (resp) {
|
||||
if (resp.status === 0) {
|
||||
$scope.messageShowList = resp.data.page.content;
|
||||
if ($scope.messageShowList.length == 0){
|
||||
$("#noMsgTip").removeAttr("style");
|
||||
}
|
||||
for (const message of $scope.messageShowList) {
|
||||
message.checked = false;
|
||||
}
|
||||
console.log($scope.messageShowList);
|
||||
if (resp.data.page.first) {
|
||||
$scope.paginationConf.currentPage = 1;
|
||||
}
|
||||
$scope.paginationConf.currentPage = resp.data.page.number + 1;
|
||||
$scope.paginationConf.totalItems = resp.data.page.totalElements;
|
||||
$scope.taskId = resp.data.taskId
|
||||
} else {
|
||||
Notification.error({message: resp.errMsg, delay: 2000});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$scope.queryDlqMessageDetail = function (messageId, consumerGroup) {
|
||||
$http({
|
||||
method: "GET",
|
||||
url: "messageTrace/viewMessage.query",
|
||||
params: {
|
||||
msgId: messageId,
|
||||
topic: DLQ_GROUP_TOPIC_PREFIX + consumerGroup
|
||||
}
|
||||
}).success(function (resp) {
|
||||
if (resp.status == 0) {
|
||||
console.log(resp);
|
||||
ngDialog.open({
|
||||
template: 'dlqMessageDetailViewDialog',
|
||||
data: resp.data
|
||||
});
|
||||
} else {
|
||||
Notification.error({message: resp.errMsg, delay: 2000});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$scope.queryDlqMessageByMessageId = function (messageId, consumerGroup) {
|
||||
$http({
|
||||
method: "GET",
|
||||
url: "messageTrace/viewMessage.query",
|
||||
params: {
|
||||
msgId: messageId,
|
||||
topic: DLQ_GROUP_TOPIC_PREFIX + consumerGroup
|
||||
}
|
||||
}).success(function (resp) {
|
||||
if (resp.status == 0) {
|
||||
$scope.queryDlqMessageByMessageIdResult = resp.data;
|
||||
} else {
|
||||
Notification.error({message: resp.errMsg, delay: 2000});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$scope.changeShowMessageList = function (currentPage, totalItem) {
|
||||
var perPage = $scope.paginationConf.itemsPerPage;
|
||||
var from = (currentPage - 1) * perPage;
|
||||
var to = (from + perPage) > totalItem ? totalItem : from + perPage;
|
||||
$scope.messageShowList = $scope.queryMessageByTopicResult.slice(from, to);
|
||||
$scope.paginationConf.totalItems = totalItem;
|
||||
};
|
||||
|
||||
$scope.onChangeQueryCondition = function () {
|
||||
console.log("change")
|
||||
$scope.taskId = "";
|
||||
$scope.paginationConf.currentPage = 1;
|
||||
$scope.paginationConf.totalItems = 0;
|
||||
}
|
||||
|
||||
$scope.resendDlqMessage = function (messageView, consumerGroup) {
|
||||
const topic = messageView.properties.RETRY_TOPIC;
|
||||
const msgId = messageView.properties.ORIGIN_MESSAGE_ID;
|
||||
$http({
|
||||
method: "POST",
|
||||
url: "message/consumeMessageDirectly.do",
|
||||
params: {
|
||||
msgId: msgId,
|
||||
consumerGroup: consumerGroup,
|
||||
topic: topic
|
||||
}
|
||||
}).success(function (resp) {
|
||||
if (resp.status == 0) {
|
||||
ngDialog.open({
|
||||
template: 'operationResultDialog',
|
||||
data: {
|
||||
result: resp.data
|
||||
}
|
||||
});
|
||||
} else {
|
||||
ngDialog.open({
|
||||
template: 'operationResultDialog',
|
||||
data: {
|
||||
result: resp.errMsg
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$scope.exportDlqMessage = function (msgId, consumerGroup) {
|
||||
window.location.href = "dlqMessage/exportDlqMessage.do?msgId=" + msgId + "&consumerGroup=" + consumerGroup;
|
||||
};
|
||||
|
||||
$scope.selectedDlqMessage = [];
|
||||
$scope.batchResendDlqMessage = function (consumerGroup) {
|
||||
if ($("#batchResendBtn").hasClass("disabled")) {
|
||||
return;
|
||||
}
|
||||
for (const message of $scope.messageCheckedList) {
|
||||
const dlqMessage = {};
|
||||
dlqMessage.topic = message.properties.RETRY_TOPIC;
|
||||
dlqMessage.msgId = message.properties.ORIGIN_MESSAGE_ID;
|
||||
dlqMessage.consumerGroup = consumerGroup;
|
||||
$scope.selectedDlqMessage.push(dlqMessage);
|
||||
}
|
||||
$http({
|
||||
method: "POST",
|
||||
url: "dlqMessage/batchResendDlqMessage.do",
|
||||
data: $scope.selectedDlqMessage
|
||||
}).success(function (resp) {
|
||||
$scope.selectedDlqMessage = [];
|
||||
if (resp.status == 0) {
|
||||
ngDialog.open({
|
||||
template: 'operationResultDialog',
|
||||
data: {
|
||||
result: resp.data
|
||||
}
|
||||
});
|
||||
} else {
|
||||
ngDialog.open({
|
||||
template: 'operationResultDialog',
|
||||
data: {
|
||||
result: resp.errMsg
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$scope.batchExportDlqMessage = function (consumerGroup) {
|
||||
if ($("#batchExportBtn").hasClass("disabled")) {
|
||||
return;
|
||||
}
|
||||
for (const message of $scope.messageCheckedList) {
|
||||
const dlqMessage = {};
|
||||
dlqMessage.msgId = message.msgId;
|
||||
dlqMessage.consumerGroup = consumerGroup;
|
||||
$scope.selectedDlqMessage.push(dlqMessage);
|
||||
}
|
||||
$http({
|
||||
method: "POST",
|
||||
url: "dlqMessage/batchExportDlqMessage.do",
|
||||
data: $scope.selectedDlqMessage,
|
||||
headers: {
|
||||
'Content-type': 'application/json'
|
||||
},
|
||||
responseType: "arraybuffer"
|
||||
}).success(function (resp) {
|
||||
$scope.selectedDlqMessage = [];
|
||||
const blob = new Blob([resp], {type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"});
|
||||
const objectUrl = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.style.display = 'none';
|
||||
a.download = 'dlqs.xlsx';
|
||||
a.href = objectUrl;
|
||||
a.click();
|
||||
document.body.removeChild(a)
|
||||
});
|
||||
};
|
||||
|
||||
$scope.checkedAll = false;
|
||||
$scope.messageCheckedList = [];
|
||||
$scope.selectAll = function () {
|
||||
$scope.messageCheckedList = [];
|
||||
if ($scope.checkedAll == true) {
|
||||
angular.forEach($scope.messageShowList, function (item, index) {
|
||||
item.checked = true;
|
||||
$scope.messageCheckedList.push(item);
|
||||
});
|
||||
} else {
|
||||
angular.forEach($scope.messageShowList, function (item, index) {
|
||||
item.checked = false;
|
||||
});
|
||||
}
|
||||
checkBtn($scope.messageCheckedList)
|
||||
console.log($scope.messageCheckedList)
|
||||
}
|
||||
|
||||
$scope.selectItem = function () {
|
||||
var flag = true;
|
||||
$scope.messageCheckedList = [];
|
||||
angular.forEach($scope.messageShowList, function (item, index) {
|
||||
if (item.checked) {
|
||||
$scope.messageCheckedList.push(item);
|
||||
} else {
|
||||
flag = false;
|
||||
}
|
||||
})
|
||||
$scope.checkedAll = flag;
|
||||
checkBtn($scope.messageCheckedList)
|
||||
console.log($scope.messageCheckedList);
|
||||
}
|
||||
|
||||
function checkBtn(messageCheckList) {
|
||||
if (messageCheckList.length == 0) {
|
||||
$("#batchResendBtn").addClass("disabled");
|
||||
$("#batchExportBtn").addClass("disabled");
|
||||
} else {
|
||||
$("#batchResendBtn").removeClass("disabled");
|
||||
$("#batchExportBtn").removeClass("disabled");
|
||||
}
|
||||
}
|
||||
}]);
|
@@ -1,11 +1,28 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
var en = {
|
||||
"TITLE": "RocketMQ-Dashboard",
|
||||
"CLOSE": "Close",
|
||||
"NO": "NO.",
|
||||
"ADDRESS": "Address",
|
||||
"VERSION": "Version",
|
||||
"PRO_MSG_TPS": "Produce Massage TPS",
|
||||
"CUS_MSG_TPS": "Consumer Massage TPS",
|
||||
"PRO_MSG_TPS": "Produce Message TPS",
|
||||
"CUS_MSG_TPS": "Consumer Message TPS",
|
||||
"YESTERDAY_PRO_COUNT": "Yesterday Produce Count",
|
||||
"YESTERDAY_CUS_COUNT": "Yesterday Consume Count",
|
||||
"TODAY_PRO_COUNT": "Today Produce Count",
|
||||
@@ -20,7 +37,11 @@ var en = {
|
||||
"CONSUMER":"Consumer",
|
||||
"PRODUCER":"Producer",
|
||||
"MESSAGE":"Message",
|
||||
"MESSAGE_DETAIL":"Message Detail",
|
||||
"RESEND_MESSAGE":"Resend Message",
|
||||
"VIEW_EXCEPTION":"View Exception",
|
||||
"MESSAGETRACE":"MessageTrace",
|
||||
"DLQ_MESSAGE":"DLQMessage",
|
||||
"COMMIT": "Commit",
|
||||
"OPERATION": "Operation",
|
||||
"ADD": "Add",
|
||||
@@ -85,5 +106,22 @@ var en = {
|
||||
"NO_DATA":"Don't have ",
|
||||
"SYSTEM":"SYSTEM",
|
||||
"WELCOME":"Hi, welcome using RocketMQ Dashboard",
|
||||
"ENABLE_MESSAGE_TRACE":"Enable Message Trace"
|
||||
"ENABLE_MESSAGE_TRACE":"Enable Message Trace",
|
||||
"MESSAGE_TRACE_DETAIL":"Message Trace Detail",
|
||||
"TRACE_TOPIC":"TraceTopic",
|
||||
"SELECT_TRACE_TOPIC":"selectTraceTopic",
|
||||
"EXPORT": "export",
|
||||
"NO_MATCH_RESULT": "no match result",
|
||||
"BATCH_RESEND": "batchReSend",
|
||||
"BATCH_EXPORT": "batchExport",
|
||||
"WHITE_LIST":"White List",
|
||||
"ACCOUNT_INFO":"Account Info",
|
||||
"IS_ADMIN":"Is Admin",
|
||||
"DEFAULT_TOPIC_PERM":"Default Topic Permission",
|
||||
"DEFAULT_GROUP_PERM":"Default Group Permission",
|
||||
"TOPIC_PERM":"Topic Permission",
|
||||
"GROUP_PERM":"Group Permission",
|
||||
"SYNCHRONIZE":"Synchronize Data",
|
||||
"SHOW":"Show",
|
||||
"HIDE":"Hide"
|
||||
}
|
@@ -1,3 +1,20 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
var zh = {
|
||||
"TITLE": "RocketMQ仪表板",
|
||||
"CLOSE": "关闭",
|
||||
@@ -21,6 +38,10 @@ var zh = {
|
||||
"CONSUMER":"消费者",
|
||||
"PRODUCER":"生产者",
|
||||
"MESSAGE":"消息",
|
||||
"MESSAGE_DETAIL":"消息详情",
|
||||
"RESEND_MESSAGE":"重新发送",
|
||||
"VIEW_EXCEPTION":"查看异常",
|
||||
"DLQ_MESSAGE":"死信消息",
|
||||
"MESSAGETRACE":"消息轨迹",
|
||||
"OPERATION": "操作",
|
||||
"ADD": "新增",
|
||||
@@ -86,5 +107,22 @@ var zh = {
|
||||
"NO_DATA":"不存在 ",
|
||||
"SYSTEM":"系统",
|
||||
"WELCOME":"您好,欢迎使用RocketMQ仪表盘",
|
||||
"ENABLE_MESSAGE_TRACE":"开启消息轨迹"
|
||||
"ENABLE_MESSAGE_TRACE":"开启消息轨迹",
|
||||
"MESSAGE_TRACE_DETAIL":"消息轨迹详情",
|
||||
"TRACE_TOPIC":"消息轨迹主题",
|
||||
"SELECT_TRACE_TOPIC":"选择消息轨迹主题",
|
||||
"EXPORT": "导出",
|
||||
"NO_MATCH_RESULT": "没有查到符合条件的结果",
|
||||
"BATCH_RESEND": "批量重发",
|
||||
"BATCH_EXPORT": "批量导出",
|
||||
"WHITE_LIST":"白名单",
|
||||
"ACCOUNT_INFO":"账户信息",
|
||||
"IS_ADMIN":"是否管理员",
|
||||
"DEFAULT_TOPIC_PERM":"topic默认权限",
|
||||
"DEFAULT_GROUP_PERM":"消费组默认权限",
|
||||
"TOPIC_PERM":"topic权限",
|
||||
"GROUP_PERM":"消费组权限",
|
||||
"SYNCHRONIZE":"同步",
|
||||
"SHOW":"显示",
|
||||
"HIDE":"隐藏"
|
||||
}
|
@@ -15,13 +15,13 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
app.controller('loginController', ['$scope','$location','$http','Notification','$cookies','$window', function ($scope,$location,$http,Notification,$cookies, $window) {
|
||||
app.controller('loginController', ['$scope', '$location', '$http', 'Notification', '$cookies', '$window', function ($scope, $location, $http, Notification, $cookies, $window) {
|
||||
$scope.login = function () {
|
||||
if(!$("#username").val()) {
|
||||
if (!$("#username").val()) {
|
||||
alert("用户名不能为空");
|
||||
return;
|
||||
}
|
||||
if(!$("#password").val()) {
|
||||
if (!$("#password").val()) {
|
||||
alert("密码不能为空");
|
||||
return;
|
||||
}
|
||||
@@ -29,14 +29,15 @@ app.controller('loginController', ['$scope','$location','$http','Notification','
|
||||
$http({
|
||||
method: "POST",
|
||||
url: "login/login.do",
|
||||
params:{username:$("#username").val(), password:$("#password").val()}
|
||||
params: {username: $("#username").val(), password: $("#password").val()}
|
||||
}).success(function (resp) {
|
||||
if (resp.status == 0) {
|
||||
Notification.info({message: 'Login successful, redirect now', delay: 2000});
|
||||
$window.sessionStorage.setItem("username", $("#username").val());
|
||||
window.location = resp.data;
|
||||
$window.sessionStorage.setItem("username", resp.data.loginUserName);
|
||||
$window.sessionStorage.setItem("userrole", resp.data.loginUserRole);
|
||||
window.location = resp.data.contextPath;
|
||||
initFlag = false;
|
||||
} else{
|
||||
} else {
|
||||
Notification.error({message: resp.errMsg, delay: 2000});
|
||||
}
|
||||
});
|
||||
|
@@ -17,31 +17,31 @@
|
||||
|
||||
var module = app;
|
||||
|
||||
module.controller('messageController', ['$scope', 'ngDialog', '$http','Notification',function ($scope, ngDialog, $http,Notification) {
|
||||
module.controller('messageController', ['$scope', 'ngDialog', '$http', 'Notification', function ($scope, ngDialog, $http, Notification) {
|
||||
$scope.allTopicList = [];
|
||||
$scope.selectedTopic =[];
|
||||
$scope.key ="";
|
||||
$scope.messageId ="";
|
||||
$scope.queryMessageByTopicResult=[];
|
||||
$scope.queryMessageByTopicAndKeyResult=[];
|
||||
$scope.queryMessageByMessageIdResult={};
|
||||
$scope.selectedTopic = [];
|
||||
$scope.key = "";
|
||||
$scope.messageId = "";
|
||||
$scope.queryMessageByTopicResult = [];
|
||||
$scope.queryMessageByTopicAndKeyResult = [];
|
||||
$scope.queryMessageByMessageIdResult = {};
|
||||
$http({
|
||||
method: "GET",
|
||||
url: "topic/list.query",
|
||||
params: {
|
||||
skipSysProcess: 'true'
|
||||
skipSysProcess: true
|
||||
}
|
||||
}).success(function (resp) {
|
||||
if(resp.status ==0){
|
||||
if (resp.status == 0) {
|
||||
$scope.allTopicList = resp.data.topicList.sort();
|
||||
console.log($scope.allTopicList);
|
||||
}else {
|
||||
} else {
|
||||
Notification.error({message: resp.errMsg, delay: 2000});
|
||||
}
|
||||
});
|
||||
$scope.timepickerBegin = moment().subtract(1, 'hour').format('YYYY-MM-DD HH:mm');
|
||||
$scope.timepickerEnd = moment().add(1,'hour').format('YYYY-MM-DD HH:mm');
|
||||
$scope.timepickerOptions ={format: 'YYYY-MM-DD HH:mm', showClear: true};
|
||||
$scope.timepickerBegin = moment().subtract(3, 'hour').format('YYYY-MM-DD HH:mm');
|
||||
$scope.timepickerEnd = moment().format('YYYY-MM-DD HH:mm');
|
||||
$scope.timepickerOptions = {format: 'YYYY-MM-DD HH:mm', showClear: true};
|
||||
|
||||
$scope.taskId = "";
|
||||
|
||||
@@ -58,11 +58,12 @@ module.controller('messageController', ['$scope', 'ngDialog', '$http','Notificat
|
||||
};
|
||||
|
||||
$scope.queryMessagePageByTopic = function () {
|
||||
$("#noMsgTip").css("display", "none");
|
||||
if ($scope.timepickerEnd < $scope.timepickerBegin) {
|
||||
Notification.error({message: "endTime is later than beginTime!", delay: 2000});
|
||||
return
|
||||
}
|
||||
if( $scope.selectedTopic === [] || (typeof $scope.selectedTopic) == "object"){
|
||||
if ($scope.selectedTopic === [] || (typeof $scope.selectedTopic) == "object") {
|
||||
return
|
||||
}
|
||||
$http({
|
||||
@@ -80,13 +81,16 @@ module.controller('messageController', ['$scope', 'ngDialog', '$http','Notificat
|
||||
if (resp.status === 0) {
|
||||
console.log(resp);
|
||||
$scope.messageShowList = resp.data.page.content;
|
||||
if(resp.data.page.first){
|
||||
if ($scope.messageShowList.length == 0){
|
||||
$("#noMsgTip").removeAttr("style");
|
||||
}
|
||||
if (resp.data.page.first) {
|
||||
$scope.paginationConf.currentPage = 1;
|
||||
}
|
||||
$scope.paginationConf.currentPage = resp.data.page.number + 1;
|
||||
$scope.paginationConf.totalItems = resp.data.page.totalElements;
|
||||
$scope.taskId = resp.data.taskId
|
||||
}else {
|
||||
} else {
|
||||
Notification.error({message: resp.errMsg, delay: 2000});
|
||||
}
|
||||
});
|
||||
@@ -114,10 +118,10 @@ module.controller('messageController', ['$scope', 'ngDialog', '$http','Notificat
|
||||
if (resp.status == 0) {
|
||||
console.log(resp);
|
||||
$scope.queryMessageByTopicResult = resp.data;
|
||||
$scope.changeShowMessageList(1,$scope.queryMessageByTopicResult.length);
|
||||
$scope.changeShowMessageList(1, $scope.queryMessageByTopicResult.length);
|
||||
// todo
|
||||
// console.log($scope.queryMessageByTopicResult);
|
||||
}else {
|
||||
} else {
|
||||
Notification.error({message: resp.errMsg, delay: 2000});
|
||||
}
|
||||
});
|
||||
@@ -131,26 +135,26 @@ module.controller('messageController', ['$scope', 'ngDialog', '$http','Notificat
|
||||
url: "message/queryMessageByTopicAndKey.query",
|
||||
params: {
|
||||
topic: $scope.selectedTopic,
|
||||
key:$scope.key
|
||||
key: $scope.key
|
||||
}
|
||||
}).success(function (resp) {
|
||||
if (resp.status == 0) {
|
||||
console.log(resp);
|
||||
$scope.queryMessageByTopicAndKeyResult = resp.data;
|
||||
console.log($scope.queryMessageByTopicAndKeyResult);
|
||||
}else {
|
||||
} else {
|
||||
Notification.error({message: resp.errMsg, delay: 2000});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$scope.queryMessageByBrokerAndOffset = function (storeHost,commitLogOffset) {
|
||||
$scope.queryMessageByBrokerAndOffset = function (storeHost, commitLogOffset) {
|
||||
$http({
|
||||
method: "GET",
|
||||
url: "message/viewMessageByBrokerAndOffset.query",
|
||||
params: {
|
||||
brokerHost: storeHost.address,
|
||||
port:storeHost.port,
|
||||
port: storeHost.port,
|
||||
offset: commitLogOffset
|
||||
}
|
||||
}).success(function (resp) {
|
||||
@@ -167,13 +171,13 @@ module.controller('messageController', ['$scope', 'ngDialog', '$http','Notificat
|
||||
});
|
||||
};
|
||||
|
||||
$scope.queryMessageByMessageId = function (messageId,topic) {
|
||||
$scope.queryMessageByMessageId = function (messageId, topic) {
|
||||
$http({
|
||||
method: "GET",
|
||||
url: "message/viewMessage.query",
|
||||
params: {
|
||||
msgId: messageId,
|
||||
topic:topic
|
||||
topic: topic
|
||||
}
|
||||
}).success(function (resp) {
|
||||
if (resp.status == 0) {
|
||||
@@ -181,23 +185,23 @@ module.controller('messageController', ['$scope', 'ngDialog', '$http','Notificat
|
||||
ngDialog.open({
|
||||
template: 'messageDetailViewDialog',
|
||||
controller: 'messageDetailViewDialogController',
|
||||
data:resp.data
|
||||
data: resp.data
|
||||
});
|
||||
}else {
|
||||
} else {
|
||||
Notification.error({message: resp.errMsg, delay: 2000});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$scope.changeShowMessageList = function (currentPage,totalItem) {
|
||||
$scope.changeShowMessageList = function (currentPage, totalItem) {
|
||||
var perPage = $scope.paginationConf.itemsPerPage;
|
||||
var from = (currentPage - 1) * perPage;
|
||||
var to = (from + perPage)>totalItem?totalItem:from + perPage;
|
||||
var to = (from + perPage) > totalItem ? totalItem : from + perPage;
|
||||
$scope.messageShowList = $scope.queryMessageByTopicResult.slice(from, to);
|
||||
$scope.paginationConf.totalItems = totalItem ;
|
||||
$scope.paginationConf.totalItems = totalItem;
|
||||
};
|
||||
|
||||
$scope.onChangeQueryCondition = function (){
|
||||
$scope.onChangeQueryCondition = function () {
|
||||
console.log("change")
|
||||
$scope.taskId = "";
|
||||
$scope.paginationConf.currentPage = 1;
|
||||
@@ -205,12 +209,13 @@ module.controller('messageController', ['$scope', 'ngDialog', '$http','Notificat
|
||||
}
|
||||
}]);
|
||||
|
||||
module.controller('messageDetailViewDialogController',['$scope', 'ngDialog', '$http','Notification', function ($scope, ngDialog, $http,Notification) {
|
||||
|
||||
$scope.resendMessage = function (messageView,consumerGroup) {
|
||||
module.controller('messageDetailViewDialogController', ['$scope', 'ngDialog', '$http', 'Notification', function ($scope, ngDialog, $http, Notification) {
|
||||
$scope.messageTrackList = $scope.ngDialogData.messageTrackList;
|
||||
$scope.messageTrackShowList = $scope.ngDialogData.messageTrackList;
|
||||
$scope.resendMessage = function (messageView, consumerGroup) {
|
||||
var topic = messageView.topic;
|
||||
var msgId = messageView.msgId;
|
||||
console.log('==='+topic+'==='+msgId);
|
||||
console.log('===' + topic + '===' + msgId);
|
||||
if (topic.startsWith('%DLQ%')) {
|
||||
if (messageView.properties.hasOwnProperty("RETRY_TOPIC")) {
|
||||
topic = messageView.properties.RETRY_TOPIC;
|
||||
@@ -220,44 +225,56 @@ module.controller('messageDetailViewDialogController',['$scope', 'ngDialog', '$h
|
||||
}
|
||||
|
||||
}
|
||||
console.log('==='+topic+'==='+msgId);
|
||||
console.log('===' + topic + '===' + msgId);
|
||||
$http({
|
||||
method: "POST",
|
||||
url: "message/consumeMessageDirectly.do",
|
||||
params: {
|
||||
msgId: msgId,
|
||||
consumerGroup:consumerGroup,
|
||||
topic:topic
|
||||
consumerGroup: consumerGroup,
|
||||
topic: topic
|
||||
}
|
||||
}).success(function (resp) {
|
||||
if (resp.status == 0) {
|
||||
ngDialog.open({
|
||||
template: 'operationResultDialog',
|
||||
data:{
|
||||
result:resp.data
|
||||
data: {
|
||||
result: resp.data
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
ngDialog.open({
|
||||
template: 'operationResultDialog',
|
||||
data:{
|
||||
result:resp.errMsg
|
||||
data: {
|
||||
result: resp.errMsg
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
$scope.showExceptionDesc = function (errmsg) {
|
||||
if(errmsg == null){
|
||||
if (errmsg == null) {
|
||||
errmsg = "Don't have Exception"
|
||||
}
|
||||
ngDialog.open({
|
||||
template: 'operationResultDialog',
|
||||
data:{
|
||||
result:errmsg
|
||||
data: {
|
||||
result: errmsg
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$scope.filterConsumerGroup = "";
|
||||
$scope.$watch('filterConsumerGroup', function () {
|
||||
const lowExceptStr = $scope.filterConsumerGroup.toLowerCase();
|
||||
const canShowList = [];
|
||||
|
||||
$scope.messageTrackList.forEach(function (element) {
|
||||
if (element.consumerGroup.toLowerCase().indexOf(lowExceptStr) != -1) {
|
||||
canShowList.push(element);
|
||||
}
|
||||
});
|
||||
$scope.messageTrackShowList = canShowList;
|
||||
});
|
||||
}]
|
||||
);
|
@@ -26,9 +26,13 @@ const TIME_FORMAT_PATTERN = "YYYY-MM-DD HH:mm:ss.SSS";
|
||||
const DEFAULT_DISPLAY_DURATION = 10 * 1000
|
||||
// transactionTraceNode do not have costTime, assume it cost 50ms
|
||||
const TRANSACTION_CHECK_COST_TIME = 50;
|
||||
const RETRY_GROUP_TOPIC_PREFIX = "%RETRY%";
|
||||
const DLQ_GROUP_TOPIC_PREFIX = "%DLQ%";
|
||||
module.controller('messageTraceController', ['$scope', '$routeParams', 'ngDialog', '$http', 'Notification', function ($scope, $routeParams, ngDialog, $http, Notification) {
|
||||
$scope.allTopicList = [];
|
||||
$scope.selectedTopic = [];
|
||||
$scope.allTraceTopicList = [];
|
||||
$scope.selectedTraceTopic = [];
|
||||
$scope.key = "";
|
||||
$scope.messageId = $routeParams.messageId;
|
||||
$scope.queryMessageByTopicAndKeyResult = [];
|
||||
@@ -39,16 +43,25 @@ module.controller('messageTraceController', ['$scope', '$routeParams', 'ngDialog
|
||||
method: "GET",
|
||||
url: "topic/list.query",
|
||||
params: {
|
||||
skipSysProcess: "true"
|
||||
skipSysProcess: true
|
||||
}
|
||||
}).success(function (resp) {
|
||||
if (resp.status == 0) {
|
||||
$scope.allTopicList = resp.data.topicList.sort();
|
||||
console.log($scope.allTopicList);
|
||||
console.log($scope.allTopicList)
|
||||
for (const topic of $scope.allTopicList) {
|
||||
if (topic.startsWith(RETRY_GROUP_TOPIC_PREFIX)
|
||||
|| topic.startsWith(DLQ_GROUP_TOPIC_PREFIX)) {
|
||||
continue;
|
||||
}
|
||||
$scope.allTraceTopicList.push(topic);
|
||||
}
|
||||
console.log($scope.allTraceTopicList)
|
||||
} else {
|
||||
Notification.error({message: resp.errMsg, delay: 2000});
|
||||
}
|
||||
});
|
||||
|
||||
$scope.timepickerBegin = moment().subtract(1, 'hour').format('YYYY-MM-DD HH:mm');
|
||||
$scope.timepickerEnd = moment().add(1, 'hour').format('YYYY-MM-DD HH:mm');
|
||||
$scope.timepickerOptions = {format: 'YYYY-MM-DD HH:mm', showClear: true};
|
||||
@@ -99,7 +112,7 @@ module.controller('messageTraceController', ['$scope', '$routeParams', 'ngDialog
|
||||
url: "messageTrace/viewMessageTraceGraph.query",
|
||||
params: {
|
||||
msgId: messageId,
|
||||
topic: topic
|
||||
traceTopic: topic
|
||||
}
|
||||
}).success(function (resp) {
|
||||
if (resp.status == 0) {
|
||||
|
@@ -15,45 +15,74 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
app.controller('opsController', ['$scope','$location','$http','Notification','remoteApi','tools', function ($scope,$location,$http,Notification,remoteApi,tools) {
|
||||
$scope.namesvrAddrList = "";
|
||||
app.controller('opsController', ['$scope', '$location', '$http', 'Notification', 'remoteApi', 'tools', '$window', function ($scope, $location, $http, Notification, remoteApi, tools, $window) {
|
||||
$scope.namesvrAddrList = [];
|
||||
$scope.useVIPChannel = true;
|
||||
$scope.useTLS = false;
|
||||
$scope.userRole = $window.sessionStorage.getItem("userrole");
|
||||
$scope.writeOperationEnabled = $scope.userRole == null ? true : ($scope.userRole == 1 ? true : false);
|
||||
$scope.inputReadonly = !$scope.writeOperationEnabled;
|
||||
$scope.newNamesrvAddr = "";
|
||||
$http({
|
||||
method: "GET",
|
||||
url: "ops/homePage.query"
|
||||
}).success(function (resp) {
|
||||
if (resp.status == 0) {
|
||||
$scope.namesvrAddrList = resp.data.namesvrAddrList.join(";");
|
||||
$scope.namesvrAddrList = resp.data.namesvrAddrList;
|
||||
$scope.useVIPChannel = resp.data.useVIPChannel;
|
||||
$scope.useTLS = resp.data.useTLS;
|
||||
}else{
|
||||
$scope.selectedNamesrv = resp.data.currentNamesrv;
|
||||
} else {
|
||||
Notification.error({message: resp.errMsg, delay: 2000});
|
||||
}
|
||||
});
|
||||
|
||||
$scope.eleChange = function (data){
|
||||
$scope.namesvrAddrList = data;
|
||||
}
|
||||
|
||||
$scope.updateNameSvrAddr = function () {
|
||||
$http({
|
||||
method: "POST",
|
||||
url: "ops/updateNameSvrAddr.do",
|
||||
params:{nameSvrAddrList:$scope.namesvrAddrList}
|
||||
params: {nameSvrAddrList: $scope.selectedNamesrv}
|
||||
}).success(function (resp) {
|
||||
if (resp.status == 0) {
|
||||
Notification.info({message: "SUCCESS", delay: 2000});
|
||||
}else{
|
||||
} else {
|
||||
Notification.error({message: resp.errMsg, delay: 2000});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$scope.addNameSvrAddr = function () {
|
||||
$http({
|
||||
method: "POST",
|
||||
url: "ops/addNameSvrAddr.do",
|
||||
params: {newNamesrvAddr: $scope.newNamesrvAddr}
|
||||
}).success(function (resp) {
|
||||
if (resp.status == 0) {
|
||||
if ($scope.namesvrAddrList.indexOf($scope.newNamesrvAddr) == -1) {
|
||||
$scope.namesvrAddrList.push($scope.newNamesrvAddr);
|
||||
}
|
||||
$("#namesrvAddr").val("");
|
||||
$scope.newNamesrvAddr = "";
|
||||
Notification.info({message: "SUCCESS", delay: 2000});
|
||||
} else {
|
||||
Notification.error({message: resp.errMsg, delay: 2000});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$scope.updateIsVIPChannel = function () {
|
||||
$http({
|
||||
method: "POST",
|
||||
url: "ops/updateIsVIPChannel.do",
|
||||
params:{useVIPChannel:$scope.useVIPChannel}
|
||||
params: {useVIPChannel: $scope.useVIPChannel}
|
||||
}).success(function (resp) {
|
||||
if (resp.status == 0) {
|
||||
Notification.info({message: "SUCCESS", delay: 2000});
|
||||
}else{
|
||||
} else {
|
||||
Notification.error({message: resp.errMsg, delay: 2000});
|
||||
}
|
||||
});
|
||||
@@ -62,11 +91,11 @@ app.controller('opsController', ['$scope','$location','$http','Notification','re
|
||||
$http({
|
||||
method: "POST",
|
||||
url: "ops/updateUseTLS.do",
|
||||
params:{useTLS:$scope.useTLS}
|
||||
params: {useTLS: $scope.useTLS}
|
||||
}).success(function (resp) {
|
||||
if (resp.status == 0) {
|
||||
Notification.info({message: "SUCCESS", delay: 2000});
|
||||
}else{
|
||||
} else {
|
||||
Notification.error({message: resp.errMsg, delay: 2000});
|
||||
}
|
||||
});
|
||||
|
@@ -23,7 +23,7 @@ module.controller('producerController', ['$scope', '$http','Notification',functi
|
||||
method: "GET",
|
||||
url: "topic/list.query",
|
||||
params:{
|
||||
skipSysProcess:"true"
|
||||
skipSysProcess: true
|
||||
}
|
||||
}).success(function (resp) {
|
||||
if(resp.status ==0){
|
||||
|
@@ -54,7 +54,7 @@ app.service('remoteApi', ['$http','tools', function ($http,tools) {
|
||||
}
|
||||
|
||||
var queryTopicCurrentData = function(callback){
|
||||
var url = 'dashboard/topicCurrent';
|
||||
var url = 'dashboard/topicCurrent.query';
|
||||
var setting = {
|
||||
type: "GET",
|
||||
timeout:15000,//data is too large,so master set time out is long enough
|
||||
|
@@ -1,23 +1,37 @@
|
||||
/**
|
||||
* Created by tcrow on 2017/1/12 0012.
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
var module = app;
|
||||
|
||||
module.directive('ngConfirmClick', [
|
||||
function(){
|
||||
function () {
|
||||
return {
|
||||
link: function (scope, element, attr) {
|
||||
var msg = attr.ngConfirmClick || "Are you sure?";
|
||||
var clickAction = attr.confirmedClick;
|
||||
element.bind('click',function (event) {
|
||||
if ( window.confirm(msg) ) {
|
||||
element.bind('click', function (event) {
|
||||
if (window.confirm(msg)) {
|
||||
scope.$eval(clickAction)
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}]);
|
||||
module.controller('topicController', ['$scope', 'ngDialog', '$http','Notification',function ($scope, ngDialog, $http,Notification) {
|
||||
module.controller('topicController', ['$scope', 'ngDialog', '$http', 'Notification', '$window', function ($scope, ngDialog, $http, Notification, $window) {
|
||||
$scope.paginationConf = {
|
||||
currentPage: 1,
|
||||
totalItems: 0,
|
||||
@@ -26,7 +40,7 @@ module.controller('topicController', ['$scope', 'ngDialog', '$http','Notificatio
|
||||
perPageOptions: [10],
|
||||
rememberPerPage: 'perPageItems',
|
||||
onChange: function () {
|
||||
$scope.showTopicList(this.currentPage,this.totalItems);
|
||||
$scope.showTopicList(this.currentPage, this.totalItems);
|
||||
|
||||
}
|
||||
};
|
||||
@@ -36,19 +50,21 @@ module.controller('topicController', ['$scope', 'ngDialog', '$http','Notificatio
|
||||
$scope.filterSystem = false
|
||||
$scope.allTopicList = [];
|
||||
$scope.topicShowList = [];
|
||||
$scope.userRole = $window.sessionStorage.getItem("userrole");
|
||||
$scope.writeOperationEnabled = $scope.userRole == null ? true : ($scope.userRole == 1 ? true : false);
|
||||
|
||||
$scope.refreshTopicList = function () {
|
||||
$http({
|
||||
method: "GET",
|
||||
url: "topic/list.query"
|
||||
}).success(function (resp) {
|
||||
if(resp.status ==0){
|
||||
if (resp.status == 0) {
|
||||
$scope.allTopicList = resp.data.topicList.sort();
|
||||
console.log($scope.allTopicList);
|
||||
console.log(JSON.stringify(resp));
|
||||
$scope.showTopicList(1,$scope.allTopicList.length);
|
||||
$scope.showTopicList(1, $scope.allTopicList.length);
|
||||
|
||||
}else {
|
||||
} else {
|
||||
Notification.error({message: resp.errMsg, delay: 5000});
|
||||
}
|
||||
});
|
||||
@@ -56,93 +72,93 @@ module.controller('topicController', ['$scope', 'ngDialog', '$http','Notificatio
|
||||
|
||||
$scope.refreshTopicList();
|
||||
|
||||
$scope.filterStr="";
|
||||
$scope.$watch('filterStr', function() {
|
||||
$scope.filterStr = "";
|
||||
$scope.$watch('filterStr', function () {
|
||||
$scope.filterList(1);
|
||||
});
|
||||
$scope.$watch('filterNormal', function() {
|
||||
$scope.$watch('filterNormal', function () {
|
||||
$scope.filterList(1);
|
||||
});
|
||||
$scope.$watch('filterRetry', function() {
|
||||
$scope.$watch('filterRetry', function () {
|
||||
$scope.filterList(1);
|
||||
});
|
||||
$scope.$watch('filterDLQ', function() {
|
||||
$scope.$watch('filterDLQ', function () {
|
||||
$scope.filterList(1);
|
||||
});
|
||||
$scope.$watch('filterSystem', function() {
|
||||
$scope.$watch('filterSystem', function () {
|
||||
$scope.filterList(1);
|
||||
});
|
||||
$scope.filterList = function (currentPage) {
|
||||
var lowExceptStr = $scope.filterStr.toLowerCase();
|
||||
var canShowList = [];
|
||||
|
||||
$scope.allTopicList.forEach(function(element) {
|
||||
if($scope.filterByType(element)){
|
||||
if (element.toLowerCase().indexOf(lowExceptStr) != -1){
|
||||
$scope.allTopicList.forEach(function (element) {
|
||||
if ($scope.filterByType(element)) {
|
||||
if (element.toLowerCase().indexOf(lowExceptStr) != -1) {
|
||||
canShowList.push(element);
|
||||
}
|
||||
}
|
||||
});
|
||||
$scope.paginationConf.totalItems =canShowList.length;
|
||||
$scope.paginationConf.totalItems = canShowList.length;
|
||||
var perPage = $scope.paginationConf.itemsPerPage;
|
||||
var from = (currentPage - 1) * perPage;
|
||||
var to = (from + perPage)>canShowList.length?canShowList.length:from + perPage;
|
||||
var to = (from + perPage) > canShowList.length ? canShowList.length : from + perPage;
|
||||
$scope.topicShowList = canShowList.slice(from, to);
|
||||
};
|
||||
|
||||
$scope.filterByType = function(str){
|
||||
if($scope.filterRetry){
|
||||
if(str.startsWith("%R")){
|
||||
$scope.filterByType = function (str) {
|
||||
if ($scope.filterRetry) {
|
||||
if (str.startsWith("%R")) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
if($scope.filterDLQ){
|
||||
if(str.startsWith("%D")){
|
||||
if ($scope.filterDLQ) {
|
||||
if (str.startsWith("%D")) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
if($scope.filterSystem){
|
||||
if(str.startsWith("%S")){
|
||||
if ($scope.filterSystem) {
|
||||
if (str.startsWith("%S")) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
if($scope.filterNormal){
|
||||
if(str.startsWith("%") == false){
|
||||
if ($scope.filterNormal) {
|
||||
if (str.startsWith("%") == false) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
$scope.showTopicList = function (currentPage,totalItem) {
|
||||
if($scope.filterStr != ""){
|
||||
$scope.showTopicList = function (currentPage, totalItem) {
|
||||
if ($scope.filterStr != "") {
|
||||
$scope.filterList(currentPage);
|
||||
return;
|
||||
}
|
||||
var perPage = $scope.paginationConf.itemsPerPage;
|
||||
var from = (currentPage - 1) * perPage;
|
||||
var to = (from + perPage)>totalItem?totalItem:from + perPage;
|
||||
var to = (from + perPage) > totalItem ? totalItem : from + perPage;
|
||||
console.log($scope.allTopicList);
|
||||
console.log(from)
|
||||
console.log(to)
|
||||
$scope.topicShowList = $scope.allTopicList.slice(from, to);
|
||||
$scope.paginationConf.totalItems = totalItem ;
|
||||
$scope.paginationConf.totalItems = totalItem;
|
||||
console.log($scope.topicShowList)
|
||||
console.log($scope.paginationConf.totalItems)
|
||||
$scope.filterList(currentPage);
|
||||
};
|
||||
$scope.deleteTopic= function (topic) {
|
||||
$scope.deleteTopic = function (topic) {
|
||||
$http({
|
||||
method: "POST",
|
||||
url: "topic/deleteTopic.do",
|
||||
params:{
|
||||
topic:topic
|
||||
params: {
|
||||
topic: topic
|
||||
}
|
||||
}).success(function (resp) {
|
||||
if(resp.status ==0){
|
||||
if (resp.status == 0) {
|
||||
Notification.info({message: "delete success!", delay: 2000});
|
||||
$scope.refreshTopicList();
|
||||
}else {
|
||||
} else {
|
||||
Notification.error({message: resp.errMsg, delay: 2000});
|
||||
}
|
||||
});
|
||||
@@ -157,13 +173,13 @@ module.controller('topicController', ['$scope', 'ngDialog', '$http','Notificatio
|
||||
console.log(JSON.stringify(resp));
|
||||
ngDialog.open({
|
||||
template: 'statsViewDialog',
|
||||
trapFocus:false,
|
||||
data:{
|
||||
topic:topic,
|
||||
statsData:resp.data
|
||||
trapFocus: false,
|
||||
data: {
|
||||
topic: topic,
|
||||
statsData: resp.data
|
||||
}
|
||||
});
|
||||
}else {
|
||||
} else {
|
||||
Notification.error({message: resp.errMsg, delay: 2000});
|
||||
}
|
||||
})
|
||||
@@ -179,13 +195,13 @@ module.controller('topicController', ['$scope', 'ngDialog', '$http','Notificatio
|
||||
ngDialog.open({
|
||||
template: 'routerViewDialog',
|
||||
controller: 'routerViewDialogController',
|
||||
trapFocus:false,
|
||||
data:{
|
||||
topic:topic,
|
||||
routeData:resp.data
|
||||
trapFocus: false,
|
||||
data: {
|
||||
topic: topic,
|
||||
routeData: resp.data
|
||||
}
|
||||
});
|
||||
}else {
|
||||
} else {
|
||||
Notification.error({message: resp.errMsg, delay: 2000});
|
||||
}
|
||||
})
|
||||
@@ -202,13 +218,13 @@ module.controller('topicController', ['$scope', 'ngDialog', '$http','Notificatio
|
||||
console.log(JSON.stringify(resp));
|
||||
ngDialog.open({
|
||||
template: 'consumerViewDialog',
|
||||
data:{
|
||||
topic:topic,
|
||||
consumerData:resp.data,
|
||||
consumerGroupCount:Object.keys(resp.data).length
|
||||
data: {
|
||||
topic: topic,
|
||||
consumerData: resp.data,
|
||||
consumerGroupCount: Object.keys(resp.data).length
|
||||
}
|
||||
});
|
||||
}else {
|
||||
} else {
|
||||
Notification.error({message: resp.errMsg, delay: 2000});
|
||||
}
|
||||
})
|
||||
@@ -217,9 +233,9 @@ module.controller('topicController', ['$scope', 'ngDialog', '$http','Notificatio
|
||||
ngDialog.open({
|
||||
template: 'deleteTopicDialog',
|
||||
controller: 'deleteTopicDialogController',
|
||||
data:{
|
||||
topic:topic,
|
||||
consumerData:"asd"
|
||||
data: {
|
||||
topic: topic,
|
||||
consumerData: "asd"
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -229,25 +245,25 @@ module.controller('topicController', ['$scope', 'ngDialog', '$http','Notificatio
|
||||
$http({
|
||||
method: "GET",
|
||||
url: "topic/queryTopicConsumerInfo.query",
|
||||
params:{
|
||||
topic:topic
|
||||
params: {
|
||||
topic: topic
|
||||
}
|
||||
}).success(function (resp) {
|
||||
if(resp.status ==0){
|
||||
if(resp.data.groupList == null){
|
||||
if (resp.status == 0) {
|
||||
if (resp.data.groupList == null) {
|
||||
Notification.error({message: "don't have consume group!", delay: 2000});
|
||||
return
|
||||
}
|
||||
ngDialog.open({
|
||||
template: 'consumerResetOffsetDialog',
|
||||
controller: 'consumerResetOffsetDialogController',
|
||||
data:{
|
||||
data: {
|
||||
topic: topic,
|
||||
selectedConsumerGroup:[],
|
||||
allConsumerGroupList:resp.data.groupList
|
||||
selectedConsumerGroup: [],
|
||||
allConsumerGroupList: resp.data.groupList
|
||||
}
|
||||
});
|
||||
}else {
|
||||
} else {
|
||||
Notification.error({message: resp.errMsg, delay: 2000});
|
||||
}
|
||||
});
|
||||
@@ -258,25 +274,25 @@ module.controller('topicController', ['$scope', 'ngDialog', '$http','Notificatio
|
||||
$http({
|
||||
method: "GET",
|
||||
url: "topic/queryTopicConsumerInfo.query",
|
||||
params:{
|
||||
topic:topic
|
||||
params: {
|
||||
topic: topic
|
||||
}
|
||||
}).success(function (resp) {
|
||||
if(resp.status ==0){
|
||||
if(resp.data.groupList == null){
|
||||
if (resp.status == 0) {
|
||||
if (resp.data.groupList == null) {
|
||||
Notification.error({message: "don't have consume group!", delay: 2000});
|
||||
return
|
||||
}
|
||||
ngDialog.open({
|
||||
template: 'skipMessageAccumulateDialog',
|
||||
controller: 'skipMessageAccumulateDialogController',
|
||||
data:{
|
||||
data: {
|
||||
topic: topic,
|
||||
selectedConsumerGroup:[],
|
||||
allConsumerGroupList:resp.data.groupList
|
||||
selectedConsumerGroup: [],
|
||||
allConsumerGroupList: resp.data.groupList
|
||||
}
|
||||
});
|
||||
}else {
|
||||
} else {
|
||||
Notification.error({message: resp.errMsg, delay: 2000});
|
||||
}
|
||||
});
|
||||
@@ -296,13 +312,13 @@ module.controller('topicController', ['$scope', 'ngDialog', '$http','Notificatio
|
||||
$http({
|
||||
method: "GET",
|
||||
url: "topic/examineTopicConfig.query",
|
||||
params:{
|
||||
topic:topic
|
||||
params: {
|
||||
topic: topic
|
||||
}
|
||||
}).success(function (resp) {
|
||||
if(resp.status ==0){
|
||||
if (resp.status == 0) {
|
||||
$scope.openCreateOrUpdateDialog(resp.data, sysFlag);
|
||||
}else {
|
||||
} else {
|
||||
Notification.error({message: resp.errMsg, delay: 2000});
|
||||
}
|
||||
});
|
||||
@@ -310,14 +326,14 @@ module.controller('topicController', ['$scope', 'ngDialog', '$http','Notificatio
|
||||
|
||||
$scope.openCreateOrUpdateDialog = function (request, sysFlag) {
|
||||
var bIsUpdate = true;
|
||||
if(request == null){
|
||||
if (request == null) {
|
||||
request = [{
|
||||
writeQueueNums:16,
|
||||
readQueueNums:16,
|
||||
perm:6,
|
||||
order:false,
|
||||
topicName:"",
|
||||
brokerNameList:[]
|
||||
writeQueueNums: 16,
|
||||
readQueueNums: 16,
|
||||
perm: 6,
|
||||
order: false,
|
||||
topicName: "",
|
||||
brokerNameList: []
|
||||
}];
|
||||
bIsUpdate = false;
|
||||
}
|
||||
@@ -325,21 +341,22 @@ module.controller('topicController', ['$scope', 'ngDialog', '$http','Notificatio
|
||||
method: "GET",
|
||||
url: "cluster/list.query"
|
||||
}).success(function (resp) {
|
||||
if(resp.status ==0){
|
||||
if (resp.status == 0) {
|
||||
console.log(resp);
|
||||
ngDialog.open({
|
||||
preCloseCallback: function(value) {
|
||||
preCloseCallback: function (value) {
|
||||
// Refresh topic list
|
||||
$scope.refreshTopicList();
|
||||
},
|
||||
template: 'topicModifyDialog',
|
||||
controller: 'topicModifyDialogController',
|
||||
data:{
|
||||
sysFlag:sysFlag,
|
||||
topicRequestList:request,
|
||||
allClusterNameList:Object.keys(resp.data.clusterInfo.clusterAddrTable),
|
||||
allBrokerNameList:Object.keys(resp.data.brokerServer),
|
||||
bIsUpdate:bIsUpdate
|
||||
data: {
|
||||
sysFlag: sysFlag,
|
||||
topicRequestList: request,
|
||||
allClusterNameList: Object.keys(resp.data.clusterInfo.clusterAddrTable),
|
||||
allBrokerNameList: Object.keys(resp.data.brokerServer),
|
||||
bIsUpdate: bIsUpdate,
|
||||
writeOperationEnabled: $scope.writeOperationEnabled
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -352,7 +369,7 @@ module.controller('topicController', ['$scope', 'ngDialog', '$http','Notificatio
|
||||
|
||||
}]);
|
||||
|
||||
module.controller('topicModifyDialogController', ['$scope', 'ngDialog', '$http','Notification',function ($scope, ngDialog, $http,Notification) {
|
||||
module.controller('topicModifyDialogController', ['$scope', 'ngDialog', '$http', 'Notification', function ($scope, ngDialog, $http, Notification) {
|
||||
$scope.postTopicRequest = function (topicRequestItem) {
|
||||
console.log(topicRequestItem);
|
||||
var request = JSON.parse(JSON.stringify(topicRequestItem));
|
||||
@@ -360,19 +377,19 @@ module.controller('topicModifyDialogController', ['$scope', 'ngDialog', '$http',
|
||||
$http({
|
||||
method: "POST",
|
||||
url: "topic/createOrUpdate.do",
|
||||
data:request
|
||||
data: request
|
||||
}).success(function (resp) {
|
||||
if(resp.status ==0){
|
||||
if (resp.status == 0) {
|
||||
Notification.info({message: "success!", delay: 2000});
|
||||
ngDialog.close(this);
|
||||
}else {
|
||||
} else {
|
||||
Notification.error({message: resp.errMsg, delay: 2000});
|
||||
}
|
||||
});
|
||||
}
|
||||
}]
|
||||
);
|
||||
module.controller('consumerResetOffsetDialogController',['$scope', 'ngDialog', '$http','Notification', function ($scope, ngDialog, $http,Notification) {
|
||||
module.controller('consumerResetOffsetDialogController', ['$scope', 'ngDialog', '$http', 'Notification', function ($scope, ngDialog, $http, Notification) {
|
||||
$scope.timepicker = {};
|
||||
$scope.timepicker.date = moment().format('YYYY-MM-DD HH:mm');
|
||||
$scope.timepicker.options = {format: 'YYYY-MM-DD HH:mm', showClear: true};
|
||||
@@ -386,19 +403,19 @@ module.controller('consumerResetOffsetDialogController',['$scope', 'ngDialog', '
|
||||
data: {
|
||||
resetTime: $scope.timepicker.date.valueOf(),
|
||||
consumerGroupList: $scope.ngDialogData.selectedConsumerGroup,
|
||||
topic:$scope.ngDialogData.topic,
|
||||
force:true
|
||||
topic: $scope.ngDialogData.topic,
|
||||
force: true
|
||||
}
|
||||
}).success(function (resp) {
|
||||
if(resp.status ==0){
|
||||
if (resp.status == 0) {
|
||||
ngDialog.open({
|
||||
template: 'resetOffsetResultDialog',
|
||||
data:{
|
||||
result:resp.data
|
||||
data: {
|
||||
result: resp.data
|
||||
}
|
||||
});
|
||||
ngDialog.close(this);
|
||||
}else {
|
||||
} else {
|
||||
Notification.error({message: resp.errMsg, delay: 2000});
|
||||
}
|
||||
})
|
||||
@@ -406,7 +423,7 @@ module.controller('consumerResetOffsetDialogController',['$scope', 'ngDialog', '
|
||||
}]
|
||||
);
|
||||
|
||||
module.controller('skipMessageAccumulateDialogController',['$scope', 'ngDialog', '$http','Notification', function ($scope, ngDialog, $http,Notification) {
|
||||
module.controller('skipMessageAccumulateDialogController', ['$scope', 'ngDialog', '$http', 'Notification', function ($scope, ngDialog, $http, Notification) {
|
||||
$scope.skipAccumulate = function () {
|
||||
console.log($scope.ngDialogData.selectedConsumerGroup);
|
||||
$http({
|
||||
@@ -415,19 +432,19 @@ module.controller('skipMessageAccumulateDialogController',['$scope', 'ngDialog',
|
||||
data: {
|
||||
resetTime: -1,
|
||||
consumerGroupList: $scope.ngDialogData.selectedConsumerGroup,
|
||||
topic:$scope.ngDialogData.topic,
|
||||
force:true
|
||||
topic: $scope.ngDialogData.topic,
|
||||
force: true
|
||||
}
|
||||
}).success(function (resp) {
|
||||
if(resp.status ==0){
|
||||
if (resp.status == 0) {
|
||||
ngDialog.open({
|
||||
template: 'resetOffsetResultDialog',
|
||||
data:{
|
||||
result:resp.data
|
||||
data: {
|
||||
result: resp.data
|
||||
}
|
||||
});
|
||||
ngDialog.close(this);
|
||||
}else {
|
||||
} else {
|
||||
Notification.error({message: resp.errMsg, delay: 2000});
|
||||
}
|
||||
})
|
||||
@@ -435,7 +452,7 @@ module.controller('skipMessageAccumulateDialogController',['$scope', 'ngDialog',
|
||||
}]
|
||||
);
|
||||
|
||||
module.controller('sendTopicMessageDialogController', ['$scope', 'ngDialog', '$http','Notification',function ($scope, ngDialog, $http,Notification) {
|
||||
module.controller('sendTopicMessageDialogController', ['$scope', 'ngDialog', '$http', 'Notification', function ($scope, ngDialog, $http, Notification) {
|
||||
$scope.sendTopicMessage = {
|
||||
topic: $scope.ngDialogData.topic,
|
||||
key: "key",
|
||||
@@ -449,37 +466,35 @@ module.controller('sendTopicMessageDialogController', ['$scope', 'ngDialog', '$h
|
||||
url: "topic/sendTopicMessage.do",
|
||||
data: $scope.sendTopicMessage
|
||||
}).success(function (resp) {
|
||||
if(resp.status ==0){
|
||||
if (resp.status == 0) {
|
||||
ngDialog.open({
|
||||
template: 'sendResultDialog',
|
||||
data:{
|
||||
result:resp.data
|
||||
data: {
|
||||
result: resp.data
|
||||
}
|
||||
});
|
||||
ngDialog.close(this);
|
||||
}else {
|
||||
} else {
|
||||
Notification.error({message: resp.errMsg, delay: 2000});
|
||||
}
|
||||
})
|
||||
}
|
||||
}]
|
||||
|
||||
);
|
||||
|
||||
module.controller('routerViewDialogController', ['$scope', 'ngDialog', '$http','Notification',function ($scope, ngDialog, $http,Notification) {
|
||||
module.controller('routerViewDialogController', ['$scope', 'ngDialog', '$http', 'Notification', function ($scope, ngDialog, $http, Notification) {
|
||||
$scope.deleteTopicByBroker = function (broker) {
|
||||
$http({
|
||||
method: "POST",
|
||||
url: "topic/deleteTopicByBroker.do",
|
||||
params: {brokerName:broker.brokerName,topic:$scope.ngDialogData.topic}
|
||||
params: {brokerName: broker.brokerName, topic: $scope.ngDialogData.topic}
|
||||
}).success(function (resp) {
|
||||
if(resp.status ==0){
|
||||
if (resp.status == 0) {
|
||||
Notification.info({message: "delete success", delay: 2000});
|
||||
}else {
|
||||
} else {
|
||||
Notification.error({message: resp.errMsg, delay: 2000});
|
||||
}
|
||||
})
|
||||
};
|
||||
}]
|
||||
|
||||
);
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user