mirror of
https://github.com/apache/rocketmq-dashboard.git
synced 2025-09-11 03:49:06 +08:00
Compare commits
39 Commits
rocketmq-d
...
optimze_qu
Author | SHA1 | Date | |
---|---|---|---|
|
e3b769db6a | ||
|
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 |
@@ -9,3 +9,10 @@ github:
|
|||||||
squash: true
|
squash: true
|
||||||
merge: false
|
merge: false
|
||||||
rebase: 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
|
||||||
|
@@ -20,3 +20,5 @@ script:
|
|||||||
# - travis_retry mvn -B package findbugs:findbugs coveralls:report
|
# - travis_retry mvn -B package findbugs:findbugs coveralls:report
|
||||||
#after_success:
|
#after_success:
|
||||||
# - mvn sonar:sonar
|
# - mvn sonar:sonar
|
||||||
|
after_success:
|
||||||
|
- bash <(curl -s https://codecov.io/bash) -t 996e4d8e-8a00-49f2-b649-38c03716d690 || echo 'Codecov failed to upload'
|
||||||
|
2
LICENSE
2
LICENSE
@@ -15,7 +15,7 @@
|
|||||||
"Legal Entity" shall mean the union of the acting entity and all
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
other entities that control, are controlled by, or are under common
|
other entities that control, are controlled by, or are under common
|
||||||
control with that entity. For the purposes of this definition,
|
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
|
direction or management of such entity, whether by contract or
|
||||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
2
NOTICE
2
NOTICE
@@ -1,5 +1,5 @@
|
|||||||
Apache RocketMQ
|
Apache RocketMQ
|
||||||
Copyright 2016-2021 The Apache Software Foundation
|
Copyright 2016-2022 The Apache Software Foundation
|
||||||
|
|
||||||
This product includes software developed at
|
This product includes software developed at
|
||||||
The Apache Software Foundation (http://www.apache.org/).
|
The Apache Software Foundation (http://www.apache.org/).
|
||||||
|
47
README.md
47
README.md
@@ -1,46 +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://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 "Average time to resolve an issue")
|
||||||
[](http://isitmaintained.com/project/apache/rocketmq-dashboard "Percentage of issues still open")
|
[](http://isitmaintained.com/project/apache/rocketmq-dashboard "Percentage of issues still open")
|
||||||
[](https://twitter.com/intent/follow?screen_name=ApacheRocketMQ)
|
[](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)
|
||||||
|
|
||||||
```
|
```shell
|
||||||
mvn clean package -Dmaven.test.skip=true docker:build
|
docker pull apacherocketmq/rocketmq-dashboard:latest
|
||||||
```
|
```
|
||||||
|
|
||||||
or
|
#### Run it (use your own `rocketmq.namesrv.addr` and `port`)
|
||||||
|
|
||||||
```
|
```shell
|
||||||
docker pull apacherocketmq/rocketmq-console:2.0.0
|
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;
|
||||||
|
|
||||||
```
|
#### Maven spring-boot run
|
||||||
docker run -e "JAVA_OPTS=-Drocketmq.namesrv.addr=127.0.0.1:9876 -Dcom.rocketmq.sendMessageWithVIPChannel=false" -p 8080:8080 -t apacherocketmq/rocketmq-console:2.0.0
|
|
||||||
```
|
|
||||||
|
|
||||||
### Without Docker
|
```shell
|
||||||
require java 1.8+
|
|
||||||
```
|
|
||||||
mvn spring-boot:run
|
mvn spring-boot:run
|
||||||
```
|
```
|
||||||
or
|
or
|
||||||
```
|
|
||||||
|
#### Maven build and run
|
||||||
|
|
||||||
|
```shell
|
||||||
mvn clean package -Dmaven.test.skip=true
|
mvn clean package -Dmaven.test.skip=true
|
||||||
java -jar target/rocketmq-dashboard-1.0.1-SNAPSHOT.jar
|
java -jar target/rocketmq-dashboard-1.0.1-SNAPSHOT.jar
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Tips
|
#### 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>
|
<mirrors>
|
||||||
@@ -53,8 +57,7 @@ java -jar target/rocketmq-dashboard-1.0.1-SNAPSHOT.jar
|
|||||||
</mirrors>
|
</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
|
## UserGuide
|
||||||
|
|
||||||
@@ -64,7 +67,7 @@ java -jar target/rocketmq-dashboard-1.0.1-SNAPSHOT.jar
|
|||||||
|
|
||||||
## Contributing
|
## 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
|
## License
|
||||||
[Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.html) Copyright (C) Apache Software Foundation
|
[Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.html) Copyright (C) Apache Software Foundation
|
||||||
|
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*
|
16464
frontend/package-lock.json
generated
Normal file
16464
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": {}
|
||||||
|
}
|
121
pom.xml
121
pom.xml
@@ -28,14 +28,14 @@
|
|||||||
<groupId>org.apache.rocketmq</groupId>
|
<groupId>org.apache.rocketmq</groupId>
|
||||||
<artifactId>rocketmq-dashboard</artifactId>
|
<artifactId>rocketmq-dashboard</artifactId>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
<version>1.0.0</version>
|
<version>1.0.1-SNAPSHOT</version>
|
||||||
<name>rocketmq-dashboard</name>
|
<name>rocketmq-dashboard</name>
|
||||||
|
|
||||||
<scm>
|
<scm>
|
||||||
<url>git@github.com:apache/rocketmq-dashboard.git</url>
|
<url>git@github.com:apache/rocketmq-dashboard.git</url>
|
||||||
<connection>scm:git:git@github.com:apache/rocketmq-dashboard.git</connection>
|
<connection>scm:git:git@github.com:apache/rocketmq-dashboard.git</connection>
|
||||||
<developerConnection>scm:git:git@github.com:apache/rocketmq-dashboard.git</developerConnection>
|
<developerConnection>scm:git:git@github.com:apache/rocketmq-dashboard.git</developerConnection>
|
||||||
<tag>rocketmq-dashboard-1.0.0</tag>
|
<tag>1.0.0</tag>
|
||||||
</scm>
|
</scm>
|
||||||
|
|
||||||
<mailingLists>
|
<mailingLists>
|
||||||
@@ -90,16 +90,24 @@
|
|||||||
<commons-lang.version>2.6</commons-lang.version>
|
<commons-lang.version>2.6</commons-lang.version>
|
||||||
<commons-io.version>2.4</commons-io.version>
|
<commons-io.version>2.4</commons-io.version>
|
||||||
<commons-cli.version>1.2</commons-cli.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>
|
<surefire.version>2.19.1</surefire.version>
|
||||||
<aspectj.version>1.9.6</aspectj.version>
|
<aspectj.version>1.9.6</aspectj.version>
|
||||||
<lombok.version>1.18.12</lombok.version>
|
<lombok.version>1.18.12</lombok.version>
|
||||||
<main.basedir>${basedir}/../..</main.basedir>
|
<main.basedir>${basedir}/../..</main.basedir>
|
||||||
<docker.image.prefix>apacherocketmq</docker.image.prefix>
|
<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>
|
<mockito-inline.version>3.3.3</mockito-inline.version>
|
||||||
<jaxb-api.version>2.3.1</jaxb-api.version>
|
<jaxb-api.version>2.3.1</jaxb-api.version>
|
||||||
<commons-pool2.version>2.4.3</commons-pool2.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>
|
</properties>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
@@ -123,11 +131,22 @@
|
|||||||
<artifactId>spring-boot-starter-test</artifactId>
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
<version>${spring.boot.version}</version>
|
<version>${spring.boot.version}</version>
|
||||||
<scope>test</scope>
|
<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>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>commons-collections</groupId>
|
<groupId>commons-collections</groupId>
|
||||||
<artifactId>commons-collections</artifactId>
|
<artifactId>commons-collections</artifactId>
|
||||||
<version>3.2.2</version>
|
<version>${commons-collections.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.rocketmq</groupId>
|
<groupId>org.apache.rocketmq</groupId>
|
||||||
@@ -201,17 +220,17 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>cglib</groupId>
|
<groupId>cglib</groupId>
|
||||||
<artifactId>cglib</artifactId>
|
<artifactId>cglib</artifactId>
|
||||||
<version>2.2.2</version>
|
<version>${cglib.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.jooq</groupId>
|
<groupId>org.jooq</groupId>
|
||||||
<artifactId>joor</artifactId>
|
<artifactId>joor</artifactId>
|
||||||
<version>0.9.6</version>
|
<version>${joor.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.bouncycastle</groupId>
|
<groupId>org.bouncycastle</groupId>
|
||||||
<artifactId>bcpkix-jdk15on</artifactId>
|
<artifactId>bcpkix-jdk15on</artifactId>
|
||||||
<version>1.68</version>
|
<version>${bcpkix-jdk15on.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>javax.xml.bind</groupId>
|
<groupId>javax.xml.bind</groupId>
|
||||||
@@ -234,6 +253,28 @@
|
|||||||
<artifactId>commons-pool2</artifactId>
|
<artifactId>commons-pool2</artifactId>
|
||||||
<version>${commons-pool2.version}</version>
|
<version>${commons-pool2.version}</version>
|
||||||
</dependency>
|
</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>
|
</dependencies>
|
||||||
<build>
|
<build>
|
||||||
<plugins>
|
<plugins>
|
||||||
@@ -361,9 +402,73 @@
|
|||||||
<exclude>docs/**</exclude>
|
<exclude>docs/**</exclude>
|
||||||
<exclude>src/main/resources/static/vendor/**</exclude>
|
<exclude>src/main/resources/static/vendor/**</exclude>
|
||||||
<exclude>src/main/resources/static/src/data/**</exclude>
|
<exclude>src/main/resources/static/src/data/**</exclude>
|
||||||
|
<exclude>frontend/node_modules/**</exclude>
|
||||||
|
<exclude>frontend/build/**</exclude>
|
||||||
|
<exclude>frontend/**.json</exclude>
|
||||||
|
<exclude>frontend/**.lock</exclude>
|
||||||
|
<exclude>frontend/public/manifest.json</exclude>
|
||||||
|
<exclude>package-lock.json</exclude>
|
||||||
</excludes>
|
</excludes>
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</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>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
</project>
|
</project>
|
||||||
|
@@ -16,6 +16,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.apache.rocketmq.dashboard.admin;
|
package org.apache.rocketmq.dashboard.admin;
|
||||||
|
|
||||||
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.rocketmq.acl.common.AclClientRPCHook;
|
import org.apache.rocketmq.acl.common.AclClientRPCHook;
|
||||||
@@ -33,6 +34,8 @@ public class MQAdminFactory {
|
|||||||
this.rmqConfigure = rmqConfigure;
|
this.rmqConfigure = rmqConfigure;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final AtomicLong adminIndex = new AtomicLong(0);
|
||||||
|
|
||||||
public MQAdminExt getInstance() throws Exception {
|
public MQAdminExt getInstance() throws Exception {
|
||||||
RPCHook rpcHook = null;
|
RPCHook rpcHook = null;
|
||||||
final String accessKey = rmqConfigure.getAccessKey();
|
final String accessKey = rmqConfigure.getAccessKey();
|
||||||
@@ -47,6 +50,7 @@ public class MQAdminFactory {
|
|||||||
} else {
|
} else {
|
||||||
mqAdminExt = new DefaultMQAdminExt(rpcHook, rmqConfigure.getTimeoutMillis());
|
mqAdminExt = new DefaultMQAdminExt(rpcHook, rmqConfigure.getTimeoutMillis());
|
||||||
}
|
}
|
||||||
|
mqAdminExt.setAdminExtGroup(mqAdminExt.getAdminExtGroup() + "_" + adminIndex.getAndIncrement());
|
||||||
mqAdminExt.setVipChannelEnabled(Boolean.parseBoolean(rmqConfigure.getIsVIPChannel()));
|
mqAdminExt.setVipChannelEnabled(Boolean.parseBoolean(rmqConfigure.getIsVIPChannel()));
|
||||||
mqAdminExt.setUseTLS(rmqConfigure.isUseTLS());
|
mqAdminExt.setUseTLS(rmqConfigure.isUseTLS());
|
||||||
mqAdminExt.setInstanceName(Long.toString(System.currentTimeMillis()));
|
mqAdminExt.setInstanceName(Long.toString(System.currentTimeMillis()));
|
||||||
|
@@ -30,6 +30,7 @@ import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
|||||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||||
import org.springframework.web.multipart.support.MissingServletRequestPartException;
|
import org.springframework.web.multipart.support.MissingServletRequestPartException;
|
||||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
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 org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
@@ -49,17 +50,19 @@ public class AuthWebMVCConfigurerAdapter extends WebMvcConfigurerAdapter {
|
|||||||
public void addInterceptors(InterceptorRegistry registry) {
|
public void addInterceptors(InterceptorRegistry registry) {
|
||||||
if (configure.isLoginRequired()) {
|
if (configure.isLoginRequired()) {
|
||||||
registry.addInterceptor(authInterceptor).addPathPatterns(
|
registry.addInterceptor(authInterceptor).addPathPatterns(
|
||||||
"/cluster/**",
|
"/cluster/**",
|
||||||
"/consumer/**",
|
"/consumer/**",
|
||||||
"/dashboard/**",
|
"/dashboard/**",
|
||||||
"/message/**",
|
"/dlqMessage/**",
|
||||||
"/messageTrace/**",
|
"/message/**",
|
||||||
"/monitor/**",
|
"/messageTrace/**",
|
||||||
"/rocketmq/**",
|
"/monitor/**",
|
||||||
"/ops/**",
|
"/rocketmq/**",
|
||||||
"/producer/**",
|
"/ops/**",
|
||||||
"/test/**",
|
"/producer/**",
|
||||||
"/topic/**");
|
"/test/**",
|
||||||
|
"/topic/**",
|
||||||
|
"/acl/**");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,9 +77,9 @@ public class AuthWebMVCConfigurerAdapter extends WebMvcConfigurerAdapter {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer,
|
public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer,
|
||||||
NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception {
|
NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception {
|
||||||
UserInfo userInfo = (UserInfo) WebUtil.getValueFromSession((HttpServletRequest) nativeWebRequest.getNativeRequest(),
|
UserInfo userInfo = (UserInfo) WebUtil.getValueFromSession((HttpServletRequest) nativeWebRequest.getNativeRequest(),
|
||||||
UserInfo.USER_INFO);
|
UserInfo.USER_INFO);
|
||||||
if (userInfo != null) {
|
if (userInfo != null) {
|
||||||
return userInfo;
|
return userInfo;
|
||||||
}
|
}
|
||||||
@@ -86,4 +89,9 @@ public class AuthWebMVCConfigurerAdapter extends WebMvcConfigurerAdapter {
|
|||||||
|
|
||||||
super.addArgumentResolvers(argumentResolvers); //REVIEW ME
|
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,6 +16,8 @@
|
|||||||
*/
|
*/
|
||||||
package org.apache.rocketmq.dashboard.config;
|
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.commons.lang3.StringUtils;
|
||||||
import org.apache.rocketmq.common.MixAll;
|
import org.apache.rocketmq.common.MixAll;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@@ -29,6 +31,7 @@ import org.springframework.context.annotation.Configuration;
|
|||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import static org.apache.rocketmq.client.ClientConfig.SEND_MESSAGE_WITH_VIP_CHANNEL_PROPERTY;
|
import static org.apache.rocketmq.client.ClientConfig.SEND_MESSAGE_WITH_VIP_CHANNEL_PROPERTY;
|
||||||
|
|
||||||
@@ -57,6 +60,8 @@ public class RMQConfigure {
|
|||||||
|
|
||||||
private Long timeoutMillis;
|
private Long timeoutMillis;
|
||||||
|
|
||||||
|
private List<String> namesrvAddrs = new ArrayList<>();
|
||||||
|
|
||||||
public String getAccessKey() {
|
public String getAccessKey() {
|
||||||
return accessKey;
|
return accessKey;
|
||||||
}
|
}
|
||||||
@@ -77,6 +82,17 @@ public class RMQConfigure {
|
|||||||
return namesrvAddr;
|
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) {
|
public void setNamesrvAddr(String namesrvAddr) {
|
||||||
if (StringUtils.isNotBlank(namesrvAddr)) {
|
if (StringUtils.isNotBlank(namesrvAddr)) {
|
||||||
this.namesrvAddr = namesrvAddr;
|
this.namesrvAddr = namesrvAddr;
|
||||||
|
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
@@ -47,8 +47,8 @@ public class ConsumerController {
|
|||||||
|
|
||||||
@RequestMapping(value = "/groupList.query")
|
@RequestMapping(value = "/groupList.query")
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
public Object list() {
|
public Object list(@RequestParam(value = "skipSysGroup", required = false) boolean skipSysGroup) {
|
||||||
return consumerService.queryGroupList();
|
return consumerService.queryGroupList(skipSysGroup);
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequestMapping(value = "/group.query")
|
@RequestMapping(value = "/group.query")
|
||||||
|
@@ -51,7 +51,7 @@ public class DashboardController {
|
|||||||
return dashboardService.queryTopicData(date,topicName);
|
return dashboardService.queryTopicData(date,topicName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequestMapping(value = "/topicCurrent", method = RequestMethod.GET)
|
@RequestMapping(value = "/topicCurrent.query", method = RequestMethod.GET)
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
public Object topicCurrent() {
|
public Object topicCurrent() {
|
||||||
return dashboardService.queryTopicCurrentData();
|
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!"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -32,7 +32,7 @@ public class NamesvrController {
|
|||||||
@Resource
|
@Resource
|
||||||
private OpsService opsService;
|
private OpsService opsService;
|
||||||
|
|
||||||
@RequestMapping(value = "/nsaddr", method = RequestMethod.GET)
|
@RequestMapping(value = "/nsaddr.query", method = RequestMethod.GET)
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
@OriginalControllerReturnValue
|
@OriginalControllerReturnValue
|
||||||
public Object nsaddr() {
|
public Object nsaddr() {
|
||||||
|
@@ -16,7 +16,9 @@
|
|||||||
*/
|
*/
|
||||||
package org.apache.rocketmq.dashboard.controller;
|
package org.apache.rocketmq.dashboard.controller;
|
||||||
|
|
||||||
|
import com.google.common.base.Preconditions;
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.rocketmq.dashboard.permisssion.Permission;
|
import org.apache.rocketmq.dashboard.permisssion.Permission;
|
||||||
import org.apache.rocketmq.dashboard.service.OpsService;
|
import org.apache.rocketmq.dashboard.service.OpsService;
|
||||||
import org.springframework.stereotype.Controller;
|
import org.springframework.stereotype.Controller;
|
||||||
@@ -46,6 +48,15 @@ public class OpsController {
|
|||||||
return true;
|
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)
|
@RequestMapping(value = "/updateIsVIPChannel.do", method = RequestMethod.POST)
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
public Object updateIsVIPChannel(@RequestParam String useVIPChannel) {
|
public Object updateIsVIPChannel(@RequestParam String useVIPChannel) {
|
||||||
@@ -53,14 +64,12 @@ public class OpsController {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@RequestMapping(value = "/rocketMqStatus.query", method = RequestMethod.GET)
|
@RequestMapping(value = "/rocketMqStatus.query", method = RequestMethod.GET)
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
public Object clusterStatus() {
|
public Object clusterStatus() {
|
||||||
return opsService.rocketMqStatusCheck();
|
return opsService.rocketMqStatusCheck();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@RequestMapping(value = "/updateUseTLS.do", method = RequestMethod.POST)
|
@RequestMapping(value = "/updateUseTLS.do", method = RequestMethod.POST)
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
public Object updateUseTLS(@RequestParam String useTLS) {
|
public Object updateUseTLS(@RequestParam String useTLS) {
|
||||||
|
@@ -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,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;
|
||||||
|
}
|
@@ -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;
|
import java.util.Set;
|
||||||
|
|
||||||
public interface ConsumerService {
|
public interface ConsumerService {
|
||||||
List<GroupConsumeInfo> queryGroupList();
|
List<GroupConsumeInfo> queryGroupList(boolean skipSysGroup);
|
||||||
|
|
||||||
GroupConsumeInfo queryGroup(String consumerGroup);
|
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);
|
||||||
|
}
|
@@ -31,4 +31,6 @@ public interface OpsService {
|
|||||||
boolean updateIsVIPChannel(String useVIPChannel);
|
boolean updateIsVIPChannel(String useVIPChannel);
|
||||||
|
|
||||||
boolean updateUseTLS(boolean useTLS);
|
boolean updateUseTLS(boolean useTLS);
|
||||||
|
|
||||||
|
void addNameSvrAddr(String namesrvAddr);
|
||||||
}
|
}
|
||||||
|
@@ -91,29 +91,34 @@ public class MQAdminExtImpl implements MQAdminExt {
|
|||||||
MQAdminInstance.threadLocalMQAdminExt().createAndUpdateTopicConfig(addr, config);
|
MQAdminInstance.threadLocalMQAdminExt().createAndUpdateTopicConfig(addr, config);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public void createAndUpdatePlainAccessConfig(String addr,
|
@Override
|
||||||
|
public void createAndUpdatePlainAccessConfig(String addr,
|
||||||
PlainAccessConfig plainAccessConfig) throws RemotingException, MQBrokerException, InterruptedException, MQClientException {
|
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 {
|
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 {
|
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 {
|
String addr) throws RemotingException, MQBrokerException, InterruptedException, MQClientException {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public AclConfig examineBrokerClusterAclConfig(
|
@Override
|
||||||
|
public AclConfig examineBrokerClusterAclConfig(
|
||||||
String addr) throws RemotingException, MQBrokerException, InterruptedException, MQClientException {
|
String addr) throws RemotingException, MQBrokerException, InterruptedException, MQClientException {
|
||||||
return null;
|
return MQAdminInstance.threadLocalMQAdminExt().examineBrokerClusterAclConfig(addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -235,6 +240,12 @@ public class MQAdminExtImpl implements MQAdminExt {
|
|||||||
return MQAdminInstance.threadLocalMQAdminExt().wipeWritePermOfBroker(namesrvAddr, brokerName);
|
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
|
@Override
|
||||||
public void putKVConfig(String namespace, String key, String value) {
|
public void putKVConfig(String namespace, String key, String value) {
|
||||||
MQAdminInstance.threadLocalMQAdminExt().putKVConfig(namespace, key, value);
|
MQAdminInstance.threadLocalMQAdminExt().putKVConfig(namespace, key, value);
|
||||||
@@ -272,8 +283,9 @@ public class MQAdminExtImpl implements MQAdminExt {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void deleteSubscriptionGroup(String addr, String groupName, boolean removeOffset) throws RemotingException, MQBrokerException, InterruptedException, MQClientException {
|
public void deleteSubscriptionGroup(String addr, String groupName, boolean removeOffset)
|
||||||
throw new UnsupportedOperationException();
|
throws RemotingException, MQBrokerException, InterruptedException, MQClientException {
|
||||||
|
MQAdminInstance.threadLocalMQAdminExt().deleteSubscriptionGroup(addr, groupName, removeOffset);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -511,9 +523,21 @@ public class MQAdminExtImpl implements MQAdminExt {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TopicConfigSerializeWrapper getAllTopicGroup(String brokerAddr,
|
public SubscriptionGroupWrapper getUserSubscriptionGroup(String brokerAddr,
|
||||||
long timeoutMillis) throws InterruptedException, RemotingTimeoutException, RemotingSendRequestException, RemotingConnectException, MQBrokerException {
|
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
|
@Override
|
||||||
|
@@ -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.Lists;
|
||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
import com.google.common.collect.Sets;
|
import com.google.common.collect.Sets;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
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.collections.CollectionUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.rocketmq.client.exception.MQClientException;
|
import org.apache.rocketmq.client.exception.MQClientException;
|
||||||
import org.apache.rocketmq.common.MQVersion;
|
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.ConsumeStats;
|
||||||
import org.apache.rocketmq.common.admin.RollbackStats;
|
import org.apache.rocketmq.common.admin.RollbackStats;
|
||||||
import org.apache.rocketmq.common.message.MessageQueue;
|
import org.apache.rocketmq.common.message.MessageQueue;
|
||||||
@@ -43,6 +56,8 @@ import org.apache.rocketmq.common.protocol.body.GroupList;
|
|||||||
import org.apache.rocketmq.common.protocol.body.SubscriptionGroupWrapper;
|
import org.apache.rocketmq.common.protocol.body.SubscriptionGroupWrapper;
|
||||||
import org.apache.rocketmq.common.protocol.route.BrokerData;
|
import org.apache.rocketmq.common.protocol.route.BrokerData;
|
||||||
import org.apache.rocketmq.common.subscription.SubscriptionGroupConfig;
|
import org.apache.rocketmq.common.subscription.SubscriptionGroupConfig;
|
||||||
|
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.ConsumerGroupRollBackStat;
|
||||||
import org.apache.rocketmq.dashboard.model.GroupConsumeInfo;
|
import org.apache.rocketmq.dashboard.model.GroupConsumeInfo;
|
||||||
import org.apache.rocketmq.dashboard.model.QueueStatInfo;
|
import org.apache.rocketmq.dashboard.model.QueueStatInfo;
|
||||||
@@ -54,16 +69,59 @@ import org.apache.rocketmq.dashboard.service.AbstractCommonService;
|
|||||||
import org.apache.rocketmq.dashboard.service.ConsumerService;
|
import org.apache.rocketmq.dashboard.service.ConsumerService;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.DisposableBean;
|
||||||
|
import org.springframework.beans.factory.InitializingBean;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import static com.google.common.base.Throwables.propagate;
|
import static com.google.common.base.Throwables.propagate;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class ConsumerServiceImpl extends AbstractCommonService implements ConsumerService {
|
public class ConsumerServiceImpl extends AbstractCommonService implements ConsumerService, InitializingBean, DisposableBean {
|
||||||
private Logger logger = LoggerFactory.getLogger(ConsumerServiceImpl.class);
|
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
|
@Override
|
||||||
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();
|
Set<String> consumerGroupSet = Sets.newHashSet();
|
||||||
try {
|
try {
|
||||||
ClusterInfo clusterInfo = mqAdminExt.examineBrokerClusterInfo();
|
ClusterInfo clusterInfo = mqAdminExt.examineBrokerClusterInfo();
|
||||||
@@ -75,9 +133,33 @@ public class ConsumerServiceImpl extends AbstractCommonService implements Consum
|
|||||||
catch (Exception err) {
|
catch (Exception err) {
|
||||||
throw Throwables.propagate(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) {
|
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);
|
Collections.sort(groupConsumeInfoList);
|
||||||
return groupConsumeInfoList;
|
return groupConsumeInfoList;
|
||||||
@@ -266,11 +348,21 @@ public class ConsumerServiceImpl extends AbstractCommonService implements Consum
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean deleteSubGroup(DeleteSubGroupRequest deleteSubGroupRequest) {
|
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 {
|
try {
|
||||||
ClusterInfo clusterInfo = mqAdminExt.examineBrokerClusterInfo();
|
ClusterInfo clusterInfo = mqAdminExt.examineBrokerClusterInfo();
|
||||||
for (String brokerName : deleteSubGroupRequest.getBrokerNameList()) {
|
for (String brokerName : deleteSubGroupRequest.getBrokerNameList()) {
|
||||||
logger.info("addr={} groupName={}", clusterInfo.getBrokerAddrTable().get(brokerName).selectBrokerAddr(), deleteSubGroupRequest.getGroupName());
|
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) {
|
catch (Exception e) {
|
||||||
@@ -279,6 +371,18 @@ public class ConsumerServiceImpl extends AbstractCommonService implements Consum
|
|||||||
return true;
|
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
|
@Override
|
||||||
public boolean createAndUpdateSubscriptionGroupConfig(ConsumerConfigInfo consumerConfigInfo) {
|
public boolean createAndUpdateSubscriptionGroupConfig(ConsumerConfigInfo consumerConfigInfo) {
|
||||||
try {
|
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.DefaultMQPullConsumer;
|
||||||
import org.apache.rocketmq.client.consumer.PullResult;
|
import org.apache.rocketmq.client.consumer.PullResult;
|
||||||
import org.apache.rocketmq.client.consumer.PullStatus;
|
import org.apache.rocketmq.client.consumer.PullStatus;
|
||||||
|
import org.apache.rocketmq.client.exception.MQClientException;
|
||||||
import org.apache.rocketmq.common.MixAll;
|
import org.apache.rocketmq.common.MixAll;
|
||||||
import org.apache.rocketmq.common.Pair;
|
import org.apache.rocketmq.common.Pair;
|
||||||
import org.apache.rocketmq.common.message.MessageClientIDSetter;
|
import org.apache.rocketmq.common.message.MessageClientIDSetter;
|
||||||
@@ -111,6 +112,9 @@ public class MessageServiceImpl implements MessageService {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
} catch (Exception err) {
|
} catch (Exception err) {
|
||||||
|
if (err instanceof MQClientException) {
|
||||||
|
throw new ServiceException(-1, ((MQClientException) err).getErrorMessage());
|
||||||
|
}
|
||||||
throw Throwables.propagate(err);
|
throw Throwables.propagate(err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -325,7 +329,7 @@ public class MessageServiceImpl implements MessageService {
|
|||||||
List<MessageExt> msgFoundList = pullResult.getMsgFoundList();
|
List<MessageExt> msgFoundList = pullResult.getMsgFoundList();
|
||||||
for (int i = msgFoundList.size() - 1; i >= 0; i--) {
|
for (int i = msgFoundList.size() - 1; i >= 0; i--) {
|
||||||
MessageExt messageExt = msgFoundList.get(i);
|
MessageExt messageExt = msgFoundList.get(i);
|
||||||
if (messageExt.getStoreTimestamp() < query.getBegin()) {
|
if (messageExt.getStoreTimestamp() > query.getEnd()) {
|
||||||
end--;
|
end--;
|
||||||
} else {
|
} else {
|
||||||
hasIllegalOffset = false;
|
hasIllegalOffset = false;
|
||||||
|
@@ -20,8 +20,6 @@ package org.apache.rocketmq.dashboard.service.impl;
|
|||||||
import com.google.common.base.Function;
|
import com.google.common.base.Function;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
|
|
||||||
import com.google.common.base.Throwables;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -35,6 +33,7 @@ import org.apache.rocketmq.common.Pair;
|
|||||||
import org.apache.rocketmq.common.message.MessageExt;
|
import org.apache.rocketmq.common.message.MessageExt;
|
||||||
import org.apache.rocketmq.common.topic.TopicValidator;
|
import org.apache.rocketmq.common.topic.TopicValidator;
|
||||||
import org.apache.rocketmq.dashboard.config.RMQConfigure;
|
import org.apache.rocketmq.dashboard.config.RMQConfigure;
|
||||||
|
import org.apache.rocketmq.dashboard.exception.ServiceException;
|
||||||
import org.apache.rocketmq.dashboard.model.MessageTraceView;
|
import org.apache.rocketmq.dashboard.model.MessageTraceView;
|
||||||
import org.apache.rocketmq.dashboard.model.trace.ProducerNode;
|
import org.apache.rocketmq.dashboard.model.trace.ProducerNode;
|
||||||
import org.apache.rocketmq.dashboard.model.trace.MessageTraceGraph;
|
import org.apache.rocketmq.dashboard.model.trace.MessageTraceGraph;
|
||||||
@@ -82,7 +81,7 @@ public class MessageTraceServiceImpl implements MessageTraceService {
|
|||||||
}
|
}
|
||||||
return messageTraceViews;
|
return messageTraceViews;
|
||||||
} catch (Exception err) {
|
} catch (Exception err) {
|
||||||
throw Throwables.propagate(err);
|
throw new ServiceException(-1, String.format("Failed to query message trace by msgId %s", key));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -16,7 +16,6 @@
|
|||||||
*/
|
*/
|
||||||
package org.apache.rocketmq.dashboard.service.impl;
|
package org.apache.rocketmq.dashboard.service.impl;
|
||||||
|
|
||||||
import com.google.common.base.Splitter;
|
|
||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -46,7 +45,8 @@ public class OpsServiceImpl extends AbstractCommonService implements OpsService
|
|||||||
@Override
|
@Override
|
||||||
public Map<String, Object> homePageInfo() {
|
public Map<String, Object> homePageInfo() {
|
||||||
Map<String, Object> homePageInfoMap = Maps.newHashMap();
|
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("useVIPChannel", Boolean.valueOf(configure.getIsVIPChannel()));
|
||||||
homePageInfoMap.put("useTLS", configure.isUseTLS());
|
homePageInfoMap.put("useTLS", configure.isUseTLS());
|
||||||
return homePageInfoMap;
|
return homePageInfoMap;
|
||||||
@@ -73,7 +73,8 @@ public class OpsServiceImpl extends AbstractCommonService implements OpsService
|
|||||||
return checkResultMap;
|
return checkResultMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public boolean updateIsVIPChannel(String useVIPChannel) {
|
@Override
|
||||||
|
public boolean updateIsVIPChannel(String useVIPChannel) {
|
||||||
configure.setIsVIPChannel(useVIPChannel);
|
configure.setIsVIPChannel(useVIPChannel);
|
||||||
mqAdminExtPool.clear();
|
mqAdminExtPool.clear();
|
||||||
return true;
|
return true;
|
||||||
@@ -85,4 +86,13 @@ public class OpsServiceImpl extends AbstractCommonService implements OpsService
|
|||||||
mqAdminExtPool.clear();
|
mqAdminExtPool.clear();
|
||||||
return true;
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -135,10 +135,10 @@ public class TopicServiceImpl extends AbstractCommonService implements TopicServ
|
|||||||
ClusterInfo clusterInfo = null;
|
ClusterInfo clusterInfo = null;
|
||||||
try {
|
try {
|
||||||
clusterInfo = mqAdminExt.examineBrokerClusterInfo();
|
clusterInfo = mqAdminExt.examineBrokerClusterInfo();
|
||||||
|
return mqAdminExt.examineTopicConfig(clusterInfo.getBrokerAddrTable().get(brokerName).selectBrokerAddr(), topic);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw Throwables.propagate(e);
|
throw Throwables.propagate(e);
|
||||||
}
|
}
|
||||||
return mqAdminExt.examineTopicConfig(clusterInfo.getBrokerAddrTable().get(brokerName).selectBrokerAddr(), topic);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@@ -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,17 +16,6 @@
|
|||||||
*/
|
*/
|
||||||
package org.apache.rocketmq.dashboard.task;
|
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.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.base.Throwables;
|
||||||
import com.google.common.cache.LoadingCache;
|
import com.google.common.cache.LoadingCache;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
@@ -42,13 +31,18 @@ import java.util.HashMap;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutorService;
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
import org.apache.rocketmq.common.MixAll;
|
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.config.RMQConfigure;
|
||||||
import org.apache.rocketmq.dashboard.service.DashboardCollectService;
|
import org.apache.rocketmq.dashboard.service.DashboardCollectService;
|
||||||
import org.apache.rocketmq.dashboard.util.JsonUtil;
|
import org.apache.rocketmq.dashboard.util.JsonUtil;
|
||||||
|
import org.apache.rocketmq.tools.admin.MQAdminExt;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.scheduling.annotation.Scheduled;
|
import org.springframework.scheduling.annotation.Scheduled;
|
||||||
@@ -67,14 +61,14 @@ public class DashboardCollectTask {
|
|||||||
|
|
||||||
private final static Logger log = LoggerFactory.getLogger(DashboardCollectTask.class);
|
private final static Logger log = LoggerFactory.getLogger(DashboardCollectTask.class);
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private ExecutorService collectExecutor;
|
||||||
|
|
||||||
@Scheduled(cron = "30 0/1 * * * ?")
|
@Scheduled(cron = "30 0/1 * * * ?")
|
||||||
public void collectTopic() {
|
public void collectTopic() {
|
||||||
if (!rmqConfigure.isEnableDashBoardCollect()) {
|
if (!rmqConfigure.isEnableDashBoardCollect()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Date date = new Date();
|
|
||||||
Stopwatch stopwatch = Stopwatch.createUnstarted();
|
|
||||||
try {
|
try {
|
||||||
TopicList topicList = mqAdminExt.fetchAllTopicList();
|
TopicList topicList = mqAdminExt.fetchAllTopicList();
|
||||||
Set<String> topicSet = topicList.getTopicList();
|
Set<String> topicSet = topicList.getTopicList();
|
||||||
@@ -85,77 +79,9 @@ public class DashboardCollectTask {
|
|||||||
|| TopicValidator.isSystemTopic(topic)) {
|
|| TopicValidator.isSystemTopic(topic)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
TopicRouteData topicRouteData = mqAdminExt.examineTopicRouteInfo(topic);
|
CollectTaskRunnble collectTask = new CollectTaskRunnble(topic, mqAdminExt, dashboardCollectService);
|
||||||
|
collectExecutor.submit(collectTask);
|
||||||
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);
|
|
||||||
}
|
|
||||||
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) {
|
catch (Exception err) {
|
||||||
throw Throwables.propagate(err);
|
throw Throwables.propagate(err);
|
||||||
@@ -328,6 +254,7 @@ public class DashboardCollectTask {
|
|||||||
for (Map.Entry<String, Set<String>> entry : clusterTable.entrySet()) {
|
for (Map.Entry<String, Set<String>> entry : clusterTable.entrySet()) {
|
||||||
String clusterName = entry.getKey();
|
String clusterName = entry.getKey();
|
||||||
TopicValidator.addSystemTopic(clusterName);
|
TopicValidator.addSystemTopic(clusterName);
|
||||||
|
TopicValidator.addSystemTopic(MixAll.getReplyTopic(clusterName));
|
||||||
Set<String> brokerNames = entry.getValue();
|
Set<String> brokerNames = entry.getValue();
|
||||||
for (String brokerName : brokerNames) {
|
for (String brokerName : brokerNames) {
|
||||||
TopicValidator.addSystemTopic(brokerName);
|
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();
|
||||||
|
}
|
||||||
|
}
|
@@ -1,54 +0,0 @@
|
|||||||
#
|
|
||||||
# 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.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=
|
|
||||||
#timeout for mqadminExt, default 5000ms
|
|
||||||
rocketmq.config.timeoutMillis=
|
|
||||||
#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
|
@@ -19,16 +19,16 @@
|
|||||||
<configuration>
|
<configuration>
|
||||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||||
<encoder charset="UTF-8">
|
<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>
|
</encoder>
|
||||||
</appender>
|
</appender>
|
||||||
|
|
||||||
<appender name="FILE"
|
<appender name="FILE"
|
||||||
class="ch.qos.logback.core.rolling.RollingFileAppender">
|
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>
|
<append>true</append>
|
||||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
<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>
|
</fileNamePattern>
|
||||||
<timeBasedFileNamingAndTriggeringPolicy
|
<timeBasedFileNamingAndTriggeringPolicy
|
||||||
class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
|
class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
|
||||||
@@ -37,7 +37,7 @@
|
|||||||
<MaxHistory>10</MaxHistory>
|
<MaxHistory>10</MaxHistory>
|
||||||
</rollingPolicy>
|
</rollingPolicy>
|
||||||
<encoder>
|
<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>
|
<charset class="java.nio.charset.Charset">UTF-8</charset>
|
||||||
</encoder>
|
</encoder>
|
||||||
</appender>
|
</appender>
|
||||||
|
@@ -23,13 +23,18 @@
|
|||||||
|
|
||||||
rolePerms:
|
rolePerms:
|
||||||
ordinary:
|
ordinary:
|
||||||
- /rocketmq/nsaddr
|
- /rocketmq/*.query
|
||||||
- /ops/*
|
- /ops/*.query
|
||||||
- /dashboard/**
|
- /dashboard/*.query
|
||||||
- /topic/*.query
|
- /topic/*.query
|
||||||
- /topic/sendTopicMessage.do
|
- /topic/sendTopicMessage.do
|
||||||
- /producer/*.query
|
- /producer/*.query
|
||||||
- /message/*
|
- /message/*.query
|
||||||
- /messageTrace/*
|
- /messageTrace/*.query
|
||||||
- /monitor/*
|
- /monitor/*.query
|
||||||
|
- /consumer/*.query
|
||||||
|
- /cluster/*.query
|
||||||
|
- /dlqMessage/*.query
|
||||||
|
- /dlqMessage/exportDlqMessage.do
|
||||||
|
- /dlqMessage/batchResendDlqMessage.do
|
||||||
|
- /acl/*.query
|
||||||
|
@@ -107,10 +107,12 @@
|
|||||||
<script type="text/javascript" src="src/consumer.js?timestamp=6"></script>
|
<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/producer.js"></script>
|
||||||
<script type="text/javascript" src="src/message.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/messageTrace.js"></script>
|
||||||
<script type="text/javascript" src="src/ops.js?timestamp=7"></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="src/remoteApi/remoteApi.js"></script>
|
||||||
<script type="text/javascript" src="vendor/preLoading/main.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/login.js"></script>
|
||||||
|
<script type="text/javascript" src="src/acl.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</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);
|
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) {
|
$rootScope.$on('$locationChangeStart', function (event, next, current) {
|
||||||
// redirect to login page if not logged in and trying to access a restricted page
|
// redirect to login page if not logged in and trying to access a restricted page
|
||||||
init(function(resp){
|
init(function(resp){
|
||||||
@@ -195,12 +204,18 @@ app.config(['$routeProvider', '$httpProvider','$cookiesProvider','getDictNamePro
|
|||||||
}).when('/message', {
|
}).when('/message', {
|
||||||
templateUrl: 'view/pages/message.html',
|
templateUrl: 'view/pages/message.html',
|
||||||
controller:'messageController'
|
controller:'messageController'
|
||||||
|
}).when('/dlqMessage', {
|
||||||
|
templateUrl: 'view/pages/dlqMessage.html',
|
||||||
|
controller:'dlqMessageController'
|
||||||
}).when('/messageTrace', {
|
}).when('/messageTrace', {
|
||||||
templateUrl: 'view/pages/messageTrace.html',
|
templateUrl: 'view/pages/messageTrace.html',
|
||||||
controller:'messageTraceController'
|
controller:'messageTraceController'
|
||||||
}).when('/ops', {
|
}).when('/ops', {
|
||||||
templateUrl: 'view/pages/ops.html',
|
templateUrl: 'view/pages/ops.html',
|
||||||
controller:'opsController'
|
controller:'opsController'
|
||||||
|
}).when('/acl', {
|
||||||
|
templateUrl: 'view/pages/acl.html',
|
||||||
|
controller: 'aclController'
|
||||||
}).when('/404', {
|
}).when('/404', {
|
||||||
templateUrl: 'view/pages/404.html'
|
templateUrl: 'view/pages/404.html'
|
||||||
}).otherwise('/404');
|
}).otherwise('/404');
|
||||||
|
@@ -43,6 +43,8 @@ module.controller('consumerController', ['$scope', 'ngDialog', '$http', 'Notific
|
|||||||
};
|
};
|
||||||
$scope.userRole = $window.sessionStorage.getItem("userrole");
|
$scope.userRole = $window.sessionStorage.getItem("userrole");
|
||||||
$scope.writeOperationEnabled = $scope.userRole == null ? true : ($scope.userRole == 1 ? true : false);
|
$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)
|
$scope.doSort = function () {// todo how to change this fe's code ? (it's dirty)
|
||||||
if ($scope.sortKey == 'diffTotal') {
|
if ($scope.sortKey == 'diffTotal') {
|
||||||
@@ -125,13 +127,37 @@ module.controller('consumerController', ['$scope', 'ngDialog', '$http', 'Notific
|
|||||||
$scope.filterList(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) {
|
$scope.filterList = function (currentPage) {
|
||||||
var lowExceptStr = $scope.filterStr.toLowerCase();
|
var lowExceptStr = $scope.filterStr.toLowerCase();
|
||||||
var canShowList = [];
|
var canShowList = [];
|
||||||
$scope.allConsumerGrouopList.forEach(function (element) {
|
$scope.allConsumerGrouopList.forEach(function (element) {
|
||||||
console.log(element)
|
console.log(element)
|
||||||
if (element.group.toLowerCase().indexOf(lowExceptStr) != -1) {
|
if ($scope.filterByType(element.group)) {
|
||||||
canShowList.push(element);
|
if (element.group.toLowerCase().indexOf(lowExceptStr) != -1) {
|
||||||
|
canShowList.push(element);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
$scope.paginationConf.totalItems = canShowList.length;
|
$scope.paginationConf.totalItems = canShowList.length;
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}]);
|
@@ -21,8 +21,8 @@ var en = {
|
|||||||
"NO": "NO.",
|
"NO": "NO.",
|
||||||
"ADDRESS": "Address",
|
"ADDRESS": "Address",
|
||||||
"VERSION": "Version",
|
"VERSION": "Version",
|
||||||
"PRO_MSG_TPS": "Produce Massage TPS",
|
"PRO_MSG_TPS": "Produce Message TPS",
|
||||||
"CUS_MSG_TPS": "Consumer Massage TPS",
|
"CUS_MSG_TPS": "Consumer Message TPS",
|
||||||
"YESTERDAY_PRO_COUNT": "Yesterday Produce Count",
|
"YESTERDAY_PRO_COUNT": "Yesterday Produce Count",
|
||||||
"YESTERDAY_CUS_COUNT": "Yesterday Consume Count",
|
"YESTERDAY_CUS_COUNT": "Yesterday Consume Count",
|
||||||
"TODAY_PRO_COUNT": "Today Produce Count",
|
"TODAY_PRO_COUNT": "Today Produce Count",
|
||||||
@@ -41,6 +41,7 @@ var en = {
|
|||||||
"RESEND_MESSAGE":"Resend Message",
|
"RESEND_MESSAGE":"Resend Message",
|
||||||
"VIEW_EXCEPTION":"View Exception",
|
"VIEW_EXCEPTION":"View Exception",
|
||||||
"MESSAGETRACE":"MessageTrace",
|
"MESSAGETRACE":"MessageTrace",
|
||||||
|
"DLQ_MESSAGE":"DLQMessage",
|
||||||
"COMMIT": "Commit",
|
"COMMIT": "Commit",
|
||||||
"OPERATION": "Operation",
|
"OPERATION": "Operation",
|
||||||
"ADD": "Add",
|
"ADD": "Add",
|
||||||
@@ -108,5 +109,19 @@ var en = {
|
|||||||
"ENABLE_MESSAGE_TRACE":"Enable Message Trace",
|
"ENABLE_MESSAGE_TRACE":"Enable Message Trace",
|
||||||
"MESSAGE_TRACE_DETAIL":"Message Trace Detail",
|
"MESSAGE_TRACE_DETAIL":"Message Trace Detail",
|
||||||
"TRACE_TOPIC":"TraceTopic",
|
"TRACE_TOPIC":"TraceTopic",
|
||||||
"SELECT_TRACE_TOPIC":"selectTraceTopic"
|
"SELECT_TRACE_TOPIC":"selectTraceTopic",
|
||||||
|
"EXPORT": "export",
|
||||||
|
"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"
|
||||||
}
|
}
|
@@ -39,8 +39,9 @@ var zh = {
|
|||||||
"PRODUCER":"生产者",
|
"PRODUCER":"生产者",
|
||||||
"MESSAGE":"消息",
|
"MESSAGE":"消息",
|
||||||
"MESSAGE_DETAIL":"消息详情",
|
"MESSAGE_DETAIL":"消息详情",
|
||||||
"RESEND_MESSAGE":"重新消费",
|
"RESEND_MESSAGE":"重新发送",
|
||||||
"VIEW_EXCEPTION":"查看异常",
|
"VIEW_EXCEPTION":"查看异常",
|
||||||
|
"DLQ_MESSAGE":"死信消息",
|
||||||
"MESSAGETRACE":"消息轨迹",
|
"MESSAGETRACE":"消息轨迹",
|
||||||
"OPERATION": "操作",
|
"OPERATION": "操作",
|
||||||
"ADD": "新增",
|
"ADD": "新增",
|
||||||
@@ -109,5 +110,19 @@ var zh = {
|
|||||||
"ENABLE_MESSAGE_TRACE":"开启消息轨迹",
|
"ENABLE_MESSAGE_TRACE":"开启消息轨迹",
|
||||||
"MESSAGE_TRACE_DETAIL":"消息轨迹详情",
|
"MESSAGE_TRACE_DETAIL":"消息轨迹详情",
|
||||||
"TRACE_TOPIC":"消息轨迹主题",
|
"TRACE_TOPIC":"消息轨迹主题",
|
||||||
"SELECT_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":"隐藏"
|
||||||
}
|
}
|
@@ -58,6 +58,7 @@ module.controller('messageController', ['$scope', 'ngDialog', '$http', 'Notifica
|
|||||||
};
|
};
|
||||||
|
|
||||||
$scope.queryMessagePageByTopic = function () {
|
$scope.queryMessagePageByTopic = function () {
|
||||||
|
$("#noMsgTip").css("display", "none");
|
||||||
if ($scope.timepickerEnd < $scope.timepickerBegin) {
|
if ($scope.timepickerEnd < $scope.timepickerBegin) {
|
||||||
Notification.error({message: "endTime is later than beginTime!", delay: 2000});
|
Notification.error({message: "endTime is later than beginTime!", delay: 2000});
|
||||||
return
|
return
|
||||||
@@ -80,6 +81,9 @@ module.controller('messageController', ['$scope', 'ngDialog', '$http', 'Notifica
|
|||||||
if (resp.status === 0) {
|
if (resp.status === 0) {
|
||||||
console.log(resp);
|
console.log(resp);
|
||||||
$scope.messageShowList = resp.data.page.content;
|
$scope.messageShowList = resp.data.page.content;
|
||||||
|
if ($scope.messageShowList.length == 0){
|
||||||
|
$("#noMsgTip").removeAttr("style");
|
||||||
|
}
|
||||||
if (resp.data.page.first) {
|
if (resp.data.page.first) {
|
||||||
$scope.paginationConf.currentPage = 1;
|
$scope.paginationConf.currentPage = 1;
|
||||||
}
|
}
|
||||||
@@ -206,7 +210,8 @@ module.controller('messageController', ['$scope', 'ngDialog', '$http', 'Notifica
|
|||||||
}]);
|
}]);
|
||||||
|
|
||||||
module.controller('messageDetailViewDialogController', ['$scope', 'ngDialog', '$http', 'Notification', function ($scope, ngDialog, $http, Notification) {
|
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) {
|
$scope.resendMessage = function (messageView, consumerGroup) {
|
||||||
var topic = messageView.topic;
|
var topic = messageView.topic;
|
||||||
var msgId = messageView.msgId;
|
var msgId = messageView.msgId;
|
||||||
@@ -258,5 +263,18 @@ module.controller('messageDetailViewDialogController', ['$scope', 'ngDialog', '$
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
$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;
|
||||||
|
});
|
||||||
}]
|
}]
|
||||||
);
|
);
|
@@ -16,29 +16,36 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
app.controller('opsController', ['$scope', '$location', '$http', 'Notification', 'remoteApi', 'tools', '$window', function ($scope, $location, $http, Notification, remoteApi, tools, $window) {
|
app.controller('opsController', ['$scope', '$location', '$http', 'Notification', 'remoteApi', 'tools', '$window', function ($scope, $location, $http, Notification, remoteApi, tools, $window) {
|
||||||
$scope.namesvrAddrList = "";
|
$scope.namesvrAddrList = [];
|
||||||
$scope.useVIPChannel = true;
|
$scope.useVIPChannel = true;
|
||||||
$scope.useTLS = false;
|
$scope.useTLS = false;
|
||||||
$scope.userRole = $window.sessionStorage.getItem("userrole");
|
$scope.userRole = $window.sessionStorage.getItem("userrole");
|
||||||
$scope.writeOperationEnabled = $scope.userRole == null ? true : ($scope.userRole == 1 ? true : false);
|
$scope.writeOperationEnabled = $scope.userRole == null ? true : ($scope.userRole == 1 ? true : false);
|
||||||
|
$scope.inputReadonly = !$scope.writeOperationEnabled;
|
||||||
|
$scope.newNamesrvAddr = "";
|
||||||
$http({
|
$http({
|
||||||
method: "GET",
|
method: "GET",
|
||||||
url: "ops/homePage.query"
|
url: "ops/homePage.query"
|
||||||
}).success(function (resp) {
|
}).success(function (resp) {
|
||||||
if (resp.status == 0) {
|
if (resp.status == 0) {
|
||||||
$scope.namesvrAddrList = resp.data.namesvrAddrList.join(";");
|
$scope.namesvrAddrList = resp.data.namesvrAddrList;
|
||||||
$scope.useVIPChannel = resp.data.useVIPChannel;
|
$scope.useVIPChannel = resp.data.useVIPChannel;
|
||||||
$scope.useTLS = resp.data.useTLS;
|
$scope.useTLS = resp.data.useTLS;
|
||||||
|
$scope.selectedNamesrv = resp.data.currentNamesrv;
|
||||||
} else {
|
} else {
|
||||||
Notification.error({message: resp.errMsg, delay: 2000});
|
Notification.error({message: resp.errMsg, delay: 2000});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$scope.eleChange = function (data){
|
||||||
|
$scope.namesvrAddrList = data;
|
||||||
|
}
|
||||||
|
|
||||||
$scope.updateNameSvrAddr = function () {
|
$scope.updateNameSvrAddr = function () {
|
||||||
$http({
|
$http({
|
||||||
method: "POST",
|
method: "POST",
|
||||||
url: "ops/updateNameSvrAddr.do",
|
url: "ops/updateNameSvrAddr.do",
|
||||||
params: {nameSvrAddrList: $scope.namesvrAddrList}
|
params: {nameSvrAddrList: $scope.selectedNamesrv}
|
||||||
}).success(function (resp) {
|
}).success(function (resp) {
|
||||||
if (resp.status == 0) {
|
if (resp.status == 0) {
|
||||||
Notification.info({message: "SUCCESS", delay: 2000});
|
Notification.info({message: "SUCCESS", delay: 2000});
|
||||||
@@ -47,6 +54,26 @@ app.controller('opsController', ['$scope', '$location', '$http', 'Notification',
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
$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 () {
|
$scope.updateIsVIPChannel = function () {
|
||||||
$http({
|
$http({
|
||||||
method: "POST",
|
method: "POST",
|
||||||
|
@@ -54,7 +54,7 @@ app.service('remoteApi', ['$http','tools', function ($http,tools) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var queryTopicCurrentData = function(callback){
|
var queryTopicCurrentData = function(callback){
|
||||||
var url = 'dashboard/topicCurrent';
|
var url = 'dashboard/topicCurrent.query';
|
||||||
var setting = {
|
var setting = {
|
||||||
type: "GET",
|
type: "GET",
|
||||||
timeout:15000,//data is too large,so master set time out is long enough
|
timeout:15000,//data is too large,so master set time out is long enough
|
||||||
|
@@ -289,3 +289,15 @@
|
|||||||
.table.text-middle>tbody>tr>td,.table.text-middle>tbody>tr>th{
|
.table.text-middle>tbody>tr>td,.table.text-middle>tbody>tr>th{
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.perm-table{width: 100%;}
|
||||||
|
.perm-table .perm-tg{width: 70%;}
|
||||||
|
.perm-table .center{border-left: 1px solid #e4dddd; border-right: 1px solid #e4dddd;}
|
||||||
|
|
||||||
|
.input-none {
|
||||||
|
border: 0;
|
||||||
|
outline: none;
|
||||||
|
background-color: rgba(0, 0, 0, 0);
|
||||||
|
cursor: text !important;
|
||||||
|
width: 60%;
|
||||||
|
}
|
@@ -34,7 +34,9 @@
|
|||||||
<li ng-class="path =='consumer' ? 'active':''"><a ng-href="#/consumer">{{'CONSUMER' | translate}}</a></li>
|
<li ng-class="path =='consumer' ? 'active':''"><a ng-href="#/consumer">{{'CONSUMER' | translate}}</a></li>
|
||||||
<li ng-class="path =='producer' ? 'active':''"><a ng-href="#/producer">{{'PRODUCER' | translate}}</a></li>
|
<li ng-class="path =='producer' ? 'active':''"><a ng-href="#/producer">{{'PRODUCER' | translate}}</a></li>
|
||||||
<li ng-class="path =='message' ? 'active':''"><a ng-href="#/message">{{'MESSAGE' | translate}}</a></li>
|
<li ng-class="path =='message' ? 'active':''"><a ng-href="#/message">{{'MESSAGE' | translate}}</a></li>
|
||||||
|
<li ng-class="path =='dlqMessage' ? 'active':''"><a ng-href="#/dlqMessage">{{'DLQ_MESSAGE' | translate}}</a></li>
|
||||||
<li ng-class="path =='messageTrace' ? 'active':''"><a ng-href="#/messageTrace">{{'MESSAGETRACE' | translate}}</a></li>
|
<li ng-class="path =='messageTrace' ? 'active':''"><a ng-href="#/messageTrace">{{'MESSAGETRACE' | translate}}</a></li>
|
||||||
|
<li ng-show="{{ show }}" ng-class="path =='acl' ? 'active':''"><a ng-href="#/acl">Acl</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<ul class="nav navbar-nav navbar-right">
|
<ul class="nav navbar-nav navbar-right">
|
||||||
<li class="dropdown">
|
<li class="dropdown">
|
||||||
|
483
src/main/resources/static/view/pages/acl.html
Normal file
483
src/main/resources/static/view/pages/acl.html
Normal file
@@ -0,0 +1,483 @@
|
|||||||
|
<!--
|
||||||
|
~ 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.
|
||||||
|
-->
|
||||||
|
<div class="container-fluid" id="deployHistoryList">
|
||||||
|
<div class="modal-body">
|
||||||
|
<div ng-cloak="" class="tabsdemoDynamicHeight">
|
||||||
|
<md-content>
|
||||||
|
<md-tabs md-dynamic-height="" md-border-bottom="">
|
||||||
|
<md-tab label="Account Info">
|
||||||
|
<md-content class="md-padding" style="min-height:600px">
|
||||||
|
<form class="form-inline pull-left col-sm-12">
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Access Key:</label>
|
||||||
|
<input class="form-control" style="width: 450px" type="text" ng-model="filterStr"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button ng-show="writeOperationEnabled" class="btn btn-raised btn-sm btn-primary"
|
||||||
|
type="button"
|
||||||
|
ng-click="openAddDialog()">{{'ADD' |
|
||||||
|
translate}}
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
<table class="table table-bordered">
|
||||||
|
<tr>
|
||||||
|
<th class="text-center">Access Key</th>
|
||||||
|
<th ng-show="writeOperationEnabled" class="text-center">Secret Key</th>
|
||||||
|
<th class="text-center">{{'IS_ADMIN' | translate}}</th>
|
||||||
|
<th class="text-center">{{'DEFAULT_TOPIC_PERM' | translate}}</th>
|
||||||
|
<th class="text-center">{{'DEFAULT_GROUP_PERM' | translate}}</th>
|
||||||
|
<th class="text-center">{{'TOPIC_PERM' | translate}}</th>
|
||||||
|
<th class="text-center">{{'GROUP_PERM' | translate}}</th>
|
||||||
|
<th ng-show="writeOperationEnabled" class="text-center">
|
||||||
|
{{'OPERATION' | translate}}
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
<tr ng-repeat="item in plainAccessConfigs">
|
||||||
|
<td class="text-center">{{item.accessKey}}</td>
|
||||||
|
<td ng-show="writeOperationEnabled" class="text-center">
|
||||||
|
<input type="{{showSecretKeyType[item.accessKey].type}}"
|
||||||
|
value="{{item.secretKey}}" class="input-none" ng-disabled="true"/>
|
||||||
|
<a href="javascript:;"
|
||||||
|
ng-click="switchSecretKeyType(item.accessKey)">{{showSecretKeyType[item.accessKey].action | translate}}</a>
|
||||||
|
</td>
|
||||||
|
<td class="text-center">{{item.admin}}</td>
|
||||||
|
<td class="text-center">{{item.defaultTopicPerm}}</td>
|
||||||
|
<td class="text-center">{{item.defaultGroupPerm}}</td>
|
||||||
|
<td class="text-center">
|
||||||
|
<table ng-repeat="topic in item.topicPerms" class="perm-table">
|
||||||
|
<tr>
|
||||||
|
<td class="perm-tg">{{topic}}</td>
|
||||||
|
<td ng-show="writeOperationEnabled" class="center"><a
|
||||||
|
href="javascript:;"
|
||||||
|
ng-click="openUpdateTopicDialog(item, topic)">
|
||||||
|
{{'UPDATE' | translate}}</a></td>
|
||||||
|
<td ng-show="writeOperationEnabled"><a href="javascript:;"
|
||||||
|
ng-confirm-click="Are you sure to delete {{topic}}?"
|
||||||
|
confirmed-click="deletePermConfig(item, topic, 'topic')">{{'DELETE' | translate}}</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
<td class="text-center">
|
||||||
|
<table ng-repeat="group in item.groupPerms" class="perm-table">
|
||||||
|
<tr>
|
||||||
|
<td class="perm-tg">{{group}}</td>
|
||||||
|
<td ng-show="writeOperationEnabled" class="center"><a
|
||||||
|
href="javascript:;"
|
||||||
|
ng-click="openUpdateGroupDialog(item, group)">
|
||||||
|
{{'UPDATE' | translate}}</a></td>
|
||||||
|
<td ng-show="writeOperationEnabled"><a href="javascript:;"
|
||||||
|
ng-confirm-click="Are you sure to delete {{group}}?"
|
||||||
|
confirmed-click="deletePermConfig(item, group, 'group')">{{'DELETE' | translate}}</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
<td ng-show="writeOperationEnabled" class="text-center">
|
||||||
|
<button class="btn btn-raised btn-sm btn-primary" type="button"
|
||||||
|
ng-click="openAddTopicDialog(item)">
|
||||||
|
{{'ADD' | translate}}topic
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-raised btn-sm btn-primary" type="button"
|
||||||
|
ng-click="openAddGroupDialog(item)">
|
||||||
|
{{'ADD' | translate}}group
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-raised btn-sm btn-primary" type="button"
|
||||||
|
ng-click="openUpdateDialog(item)">
|
||||||
|
{{'UPDATE' | translate}}
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-raised btn-sm btn-danger" type="button"
|
||||||
|
ng-confirm-click="Are you sure to delete {{item.accessKey}}?"
|
||||||
|
confirmed-click="deleteAclConfig(item.accessKey)">{{'DELETE' | translate}}
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-raised btn-sm btn-danger" type="button"
|
||||||
|
ng-click="synchronizeData(item)">{{'SYNCHRONIZE' | translate}}
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<tm-pagination conf="paginationConf"></tm-pagination>
|
||||||
|
</md-content>
|
||||||
|
</md-tab>
|
||||||
|
<md-tab label="Global White List">
|
||||||
|
<md-content class="md-padding" style="min-height:600px">
|
||||||
|
<form class="form-inline pull-left col-sm-12">
|
||||||
|
<button ng-show="writeOperationEnabled" class="btn btn-raised btn-sm btn-primary"
|
||||||
|
type="button"
|
||||||
|
ng-click="openAddAddrDialog()">{{'ADD' |
|
||||||
|
translate}}
|
||||||
|
</button>
|
||||||
|
<button ng-show="writeOperationEnabled" class="btn btn-raised btn-sm btn-danger"
|
||||||
|
type="button"
|
||||||
|
ng-confirm-click="Are you sure to synchronize white list to all broker int the cluster?"
|
||||||
|
confirmed-click="synchronizeWhiteList(allGlobalWhiteAddrs)">
|
||||||
|
{{'SYNCHRONIZE' | translate}}
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
<table class="table table-bordered">
|
||||||
|
<tr>
|
||||||
|
<th class="text-center">{{'WHITE_LIST' | translate}}</th>
|
||||||
|
<th ng-show="writeOperationEnabled" class="text-center">
|
||||||
|
{{'OPERATION' | translate}}
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
<tr ng-repeat="item in allGlobalWhiteAddrs">
|
||||||
|
<td class="text-center">{{item}}
|
||||||
|
</td>
|
||||||
|
<td ng-show="writeOperationEnabled" class="text-center">
|
||||||
|
<button class="btn btn-raised btn-sm btn-danger" type="button"
|
||||||
|
ng-confirm-click="Are you sure to delete {{item}}?"
|
||||||
|
confirmed-click="deleteGlobalWhiteAddr(item)">{{'DELETE' | translate}}
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</md-content>
|
||||||
|
</md-tab>
|
||||||
|
</md-tabs>
|
||||||
|
</md-content>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script type="text/ng-template" id="addAclAccountDialog">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h4 class="modal-title">{{'ADD' | translate }}</h4>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body ">
|
||||||
|
<form id="addAppForm" name="addAppForm" class="form-horizontal" novalidate>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="control-label col-sm-2">Access Key:</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<input class="form-control" ng-model="ngDialogData.accessKey" type="text" required/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="control-label col-sm-2">Secret Key:</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<input class="form-control" ng-model="ngDialogData.secretKey" type="text" required/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="control-label col-sm-2">{{'IS_ADMIN' | translate}}:</label>
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<md-switch class="md-primary" md-no-ink aria-label="Switch No Ink"
|
||||||
|
ng-model="ngDialogData.admin">
|
||||||
|
</md-switch>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="control-label col-sm-2">{{'DEFAULT_TOPIC_PERM' | translate}}:</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<input class="form-control" ng-model="ngDialogData.defaultTopicPerm" type="text" readonly/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="control-label col-sm-2">{{'DEFAULT_GROUP_PERM' | translate}}:</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<input class="form-control" ng-model="ngDialogData.defaultGroupPerm" type="text" readonly/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<div class="ngdialog-buttons">
|
||||||
|
<button type="button" class="ngdialog-button ngdialog-button-primary"
|
||||||
|
ng-click="addRequest({'accessKey':ngDialogData.accessKey, 'secretKey': ngDialogData.secretKey, 'admin': ngDialogData.admin, 'defaultTopicPerm': ngDialogData.defaultTopicPerm, 'defaultGroupPerm': ngDialogData.defaultGroupPerm})">
|
||||||
|
{{ 'COMMIT' | translate }}
|
||||||
|
</button>
|
||||||
|
<button type="button" class="ngdialog-button ngdialog-button-secondary"
|
||||||
|
ng-click="closeThisDialog('Cancel')">{{ 'CLOSE' | translate }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script type="text/ng-template" id="updateAclAccountDialog">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h4 class="modal-title">{{'UPDATE' | translate }}</h4>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body ">
|
||||||
|
<form id="updateAccountForm" name="updateAccountForm" class="form-horizontal" novalidate>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="control-label col-sm-2">Access Key:</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<input class="form-control" ng-model="ngDialogData.accessKey" type="text" disabled/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="control-label col-sm-2">Secret Key:</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<input class="form-control" ng-model="ngDialogData.secretKey" type="text" required/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="control-label col-sm-2">{{'IS_ADMIN' | translate}}:</label>
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<md-switch class="md-primary" md-no-ink aria-label="Switch No Ink"
|
||||||
|
ng-model="ngDialogData.admin">
|
||||||
|
</md-switch>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="control-label col-sm-2">{{'DEFAULT_TOPIC_PERM' | translate}}:</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<input class="form-control" ng-model="ngDialogData.defaultTopicPerm" type="text" disabled/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="control-label col-sm-2">{{'DEFAULT_GROUP_PERM' | translate}}:</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<input class="form-control" ng-model="ngDialogData.defaultGroupPerm" type="text" disabled/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<div class="ngdialog-buttons">
|
||||||
|
<button type="button" class="ngdialog-button ngdialog-button-primary"
|
||||||
|
ng-click="updateAclAccountRequest({'accessKey':ngDialogData.accessKey, 'secretKey': ngDialogData.secretKey, 'admin': ngDialogData.admin, 'defaultTopicPerm': ngDialogData.defaultTopicPerm, 'defaultGroupPerm': ngDialogData.defaultGroupPerm})">
|
||||||
|
{{ 'COMMIT' | translate }}
|
||||||
|
</button>
|
||||||
|
<button type="button" class="ngdialog-button ngdialog-button-secondary"
|
||||||
|
ng-click="closeThisDialog('Cancel')">{{ 'CLOSE' | translate }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script type="text/ng-template" id="addAclTopicDialog">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h4 class="modal-title">{{'ADD' | translate }}{{'TOPIC_PERM' | translate }}</h4>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body ">
|
||||||
|
<form id="addAclTopicForm" name="addAclTopicForm" class="form-horizontal" novalidate>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="control-label col-sm-2">Access Key:</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<input class="form-control" ng-model="ngDialogData.accessKey" type="text" disabled/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="control-label col-sm-2">Secret Key:</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<input class="form-control" ng-model="ngDialogData.secretKey" type="text" disabled/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="control-label col-sm-2">{{'TOPIC' | translate}}:</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<input class="form-control" ng-model="topic" type="text" required/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="control-label col-sm-2">{{'TOPIC_PERM' | translate}}:</label>
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<md-checkbox class="md-primary" ng-model="ngDialogData.pub">PUB</md-checkbox>
|
||||||
|
<md-checkbox class="md-primary" ng-model="ngDialogData.sub">SUB</md-checkbox>
|
||||||
|
<md-checkbox class="md-primary" ng-model="ngDialogData.deny">DENY</md-checkbox>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<div class="ngdialog-buttons">
|
||||||
|
<button type="button" class="ngdialog-button ngdialog-button-primary"
|
||||||
|
ng-click="updateAclAccountRequest({'originalData':ngDialogData,'topic': topic , 'pub': ngDialogData.pub, 'sub': ngDialogData.sub, 'deny': ngDialogData.deny})">
|
||||||
|
{{ 'COMMIT' | translate }}
|
||||||
|
</button>
|
||||||
|
<button type="button" class="ngdialog-button ngdialog-button-secondary"
|
||||||
|
ng-click="closeThisDialog('Cancel')">{{ 'CLOSE' | translate }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script type="text/ng-template" id="updateAclTopicDialog">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h4 class="modal-title">{{'UPDATE' | translate }}{{'TOPIC_PERM' | translate }}</h4>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body ">
|
||||||
|
<form id="updateAclTopicForm" name="updateAclTopicForm" class="form-horizontal" novalidate>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="control-label col-sm-2">Access Key:</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<input class="form-control" ng-model="ngDialogData.accessKey" type="text" disabled/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="control-label col-sm-2">Secret Key:</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<input class="form-control" ng-model="ngDialogData.secretKey" type="text" disabled/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="control-label col-sm-2">{{'TOPIC' | translate}}:</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<input class="form-control" ng-model="ngDialogData.topic" type="text" disabled/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="control-label col-sm-2">{{'TOPIC_PERM' | translate}}:</label>
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<md-checkbox class="md-primary" ng-model="ngDialogData.pub">PUB</md-checkbox>
|
||||||
|
<md-checkbox class="md-primary" ng-model="ngDialogData.sub">SUB</md-checkbox>
|
||||||
|
<md-checkbox class="md-primary" ng-model="ngDialogData.deny">DENY</md-checkbox>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<div class="ngdialog-buttons">
|
||||||
|
<button type="button" class="ngdialog-button ngdialog-button-primary"
|
||||||
|
ng-click="updateAclAccountRequest({'originalData':ngDialogData,'topic': ngDialogData.topic , 'pub': ngDialogData.pub, 'sub': ngDialogData.sub, 'deny': ngDialogData.deny})">
|
||||||
|
{{ 'COMMIT' | translate }}
|
||||||
|
</button>
|
||||||
|
<button type="button" class="ngdialog-button ngdialog-button-secondary"
|
||||||
|
ng-click="closeThisDialog('Cancel')">{{ 'CLOSE' | translate }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script type="text/ng-template" id="addAclGroupDialog">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h4 class="modal-title">{{'ADD' | translate }}{{'GROUP_PERM' | translate }}</h4>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body ">
|
||||||
|
<form id="addAclGroupForm" name="addAclGroupForm" class="form-horizontal" novalidate>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="control-label col-sm-2">Access Key:</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<input class="form-control" ng-model="ngDialogData.accessKey" type="text" disabled/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="control-label col-sm-2">Secret Key:</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<input class="form-control" ng-model="ngDialogData.secretKey" type="text" disabled/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="control-label col-sm-2">{{'CONSUMER' | translate}}:</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<input class="form-control" ng-model="group" type="text" required/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="control-label col-sm-2">{{'GROUP_PERM' | translate}}:</label>
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<md-checkbox class="md-primary" ng-model="ngDialogData.pub">PUB</md-checkbox>
|
||||||
|
<md-checkbox class="md-primary" ng-model="ngDialogData.sub">SUB</md-checkbox>
|
||||||
|
<md-checkbox class="md-primary" ng-model="ngDialogData.deny">DENY</md-checkbox>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<div class="ngdialog-buttons">
|
||||||
|
<button type="button" class="ngdialog-button ngdialog-button-primary"
|
||||||
|
ng-click="updateAclAccountRequest({'originalData':ngDialogData,'group': group , 'pub': ngDialogData.pub, 'sub': ngDialogData.sub, 'deny': ngDialogData.deny})">
|
||||||
|
{{ 'COMMIT' | translate }}
|
||||||
|
</button>
|
||||||
|
<button type="button" class="ngdialog-button ngdialog-button-secondary"
|
||||||
|
ng-click="closeThisDialog('Cancel')">{{ 'CLOSE' | translate }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script type="text/ng-template" id="updateAclGroupDialog">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h4 class="modal-title">{{'UPDATE' | translate }}{{'GROUP_PERM' | translate }}</h4>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body ">
|
||||||
|
<form id="updateAclGroupForm" name="updateAclGroupForm" class="form-horizontal" novalidate>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="control-label col-sm-2">Access Key:</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<input class="form-control" ng-model="ngDialogData.accessKey" type="text" disabled/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="control-label col-sm-2">Secret Key:</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<input class="form-control" ng-model="ngDialogData.secretKey" type="text" disabled/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="control-label col-sm-2">{{'CONSUMER' | translate}}:</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<input class="form-control" ng-model="ngDialogData.group" type="text" disabled/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="control-label col-sm-2">{{'GROUP_PERM' | translate}}:</label>
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<md-checkbox class="md-primary" ng-model="ngDialogData.pub">PUB</md-checkbox>
|
||||||
|
<md-checkbox class="md-primary" ng-model="ngDialogData.sub">SUB</md-checkbox>
|
||||||
|
<md-checkbox class="md-primary" ng-model="ngDialogData.deny">DENY</md-checkbox>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<div class="ngdialog-buttons">
|
||||||
|
<button type="button" class="ngdialog-button ngdialog-button-primary"
|
||||||
|
ng-click="updateAclAccountRequest({'originalData':ngDialogData,'group': ngDialogData.group , 'pub': ngDialogData.pub, 'sub': ngDialogData.sub, 'deny': ngDialogData.deny})">
|
||||||
|
{{ 'COMMIT' | translate }}
|
||||||
|
</button>
|
||||||
|
<button type="button" class="ngdialog-button ngdialog-button-secondary"
|
||||||
|
ng-click="closeThisDialog('Cancel')">{{ 'CLOSE' | translate }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script type="text/ng-template" id="addWhiteListDialog">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h4 class="modal-title">{{'ADD' | translate }}{{'WHITE_LIST' | translate }}</h4>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body ">
|
||||||
|
<form id="addWhiteListForm" name="addWhiteListForm" class="form-horizontal" novalidate>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="control-label col-sm-2">{{'WHITE_LIST' | translate }}:</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<input class="form-control" ng-model="ip" type="text"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<div class="ngdialog-buttons">
|
||||||
|
<button type="button" class="ngdialog-button ngdialog-button-primary"
|
||||||
|
ng-click="addWhiteListRequest(ip)">
|
||||||
|
{{ 'COMMIT' | translate }}
|
||||||
|
</button>
|
||||||
|
<button type="button" class="ngdialog-button ngdialog-button-secondary"
|
||||||
|
ng-click="closeThisDialog('Cancel')">{{ 'CLOSE' | translate }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</script>
|
@@ -24,27 +24,25 @@
|
|||||||
<label>{{ 'SUBSCRIPTION_GROUP' | translate}}:</label>
|
<label>{{ 'SUBSCRIPTION_GROUP' | translate}}:</label>
|
||||||
<input type="text" class="form-control" ng-model="filterStr">
|
<input type="text" class="form-control" ng-model="filterStr">
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group form-group-sm">
|
<md-checkbox aria-label="Checkbox" ng-model="filterNormal" class="md-primary">{{'NORMAL' | translate}}
|
||||||
<button class="btn btn-raised btn-sm btn-primary" type="button" ng-show="{{writeOperationEnabled}}"
|
</md-checkbox>
|
||||||
ng-click="openAddDialog()">{{'ADD' | translate}}/ {{'UPDATE' | translate}}
|
<md-checkbox aria-label="Checkbox" ng-model="filterSystem" class="md-primary">{{'SYSTEM' | translate}}
|
||||||
</button>
|
</md-checkbox>
|
||||||
</div>
|
<button class="btn btn-raised btn-sm btn-primary" type="button" ng-show="{{writeOperationEnabled}}"
|
||||||
<div class="form-group form-group-sm">
|
ng-click="openAddDialog()">{{'ADD' | translate}}/ {{'UPDATE' | translate}}
|
||||||
<button class="btn btn-raised btn-sm btn-primary" type="button" ng-click="refreshConsumerData()">
|
</button>
|
||||||
{{'REFRESH' | translate}}
|
<button class="btn btn-raised btn-sm btn-primary" type="button" ng-click="refreshConsumerData()">
|
||||||
</button>
|
{{'REFRESH' | translate}}
|
||||||
</div>
|
</button>
|
||||||
<div class="form-group form-group-sm">
|
<md-switch class="md-primary" md-no-ink aria-label="Switch No Ink" ng-model="intervalProcessSwitch">
|
||||||
<md-switch class="md-primary" md-no-ink aria-label="Switch No Ink" ng-model="intervalProcessSwitch">
|
{{'AUTO_REFRESH' | translate}}
|
||||||
{{'AUTO_REFRESH' | translate}}
|
</md-switch>
|
||||||
</md-switch>
|
|
||||||
</div>
|
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<br>
|
<br>
|
||||||
<div>
|
<div>
|
||||||
<div id="deployList" class="row">
|
<div id="deployList" class="row">
|
||||||
<table class="table table-bordered">
|
<table class="table table-bordered text-middle">
|
||||||
<tr>
|
<tr>
|
||||||
<th class="text-center"><a ng-click="sortByKey('group')">{{ 'SUBSCRIPTION_GROUP' | translate}}</a></th>
|
<th class="text-center"><a ng-click="sortByKey('group')">{{ 'SUBSCRIPTION_GROUP' | translate}}</a></th>
|
||||||
<th class="text-center"><a ng-click="sortByKey('count')">{{ 'QUANTITY' | translate}}</a></th>
|
<th class="text-center"><a ng-click="sortByKey('count')">{{ 'QUANTITY' | translate}}</a></th>
|
||||||
@@ -55,15 +53,17 @@
|
|||||||
<th class="text-center"><a ng-click="sortByKey('diffTotal')">{{ 'DELAY' | translate}}</a></th>
|
<th class="text-center"><a ng-click="sortByKey('diffTotal')">{{ 'DELAY' | translate}}</a></th>
|
||||||
<th class="text-center">{{ 'OPERATION' | translate}}</th>
|
<th class="text-center">{{ 'OPERATION' | translate}}</th>
|
||||||
</tr>
|
</tr>
|
||||||
<tr ng-repeat="consumerGroup in consumerGroupShowList">
|
<tr ng-repeat="consumerGroup in consumerGroupShowList"
|
||||||
<td class="text-left">{{consumerGroup.group}}</td>
|
ng-init="sysFlag = consumerGroup.group.startsWith('%SYS%')">
|
||||||
|
<td class="text-center"><font color={{sysFlag?"red":""}}>
|
||||||
|
{{sysFlag?consumerGroup.group.substring(5):consumerGroup.group}}</font></td>
|
||||||
<td class="text-center">{{consumerGroup.count}}</td>
|
<td class="text-center">{{consumerGroup.count}}</td>
|
||||||
<td class="text-center">{{consumerGroup.version}}</td>
|
<td class="text-center">{{consumerGroup.version}}</td>
|
||||||
<td class="text-center">{{consumerGroup.consumeType}}</td>
|
<td class="text-center">{{consumerGroup.consumeType}}</td>
|
||||||
<td class="text-center">{{consumerGroup.messageModel}}</td>
|
<td class="text-center">{{consumerGroup.messageModel}}</td>
|
||||||
<td class="text-center">{{consumerGroup.consumeTps}}</td>
|
<td class="text-center">{{consumerGroup.consumeTps}}</td>
|
||||||
<td class="text-center">{{consumerGroup.diffTotal}}</td>
|
<td class="text-center">{{consumerGroup.diffTotal}}</td>
|
||||||
<td class="text-center">
|
<td class="text-left">
|
||||||
<button name="client" ng-click="client(consumerGroup.group)"
|
<button name="client" ng-click="client(consumerGroup.group)"
|
||||||
class="btn btn-raised btn-sm btn-primary"
|
class="btn btn-raised btn-sm btn-primary"
|
||||||
type="button">{{'CLIENT' | translate}}
|
type="button">{{'CLIENT' | translate}}
|
||||||
@@ -80,7 +80,7 @@
|
|||||||
<!--</button>-->
|
<!--</button>-->
|
||||||
<button name="client" ng-click="delete(consumerGroup.group)"
|
<button name="client" ng-click="delete(consumerGroup.group)"
|
||||||
class="btn btn-raised btn-sm btn-danger"
|
class="btn btn-raised btn-sm btn-danger"
|
||||||
ng-show="{{writeOperationEnabled}}"
|
ng-show="{{!sysFlag && writeOperationEnabled}}"
|
||||||
type="button">{{'DELETE' | translate}}
|
type="button">{{'DELETE' | translate}}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
@@ -209,8 +209,8 @@
|
|||||||
<div class="modal-body " ng-repeat="item in ngDialogData.consumerRequestList">
|
<div class="modal-body " ng-repeat="item in ngDialogData.consumerRequestList">
|
||||||
<form id="addAppForm" name="addAppForm" class="form-horizontal" novalidate>
|
<form id="addAppForm" name="addAppForm" class="form-horizontal" novalidate>
|
||||||
<div class="form-group" ng-hide="ngDialogData.bIsUpdate">
|
<div class="form-group" ng-hide="ngDialogData.bIsUpdate">
|
||||||
<label class="control-label col-sm-4">clusterName:</label>
|
<label class="control-label col-sm-3">clusterName:</label>
|
||||||
<div class="col-sm-8">
|
<div class="col-sm-9">
|
||||||
<select name="mySelectClusterNameList" multiple chosen
|
<select name="mySelectClusterNameList" multiple chosen
|
||||||
ng-model="item.clusterNameList"
|
ng-model="item.clusterNameList"
|
||||||
ng-options="clusterNameItem for clusterNameItem in ngDialogData.allClusterNameList">
|
ng-options="clusterNameItem for clusterNameItem in ngDialogData.allClusterNameList">
|
||||||
@@ -219,8 +219,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="control-label col-sm-4">brokerName:</label>
|
<label class="control-label col-sm-3">brokerName:</label>
|
||||||
<div class="col-sm-8">
|
<div class="col-sm-9">
|
||||||
<select name="mySelectBrokerNameList" multiple chosen
|
<select name="mySelectBrokerNameList" multiple chosen
|
||||||
ng-disabled="ngDialogData.bIsUpdate"
|
ng-disabled="ngDialogData.bIsUpdate"
|
||||||
ng-model="item.brokerNameList"
|
ng-model="item.brokerNameList"
|
||||||
@@ -230,16 +230,16 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="control-label col-sm-4">groupName:</label>
|
<label class="control-label col-sm-3">groupName:</label>
|
||||||
<div class="col-sm-8">
|
<div class="col-sm-9">
|
||||||
<input class="form-control" ng-model="item.subscriptionGroupConfig.groupName" type="text"
|
<input class="form-control" ng-model="item.subscriptionGroupConfig.groupName" type="text"
|
||||||
ng-disabled="ngDialogData.bIsUpdate" required/>
|
ng-disabled="ngDialogData.bIsUpdate" required/>
|
||||||
<span class="text-danger" ng-show="addAppForm.name.$error.required">编号不能为空.</span>
|
<span class="text-danger" ng-show="addAppForm.name.$error.required">编号不能为空.</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="control-label col-sm-4">consumeEnable:</label>
|
<label class="control-label col-sm-3">consumeEnable:</label>
|
||||||
<div class="col-sm-8">
|
<div class="col-sm-9">
|
||||||
<md-switch class="md-primary" ng-disabled="{{!ngDialogData.writeOperationEnabled}}" md-no-ink
|
<md-switch class="md-primary" ng-disabled="{{!ngDialogData.writeOperationEnabled}}" md-no-ink
|
||||||
aria-label="Switch No Ink" ng-model="item.subscriptionGroupConfig.consumeEnable">
|
aria-label="Switch No Ink" ng-model="item.subscriptionGroupConfig.consumeEnable">
|
||||||
</md-switch>
|
</md-switch>
|
||||||
@@ -257,8 +257,8 @@
|
|||||||
<!--</div>-->
|
<!--</div>-->
|
||||||
<!--</div>-->
|
<!--</div>-->
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="control-label col-sm-4">consumeBroadcastEnable:</label>
|
<label class="control-label col-sm-3">consumeBroadcastEnable:</label>
|
||||||
<div class="col-sm-8">
|
<div class="col-sm-9">
|
||||||
<md-switch class="md-primary" ng-disabled="{{!ngDialogData.writeOperationEnabled}}" md-no-ink
|
<md-switch class="md-primary" ng-disabled="{{!ngDialogData.writeOperationEnabled}}" md-no-ink
|
||||||
aria-label="Switch No Ink"
|
aria-label="Switch No Ink"
|
||||||
ng-model="item.subscriptionGroupConfig.consumeBroadcastEnable">
|
ng-model="item.subscriptionGroupConfig.consumeBroadcastEnable">
|
||||||
@@ -266,8 +266,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="control-label col-sm-4">retryQueueNums:</label>
|
<label class="control-label col-sm-3">retryQueueNums:</label>
|
||||||
<div class="col-sm-8">
|
<div class="col-sm-9">
|
||||||
<input class="form-control" ng-model="item.subscriptionGroupConfig.retryQueueNums"
|
<input class="form-control" ng-model="item.subscriptionGroupConfig.retryQueueNums"
|
||||||
type="text" ng-disabled="{{!ngDialogData.writeOperationEnabled}}"
|
type="text" ng-disabled="{{!ngDialogData.writeOperationEnabled}}"
|
||||||
required/>
|
required/>
|
||||||
@@ -284,16 +284,16 @@
|
|||||||
<!--</div>-->
|
<!--</div>-->
|
||||||
<!--</div>-->
|
<!--</div>-->
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="control-label col-sm-4">brokerId:</label>
|
<label class="control-label col-sm-3">brokerId:</label>
|
||||||
<div class="col-sm-8">
|
<div class="col-sm-9">
|
||||||
<input class="form-control" ng-model="item.subscriptionGroupConfig.brokerId" type="text"
|
<input class="form-control" ng-model="item.subscriptionGroupConfig.brokerId" type="text"
|
||||||
ng-disabled="{{!ngDialogData.writeOperationEnabled}}" required/>
|
ng-disabled="{{!ngDialogData.writeOperationEnabled}}" required/>
|
||||||
<span class="text-danger" ng-show="addAppForm.name.$error.required">编号不能为空.</span>
|
<span class="text-danger" ng-show="addAppForm.name.$error.required">编号不能为空.</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="control-label col-sm-4">whichBrokerWhenConsumeSlowly:</label>
|
<label class="control-label col-sm-3">whichBrokerWhenConsumeSlowly:</label>
|
||||||
<div class="col-sm-8">
|
<div class="col-sm-9">
|
||||||
<input class="form-control"
|
<input class="form-control"
|
||||||
ng-model="item.subscriptionGroupConfig.whichBrokerWhenConsumeSlowly" type="text"
|
ng-model="item.subscriptionGroupConfig.whichBrokerWhenConsumeSlowly" type="text"
|
||||||
ng-disabled="{{!ngDialogData.writeOperationEnabled}}" required/>
|
ng-disabled="{{!ngDialogData.writeOperationEnabled}}" required/>
|
||||||
|
282
src/main/resources/static/view/pages/dlqMessage.html
Normal file
282
src/main/resources/static/view/pages/dlqMessage.html
Normal file
@@ -0,0 +1,282 @@
|
|||||||
|
<!--
|
||||||
|
~ 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.
|
||||||
|
-->
|
||||||
|
<div class="container-fluid" id="deployHistoryList">
|
||||||
|
<div class="modal-body">
|
||||||
|
<div ng-cloak="" class="tabsdemoDynamicHeight">
|
||||||
|
<md-content>
|
||||||
|
<md-tabs md-dynamic-height="" md-border-bottom="">
|
||||||
|
<md-tab label="Consumer">
|
||||||
|
<h5 class="md-display-5">Total {{paginationConf.totalItems}} Messages</h5>
|
||||||
|
<md-content class="md-padding" style="min-height:600px">
|
||||||
|
<div class="row">
|
||||||
|
<form class="form-inline pull-left">
|
||||||
|
<div class="form-group">
|
||||||
|
<label>{{'CONSUMER' | translate}}:</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-group ">
|
||||||
|
<div style="width: 300px">
|
||||||
|
<select name="mySelectGroup" chosen
|
||||||
|
ng-model="selectedConsumerGroup"
|
||||||
|
ng-options="item for item in allConsumerGroupList"
|
||||||
|
ng-change="onChangeQueryCondition()"
|
||||||
|
required>
|
||||||
|
<option value=""></option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group ">
|
||||||
|
<label>{{'BEGIN' | translate}}:</label>
|
||||||
|
<div class="input-group">
|
||||||
|
<input class="form-control" datetimepicker
|
||||||
|
ng-change="onChangeQueryCondition()" ng-model="timepickerBegin"
|
||||||
|
options="timepickerOptions"/>
|
||||||
|
<span class="input-group-addon"><span
|
||||||
|
class="glyphicon glyphicon-calendar"></span></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>{{'END' | translate}}:</label>
|
||||||
|
<div class="input-group">
|
||||||
|
<input class="form-control" datetimepicker
|
||||||
|
ng-change="onChangeQueryCondition()" ng-model="timepickerEnd"
|
||||||
|
options="timepickerOptions"/>
|
||||||
|
<span class="input-group-addon"><span
|
||||||
|
class="glyphicon glyphicon-calendar"></span></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button id="searchAppsButton" type="button"
|
||||||
|
class="btn btn-raised btn-sm btn-primary"
|
||||||
|
data-toggle="modal"
|
||||||
|
ng-click="queryDlqMessageByConsumerGroup()">
|
||||||
|
<span class="glyphicon glyphicon-search"></span> {{ 'SEARCH' | translate}}
|
||||||
|
</button>
|
||||||
|
<button type="button" id="batchResendBtn"
|
||||||
|
class="btn btn-raised btn-sm btn-primary disabled"
|
||||||
|
data-toggle="modal"
|
||||||
|
ng-click="batchResendDlqMessage(selectedConsumerGroup)">
|
||||||
|
<span class="glyphicon glyphicon-send"></span> {{ 'BATCH_RESEND' | translate}}
|
||||||
|
</button>
|
||||||
|
<button type="button" id="batchExportBtn"
|
||||||
|
class="btn btn-raised btn-sm btn-primary disabled"
|
||||||
|
data-toggle="modal"
|
||||||
|
ng-click="batchExportDlqMessage(selectedConsumerGroup)">
|
||||||
|
<span class="glyphicon glyphicon-export"></span> {{ 'BATCH_EXPORT' | translate}}
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
<table class="table table-bordered text-middle">
|
||||||
|
<tr>
|
||||||
|
<th class="text-center"><input type="checkbox" ng-model='checkedAll' ng-change="selectAll()"/></th>
|
||||||
|
<th class="text-center">Message ID</th>
|
||||||
|
<th class="text-center">Tag</th>
|
||||||
|
<th class="text-center">Key</th>
|
||||||
|
<th class="text-center">StoreTime</th>
|
||||||
|
<th class="text-center">Operation</th>
|
||||||
|
</tr>
|
||||||
|
<tr style="display: none" id="noMsgTip">
|
||||||
|
<td colspan="6" style="text-align: center">{{'NO_MATCH_RESULT' | translate}}</td>
|
||||||
|
</tr>
|
||||||
|
<tr ng-repeat="item in messageShowList">
|
||||||
|
<td class="text-center">
|
||||||
|
<input type="checkbox" ng-model='item.checked' ng-change="selectItem()"/>
|
||||||
|
</td>
|
||||||
|
<td class="text-center">{{item.msgId}}</td>
|
||||||
|
<td class="text-center">{{item.properties.TAGS}}</td>
|
||||||
|
<td class="text-center">{{item.properties.KEYS}}</td>
|
||||||
|
<td class="text-center">{{item.storeTimestamp | date:'yyyy-MM-dd HH:mm:ss'}}
|
||||||
|
</td>
|
||||||
|
<td class="text-center">
|
||||||
|
<button class="btn btn-raised btn-sm btn-primary" type="button"
|
||||||
|
ng-click="queryDlqMessageDetail(item.msgId, selectedConsumerGroup)">
|
||||||
|
{{'MESSAGE_DETAIL' | translate}}
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-raised btn-sm btn-primary" type="button"
|
||||||
|
ng-click="resendDlqMessage(item, selectedConsumerGroup)">
|
||||||
|
{{'RESEND_MESSAGE' | translate}}
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-raised btn-sm btn-primary" type="button"
|
||||||
|
ng-click="exportDlqMessage(item.msgId, selectedConsumerGroup)">
|
||||||
|
{{'EXPORT' | translate}}
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<tm-pagination conf="paginationConf"></tm-pagination>
|
||||||
|
</div>
|
||||||
|
</md-content>
|
||||||
|
</md-tab>
|
||||||
|
<md-tab label="Message ID">
|
||||||
|
<h5 class="md-display-5">topic can't be empty if you producer client version>=v3.5.8</h5>
|
||||||
|
<md-content class="md-padding" style="min-height:600px">
|
||||||
|
<div class="row">
|
||||||
|
<form class="form-inline pull-left">
|
||||||
|
<div class="form-group">
|
||||||
|
<label>{{'CONSUMER' | translate}}:</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-group ">
|
||||||
|
<div style="width: 300px">
|
||||||
|
<select name="mySelectGroup" chosen
|
||||||
|
ng-model="selectedConsumerGroup"
|
||||||
|
ng-options="item for item in allConsumerGroupList"
|
||||||
|
ng-change="onChangeQueryCondition()"
|
||||||
|
required>
|
||||||
|
<option value=""></option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>MessageId:</label>
|
||||||
|
<input class="form-control" style="width: 450px" type="text" ng-model="messageId"
|
||||||
|
required/>
|
||||||
|
</div>
|
||||||
|
<button type="button" class="btn btn-raised btn-sm btn-primary" data-toggle="modal"
|
||||||
|
ng-click="queryDlqMessageByMessageId(messageId, selectedConsumerGroup)">
|
||||||
|
<span class="glyphicon glyphicon-search"></span> {{ 'SEARCH' | translate}}
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
<table class="table table-bordered text-middle">
|
||||||
|
<tr>
|
||||||
|
<th class="text-center">Message ID</th>
|
||||||
|
<th class="text-center">Tag</th>
|
||||||
|
<th class="text-center">Key</th>
|
||||||
|
<th class="text-center">StoreTime</th>
|
||||||
|
<th class="text-center">Operation</th>
|
||||||
|
</tr>
|
||||||
|
<tr ng-repeat="item in queryDlqMessageByMessageIdResult">
|
||||||
|
<td class="text-center">{{item.msgId}}</td>
|
||||||
|
<td class="text-center">{{item.properties.TAGS}}</td>
|
||||||
|
<td class="text-center">{{item.properties.KEYS}}</td>
|
||||||
|
<td class="text-center">{{item.storeTimestamp | date:'yyyy-MM-dd HH:mm:ss'}}
|
||||||
|
</td>
|
||||||
|
<td class="text-center">
|
||||||
|
<button class="btn btn-raised btn-sm btn-primary" type="button"
|
||||||
|
ng-click="queryDlqMessageDetail(item.msgId, selectedConsumerGroup)">
|
||||||
|
{{'MESSAGE_DETAIL' | translate}}
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-raised btn-sm btn-primary" type="button"
|
||||||
|
ng-click="resendDlqMessage(item, selectedConsumerGroup)">
|
||||||
|
{{'RESEND_MESSAGE' | translate}}
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-raised btn-sm btn-primary" type="button"
|
||||||
|
ng-click="exportDlqMessage(item.msgId, selectedConsumerGroup)">
|
||||||
|
{{'EXPORT' | translate}}
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</md-content>
|
||||||
|
</md-tab>
|
||||||
|
</md-tabs>
|
||||||
|
</md-content>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<script type="text/ng-template" id="dlqMessageDetailViewDialog">
|
||||||
|
<md-content class="md-padding">
|
||||||
|
<div>
|
||||||
|
<form id="addAppForm" name="addAppForm" class="form-horizontal" novalidate>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="control-label col-sm-2">Message ID:</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<label class="form-control">{{ngDialogData.messageView.msgId}}</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="control-label col-sm-2">Topic:</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<label class="form-control">{{ngDialogData.messageView.topic}}</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="control-label col-sm-2">Properties:</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<textarea class="form-control"
|
||||||
|
style="min-height:100px; resize: none"
|
||||||
|
readonly>{{ngDialogData.messageView.properties}}</textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="control-label col-sm-2">ReconsumeTimes:</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<label class="form-control">{{ngDialogData.messageView.reconsumeTimes}}</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="control-label col-sm-2">Tag:</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<label class="form-control">{{ngDialogData.messageView.properties.TAGS}}</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="control-label col-sm-2">Key:</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<label class="form-control">{{ngDialogData.messageView.properties.KEYS}}</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="control-label col-sm-2">Storetime:</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<label class="form-control">{{ngDialogData.messageView.storeTimestamp | date:'yyyy-MM-dd HH:mm:ss'}}</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="control-label col-sm-2">StoreHost:</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<label class="form-control">{{ngDialogData.messageView.storeHost}}</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="control-label col-sm-2">Message body:</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<textarea class="form-control"
|
||||||
|
ng-model="ngDialogData.messageView.messageBody"
|
||||||
|
style="min-height:100px; resize: none"
|
||||||
|
readonly></textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</md-content>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<div class="ngdialog-buttons">
|
||||||
|
<button type="button" class="ngdialog-button ngdialog-button-secondary"
|
||||||
|
ng-click="closeThisDialog('Cancel')">{{ 'CLOSE' | translate }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script type="text/ng-template" id="operationResultDialog">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h4 class="modal-title">Result</h4>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body ">
|
||||||
|
<form class="form-horizontal" novalidate>
|
||||||
|
{{ngDialogData.result}}
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<div class="ngdialog-buttons">
|
||||||
|
<button type="button" class="ngdialog-button ngdialog-button-secondary"
|
||||||
|
ng-click="closeThisDialog('Cancel')">{{ 'CLOSE' | translate }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</script>
|
@@ -20,10 +20,10 @@
|
|||||||
<md-content>
|
<md-content>
|
||||||
<md-tabs md-dynamic-height="" md-border-bottom="">
|
<md-tabs md-dynamic-height="" md-border-bottom="">
|
||||||
<md-tab label="Topic">
|
<md-tab label="Topic">
|
||||||
|
<h5 class="md-display-5">Total {{paginationConf.totalItems}} Messages</h5>
|
||||||
<md-content class="md-padding" style="min-height:600px">
|
<md-content class="md-padding" style="min-height:600px">
|
||||||
<h5 class="md-display-5">Total {{paginationConf.totalItems}} Messages</h5>
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<form class="form-inline pull-left col-sm-12">
|
<form class="form-inline pull-left">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>{{'TOPIC' | translate}}:</label>
|
<label>{{'TOPIC' | translate}}:</label>
|
||||||
</div>
|
</div>
|
||||||
@@ -65,7 +65,7 @@
|
|||||||
<span class="glyphicon glyphicon-search"></span>{{ 'SEARCH' | translate}}
|
<span class="glyphicon glyphicon-search"></span>{{ 'SEARCH' | translate}}
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
<table class="table table-bordered">
|
<table class="table table-bordered text-middle">
|
||||||
<tr>
|
<tr>
|
||||||
<th class="text-center">Message ID</th>
|
<th class="text-center">Message ID</th>
|
||||||
<th class="text-center">Tag</th>
|
<th class="text-center">Tag</th>
|
||||||
@@ -73,6 +73,9 @@
|
|||||||
<th class="text-center">StoreTime</th>
|
<th class="text-center">StoreTime</th>
|
||||||
<th class="text-center">Operation</th>
|
<th class="text-center">Operation</th>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr style="display: none" id="noMsgTip">
|
||||||
|
<td colspan="5" style="text-align: center">{{'NO_MATCH_RESULT' | translate}}</td>
|
||||||
|
</tr>
|
||||||
<tr ng-repeat="item in messageShowList">
|
<tr ng-repeat="item in messageShowList">
|
||||||
<td class="text-center">{{item.msgId}}</td>
|
<td class="text-center">{{item.msgId}}</td>
|
||||||
<td class="text-center">{{item.properties.TAGS}}</td>
|
<td class="text-center">{{item.properties.TAGS}}</td>
|
||||||
@@ -91,84 +94,87 @@
|
|||||||
</md-content>
|
</md-content>
|
||||||
</md-tab>
|
</md-tab>
|
||||||
<md-tab label="Message Key">
|
<md-tab label="Message Key">
|
||||||
|
<h5 class="md-display-5">Only Return 64 Messages</h5>
|
||||||
<md-content class="md-padding" style="min-height:600px">
|
<md-content class="md-padding" style="min-height:600px">
|
||||||
<h5 class="md-display-5">Only Return 64 Messages</h5>
|
<div class="row">
|
||||||
|
<form class="form-inline pull-left">
|
||||||
<form class="form-inline pull-left col-sm-12">
|
<div class="form-group">
|
||||||
<div class="form-group">
|
<label>Topic:</label>
|
||||||
<label>Topic:</label>
|
</div>
|
||||||
</div>
|
<div class="form-group">
|
||||||
<div class="form-group">
|
<div style="width: 300px">
|
||||||
<div style="width: 300px">
|
<select name="mySelectTopic" chosen
|
||||||
<select name="mySelectTopic" chosen
|
ng-model="selectedTopic"
|
||||||
ng-model="selectedTopic"
|
ng-options="item for item in allTopicList"
|
||||||
ng-options="item for item in allTopicList"
|
required>
|
||||||
required>
|
<option value=""></option>
|
||||||
<option value=""></option>
|
</select>
|
||||||
</select>
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Key:</label>
|
||||||
|
<input class="form-control" style="width: 450px" type="text" ng-model="key"
|
||||||
|
required/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label>Key:</label>
|
|
||||||
<input class="form-control" style="width: 450px" type="text" ng-model="key"
|
|
||||||
required/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button type="button" class="btn btn-raised btn-sm btn-primary" data-toggle="modal"
|
<button type="button" class="btn btn-raised btn-sm btn-primary" data-toggle="modal"
|
||||||
ng-click="queryMessageByTopicAndKey()">
|
ng-click="queryMessageByTopicAndKey()">
|
||||||
<span class="glyphicon glyphicon-search"></span>{{ 'SEARCH' | translate}}
|
<span class="glyphicon glyphicon-search"></span>{{ 'SEARCH' | translate}}
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
<table class="table table-bordered">
|
<table class="table table-bordered text-middle">
|
||||||
<tr>
|
<tr>
|
||||||
<th class="text-center">Message ID</th>
|
<th class="text-center">Message ID</th>
|
||||||
<th class="text-center">Tag</th>
|
<th class="text-center">Tag</th>
|
||||||
<th class="text-center">Key</th>
|
<th class="text-center">Key</th>
|
||||||
<th class="text-center">StoreTime</th>
|
<th class="text-center">StoreTime</th>
|
||||||
<th class="text-center">Operation</th>
|
<th class="text-center">Operation</th>
|
||||||
</tr>
|
</tr>
|
||||||
<tr ng-repeat="item in queryMessageByTopicAndKeyResult">
|
<tr ng-repeat="item in queryMessageByTopicAndKeyResult">
|
||||||
<td class="text-center">{{item.msgId}}</td>
|
<td class="text-center">{{item.msgId}}</td>
|
||||||
<td class="text-center">{{item.properties.TAGS}}</td>
|
<td class="text-center">{{item.properties.TAGS}}</td>
|
||||||
<td class="text-center">{{item.properties.KEYS}}</td>
|
<td class="text-center">{{item.properties.KEYS}}</td>
|
||||||
<td class="text-center">{{item.storeTimestamp | date:'yyyy-MM-dd HH:mm:ss'}}
|
<td class="text-center">{{item.storeTimestamp | date:'yyyy-MM-dd HH:mm:ss'}}
|
||||||
</td>
|
</td>
|
||||||
<td class="text-center">
|
<td class="text-center">
|
||||||
<button class="btn btn-raised btn-sm btn-primary" type="button"
|
<button class="btn btn-raised btn-sm btn-primary" type="button"
|
||||||
ng-click="queryMessageByMessageId(item.msgId,item.topic)">Message Detail
|
ng-click="queryMessageByMessageId(item.msgId,item.topic)">Message Detail
|
||||||
</button>
|
</button>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
</div>
|
||||||
</md-content>
|
</md-content>
|
||||||
</md-tab>
|
</md-tab>
|
||||||
<md-tab label="Message ID">
|
<md-tab label="Message ID">
|
||||||
<h5 class="md-display-5">topic can't be empty if you producer client version>=v3.5.8</h5>
|
<h5 class="md-display-5">topic can't be empty if you producer client version>=v3.5.8</h5>
|
||||||
<md-content class="md-padding" style="min-height:600px">
|
<md-content class="md-padding" style="min-height:600px">
|
||||||
<form class="form-inline pull-left col-sm-12">
|
<div class="row">
|
||||||
<div class="form-group">
|
<form class="form-inline pull-left">
|
||||||
<label>Topic:</label>
|
<div class="form-group">
|
||||||
</div>
|
<label>Topic:</label>
|
||||||
<div class="form-group ">
|
|
||||||
<div style="width: 300px">
|
|
||||||
<select name="mySelectTopic" chosen
|
|
||||||
ng-model="selectedTopic"
|
|
||||||
ng-options="item for item in allTopicList"
|
|
||||||
required>
|
|
||||||
<option value=""></option>
|
|
||||||
</select>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="form-group ">
|
||||||
<div class="form-group">
|
<div style="width: 300px">
|
||||||
<label>MessageId:</label>
|
<select name="mySelectTopic" chosen
|
||||||
<input class="form-control" style="width: 450px" type="text" ng-model="messageId"
|
ng-model="selectedTopic"
|
||||||
required/>
|
ng-options="item for item in allTopicList"
|
||||||
</div>
|
required>
|
||||||
<button type="button" class="btn btn-raised btn-sm btn-primary" data-toggle="modal"
|
<option value=""></option>
|
||||||
ng-click="queryMessageByMessageId(messageId,selectedTopic)">
|
</select>
|
||||||
<span class="glyphicon glyphicon-search"></span>{{ 'SEARCH' | translate}}
|
</div>
|
||||||
</button>
|
</div>
|
||||||
</form>
|
<div class="form-group">
|
||||||
|
<label>MessageId:</label>
|
||||||
|
<input class="form-control" style="width: 450px" type="text" ng-model="messageId"
|
||||||
|
required/>
|
||||||
|
</div>
|
||||||
|
<button type="button" class="btn btn-raised btn-sm btn-primary" data-toggle="modal"
|
||||||
|
ng-click="queryMessageByMessageId(messageId,selectedTopic)">
|
||||||
|
<span class="glyphicon glyphicon-search"></span>{{ 'SEARCH' | translate}}
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
</md-content>
|
</md-content>
|
||||||
</md-tab>
|
</md-tab>
|
||||||
</md-tabs>
|
</md-tabs>
|
||||||
@@ -224,15 +230,21 @@
|
|||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<p>messageTrackList:</p>
|
<div style="font-weight:700; color:#000">messageTrackList:</div>
|
||||||
<table class="table-bordered table">
|
<form class="form-inline">
|
||||||
|
<div class="form-group">
|
||||||
|
<label>consumerGroup:</label>
|
||||||
|
<input type="text" class="form-control" ng-model="filterConsumerGroup">
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<table class="table-bordered table text-middle">
|
||||||
<tr>
|
<tr>
|
||||||
<th class="text-center">consumerGroup</th>
|
<th class="text-center">consumerGroup</th>
|
||||||
<th class="text-center">trackType</th>
|
<th class="text-center">trackType</th>
|
||||||
<!--<th class="text-center">exceptionDesc</th>-->
|
<!--<th class="text-center">exceptionDesc</th>-->
|
||||||
<th class="text-center">Operation</th>
|
<th class="text-center">Operation</th>
|
||||||
</tr>
|
</tr>
|
||||||
<tr ng-repeat="item in ngDialogData.messageTrackList">
|
<tr ng-repeat="item in messageTrackShowList">
|
||||||
<td class="text-center">{{item.consumerGroup}}</td>
|
<td class="text-center">{{item.consumerGroup}}</td>
|
||||||
<td class="text-center">{{item.trackType}}</td>
|
<td class="text-center">{{item.trackType}}</td>
|
||||||
<td class="text-center">
|
<td class="text-center">
|
||||||
|
@@ -15,8 +15,8 @@
|
|||||||
~ limitations under the License.
|
~ limitations under the License.
|
||||||
-->
|
-->
|
||||||
<div class="container-fluid" id="deployHistoryList">
|
<div class="container-fluid" id="deployHistoryList">
|
||||||
<div class="modal-header">
|
<div class="modal-header" style="border-bottom: 0px">
|
||||||
<div class="row">
|
<div class="row" style="margin-left: 0px">
|
||||||
<label style="color: #000000">{{ 'TRACE_TOPIC' | translate }}:</label>
|
<label style="color: #000000">{{ 'TRACE_TOPIC' | translate }}:</label>
|
||||||
<div style="display: inline-block; min-width: 300px">
|
<div style="display: inline-block; min-width: 300px">
|
||||||
<select name="mySelect" chosen
|
<select name="mySelect" chosen
|
||||||
@@ -34,105 +34,110 @@
|
|||||||
<md-content>
|
<md-content>
|
||||||
<md-tabs md-dynamic-height="" md-border-bottom="">
|
<md-tabs md-dynamic-height="" md-border-bottom="">
|
||||||
<md-tab label="Message Key">
|
<md-tab label="Message Key">
|
||||||
|
<h5 class="md-display-5">Only Return 64 Messages</h5>
|
||||||
<md-content class="md-padding" style="min-height:600px">
|
<md-content class="md-padding" style="min-height:600px">
|
||||||
<h5 class="md-display-5">Only Return 64 Messages</h5>
|
<div class="row">
|
||||||
|
|
||||||
<form class="form-inline pull-left col-sm-12">
|
<form class="form-inline pull-left">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>Topic:</label>
|
<label>Topic:</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div style="width: 300px">
|
<div style="width: 300px">
|
||||||
<select name="mySelectTopic" chosen
|
<select name="mySelectTopic" chosen
|
||||||
ng-model="selectedTopic"
|
ng-model="selectedTopic"
|
||||||
ng-options="item for item in allTopicList"
|
ng-options="item for item in allTopicList"
|
||||||
required>
|
required>
|
||||||
<option value=""></option>
|
<option value=""></option>
|
||||||
</select>
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Key:</label>
|
||||||
|
<input class="form-control" style="width: 450px" type="text" ng-model="key"
|
||||||
|
required/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label>Key:</label>
|
|
||||||
<input class="form-control" style="width: 450px" type="text" ng-model="key"
|
|
||||||
required/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button type="button" class="btn btn-raised btn-sm btn-primary" data-toggle="modal"
|
<button type="button" class="btn btn-raised btn-sm btn-primary" data-toggle="modal"
|
||||||
ng-click="queryMessageByTopicAndKey()">
|
ng-click="queryMessageByTopicAndKey()">
|
||||||
<span class="glyphicon glyphicon-search"></span>{{ 'SEARCH' | translate}}
|
<span class="glyphicon glyphicon-search"></span>{{ 'SEARCH' | translate}}
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
<table class="table table-bordered">
|
<table class="table table-bordered text-middle">
|
||||||
<tr>
|
<tr>
|
||||||
<th class="text-center">Message ID</th>
|
<th class="text-center">Message ID</th>
|
||||||
<th class="text-center">Tag</th>
|
<th class="text-center">Tag</th>
|
||||||
<th class="text-center">Message Key</th>
|
<th class="text-center">Message Key</th>
|
||||||
<th class="text-center">StoreTime</th>
|
<th class="text-center">StoreTime</th>
|
||||||
<th class="text-center">Operation</th>
|
<th class="text-center">Operation</th>
|
||||||
</tr>
|
</tr>
|
||||||
<tr ng-repeat="item in queryMessageByTopicAndKeyResult">
|
<tr ng-repeat="item in queryMessageByTopicAndKeyResult">
|
||||||
<td class="text-center">{{item.msgId}}</td>
|
<td class="text-center">{{item.msgId}}</td>
|
||||||
<td class="text-center">{{item.properties.TAGS}}</td>
|
<td class="text-center">{{item.properties.TAGS}}</td>
|
||||||
<td class="text-center">{{item.properties.KEYS}}</td>
|
<td class="text-center">{{item.properties.KEYS}}</td>
|
||||||
<td class="text-center">{{item.storeTimestamp | date:'yyyy-MM-dd HH:mm:ss'}}
|
<td class="text-center">{{item.storeTimestamp | date:'yyyy-MM-dd HH:mm:ss'}}
|
||||||
</td>
|
</td>
|
||||||
<td class="text-center">
|
<td class="text-center">
|
||||||
<button class="btn btn-raised btn-sm btn-primary" type="button"
|
<button class="btn btn-raised btn-sm btn-primary" type="button"
|
||||||
ng-click="queryMessageTraceByMessageId(item.msgId, selectedTraceTopic)">{{ 'MESSAGE_TRACE_DETAIL' | translate }}
|
ng-click="queryMessageTraceByMessageId(item.msgId, selectedTraceTopic)">{{ 'MESSAGE_TRACE_DETAIL' | translate }}
|
||||||
</button>
|
</button>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
</div>
|
||||||
</md-content>
|
</md-content>
|
||||||
</md-tab>
|
</md-tab>
|
||||||
<md-tab label="Message ID">
|
<md-tab label="Message ID">
|
||||||
<h5 class="md-display-5">topic can't be empty if you producer client version>=v3.5.8</h5>
|
<h5 class="md-display-5">topic can't be empty if you producer client version>=v3.5.8</h5>
|
||||||
<md-content class="md-padding" style="min-height:600px">
|
<md-content class="md-padding" style="min-height:600px">
|
||||||
<form class="form-inline pull-left col-sm-12">
|
<div class="row">
|
||||||
<div class="form-group">
|
|
||||||
<label>Topic:</label>
|
<form class="form-inline pull-left">
|
||||||
</div>
|
<div class="form-group">
|
||||||
<div class="form-group ">
|
<label>Topic:</label>
|
||||||
<div style="width: 300px">
|
|
||||||
<select name="mySelectTopic" chosen
|
|
||||||
ng-model="selectedTopic"
|
|
||||||
ng-options="item for item in allTopicList"
|
|
||||||
required>
|
|
||||||
<option value=""></option>
|
|
||||||
</select>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="form-group ">
|
||||||
<div class="form-group">
|
<div style="width: 300px">
|
||||||
<label>MessageId:</label>
|
<select name="mySelectTopic" chosen
|
||||||
<input class="form-control" style="width: 450px" type="text" ng-model="messageId"
|
ng-model="selectedTopic"
|
||||||
required/>
|
ng-options="item for item in allTopicList"
|
||||||
</div>
|
required>
|
||||||
<button type="button" class="btn btn-raised btn-sm btn-primary" data-toggle="modal"
|
<option value=""></option>
|
||||||
ng-click="queryMessageByMessageId(messageId,selectedTopic)">
|
</select>
|
||||||
<span class="glyphicon glyphicon-search"></span>{{ 'SEARCH' | translate}}
|
</div>
|
||||||
</button>
|
</div>
|
||||||
</form>
|
<div class="form-group">
|
||||||
<table class="table table-bordered">
|
<label>MessageId:</label>
|
||||||
<tr>
|
<input class="form-control" style="width: 450px" type="text" ng-model="messageId"
|
||||||
<th class="text-center">Message ID</th>
|
required/>
|
||||||
<th class="text-center">Tag</th>
|
</div>
|
||||||
<th class="text-center">Message Key</th>
|
<button type="button" class="btn btn-raised btn-sm btn-primary" data-toggle="modal"
|
||||||
<th class="text-center">StoreTime</th>
|
ng-click="queryMessageByMessageId(messageId,selectedTopic)">
|
||||||
<th class="text-center">Operation</th>
|
<span class="glyphicon glyphicon-search"></span>{{ 'SEARCH' | translate}}
|
||||||
</tr>
|
</button>
|
||||||
<tr ng-repeat="item in queryMessageByMessageIdResult">
|
</form>
|
||||||
<td class="text-center">{{item.msgId}}</td>
|
<table class="table table-bordered text-middle">
|
||||||
<td class="text-center">{{item.properties.TAGS}}</td>
|
<tr>
|
||||||
<td class="text-center">{{item.properties.KEYS}}</td>
|
<th class="text-center">Message ID</th>
|
||||||
<td class="text-center">{{item.storeTimestamp | date:'yyyy-MM-dd HH:mm:ss'}}
|
<th class="text-center">Tag</th>
|
||||||
</td>
|
<th class="text-center">Message Key</th>
|
||||||
<td class="text-center">
|
<th class="text-center">StoreTime</th>
|
||||||
<button class="btn btn-raised btn-sm btn-primary" type="button"
|
<th class="text-center">Operation</th>
|
||||||
ng-click="queryMessageTraceByMessageId(item.msgId, selectedTraceTopic)">{{ 'MESSAGE_TRACE_DETAIL' | translate }}
|
</tr>
|
||||||
</button>
|
<tr ng-repeat="item in queryMessageByMessageIdResult">
|
||||||
</td>
|
<td class="text-center">{{item.msgId}}</td>
|
||||||
</tr>
|
<td class="text-center">{{item.properties.TAGS}}</td>
|
||||||
</table>
|
<td class="text-center">{{item.properties.KEYS}}</td>
|
||||||
|
<td class="text-center">{{item.storeTimestamp | date:'yyyy-MM-dd HH:mm:ss'}}
|
||||||
|
</td>
|
||||||
|
<td class="text-center">
|
||||||
|
<button class="btn btn-raised btn-sm btn-primary" type="button"
|
||||||
|
ng-click="queryMessageTraceByMessageId(item.msgId, selectedTraceTopic)">{{ 'MESSAGE_TRACE_DETAIL' | translate }}
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
</md-content>
|
</md-content>
|
||||||
</md-tab>
|
</md-tab>
|
||||||
</md-tabs>
|
</md-tabs>
|
||||||
|
@@ -17,14 +17,26 @@
|
|||||||
<div class="container-fluid" id="deployHistoryList">
|
<div class="container-fluid" id="deployHistoryList">
|
||||||
<div class="page-content">
|
<div class="page-content">
|
||||||
<h2 class="md-title">NameServerAddressList</h2>
|
<h2 class="md-title">NameServerAddressList</h2>
|
||||||
<div class="pull-left">
|
<div class="pull-left" style="min-width: 400px; max-width: 500px; padding: 10px 10px 10px 0">
|
||||||
<input type="text" class="form-control" ng-model="namesvrAddrList" ng-disabled="{{!writeOperationEnabled}}"/>
|
<select ng-model="selectedNamesrv" chosen
|
||||||
|
ng-options="x for x in namesvrAddrList"
|
||||||
|
ng-change="updateNameSvrAddr()"
|
||||||
|
required></select>
|
||||||
</div>
|
</div>
|
||||||
<div class="pull-left">
|
<div class="pull-left">
|
||||||
<button class="btn btn-raised btn-sm btn-primary" type="button" ng-show="{{writeOperationEnabled}}"
|
<button class="btn btn-raised btn-sm btn-primary" type="button" ng-show="{{writeOperationEnabled}}"
|
||||||
ng-click="updateNameSvrAddr()">{{'UPDATE' | translate}}
|
ng-click="updateNameSvrAddr()">{{'UPDATE' | translate}}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
<form class="form-inline pull-left" style="margin-left: 20px" ng-show="{{writeOperationEnabled}}">
|
||||||
|
<div class="form-group" style="margin: 0">
|
||||||
|
<label for="namesrvAddr">NamesrvAddr:</label>
|
||||||
|
<input id="namesrvAddr" class="form-control" style="width: 300px; margin: 0 10px 0 10px" type="text" ng-model="newNamesrvAddr" required/>
|
||||||
|
<button class="btn btn-raised btn-sm btn-primary" type="button"
|
||||||
|
ng-click="addNameSvrAddr()"> {{ 'ADD' | translate}}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
<br/>
|
<br/>
|
||||||
<br/>
|
<br/>
|
||||||
<h2 class="md-title">IsUseVIPChannel</h2>
|
<h2 class="md-title">IsUseVIPChannel</h2>
|
||||||
|
@@ -48,7 +48,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
<tr ng-repeat="fTopic in topicShowList"
|
<tr ng-repeat="fTopic in topicShowList"
|
||||||
ng-init="sysFlag = fTopic.startsWith('%SYS%'); topic = sysFlag?fTopic.substring(5):fTopic">
|
ng-init="sysFlag = fTopic.startsWith('%SYS%'); topic = sysFlag?fTopic.substring(5):fTopic">
|
||||||
<td class="text-left"><font color={{sysFlag?"red":""}}>{{topic}}</font></td>
|
<td class="text-center"><font color={{sysFlag?"red":""}}>{{topic}}</font></td>
|
||||||
<td class="text-left">
|
<td class="text-left">
|
||||||
<button class="btn btn-raised btn-sm btn-primary" type="button"
|
<button class="btn btn-raised btn-sm btn-primary" type="button"
|
||||||
ng-click="statsView(topic)">{{'STATUS' | translate}}
|
ng-click="statsView(topic)">{{'STATUS' | translate}}
|
||||||
|
@@ -418,8 +418,10 @@ public class MQAdminExtImplTest {
|
|||||||
assertNotNull(mqAdminExtImpl);
|
assertNotNull(mqAdminExtImpl);
|
||||||
{
|
{
|
||||||
doNothing().when(defaultMQAdminExt).deleteSubscriptionGroup(anyString(), anyString());
|
doNothing().when(defaultMQAdminExt).deleteSubscriptionGroup(anyString(), anyString());
|
||||||
|
doNothing().when(defaultMQAdminExt).deleteSubscriptionGroup(anyString(), anyString(), anyBoolean());
|
||||||
}
|
}
|
||||||
mqAdminExtImpl.deleteSubscriptionGroup(brokerAddr, "group_test");
|
mqAdminExtImpl.deleteSubscriptionGroup(brokerAddr, "group_test");
|
||||||
|
mqAdminExtImpl.deleteSubscriptionGroup(brokerAddr, "group_test", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -758,16 +760,6 @@ public class MQAdminExtImplTest {
|
|||||||
Assert.assertNotNull(wrapper);
|
Assert.assertNotNull(wrapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testGetAllTopicGroup() throws Exception {
|
|
||||||
assertNotNull(mqAdminExtImpl);
|
|
||||||
{
|
|
||||||
when(defaultMQAdminExt.getAllTopicGroup(anyString(), anyLong())).thenReturn(new TopicConfigSerializeWrapper());
|
|
||||||
}
|
|
||||||
TopicConfigSerializeWrapper wrapper = mqAdminExtImpl.getAllTopicGroup(brokerAddr, 5000L);
|
|
||||||
Assert.assertNotNull(wrapper);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testUpdateConsumeOffset() throws Exception {
|
public void testUpdateConsumeOffset() throws Exception {
|
||||||
assertNotNull(mqAdminExtImpl);
|
assertNotNull(mqAdminExtImpl);
|
||||||
@@ -801,4 +793,42 @@ public class MQAdminExtImplTest {
|
|||||||
Assert.assertFalse(mqAdminExtImpl.resumeCheckHalfMessage("topic_test", "7F000001ACC018B4AAC2116AF6500000"));
|
Assert.assertFalse(mqAdminExtImpl.resumeCheckHalfMessage("topic_test", "7F000001ACC018B4AAC2116AF6500000"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAddWritePermOfBroker() throws Exception {
|
||||||
|
assertNotNull(mqAdminExtImpl);
|
||||||
|
{
|
||||||
|
when(defaultMQAdminExt.addWritePermOfBroker(anyString(), anyString())).thenReturn(6);
|
||||||
|
}
|
||||||
|
Assert.assertEquals(mqAdminExtImpl.addWritePermOfBroker("127.0.0.1:9876", "broker-a"), 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetUserSubscriptionGroup() throws Exception {
|
||||||
|
assertNotNull(mqAdminExtImpl);
|
||||||
|
SubscriptionGroupWrapper wrapper = new SubscriptionGroupWrapper();
|
||||||
|
{
|
||||||
|
when(defaultMQAdminExt.getUserSubscriptionGroup(anyString(), anyLong())).thenReturn(wrapper);
|
||||||
|
}
|
||||||
|
Assert.assertEquals(mqAdminExtImpl.getUserSubscriptionGroup("127.0.0.1:10911", 3000), wrapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetAllTopicConfig() throws Exception {
|
||||||
|
assertNotNull(mqAdminExtImpl);
|
||||||
|
TopicConfigSerializeWrapper wrapper = new TopicConfigSerializeWrapper();
|
||||||
|
{
|
||||||
|
when(defaultMQAdminExt.getAllTopicConfig(anyString(), anyLong())).thenReturn(wrapper);
|
||||||
|
}
|
||||||
|
Assert.assertEquals(mqAdminExtImpl.getAllTopicConfig("127.0.0.1:10911", 3000), wrapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetUserTopicConfig() throws Exception {
|
||||||
|
assertNotNull(mqAdminExtImpl);
|
||||||
|
TopicConfigSerializeWrapper wrapper = new TopicConfigSerializeWrapper();
|
||||||
|
{
|
||||||
|
when(defaultMQAdminExt.getUserTopicConfig(anyString(), anyBoolean(), anyLong())).thenReturn(wrapper);
|
||||||
|
}
|
||||||
|
Assert.assertEquals(mqAdminExtImpl.getUserTopicConfig("127.0.0.1:10911", true, 3000), wrapper);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,73 @@
|
|||||||
|
/*
|
||||||
|
* 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 org.apache.rocketmq.dashboard.BaseTest;
|
||||||
|
import org.apache.rocketmq.dashboard.interceptor.AuthInterceptor;
|
||||||
|
import org.assertj.core.util.Lists;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.jupiter.api.Assertions;
|
||||||
|
import org.mockito.InjectMocks;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.Mockito;
|
||||||
|
import org.mockito.MockitoAnnotations;
|
||||||
|
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||||
|
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||||
|
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||||
|
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class AuthWebMVCConfigurerAdapterTest extends BaseTest {
|
||||||
|
|
||||||
|
@InjectMocks
|
||||||
|
private AuthWebMVCConfigurerAdapter authWebMVCConfigurerAdapter;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private RMQConfigure configure;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private AuthInterceptor authInterceptor;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void init() throws Exception {
|
||||||
|
MockitoAnnotations.initMocks(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void addInterceptors() {
|
||||||
|
Mockito.when(configure.isLoginRequired()).thenReturn(true);
|
||||||
|
InterceptorRegistry registry = new InterceptorRegistry();
|
||||||
|
Assertions.assertDoesNotThrow(() -> authWebMVCConfigurerAdapter.addInterceptors(registry));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void addArgumentResolvers() {
|
||||||
|
List<HandlerMethodArgumentResolver> argumentResolvers = Lists.newArrayList();
|
||||||
|
authWebMVCConfigurerAdapter.addArgumentResolvers(argumentResolvers);
|
||||||
|
Assertions.assertEquals(1, argumentResolvers.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void addViewControllers() {
|
||||||
|
ViewControllerRegistry registry = new ViewControllerRegistry(new ClassPathXmlApplicationContext());
|
||||||
|
Assertions.assertDoesNotThrow(() -> authWebMVCConfigurerAdapter.addViewControllers(registry));
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* 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.CountDownLatch;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class CollectExecutorConfigTest {
|
||||||
|
|
||||||
|
private final static int COUNT = 100;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCollectExecutor() throws Exception {
|
||||||
|
AtomicInteger num = new AtomicInteger(0);
|
||||||
|
CollectExecutorConfig config = new CollectExecutorConfig();
|
||||||
|
config.setCoreSize(10);
|
||||||
|
config.setMaxSize(10);
|
||||||
|
config.setQueueSize(500);
|
||||||
|
config.setKeepAliveTime(3000);
|
||||||
|
ExecutorService collectExecutor = config.collectExecutor(config);
|
||||||
|
Assert.assertNotNull(collectExecutor);
|
||||||
|
CountDownLatch countDownLatch = new CountDownLatch(COUNT);
|
||||||
|
for (int i = 0; i < COUNT; i++) {
|
||||||
|
collectExecutor.submit(() -> {
|
||||||
|
num.getAndIncrement();
|
||||||
|
countDownLatch.countDown();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
countDownLatch.await();
|
||||||
|
System.out.println(collectExecutor.isTerminated());
|
||||||
|
Assert.assertEquals(COUNT, num.get());
|
||||||
|
}
|
||||||
|
}
|
@@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
package org.apache.rocketmq.dashboard.config;
|
package org.apache.rocketmq.dashboard.config;
|
||||||
|
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
@@ -39,6 +40,7 @@ public class RMQConfigureTest {
|
|||||||
rmqConfigure.setLoginRequired(true);
|
rmqConfigure.setLoginRequired(true);
|
||||||
rmqConfigure.setNamesrvAddr("127.0.0.1:9876");
|
rmqConfigure.setNamesrvAddr("127.0.0.1:9876");
|
||||||
rmqConfigure.setTimeoutMillis(3000L);
|
rmqConfigure.setTimeoutMillis(3000L);
|
||||||
|
rmqConfigure.setNamesrvAddrs(Lists.asList("127.0.0.1:9876", new String[] {"127.0.0.2:9876"}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -54,6 +56,7 @@ public class RMQConfigureTest {
|
|||||||
Assert.assertTrue(rmqConfigure.isEnableDashBoardCollect());
|
Assert.assertTrue(rmqConfigure.isEnableDashBoardCollect());
|
||||||
Assert.assertTrue(rmqConfigure.isLoginRequired());
|
Assert.assertTrue(rmqConfigure.isLoginRequired());
|
||||||
Assert.assertEquals(rmqConfigure.getNamesrvAddr(), "127.0.0.1:9876");
|
Assert.assertEquals(rmqConfigure.getNamesrvAddr(), "127.0.0.1:9876");
|
||||||
|
Assert.assertEquals(rmqConfigure.getNamesrvAddrs().size(), 2);
|
||||||
Assert.assertEquals(rmqConfigure.getTimeoutMillis().longValue(), 3000L);
|
Assert.assertEquals(rmqConfigure.getTimeoutMillis().longValue(), 3000L);
|
||||||
ErrorPageRegistrar registrar = rmqConfigure.errorPageRegistrar();
|
ErrorPageRegistrar registrar = rmqConfigure.errorPageRegistrar();
|
||||||
registrar.registerErrorPages(new ErrorPageRegistry() {
|
registrar.registerErrorPages(new ErrorPageRegistry() {
|
||||||
|
@@ -0,0 +1,368 @@
|
|||||||
|
/*
|
||||||
|
* 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.alibaba.fastjson.JSON;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import java.util.List;
|
||||||
|
import org.apache.rocketmq.common.AclConfig;
|
||||||
|
import org.apache.rocketmq.common.PlainAccessConfig;
|
||||||
|
import org.apache.rocketmq.common.protocol.body.ClusterInfo;
|
||||||
|
import org.apache.rocketmq.dashboard.model.request.AclRequest;
|
||||||
|
import org.apache.rocketmq.dashboard.service.impl.AclServiceImpl;
|
||||||
|
import org.apache.rocketmq.dashboard.util.MockObjectUtil;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.mockito.InjectMocks;
|
||||||
|
import org.mockito.Spy;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||||
|
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyString;
|
||||||
|
import static org.mockito.Mockito.doNothing;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||||
|
|
||||||
|
public class AclControllerTest extends BaseControllerTest {
|
||||||
|
|
||||||
|
@InjectMocks
|
||||||
|
private AclController aclController;
|
||||||
|
|
||||||
|
@Spy
|
||||||
|
private AclServiceImpl aclService;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void init() throws Exception {
|
||||||
|
AclConfig aclConfig = MockObjectUtil.createAclConfig();
|
||||||
|
when(mqAdminExt.examineBrokerClusterAclConfig(anyString())).thenReturn(aclConfig);
|
||||||
|
ClusterInfo clusterInfo = MockObjectUtil.createClusterInfo();
|
||||||
|
when(mqAdminExt.examineBrokerClusterInfo()).thenReturn(clusterInfo);
|
||||||
|
doNothing().when(mqAdminExt).createAndUpdatePlainAccessConfig(anyString(), any(PlainAccessConfig.class));
|
||||||
|
doNothing().when(mqAdminExt).deletePlainAccessConfig(anyString(), anyString());
|
||||||
|
doNothing().when(mqAdminExt).updateGlobalWhiteAddrConfig(anyString(), anyString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testIsEnableAcl() throws Exception {
|
||||||
|
final String url = "/acl/enable.query";
|
||||||
|
// 1. disable acl.
|
||||||
|
requestBuilder = MockMvcRequestBuilders.get(url);
|
||||||
|
perform = mockMvc.perform(requestBuilder);
|
||||||
|
perform.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.data").value(false));
|
||||||
|
|
||||||
|
// 2.enable acl.
|
||||||
|
super.mockRmqConfigure();
|
||||||
|
perform = mockMvc.perform(requestBuilder);
|
||||||
|
perform.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.data").value(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetAclConfig() throws Exception {
|
||||||
|
final String url = "/acl/config.query";
|
||||||
|
|
||||||
|
// 1. broker addr table is not empty.
|
||||||
|
ClusterInfo clusterInfo = MockObjectUtil.createClusterInfo();
|
||||||
|
when(mqAdminExt.examineBrokerClusterInfo()).thenReturn(clusterInfo);
|
||||||
|
requestBuilder = MockMvcRequestBuilders.get(url);
|
||||||
|
perform = mockMvc.perform(requestBuilder);
|
||||||
|
perform.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.data").isMap())
|
||||||
|
.andExpect(jsonPath("$.data.globalWhiteAddrs").isNotEmpty())
|
||||||
|
.andExpect(jsonPath("$.data.plainAccessConfigs").isNotEmpty())
|
||||||
|
.andExpect(jsonPath("$.data.plainAccessConfigs[0].secretKey").isNotEmpty());
|
||||||
|
|
||||||
|
// 2. broker addr table is empty.
|
||||||
|
clusterInfo.getBrokerAddrTable().clear();
|
||||||
|
perform = mockMvc.perform(requestBuilder);
|
||||||
|
perform.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.data").isMap())
|
||||||
|
.andExpect(jsonPath("$.data.globalWhiteAddrs").isEmpty())
|
||||||
|
.andExpect(jsonPath("$.data.plainAccessConfigs").isEmpty());
|
||||||
|
|
||||||
|
// 3. login required and user info is null.
|
||||||
|
when(configure.isLoginRequired()).thenReturn(true);
|
||||||
|
when(mqAdminExt.examineBrokerClusterInfo()).thenReturn(MockObjectUtil.createClusterInfo());
|
||||||
|
perform = mockMvc.perform(requestBuilder);
|
||||||
|
perform.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.data").isMap())
|
||||||
|
.andExpect(jsonPath("$.data.globalWhiteAddrs").isNotEmpty())
|
||||||
|
.andExpect(jsonPath("$.data.plainAccessConfigs").isNotEmpty())
|
||||||
|
.andExpect(jsonPath("$.data.plainAccessConfigs[0].secretKey").isEmpty());
|
||||||
|
// 4. login required, but user is not admin. emmmm, Mockito may can not mock static method.
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAddAclConfig() throws Exception {
|
||||||
|
final String url = "/acl/add.do";
|
||||||
|
PlainAccessConfig accessConfig = new PlainAccessConfig();
|
||||||
|
requestBuilder = MockMvcRequestBuilders.post(url);
|
||||||
|
requestBuilder.contentType(MediaType.APPLICATION_JSON_UTF8);
|
||||||
|
|
||||||
|
// 1. access key is null.
|
||||||
|
requestBuilder.content(JSON.toJSONString(accessConfig));
|
||||||
|
perform = mockMvc.perform(requestBuilder);
|
||||||
|
perform.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.status").value(-1))
|
||||||
|
.andExpect(jsonPath("$.errMsg").exists());
|
||||||
|
|
||||||
|
// 2. secret key is null.
|
||||||
|
accessConfig.setAccessKey("test-access-key");
|
||||||
|
requestBuilder.content(JSON.toJSONString(accessConfig));
|
||||||
|
perform = mockMvc.perform(requestBuilder);
|
||||||
|
perform.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.status").value(-1))
|
||||||
|
.andExpect(jsonPath("$.errMsg").exists());
|
||||||
|
|
||||||
|
ClusterInfo clusterInfo = MockObjectUtil.createClusterInfo();
|
||||||
|
when(mqAdminExt.examineBrokerClusterInfo()).thenReturn(clusterInfo);
|
||||||
|
|
||||||
|
// 3. add if the access key not exist.
|
||||||
|
accessConfig.setSecretKey("12345678");
|
||||||
|
requestBuilder.content(JSON.toJSONString(accessConfig));
|
||||||
|
perform = mockMvc.perform(requestBuilder);
|
||||||
|
perform.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.status").value(0));
|
||||||
|
|
||||||
|
// 4. add failed if the access key is existed.
|
||||||
|
accessConfig.setAccessKey("rocketmq2");
|
||||||
|
requestBuilder.content(JSON.toJSONString(accessConfig));
|
||||||
|
perform = mockMvc.perform(requestBuilder);
|
||||||
|
perform.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.status").value(-1))
|
||||||
|
.andExpect(jsonPath("$.errMsg").exists());
|
||||||
|
|
||||||
|
// 5. add failed if there is no alive broker.
|
||||||
|
clusterInfo.getBrokerAddrTable().clear();
|
||||||
|
accessConfig.setAccessKey("test-access-key");
|
||||||
|
requestBuilder.content(JSON.toJSONString(accessConfig));
|
||||||
|
perform = mockMvc.perform(requestBuilder);
|
||||||
|
perform.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.status").value(-1))
|
||||||
|
.andExpect(jsonPath("$.errMsg").exists());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDeleteAclConfig() throws Exception {
|
||||||
|
final String url = "/acl/delete.do";
|
||||||
|
PlainAccessConfig accessConfig = new PlainAccessConfig();
|
||||||
|
requestBuilder = MockMvcRequestBuilders.post(url);
|
||||||
|
requestBuilder.contentType(MediaType.APPLICATION_JSON_UTF8);
|
||||||
|
|
||||||
|
// 1. access key is null.
|
||||||
|
requestBuilder.content(JSON.toJSONString(accessConfig));
|
||||||
|
perform = mockMvc.perform(requestBuilder);
|
||||||
|
perform.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.status").value(-1))
|
||||||
|
.andExpect(jsonPath("$.errMsg").exists());
|
||||||
|
|
||||||
|
// 2. access key is not null.
|
||||||
|
accessConfig.setAccessKey("rocketmq");
|
||||||
|
requestBuilder.content(JSON.toJSONString(accessConfig));
|
||||||
|
perform = mockMvc.perform(requestBuilder);
|
||||||
|
perform.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.status").value(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUpdateAclConfig() throws Exception {
|
||||||
|
final String url = "/acl/update.do";
|
||||||
|
PlainAccessConfig accessConfig = new PlainAccessConfig();
|
||||||
|
requestBuilder = MockMvcRequestBuilders.post(url);
|
||||||
|
requestBuilder.contentType(MediaType.APPLICATION_JSON_UTF8);
|
||||||
|
|
||||||
|
// 1. secret key is null.
|
||||||
|
accessConfig.setAccessKey("rocketmq");
|
||||||
|
requestBuilder.content(JSON.toJSONString(accessConfig));
|
||||||
|
perform = mockMvc.perform(requestBuilder);
|
||||||
|
perform.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.status").value(-1))
|
||||||
|
.andExpect(jsonPath("$.errMsg").exists());
|
||||||
|
|
||||||
|
// 2. update.
|
||||||
|
accessConfig.setSecretKey("abcdefghjkl");
|
||||||
|
requestBuilder.content(JSON.toJSONString(accessConfig));
|
||||||
|
perform = mockMvc.perform(requestBuilder);
|
||||||
|
perform.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.status").value(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAddAclTopicConfig() throws Exception {
|
||||||
|
final String url = "/acl/topic/add.do";
|
||||||
|
AclRequest request = new AclRequest();
|
||||||
|
request.setConfig(createDefaultPlainAccessConfig());
|
||||||
|
|
||||||
|
// 1. if not exist.
|
||||||
|
request.setTopicPerm("test_topic=PUB");
|
||||||
|
requestBuilder = MockMvcRequestBuilders.post(url);
|
||||||
|
requestBuilder.contentType(MediaType.APPLICATION_JSON_UTF8);
|
||||||
|
requestBuilder.content(JSON.toJSONString(request));
|
||||||
|
perform = mockMvc.perform(requestBuilder);
|
||||||
|
perform.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.status").value(0));
|
||||||
|
|
||||||
|
// 2. if exist.
|
||||||
|
request.setTopicPerm("topicA=PUB");
|
||||||
|
requestBuilder.content(JSON.toJSONString(request));
|
||||||
|
perform = mockMvc.perform(requestBuilder);
|
||||||
|
perform.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.status").value(0));
|
||||||
|
|
||||||
|
// 3. if access key not exist.
|
||||||
|
request.getConfig().setAccessKey("test_access_key123");
|
||||||
|
requestBuilder.content(JSON.toJSONString(request));
|
||||||
|
perform = mockMvc.perform(requestBuilder);
|
||||||
|
perform.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.status").value(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAddAclGroupConfig() throws Exception {
|
||||||
|
final String url = "/acl/group/add.do";
|
||||||
|
AclRequest request = new AclRequest();
|
||||||
|
request.setConfig(createDefaultPlainAccessConfig());
|
||||||
|
|
||||||
|
// 1. if not exist.
|
||||||
|
request.setGroupPerm("test_consumer=PUB|SUB");
|
||||||
|
requestBuilder = MockMvcRequestBuilders.post(url);
|
||||||
|
requestBuilder.contentType(MediaType.APPLICATION_JSON_UTF8);
|
||||||
|
requestBuilder.content(JSON.toJSONString(request));
|
||||||
|
perform = mockMvc.perform(requestBuilder);
|
||||||
|
perform.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.status").value(0));
|
||||||
|
|
||||||
|
// 2. if exist.
|
||||||
|
request.setGroupPerm("groupA=PUB|SUB");
|
||||||
|
requestBuilder.content(JSON.toJSONString(request));
|
||||||
|
perform = mockMvc.perform(requestBuilder);
|
||||||
|
perform.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.status").value(0));
|
||||||
|
|
||||||
|
// 3. if access key not exist.
|
||||||
|
request.getConfig().setAccessKey("test_access_key123");
|
||||||
|
requestBuilder.content(JSON.toJSONString(request));
|
||||||
|
perform = mockMvc.perform(requestBuilder);
|
||||||
|
perform.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.status").value(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDeletePermConfig() throws Exception {
|
||||||
|
final String url = "/acl/perm/delete.do";
|
||||||
|
AclRequest request = new AclRequest();
|
||||||
|
request.setConfig(createDefaultPlainAccessConfig());
|
||||||
|
requestBuilder = MockMvcRequestBuilders.post(url);
|
||||||
|
requestBuilder.contentType(MediaType.APPLICATION_JSON_UTF8);
|
||||||
|
requestBuilder.content(JSON.toJSONString(request));
|
||||||
|
perform = mockMvc.perform(requestBuilder);
|
||||||
|
perform.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.status").value(0));
|
||||||
|
|
||||||
|
// if access key not exist.
|
||||||
|
request.getConfig().setAccessKey("test_access_key123");
|
||||||
|
requestBuilder.content(JSON.toJSONString(request));
|
||||||
|
perform = mockMvc.perform(requestBuilder);
|
||||||
|
perform.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.status").value(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSyncConfig() throws Exception {
|
||||||
|
final String url = "/acl/sync.do";
|
||||||
|
requestBuilder = MockMvcRequestBuilders.post(url);
|
||||||
|
requestBuilder.contentType(MediaType.APPLICATION_JSON_UTF8);
|
||||||
|
requestBuilder.content(JSON.toJSONString(createDefaultPlainAccessConfig()));
|
||||||
|
perform = mockMvc.perform(requestBuilder);
|
||||||
|
perform.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.status").value(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAddWhiteList() throws Exception {
|
||||||
|
final String url = "/acl/white/list/add.do";
|
||||||
|
List<String> whiteList = Lists.newArrayList("192.168.0.1");
|
||||||
|
|
||||||
|
// 1. if global white list is not null.
|
||||||
|
requestBuilder = MockMvcRequestBuilders.post(url);
|
||||||
|
requestBuilder.contentType(MediaType.APPLICATION_JSON_UTF8);
|
||||||
|
requestBuilder.content(JSON.toJSONString(whiteList));
|
||||||
|
perform = mockMvc.perform(requestBuilder);
|
||||||
|
perform.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.status").value(0));
|
||||||
|
|
||||||
|
// 2. if global white list is null.
|
||||||
|
AclConfig aclConfig = MockObjectUtil.createAclConfig();
|
||||||
|
aclConfig.setGlobalWhiteAddrs(null);
|
||||||
|
when(mqAdminExt.examineBrokerClusterAclConfig(anyString())).thenReturn(aclConfig);
|
||||||
|
perform = mockMvc.perform(requestBuilder);
|
||||||
|
perform.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.status").value(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDeleteWhiteAddr() throws Exception {
|
||||||
|
final String url = "/acl/white/list/delete.do";
|
||||||
|
requestBuilder = MockMvcRequestBuilders.delete(url);
|
||||||
|
requestBuilder.param("request", "localhost");
|
||||||
|
perform = mockMvc.perform(requestBuilder);
|
||||||
|
perform.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.status").value(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSynchronizeWhiteList() throws Exception {
|
||||||
|
final String url = "/acl/white/list/sync.do";
|
||||||
|
List<String> whiteList = Lists.newArrayList();
|
||||||
|
|
||||||
|
// 1. if white list for syncing is empty.
|
||||||
|
requestBuilder = MockMvcRequestBuilders.post(url);
|
||||||
|
requestBuilder.contentType(MediaType.APPLICATION_JSON_UTF8);
|
||||||
|
requestBuilder.content(JSON.toJSONString(whiteList));
|
||||||
|
perform = mockMvc.perform(requestBuilder);
|
||||||
|
perform.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.status").value(-1))
|
||||||
|
.andExpect(jsonPath("$.errMsg").exists());
|
||||||
|
|
||||||
|
// 2. if white list for syncing is not empty.
|
||||||
|
whiteList.add("localhost");
|
||||||
|
requestBuilder.content(JSON.toJSONString(whiteList));
|
||||||
|
perform = mockMvc.perform(requestBuilder);
|
||||||
|
perform.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.status").value(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override protected Object getTestController() {
|
||||||
|
return aclController;
|
||||||
|
}
|
||||||
|
|
||||||
|
private PlainAccessConfig createDefaultPlainAccessConfig() {
|
||||||
|
PlainAccessConfig config = new PlainAccessConfig();
|
||||||
|
config.setAdmin(false);
|
||||||
|
config.setAccessKey("rocketmq");
|
||||||
|
config.setSecretKey("123456789");
|
||||||
|
config.setDefaultGroupPerm("SUB");
|
||||||
|
config.setDefaultTopicPerm("DENY");
|
||||||
|
config.setTopicPerms(Lists.newArrayList("topicA=DENY", "topicB=PUB|SUB"));
|
||||||
|
config.setGroupPerms(Lists.newArrayList("groupA=DENY", "groupB=PUB|SUB"));
|
||||||
|
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
}
|
@@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
package org.apache.rocketmq.dashboard.controller;
|
package org.apache.rocketmq.dashboard.controller;
|
||||||
|
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
import org.apache.rocketmq.dashboard.BaseTest;
|
import org.apache.rocketmq.dashboard.BaseTest;
|
||||||
import org.apache.rocketmq.dashboard.config.RMQConfigure;
|
import org.apache.rocketmq.dashboard.config.RMQConfigure;
|
||||||
import org.apache.rocketmq.dashboard.support.GlobalExceptionHandler;
|
import org.apache.rocketmq.dashboard.support.GlobalExceptionHandler;
|
||||||
@@ -62,6 +63,7 @@ public abstract class BaseControllerTest extends BaseTest {
|
|||||||
when(configure.getAccessKey()).thenReturn("12345678");
|
when(configure.getAccessKey()).thenReturn("12345678");
|
||||||
when(configure.getSecretKey()).thenReturn("rocketmq");
|
when(configure.getSecretKey()).thenReturn("rocketmq");
|
||||||
when(configure.getNamesrvAddr()).thenReturn("127.0.0.1:9876");
|
when(configure.getNamesrvAddr()).thenReturn("127.0.0.1:9876");
|
||||||
|
when(configure.getNamesrvAddrs()).thenReturn(Lists.newArrayList("127.0.0.1:9876", "127.0.0.2:9876"));
|
||||||
when(configure.isACLEnabled()).thenReturn(true);
|
when(configure.isACLEnabled()).thenReturn(true);
|
||||||
when(configure.isUseTLS()).thenReturn(false);
|
when(configure.isUseTLS()).thenReturn(false);
|
||||||
}
|
}
|
||||||
|
@@ -67,6 +67,7 @@ public class ConsumerControllerTest extends BaseControllerTest {
|
|||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void init() throws Exception {
|
public void init() throws Exception {
|
||||||
|
consumerService.afterPropertiesSet();
|
||||||
super.mockRmqConfigure();
|
super.mockRmqConfigure();
|
||||||
ClusterInfo clusterInfo = MockObjectUtil.createClusterInfo();
|
ClusterInfo clusterInfo = MockObjectUtil.createClusterInfo();
|
||||||
when(mqAdminExt.examineBrokerClusterInfo()).thenReturn(clusterInfo);
|
when(mqAdminExt.examineBrokerClusterInfo()).thenReturn(clusterInfo);
|
||||||
@@ -92,10 +93,11 @@ public class ConsumerControllerTest extends BaseControllerTest {
|
|||||||
requestBuilder = MockMvcRequestBuilders.get(url);
|
requestBuilder = MockMvcRequestBuilders.get(url);
|
||||||
perform = mockMvc.perform(requestBuilder);
|
perform = mockMvc.perform(requestBuilder);
|
||||||
perform.andExpect(status().isOk())
|
perform.andExpect(status().isOk())
|
||||||
.andExpect(jsonPath("$.data", hasSize(1)))
|
.andExpect(jsonPath("$.data", hasSize(2)))
|
||||||
.andExpect(jsonPath("$.data[0].group").value("group_test"))
|
|
||||||
.andExpect(jsonPath("$.data[0].consumeType").value(ConsumeType.CONSUME_ACTIVELY.name()))
|
.andExpect(jsonPath("$.data[0].consumeType").value(ConsumeType.CONSUME_ACTIVELY.name()))
|
||||||
.andExpect(jsonPath("$.data[0].messageModel").value(MessageModel.CLUSTERING.name()));
|
.andExpect(jsonPath("$.data[0].messageModel").value(MessageModel.CLUSTERING.name()));
|
||||||
|
// executorService shutdown
|
||||||
|
consumerService.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -182,6 +184,8 @@ public class ConsumerControllerTest extends BaseControllerTest {
|
|||||||
final String url = "/consumer/deleteSubGroup.do";
|
final String url = "/consumer/deleteSubGroup.do";
|
||||||
{
|
{
|
||||||
doNothing().when(mqAdminExt).deleteSubscriptionGroup(any(), anyString());
|
doNothing().when(mqAdminExt).deleteSubscriptionGroup(any(), anyString());
|
||||||
|
doNothing().when(mqAdminExt).deleteTopicInBroker(any(), anyString());
|
||||||
|
doNothing().when(mqAdminExt).deleteTopicInNameServer(any(), anyString());
|
||||||
}
|
}
|
||||||
DeleteSubGroupRequest request = new DeleteSubGroupRequest();
|
DeleteSubGroupRequest request = new DeleteSubGroupRequest();
|
||||||
request.setBrokerNameList(Lists.newArrayList("broker-a"));
|
request.setBrokerNameList(Lists.newArrayList("broker-a"));
|
||||||
|
@@ -152,7 +152,7 @@ public class DashboardControllerTest extends BaseControllerTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testTopicCurrent() throws Exception {
|
public void testTopicCurrent() throws Exception {
|
||||||
final String url = "/dashboard/topicCurrent";
|
final String url = "/dashboard/topicCurrent.query";
|
||||||
requestBuilder = MockMvcRequestBuilders.get(url);
|
requestBuilder = MockMvcRequestBuilders.get(url);
|
||||||
perform = mockMvc.perform(requestBuilder);
|
perform = mockMvc.perform(requestBuilder);
|
||||||
perform.andExpect(status().isOk())
|
perform.andExpect(status().isOk())
|
||||||
|
@@ -0,0 +1,178 @@
|
|||||||
|
/*
|
||||||
|
* 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.alibaba.fastjson.JSON;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import java.util.List;
|
||||||
|
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.CMResult;
|
||||||
|
import org.apache.rocketmq.common.protocol.body.ConsumeMessageDirectlyResult;
|
||||||
|
import org.apache.rocketmq.common.protocol.route.TopicRouteData;
|
||||||
|
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.impl.DlqMessageServiceImpl;
|
||||||
|
import org.apache.rocketmq.dashboard.service.impl.MessageServiceImpl;
|
||||||
|
import org.apache.rocketmq.dashboard.util.MockObjectUtil;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.mockito.InjectMocks;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.Spy;
|
||||||
|
import org.springframework.data.domain.PageImpl;
|
||||||
|
import org.springframework.data.domain.PageRequest;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||||
|
|
||||||
|
import static org.hamcrest.Matchers.hasSize;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||||
|
|
||||||
|
public class DlqMessageControllerTest extends BaseControllerTest {
|
||||||
|
|
||||||
|
@InjectMocks
|
||||||
|
private DlqMessageController dlqMessageController;
|
||||||
|
|
||||||
|
@Spy
|
||||||
|
private DlqMessageServiceImpl dlqMessageService;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private MessageServiceImpl messageService;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testQueryDlqMessageByConsumerGroup() throws Exception {
|
||||||
|
final String url = "/dlqMessage/queryDlqMessageByConsumerGroup.query";
|
||||||
|
MessageQuery query = new MessageQuery();
|
||||||
|
query.setPageNum(1);
|
||||||
|
query.setPageSize(10);
|
||||||
|
query.setTopic(MixAll.DLQ_GROUP_TOPIC_PREFIX + "group_test");
|
||||||
|
query.setTaskId("");
|
||||||
|
query.setBegin(System.currentTimeMillis() - 3 * 24 * 60 * 60 * 1000);
|
||||||
|
query.setEnd(System.currentTimeMillis());
|
||||||
|
{
|
||||||
|
TopicRouteData topicRouteData = MockObjectUtil.createTopicRouteData();
|
||||||
|
when(mqAdminExt.examineTopicRouteInfo(any()))
|
||||||
|
.thenThrow(new MQClientException(ResponseCode.TOPIC_NOT_EXIST, "topic not exist"))
|
||||||
|
.thenThrow(new MQClientException(ResponseCode.NO_MESSAGE, "query no message"))
|
||||||
|
.thenThrow(new RuntimeException())
|
||||||
|
.thenReturn(topicRouteData);
|
||||||
|
MessageView messageView = MessageView.fromMessageExt(MockObjectUtil.createMessageExt());
|
||||||
|
PageRequest page = PageRequest.of(query.getPageNum(), query.getPageSize());
|
||||||
|
MessagePage messagePage = new MessagePage
|
||||||
|
(new PageImpl<>(Lists.newArrayList(messageView), page, 0), query.getTaskId());
|
||||||
|
when(messageService.queryMessageByPage(any())).thenReturn(messagePage);
|
||||||
|
}
|
||||||
|
requestBuilder = MockMvcRequestBuilders.post(url);
|
||||||
|
requestBuilder.contentType(MediaType.APPLICATION_JSON_UTF8);
|
||||||
|
requestBuilder.content(JSON.toJSONString(query));
|
||||||
|
// 1、%DLQ%group_test is not exist
|
||||||
|
perform = mockMvc.perform(requestBuilder);
|
||||||
|
perform.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.data.page.content", hasSize(0)));
|
||||||
|
|
||||||
|
// 2、Other MQClientException occur
|
||||||
|
perform = mockMvc.perform(requestBuilder);
|
||||||
|
perform.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.data").doesNotExist())
|
||||||
|
.andExpect(jsonPath("$.status").value(-1))
|
||||||
|
.andExpect(jsonPath("$.errMsg").isNotEmpty());
|
||||||
|
|
||||||
|
// 3、Other Exception occur
|
||||||
|
perform = mockMvc.perform(requestBuilder);
|
||||||
|
perform.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.data").doesNotExist())
|
||||||
|
.andExpect(jsonPath("$.status").value(-1))
|
||||||
|
.andExpect(jsonPath("$.errMsg").isNotEmpty());
|
||||||
|
|
||||||
|
// 4、query dlq message success
|
||||||
|
perform = mockMvc.perform(requestBuilder);
|
||||||
|
perform.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.data.page.content", hasSize(1)))
|
||||||
|
.andExpect(jsonPath("$.data.page.content[0].msgId").value("0A9A003F00002A9F0000000000000319"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testExportDlqMessage() throws Exception {
|
||||||
|
final String url = "/dlqMessage/exportDlqMessage.do";
|
||||||
|
{
|
||||||
|
when(mqAdminExt.viewMessage(any(), any()))
|
||||||
|
.thenThrow(new RuntimeException())
|
||||||
|
.thenReturn(MockObjectUtil.createMessageExt());
|
||||||
|
}
|
||||||
|
requestBuilder = MockMvcRequestBuilders.get(url);
|
||||||
|
requestBuilder.param("consumerGroup", "group_test");
|
||||||
|
requestBuilder.param("msgId", "0A9A003F00002A9F0000000000000319");
|
||||||
|
// 1、viewMessage exception
|
||||||
|
perform = mockMvc.perform(requestBuilder);
|
||||||
|
perform.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.data").doesNotExist())
|
||||||
|
.andExpect(jsonPath("$.status").value(-1))
|
||||||
|
.andExpect(jsonPath("$.errMsg").isNotEmpty());
|
||||||
|
|
||||||
|
// 2、export dlqMessage success
|
||||||
|
perform = mockMvc.perform(requestBuilder);
|
||||||
|
perform.andExpect(status().is(200))
|
||||||
|
.andExpect(content().contentType("application/vnd.ms-excel;charset=utf-8"));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBatchResendDlqMessage() throws Exception {
|
||||||
|
final String url = "/dlqMessage/batchResendDlqMessage.do";
|
||||||
|
List<DlqMessageRequest> dlqMessages = MockObjectUtil.createDlqMessageRequest();
|
||||||
|
{
|
||||||
|
ConsumeMessageDirectlyResult result = new ConsumeMessageDirectlyResult();
|
||||||
|
result.setConsumeResult(CMResult.CR_SUCCESS);
|
||||||
|
when(messageService.consumeMessageDirectly(any(), any(), any(), any())).thenReturn(result);
|
||||||
|
}
|
||||||
|
requestBuilder = MockMvcRequestBuilders.post(url);
|
||||||
|
requestBuilder.contentType(MediaType.APPLICATION_JSON_UTF8);
|
||||||
|
requestBuilder.content(JSON.toJSONString(dlqMessages));
|
||||||
|
perform = mockMvc.perform(requestBuilder);
|
||||||
|
perform.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.data", hasSize(2)))
|
||||||
|
.andExpect(jsonPath("$.data[0].consumeResult").value("CR_SUCCESS"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBatchExportDlqMessage() throws Exception {
|
||||||
|
final String url = "/dlqMessage/batchExportDlqMessage.do";
|
||||||
|
{
|
||||||
|
when(mqAdminExt.viewMessage("%DLQ%group_test", "0A9A003F00002A9F0000000000000310"))
|
||||||
|
.thenThrow(new RuntimeException());
|
||||||
|
when(mqAdminExt.viewMessage("%DLQ%group_test", "0A9A003F00002A9F0000000000000311"))
|
||||||
|
.thenReturn(MockObjectUtil.createMessageExt());
|
||||||
|
}
|
||||||
|
List<DlqMessageRequest> dlqMessages = MockObjectUtil.createDlqMessageRequest();
|
||||||
|
requestBuilder = MockMvcRequestBuilders.post(url);
|
||||||
|
requestBuilder.contentType(MediaType.APPLICATION_JSON_UTF8);
|
||||||
|
requestBuilder.content(JSON.toJSONString(dlqMessages));
|
||||||
|
perform = mockMvc.perform(requestBuilder);
|
||||||
|
perform.andExpect(status().is(200))
|
||||||
|
.andExpect(content().contentType("application/vnd.ms-excel;charset=utf-8"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override protected Object getTestController() {
|
||||||
|
return dlqMessageController;
|
||||||
|
}
|
||||||
|
}
|
@@ -62,6 +62,7 @@ public class MessageTraceControllerTest extends BaseControllerTest {
|
|||||||
messageList.add(messageExt);
|
messageList.add(messageExt);
|
||||||
QueryResult queryResult = new QueryResult(System.currentTimeMillis(), messageList);
|
QueryResult queryResult = new QueryResult(System.currentTimeMillis(), messageList);
|
||||||
when(mqAdminExt.queryMessage(anyString(), anyString(), anyInt(), anyLong(), anyLong()))
|
when(mqAdminExt.queryMessage(anyString(), anyString(), anyInt(), anyLong(), anyLong()))
|
||||||
|
.thenThrow(new RuntimeException())
|
||||||
.thenReturn(queryResult);
|
.thenReturn(queryResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,6 +101,11 @@ public class MessageTraceControllerTest extends BaseControllerTest {
|
|||||||
final String url = "/messageTrace/viewMessageTraceDetail.query";
|
final String url = "/messageTrace/viewMessageTraceDetail.query";
|
||||||
requestBuilder = MockMvcRequestBuilders.get(url);
|
requestBuilder = MockMvcRequestBuilders.get(url);
|
||||||
requestBuilder.param("msgId", "0A9A003F00002A9F0000000000000319");
|
requestBuilder.param("msgId", "0A9A003F00002A9F0000000000000319");
|
||||||
|
// query message trace exception
|
||||||
|
perform = mockMvc.perform(requestBuilder);
|
||||||
|
performErrorExpect(perform);
|
||||||
|
|
||||||
|
// query message trace success
|
||||||
perform = mockMvc.perform(requestBuilder);
|
perform = mockMvc.perform(requestBuilder);
|
||||||
perform.andExpect(status().isOk())
|
perform.andExpect(status().isOk())
|
||||||
.andExpect(jsonPath("$.data", hasSize(4)))
|
.andExpect(jsonPath("$.data", hasSize(4)))
|
||||||
@@ -114,6 +120,11 @@ public class MessageTraceControllerTest extends BaseControllerTest {
|
|||||||
final String url = "/messageTrace/viewMessageTraceGraph.query";
|
final String url = "/messageTrace/viewMessageTraceGraph.query";
|
||||||
requestBuilder = MockMvcRequestBuilders.get(url);
|
requestBuilder = MockMvcRequestBuilders.get(url);
|
||||||
requestBuilder.param("msgId", "0A9A003F00002A9F0000000000000319");
|
requestBuilder.param("msgId", "0A9A003F00002A9F0000000000000319");
|
||||||
|
// query message trace exception
|
||||||
|
perform = mockMvc.perform(requestBuilder);
|
||||||
|
performErrorExpect(perform);
|
||||||
|
|
||||||
|
// query message trace success
|
||||||
perform = mockMvc.perform(requestBuilder);
|
perform = mockMvc.perform(requestBuilder);
|
||||||
perform.andExpect(status().isOk())
|
perform.andExpect(status().isOk())
|
||||||
.andExpect(jsonPath("$.data").isMap())
|
.andExpect(jsonPath("$.data").isMap())
|
||||||
|
@@ -34,7 +34,7 @@ public class NamesvrControllerTest extends BaseControllerTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testNsaddr() throws Exception {
|
public void testNsaddr() throws Exception {
|
||||||
final String url = "/rocketmq/nsaddr";
|
final String url = "/rocketmq/nsaddr.query";
|
||||||
{
|
{
|
||||||
super.mockRmqConfigure();
|
super.mockRmqConfigure();
|
||||||
}
|
}
|
||||||
|
@@ -35,6 +35,7 @@ import org.springframework.test.util.ReflectionTestUtils;
|
|||||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||||
|
|
||||||
import static org.hamcrest.Matchers.hasSize;
|
import static org.hamcrest.Matchers.hasSize;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.mockito.ArgumentMatchers.anyString;
|
import static org.mockito.ArgumentMatchers.anyString;
|
||||||
import static org.mockito.Mockito.doNothing;
|
import static org.mockito.Mockito.doNothing;
|
||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||||
@@ -64,7 +65,7 @@ public class OpsControllerTest extends BaseControllerTest {
|
|||||||
.andExpect(jsonPath("$.data").isMap())
|
.andExpect(jsonPath("$.data").isMap())
|
||||||
.andExpect(jsonPath("$.data.useVIPChannel").value(false))
|
.andExpect(jsonPath("$.data.useVIPChannel").value(false))
|
||||||
.andExpect(jsonPath("$.data.namesvrAddrList").isArray())
|
.andExpect(jsonPath("$.data.namesvrAddrList").isArray())
|
||||||
.andExpect(jsonPath("$.data.namesvrAddrList", hasSize(1)))
|
.andExpect(jsonPath("$.data.namesvrAddrList", hasSize(2)))
|
||||||
.andExpect(jsonPath("$.data.namesvrAddrList[0]").value("127.0.0.1:9876"));
|
.andExpect(jsonPath("$.data.namesvrAddrList[0]").value("127.0.0.1:9876"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,6 +84,20 @@ public class OpsControllerTest extends BaseControllerTest {
|
|||||||
Assert.assertEquals(configure.getNamesrvAddr(), "127.0.0.1:9876");
|
Assert.assertEquals(configure.getNamesrvAddr(), "127.0.0.1:9876");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAddNameSvrAddr() throws Exception {
|
||||||
|
final String url = "/ops/addNameSvrAddr.do";
|
||||||
|
{
|
||||||
|
doNothing().when(configure).setNamesrvAddrs(any());
|
||||||
|
}
|
||||||
|
requestBuilder = MockMvcRequestBuilders.post(url);
|
||||||
|
requestBuilder.param("newNamesrvAddr", "127.0.0.3:9876");
|
||||||
|
perform = mockMvc.perform(requestBuilder);
|
||||||
|
perform.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.data").value(true));
|
||||||
|
Assert.assertEquals(configure.getNamesrvAddrs().size(), 3);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testUpdateIsVIPChannel() throws Exception {
|
public void testUpdateIsVIPChannel() throws Exception {
|
||||||
final String url = "/ops/updateIsVIPChannel.do";
|
final String url = "/ops/updateIsVIPChannel.do";
|
||||||
@@ -96,7 +111,6 @@ public class OpsControllerTest extends BaseControllerTest {
|
|||||||
.andExpect(jsonPath("$.data").value(true));
|
.andExpect(jsonPath("$.data").value(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testUpdateUseTLS() throws Exception {
|
public void testUpdateUseTLS() throws Exception {
|
||||||
final String url = "/ops/updateUseTLS.do";
|
final String url = "/ops/updateUseTLS.do";
|
||||||
|
@@ -54,6 +54,7 @@ import org.springframework.http.MediaType;
|
|||||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||||
|
|
||||||
import static org.hamcrest.Matchers.hasSize;
|
import static org.hamcrest.Matchers.hasSize;
|
||||||
|
import static org.hamcrest.Matchers.containsInAnyOrder;
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||||
import static org.mockito.ArgumentMatchers.anyLong;
|
import static org.mockito.ArgumentMatchers.anyLong;
|
||||||
@@ -135,8 +136,9 @@ public class TopicControllerTest extends BaseControllerTest {
|
|||||||
// 3、filter system topic
|
// 3、filter system topic
|
||||||
requestBuilder = MockMvcRequestBuilders.get(url);
|
requestBuilder = MockMvcRequestBuilders.get(url);
|
||||||
perform = mockMvc.perform(requestBuilder);
|
perform = mockMvc.perform(requestBuilder);
|
||||||
|
String[] topicString = {"%SYS%system_topic2","common_topic2","%SYS%system_topic1","common_topic1"};
|
||||||
perform.andExpect(status().isOk())
|
perform.andExpect(status().isOk())
|
||||||
.andExpect(jsonPath("$.data.topicList[2]").value("%SYS%system_topic1"));
|
.andExpect(jsonPath("$.data.topicList").value(containsInAnyOrder(topicString)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@@ -30,6 +30,8 @@ import java.util.HashSet;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
import org.apache.rocketmq.common.MixAll;
|
import org.apache.rocketmq.common.MixAll;
|
||||||
import org.apache.rocketmq.common.protocol.body.BrokerStatsData;
|
import org.apache.rocketmq.common.protocol.body.BrokerStatsData;
|
||||||
import org.apache.rocketmq.common.protocol.body.ClusterInfo;
|
import org.apache.rocketmq.common.protocol.body.ClusterInfo;
|
||||||
@@ -38,6 +40,7 @@ import org.apache.rocketmq.common.protocol.body.KVTable;
|
|||||||
import org.apache.rocketmq.common.protocol.body.TopicList;
|
import org.apache.rocketmq.common.protocol.body.TopicList;
|
||||||
import org.apache.rocketmq.common.protocol.route.TopicRouteData;
|
import org.apache.rocketmq.common.protocol.route.TopicRouteData;
|
||||||
import org.apache.rocketmq.dashboard.BaseTest;
|
import org.apache.rocketmq.dashboard.BaseTest;
|
||||||
|
import org.apache.rocketmq.dashboard.config.CollectExecutorConfig;
|
||||||
import org.apache.rocketmq.dashboard.config.RMQConfigure;
|
import org.apache.rocketmq.dashboard.config.RMQConfigure;
|
||||||
import org.apache.rocketmq.dashboard.service.impl.DashboardCollectServiceImpl;
|
import org.apache.rocketmq.dashboard.service.impl.DashboardCollectServiceImpl;
|
||||||
import org.apache.rocketmq.dashboard.util.JsonUtil;
|
import org.apache.rocketmq.dashboard.util.JsonUtil;
|
||||||
@@ -68,6 +71,9 @@ public class DashboardCollectTaskTest extends BaseTest {
|
|||||||
@Mock
|
@Mock
|
||||||
private RMQConfigure rmqConfigure;
|
private RMQConfigure rmqConfigure;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private ExecutorService collectExecutor;
|
||||||
|
|
||||||
private int taskExecuteNum = 10;
|
private int taskExecuteNum = 10;
|
||||||
|
|
||||||
private File brokerFile;
|
private File brokerFile;
|
||||||
@@ -96,6 +102,7 @@ public class DashboardCollectTaskTest extends BaseTest {
|
|||||||
{
|
{
|
||||||
TopicList topicList = new TopicList();
|
TopicList topicList = new TopicList();
|
||||||
Set<String> topicSet = new HashSet<>();
|
Set<String> topicSet = new HashSet<>();
|
||||||
|
topicSet.add("rmq_sys_xxx");
|
||||||
topicSet.add("topic_test");
|
topicSet.add("topic_test");
|
||||||
topicSet.add("%RETRY%group_test");
|
topicSet.add("%RETRY%group_test");
|
||||||
topicSet.add("%DLQ%group_test");
|
topicSet.add("%DLQ%group_test");
|
||||||
@@ -121,19 +128,35 @@ public class DashboardCollectTaskTest extends BaseTest {
|
|||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Assert.assertEquals(e.getMessage(), "fetchAllTopicList exception");
|
Assert.assertEquals(e.getMessage(), "fetchAllTopicList exception");
|
||||||
}
|
}
|
||||||
|
dashboardCollectTask.collectTopic();
|
||||||
|
|
||||||
|
// multiple topic collection
|
||||||
|
CollectExecutorConfig config = new CollectExecutorConfig();
|
||||||
|
config.setCoreSize(10);
|
||||||
|
config.setMaxSize(10);
|
||||||
|
config.setQueueSize(500);
|
||||||
|
config.setKeepAliveTime(3000);
|
||||||
|
ExecutorService collectExecutor = config.collectExecutor(config);
|
||||||
for (int i = 0; i < taskExecuteNum; i++) {
|
for (int i = 0; i < taskExecuteNum; i++) {
|
||||||
dashboardCollectTask.collectTopic();
|
CollectTaskRunnble collectTask = new CollectTaskRunnble("topic_test" + i, mqAdminExt, dashboardCollectService);
|
||||||
|
collectExecutor.submit(collectTask);
|
||||||
}
|
}
|
||||||
|
collectExecutor.shutdown();
|
||||||
|
boolean loop = true;
|
||||||
|
do {
|
||||||
|
// Wait for all collectTasks to complete
|
||||||
|
loop = !collectExecutor.awaitTermination(Long.MAX_VALUE, TimeUnit.MINUTES);
|
||||||
|
}
|
||||||
|
while (loop);
|
||||||
LoadingCache<String, List<String>> map = dashboardCollectService.getTopicMap();
|
LoadingCache<String, List<String>> map = dashboardCollectService.getTopicMap();
|
||||||
Assert.assertEquals(map.size(), 1);
|
Assert.assertEquals(map.size(), taskExecuteNum);
|
||||||
Assert.assertEquals(map.get("topic_test").size(), taskExecuteNum);
|
|
||||||
dashboardCollectTask.saveData();
|
dashboardCollectTask.saveData();
|
||||||
Assert.assertEquals(topicFile.exists(), true);
|
Assert.assertEquals(topicFile.exists(), true);
|
||||||
Map<String, List<String>> topicData =
|
Map<String, List<String>> topicData =
|
||||||
JsonUtil.string2Obj(MixAll.file2String(topicFile),
|
JsonUtil.string2Obj(MixAll.file2String(topicFile),
|
||||||
new TypeReference<Map<String, List<String>>>() {
|
new TypeReference<Map<String, List<String>>>() {
|
||||||
});
|
});
|
||||||
Assert.assertEquals(topicData.get("topic_test").size(), taskExecuteNum);
|
Assert.assertEquals(topicData.size(), taskExecuteNum);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -187,8 +210,8 @@ public class DashboardCollectTaskTest extends BaseTest {
|
|||||||
|
|
||||||
private void mockBrokerFileExistBeforeSaveData() throws Exception {
|
private void mockBrokerFileExistBeforeSaveData() throws Exception {
|
||||||
Map<String, List<String>> map = new HashMap<>();
|
Map<String, List<String>> map = new HashMap<>();
|
||||||
map.put("broker-a" + ":" + MixAll.MASTER_ID, Lists.asList("1000", new String[] {"1000"}));
|
map.put("broker-a" + ":" + MixAll.MASTER_ID, Lists.asList("1000", new String[] {"1000"}));
|
||||||
map.put("broker-b" + ":" + MixAll.MASTER_ID, Lists.asList("1000", new String[] {"1000"}));
|
map.put("broker-b" + ":" + MixAll.MASTER_ID, Lists.asList("1000", new String[] {"1000"}));
|
||||||
MixAll.string2File(JsonUtil.obj2String(map), brokerFile.getAbsolutePath());
|
MixAll.string2File(JsonUtil.obj2String(map), brokerFile.getAbsolutePath());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -16,8 +16,10 @@
|
|||||||
*/
|
*/
|
||||||
package org.apache.rocketmq.dashboard.util;
|
package org.apache.rocketmq.dashboard.util;
|
||||||
|
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -30,8 +32,10 @@ import java.util.concurrent.ConcurrentMap;
|
|||||||
import org.apache.rocketmq.client.producer.LocalTransactionState;
|
import org.apache.rocketmq.client.producer.LocalTransactionState;
|
||||||
import org.apache.rocketmq.client.trace.TraceConstants;
|
import org.apache.rocketmq.client.trace.TraceConstants;
|
||||||
import org.apache.rocketmq.client.trace.TraceType;
|
import org.apache.rocketmq.client.trace.TraceType;
|
||||||
|
import org.apache.rocketmq.common.AclConfig;
|
||||||
import org.apache.rocketmq.common.DataVersion;
|
import org.apache.rocketmq.common.DataVersion;
|
||||||
import org.apache.rocketmq.common.MixAll;
|
import org.apache.rocketmq.common.MixAll;
|
||||||
|
import org.apache.rocketmq.common.PlainAccessConfig;
|
||||||
import org.apache.rocketmq.common.TopicConfig;
|
import org.apache.rocketmq.common.TopicConfig;
|
||||||
import org.apache.rocketmq.common.admin.ConsumeStats;
|
import org.apache.rocketmq.common.admin.ConsumeStats;
|
||||||
import org.apache.rocketmq.common.admin.OffsetWrapper;
|
import org.apache.rocketmq.common.admin.OffsetWrapper;
|
||||||
@@ -57,7 +61,9 @@ import org.apache.rocketmq.common.protocol.route.BrokerData;
|
|||||||
import org.apache.rocketmq.common.protocol.route.QueueData;
|
import org.apache.rocketmq.common.protocol.route.QueueData;
|
||||||
import org.apache.rocketmq.common.protocol.route.TopicRouteData;
|
import org.apache.rocketmq.common.protocol.route.TopicRouteData;
|
||||||
import org.apache.rocketmq.common.subscription.SubscriptionGroupConfig;
|
import org.apache.rocketmq.common.subscription.SubscriptionGroupConfig;
|
||||||
|
import org.apache.rocketmq.dashboard.model.DlqMessageRequest;
|
||||||
import org.apache.rocketmq.remoting.protocol.LanguageCode;
|
import org.apache.rocketmq.remoting.protocol.LanguageCode;
|
||||||
|
import org.checkerframework.checker.units.qual.A;
|
||||||
|
|
||||||
import static org.apache.rocketmq.common.protocol.heartbeat.ConsumeType.CONSUME_ACTIVELY;
|
import static org.apache.rocketmq.common.protocol.heartbeat.ConsumeType.CONSUME_ACTIVELY;
|
||||||
|
|
||||||
@@ -135,10 +141,13 @@ public class MockObjectUtil {
|
|||||||
|
|
||||||
public static SubscriptionGroupWrapper createSubscriptionGroupWrapper() {
|
public static SubscriptionGroupWrapper createSubscriptionGroupWrapper() {
|
||||||
SubscriptionGroupWrapper wrapper = new SubscriptionGroupWrapper();
|
SubscriptionGroupWrapper wrapper = new SubscriptionGroupWrapper();
|
||||||
|
ConcurrentMap<String, SubscriptionGroupConfig> subscriptionGroupTable = new ConcurrentHashMap(2);
|
||||||
SubscriptionGroupConfig config = new SubscriptionGroupConfig();
|
SubscriptionGroupConfig config = new SubscriptionGroupConfig();
|
||||||
config.setGroupName("group_test");
|
config.setGroupName("group_test");
|
||||||
ConcurrentMap<String, SubscriptionGroupConfig> subscriptionGroupTable = new ConcurrentHashMap(2);
|
|
||||||
subscriptionGroupTable.put("group_test", config);
|
subscriptionGroupTable.put("group_test", config);
|
||||||
|
SubscriptionGroupConfig sysGroupConfig = new SubscriptionGroupConfig();
|
||||||
|
sysGroupConfig.setGroupName(MixAll.TOOLS_CONSUMER_GROUP);
|
||||||
|
subscriptionGroupTable.put(MixAll.TOOLS_CONSUMER_GROUP, sysGroupConfig);
|
||||||
wrapper.setSubscriptionGroupTable(subscriptionGroupTable);
|
wrapper.setSubscriptionGroupTable(subscriptionGroupTable);
|
||||||
wrapper.setDataVersion(new DataVersion());
|
wrapper.setDataVersion(new DataVersion());
|
||||||
return wrapper;
|
return wrapper;
|
||||||
@@ -295,4 +304,38 @@ public class MockObjectUtil {
|
|||||||
brokerStatsData.setStatsMinute(statsMinute);
|
brokerStatsData.setStatsMinute(statsMinute);
|
||||||
return brokerStatsData;
|
return brokerStatsData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static List<DlqMessageRequest> createDlqMessageRequest() {
|
||||||
|
List<DlqMessageRequest> dlqMessages = new ArrayList<>();
|
||||||
|
for (int i = 0; i < 2; i++) {
|
||||||
|
DlqMessageRequest dlqMessageRequest = new DlqMessageRequest();
|
||||||
|
dlqMessageRequest.setConsumerGroup("group_test");
|
||||||
|
dlqMessageRequest.setTopicName("topic_test");
|
||||||
|
dlqMessageRequest.setMsgId("0A9A003F00002A9F000000000000031" + i);
|
||||||
|
dlqMessages.add(dlqMessageRequest);
|
||||||
|
}
|
||||||
|
return dlqMessages;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AclConfig createAclConfig() {
|
||||||
|
PlainAccessConfig adminConfig = new PlainAccessConfig();
|
||||||
|
adminConfig.setAdmin(true);
|
||||||
|
adminConfig.setAccessKey("rocketmq2");
|
||||||
|
adminConfig.setSecretKey("12345678");
|
||||||
|
|
||||||
|
PlainAccessConfig normalConfig = new PlainAccessConfig();
|
||||||
|
normalConfig.setAdmin(false);
|
||||||
|
normalConfig.setAccessKey("rocketmq");
|
||||||
|
normalConfig.setSecretKey("123456789");
|
||||||
|
normalConfig.setDefaultGroupPerm("SUB");
|
||||||
|
normalConfig.setDefaultTopicPerm("DENY");
|
||||||
|
normalConfig.setTopicPerms(Lists.newArrayList("topicA=DENY", "topicB=PUB|SUB"));
|
||||||
|
normalConfig.setGroupPerms(Lists.newArrayList("groupA=DENY", "groupB=PUB|SUB"));
|
||||||
|
|
||||||
|
|
||||||
|
AclConfig aclConfig = new AclConfig();
|
||||||
|
aclConfig.setPlainAccessConfigs(Lists.newArrayList(adminConfig, normalConfig));
|
||||||
|
aclConfig.setGlobalWhiteAddrs(Lists.newArrayList("localhost"));
|
||||||
|
return aclConfig;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user