mirror of
https://github.com/apache/rocketmq-dashboard.git
synced 2025-09-10 11:40:01 +08:00
@@ -6,19 +6,14 @@ This project was bootstrapped with [Create React App](https://github.com/faceboo
|
||||
|
||||
In the project directory, you can run:
|
||||
|
||||
### `npm start`
|
||||
### `npm run start`
|
||||
|
||||
Runs the app in the development mode.\
|
||||
Open [http://localhost:3000](http://localhost:3000) to view it in your browser.
|
||||
Open [http://localhost:3003](http://localhost:3000) to view it in your browser.
|
||||
|
||||
The page will reload when you make changes.\
|
||||
You may also see any lint errors in the console.
|
||||
|
||||
### `npm test`
|
||||
|
||||
Launches the test runner in the interactive watch mode.\
|
||||
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
|
||||
|
||||
### `npm run build`
|
||||
|
||||
Builds the app for production to the `build` folder.\
|
||||
|
@@ -16,7 +16,7 @@
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { Form, Input, Typography, Modal } from 'antd';
|
||||
import {Form, Input, Typography} from 'antd';
|
||||
import moment from 'moment';
|
||||
import {useLanguage} from '../i18n/LanguageContext'; // 根据实际路径调整
|
||||
|
||||
|
@@ -16,9 +16,9 @@
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { Modal, Button, Typography, Descriptions, Tag, Spin, notification } from 'antd';
|
||||
import {Button, Descriptions, Modal, notification, Spin, Tag, Typography} from 'antd';
|
||||
import moment from 'moment';
|
||||
import { ExclamationCircleOutlined, SyncOutlined } from '@ant-design/icons';
|
||||
import {SyncOutlined} from '@ant-design/icons';
|
||||
import {useLanguage} from '../i18n/LanguageContext';
|
||||
import {remoteApi} from '../api/remoteApi/remoteApi'; // 确保这个路径正确
|
||||
|
||||
@@ -96,10 +96,14 @@ const MessageDetailViewDialog = ({ visible, onCancel, messageId, topic, onResend
|
||||
{messageDetail ? ( // 确保 messageDetail 存在时才渲染内容
|
||||
<>
|
||||
{/* 消息信息部分 */}
|
||||
<Descriptions title={<Text strong>{t.MESSAGE_INFO}</Text>} bordered column={2} size="small" style={{ marginBottom: 20 }}>
|
||||
<Descriptions.Item label="Topic" span={2}><Text copyable>{messageDetail.messageView.topic}</Text></Descriptions.Item>
|
||||
<Descriptions.Item label="Message ID" span={2}><Text copyable>{messageDetail.messageView.msgId}</Text></Descriptions.Item>
|
||||
<Descriptions.Item label="StoreHost">{messageDetail.messageView.storeHost}</Descriptions.Item>
|
||||
<Descriptions title={<Text strong>{t.MESSAGE_INFO}</Text>} bordered column={2} size="small"
|
||||
style={{marginBottom: 20}}>
|
||||
<Descriptions.Item label="Topic" span={2}><Text
|
||||
copyable>{messageDetail.messageView.topic}</Text></Descriptions.Item>
|
||||
<Descriptions.Item label="Message ID" span={2}><Text
|
||||
copyable>{messageDetail.messageView.msgId}</Text></Descriptions.Item>
|
||||
<Descriptions.Item
|
||||
label="StoreHost">{messageDetail.messageView.storeHost}</Descriptions.Item>
|
||||
<Descriptions.Item label="BornHost">{messageDetail.messageView.bornHost}</Descriptions.Item>
|
||||
<Descriptions.Item label="StoreTime">
|
||||
{moment(messageDetail.messageView.storeTimestamp).format("YYYY-MM-DD HH:mm:ss")}
|
||||
@@ -108,26 +112,33 @@ const MessageDetailViewDialog = ({ visible, onCancel, messageId, topic, onResend
|
||||
{moment(messageDetail.messageView.bornTimestamp).format("YYYY-MM-DD HH:mm:ss")}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="Queue ID">{messageDetail.messageView.queueId}</Descriptions.Item>
|
||||
<Descriptions.Item label="Queue Offset">{messageDetail.messageView.queueOffset}</Descriptions.Item>
|
||||
<Descriptions.Item label="StoreSize">{messageDetail.messageView.storeSize} bytes</Descriptions.Item>
|
||||
<Descriptions.Item label="ReconsumeTimes">{messageDetail.messageView.reconsumeTimes}</Descriptions.Item>
|
||||
<Descriptions.Item
|
||||
label="Queue Offset">{messageDetail.messageView.queueOffset}</Descriptions.Item>
|
||||
<Descriptions.Item
|
||||
label="StoreSize">{messageDetail.messageView.storeSize} bytes</Descriptions.Item>
|
||||
<Descriptions.Item
|
||||
label="ReconsumeTimes">{messageDetail.messageView.reconsumeTimes}</Descriptions.Item>
|
||||
<Descriptions.Item label="BodyCRC">{messageDetail.messageView.bodyCRC}</Descriptions.Item>
|
||||
<Descriptions.Item label="SysFlag">{messageDetail.messageView.sysFlag}</Descriptions.Item>
|
||||
<Descriptions.Item label="Flag">{messageDetail.messageView.flag}</Descriptions.Item>
|
||||
<Descriptions.Item label="PreparedTransactionOffset">{messageDetail.messageView.preparedTransactionOffset}</Descriptions.Item>
|
||||
<Descriptions.Item
|
||||
label="PreparedTransactionOffset">{messageDetail.messageView.preparedTransactionOffset}</Descriptions.Item>
|
||||
</Descriptions>
|
||||
|
||||
{/* 消息属性部分 */}
|
||||
{Object.keys(messageDetail.messageView.properties).length > 0 && (
|
||||
<Descriptions title={<Text strong>{t.MESSAGE_PROPERTIES}</Text>} bordered column={1} size="small" style={{ marginBottom: 20 }}>
|
||||
<Descriptions title={<Text strong>{t.MESSAGE_PROPERTIES}</Text>} bordered column={1}
|
||||
size="small" style={{marginBottom: 20}}>
|
||||
{Object.entries(messageDetail.messageView.properties).map(([key, value]) => (
|
||||
<Descriptions.Item label={key} key={key}><Text copyable>{value}</Text></Descriptions.Item>
|
||||
<Descriptions.Item label={key} key={key}><Text
|
||||
copyable>{value}</Text></Descriptions.Item>
|
||||
))}
|
||||
</Descriptions>
|
||||
)}
|
||||
|
||||
{/* 消息体部分 */}
|
||||
<Descriptions title={<Text strong>{t.MESSAGE_BODY}</Text>} bordered column={1} size="small" style={{ marginBottom: 20 }}>
|
||||
<Descriptions title={<Text strong>{t.MESSAGE_BODY}</Text>} bordered column={1} size="small"
|
||||
style={{marginBottom: 20}}>
|
||||
<Descriptions.Item>
|
||||
<Paragraph
|
||||
copyable
|
||||
@@ -148,7 +159,8 @@ const MessageDetailViewDialog = ({ visible, onCancel, messageId, topic, onResend
|
||||
<Text strong>{t.MESSAGE_TRACKING}</Text>
|
||||
<div style={{marginTop: 10}}>
|
||||
{messageDetail.messageTrackList.map((track, index) => (
|
||||
<Descriptions bordered column={1} size="small" key={index} style={{ marginBottom: 15 }}>
|
||||
<Descriptions bordered column={1} size="small" key={index}
|
||||
style={{marginBottom: 15}}>
|
||||
<Descriptions.Item label={t.CONSUMER_GROUP}>
|
||||
{track.consumerGroup}
|
||||
</Descriptions.Item>
|
||||
@@ -181,7 +193,10 @@ const MessageDetailViewDialog = ({ visible, onCancel, messageId, topic, onResend
|
||||
ellipsis={{
|
||||
rows: 2, // 默认显示2行
|
||||
expandable: true,
|
||||
symbol: <Text style={{ color: '#1890ff', cursor: 'pointer' }}>{t.READ_MORE}</Text>, // 蓝色展开文本
|
||||
symbol: <Text style={{
|
||||
color: '#1890ff',
|
||||
cursor: 'pointer'
|
||||
}}>{t.READ_MORE}</Text>, // 蓝色展开文本
|
||||
}}
|
||||
>
|
||||
{track.exceptionDesc}
|
||||
@@ -198,7 +213,8 @@ const MessageDetailViewDialog = ({ visible, onCancel, messageId, topic, onResend
|
||||
</>
|
||||
) : (
|
||||
// 当 messageDetail 为 null 时,可以显示一个占位符或者不显示内容
|
||||
!loading && !error && <Paragraph style={{ textAlign: 'center' }}>{t.NO_MESSAGE_DETAIL_AVAILABLE}</Paragraph>
|
||||
!loading && !error &&
|
||||
<Paragraph style={{textAlign: 'center'}}>{t.NO_MESSAGE_DETAIL_AVAILABLE}</Paragraph>
|
||||
)}
|
||||
</Spin>
|
||||
</Modal>
|
||||
|
@@ -16,7 +16,7 @@
|
||||
*/
|
||||
|
||||
import React, {useEffect, useRef} from 'react';
|
||||
import { Form, Input, Typography, Collapse, Table, Tag } from 'antd';
|
||||
import {Collapse, Form, Input, Table, Tag, Typography} from 'antd';
|
||||
import moment from 'moment';
|
||||
import {useLanguage} from '../i18n/LanguageContext';
|
||||
import Paragraph from "antd/es/skeleton/Paragraph";
|
||||
@@ -125,6 +125,7 @@ const MessageTraceDetailViewDialog = ({ ngDialogData }) => {
|
||||
}
|
||||
return `Cost Time: ${formatCostTimeStr(costTime)}<br/>`
|
||||
}
|
||||
|
||||
function buildTimeStamp(timestamp) {
|
||||
if (timestamp < 0) {
|
||||
return 'N/A';
|
||||
@@ -323,50 +324,130 @@ const MessageTraceDetailViewDialog = ({ ngDialogData }) => {
|
||||
|
||||
// ... (rest of your existing component code)
|
||||
const transactionColumns = [
|
||||
{ title: t.TIMESTAMP, dataIndex: 'beginTimestamp', key: 'beginTimestamp', align: 'center', render: (text) => moment(text).format('YYYY-MM-DD HH:mm:ss.SSS') },
|
||||
{ title: t.TRANSACTION_STATE, dataIndex: 'transactionState', key: 'transactionState', align: 'center', render: (text) => <Tag color={text === 'COMMIT_MESSAGE' ? 'green' : (text === 'ROLLBACK_MESSAGE' ? 'red' : 'default')}>{text}</Tag> },
|
||||
{ title: t.FROM_TRANSACTION_CHECK, dataIndex: 'fromTransactionCheck', key: 'fromTransactionCheck', align: 'center', render: (text) => (text ? <Tag color="blue">{t.YES}</Tag> : <Tag color="purple">{t.NO}</Tag>) },
|
||||
{
|
||||
title: t.TIMESTAMP,
|
||||
dataIndex: 'beginTimestamp',
|
||||
key: 'beginTimestamp',
|
||||
align: 'center',
|
||||
render: (text) => moment(text).format('YYYY-MM-DD HH:mm:ss.SSS')
|
||||
},
|
||||
{
|
||||
title: t.TRANSACTION_STATE,
|
||||
dataIndex: 'transactionState',
|
||||
key: 'transactionState',
|
||||
align: 'center',
|
||||
render: (text) => <Tag
|
||||
color={text === 'COMMIT_MESSAGE' ? 'green' : (text === 'ROLLBACK_MESSAGE' ? 'red' : 'default')}>{text}</Tag>
|
||||
},
|
||||
{
|
||||
title: t.FROM_TRANSACTION_CHECK,
|
||||
dataIndex: 'fromTransactionCheck',
|
||||
key: 'fromTransactionCheck',
|
||||
align: 'center',
|
||||
render: (text) => (text ? <Tag color="blue">{t.YES}</Tag> : <Tag color="purple">{t.NO}</Tag>)
|
||||
},
|
||||
{title: t.CLIENT_HOST, dataIndex: 'clientHost', key: 'clientHost', align: 'center'},
|
||||
{title: t.STORE_HOST, dataIndex: 'storeHost', key: 'storeHost', align: 'center'},
|
||||
];
|
||||
|
||||
const consumeColumns = [
|
||||
{ title: t.BEGIN_TIMESTAMP, dataIndex: 'beginTimestamp', key: 'beginTimestamp', align: 'center', render: (text) => text < 0 ? 'N/A' : moment(text).format('YYYY-MM-DD HH:mm:ss.SSS') },
|
||||
{ title: t.END_TIMESTAMP, dataIndex: 'endTimestamp', key: 'endTimestamp', align: 'center', render: (text) => text < 0 ? 'N/A' : moment(text).format('YYYY-MM-DD HH:mm:ss.SSS') },
|
||||
{ title: t.COST_TIME, dataIndex: 'costTime', key: 'costTime', align: 'center', render: (text) => text < 0 ? 'N/A' : `${text === 0 ? '<1' : text}ms` },
|
||||
{ title: t.STATUS, dataIndex: 'status', key: 'status', align: 'center', render: (text) => <Tag color={text === 'SUCCESS' ? 'green' : (text === 'FAILED' ? 'red' : 'default')}>{text}</Tag> },
|
||||
{ title: t.RETRY_TIMES, dataIndex: 'retryTimes', key: 'retryTimes', align: 'center', render: (text) => text < 0 ? 'N/A' : text },
|
||||
{
|
||||
title: t.BEGIN_TIMESTAMP,
|
||||
dataIndex: 'beginTimestamp',
|
||||
key: 'beginTimestamp',
|
||||
align: 'center',
|
||||
render: (text) => text < 0 ? 'N/A' : moment(text).format('YYYY-MM-DD HH:mm:ss.SSS')
|
||||
},
|
||||
{
|
||||
title: t.END_TIMESTAMP,
|
||||
dataIndex: 'endTimestamp',
|
||||
key: 'endTimestamp',
|
||||
align: 'center',
|
||||
render: (text) => text < 0 ? 'N/A' : moment(text).format('YYYY-MM-DD HH:mm:ss.SSS')
|
||||
},
|
||||
{
|
||||
title: t.COST_TIME,
|
||||
dataIndex: 'costTime',
|
||||
key: 'costTime',
|
||||
align: 'center',
|
||||
render: (text) => text < 0 ? 'N/A' : `${text === 0 ? '<1' : text}ms`
|
||||
},
|
||||
{
|
||||
title: t.STATUS,
|
||||
dataIndex: 'status',
|
||||
key: 'status',
|
||||
align: 'center',
|
||||
render: (text) => <Tag
|
||||
color={text === 'SUCCESS' ? 'green' : (text === 'FAILED' ? 'red' : 'default')}>{text}</Tag>
|
||||
},
|
||||
{
|
||||
title: t.RETRY_TIMES,
|
||||
dataIndex: 'retryTimes',
|
||||
key: 'retryTimes',
|
||||
align: 'center',
|
||||
render: (text) => text < 0 ? 'N/A' : text
|
||||
},
|
||||
{title: t.CLIENT_HOST, dataIndex: 'clientHost', key: 'clientHost', align: 'center'},
|
||||
{title: t.STORE_HOST, dataIndex: 'storeHost', key: 'storeHost', align: 'center'},
|
||||
];
|
||||
|
||||
return (
|
||||
<div style={{padding: '20px', backgroundColor: '#f0f2f5'}}>
|
||||
<div style={{ marginBottom: '20px', borderRadius: '8px', overflow: 'hidden', boxShadow: '0 4px 12px rgba(0, 0, 0, 0.08)' }}>
|
||||
<div style={{
|
||||
marginBottom: '20px',
|
||||
borderRadius: '8px',
|
||||
overflow: 'hidden',
|
||||
boxShadow: '0 4px 12px rgba(0, 0, 0, 0.08)'
|
||||
}}>
|
||||
<Collapse defaultActiveKey={['messageTraceGraph']} expandIconPosition="right">
|
||||
<Panel header={<Typography.Title level={3} style={{ margin: 0, color: '#333' }}>{t.MESSAGE_TRACE_GRAPH}</Typography.Title>} key="messageTraceGraph">
|
||||
<div ref={messageTraceGraphRef} style={{ height: 500, width: '100%', backgroundColor: '#fff', padding: '10px' }}>
|
||||
<Panel header={<Typography.Title level={3} style={{
|
||||
margin: 0,
|
||||
color: '#333'
|
||||
}}>{t.MESSAGE_TRACE_GRAPH}</Typography.Title>} key="messageTraceGraph">
|
||||
<div ref={messageTraceGraphRef}
|
||||
style={{height: 500, width: '100%', backgroundColor: '#fff', padding: '10px'}}>
|
||||
{/* ECharts message trace graph will be rendered here */}
|
||||
{(!producerNode && subscriptionNodeList.length === 0) && (
|
||||
<Text type="secondary" style={{ display: 'block', textAlign: 'center', marginTop: '150px' }}>{t.TRACE_GRAPH_PLACEHOLDER}</Text>
|
||||
<Text type="secondary" style={{
|
||||
display: 'block',
|
||||
textAlign: 'center',
|
||||
marginTop: '150px'
|
||||
}}>{t.TRACE_GRAPH_PLACEHOLDER}</Text>
|
||||
)}
|
||||
</div>
|
||||
</Panel>
|
||||
</Collapse>
|
||||
</div>
|
||||
|
||||
<div style={{ marginBottom: '20px', borderRadius: '8px', overflow: 'hidden', boxShadow: '0 4px 12px rgba(0, 0, 0, 0.08)' }}>
|
||||
<div style={{
|
||||
marginBottom: '20px',
|
||||
borderRadius: '8px',
|
||||
overflow: 'hidden',
|
||||
boxShadow: '0 4px 12px rgba(0, 0, 0, 0.08)'
|
||||
}}>
|
||||
<Collapse defaultActiveKey={['sendMessageTrace']} expandIconPosition="right">
|
||||
<Panel header={<Typography.Title level={3} style={{ margin: 0, color: '#333' }}>{t.SEND_MESSAGE_TRACE}</Typography.Title>} key="sendMessageTrace">
|
||||
<Panel header={<Typography.Title level={3} style={{
|
||||
margin: 0,
|
||||
color: '#333'
|
||||
}}>{t.SEND_MESSAGE_TRACE}</Typography.Title>} key="sendMessageTrace">
|
||||
{!producerNode ? (
|
||||
<Paragraph style={{ padding: '16px', textAlign: 'center', color: '#666' }}>{t.NO_PRODUCER_TRACE_DATA}</Paragraph>
|
||||
<Paragraph style={{
|
||||
padding: '16px',
|
||||
textAlign: 'center',
|
||||
color: '#666'
|
||||
}}>{t.NO_PRODUCER_TRACE_DATA}</Paragraph>
|
||||
) : (
|
||||
<div style={{padding: '16px', backgroundColor: '#fff'}}>
|
||||
<Typography.Title level={4} style={{marginBottom: '20px'}}>
|
||||
{t.SEND_MESSAGE_INFO} : ( {t.MESSAGE_ID} <Text strong copyable>{producerNode.msgId}</Text> )
|
||||
{t.SEND_MESSAGE_INFO} : ( {t.MESSAGE_ID} <Text strong
|
||||
copyable>{producerNode.msgId}</Text> )
|
||||
</Typography.Title>
|
||||
<Form layout="vertical" colon={false}>
|
||||
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(280px, 1fr))', gap: '16px' }}>
|
||||
<div style={{
|
||||
display: 'grid',
|
||||
gridTemplateColumns: 'repeat(auto-fill, minmax(280px, 1fr))',
|
||||
gap: '16px'
|
||||
}}>
|
||||
<Form.Item label={<Text strong>{t.TOPIC}</Text>}>
|
||||
<Input value={producerNode.topic} readOnly/>
|
||||
</Form.Item>
|
||||
@@ -381,13 +462,19 @@ const MessageTraceDetailViewDialog = ({ ngDialogData }) => {
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label={<Text strong>{t.BEGIN_TIMESTAMP}</Text>}>
|
||||
<Input value={moment(producerNode.traceNode.beginTimestamp).format('YYYY-MM-DD HH:mm:ss.SSS')} readOnly />
|
||||
<Input
|
||||
value={moment(producerNode.traceNode.beginTimestamp).format('YYYY-MM-DD HH:mm:ss.SSS')}
|
||||
readOnly/>
|
||||
</Form.Item>
|
||||
<Form.Item label={<Text strong>{t.END_TIMESTAMP}</Text>}>
|
||||
<Input value={moment(producerNode.traceNode.endTimestamp).format('YYYY-MM-DD HH:mm:ss.SSS')} readOnly />
|
||||
<Input
|
||||
value={moment(producerNode.traceNode.endTimestamp).format('YYYY-MM-DD HH:mm:ss.SSS')}
|
||||
readOnly/>
|
||||
</Form.Item>
|
||||
<Form.Item label={<Text strong>{t.COST_TIME}</Text>}>
|
||||
<Input value={`${producerNode.traceNode.costTime === 0 ? '<1' : producerNode.traceNode.costTime}ms`} readOnly />
|
||||
<Input
|
||||
value={`${producerNode.traceNode.costTime === 0 ? '<1' : producerNode.traceNode.costTime}ms`}
|
||||
readOnly/>
|
||||
</Form.Item>
|
||||
<Form.Item label={<Text strong>{t.MSG_TYPE}</Text>}>
|
||||
<Input value={producerNode.traceNode.msgType} readOnly/>
|
||||
@@ -410,7 +497,8 @@ const MessageTraceDetailViewDialog = ({ ngDialogData }) => {
|
||||
|
||||
{producerNode.transactionNodeList && producerNode.transactionNodeList.length > 0 && (
|
||||
<div style={{marginTop: '30px'}}>
|
||||
<Typography.Title level={4} style={{ marginBottom: '15px' }}>{t.CHECK_TRANSACTION_INFO}:</Typography.Title>
|
||||
<Typography.Title level={4}
|
||||
style={{marginBottom: '15px'}}>{t.CHECK_TRANSACTION_INFO}:</Typography.Title>
|
||||
<Table
|
||||
columns={transactionColumns}
|
||||
dataSource={producerNode.transactionNodeList}
|
||||
@@ -430,9 +518,16 @@ const MessageTraceDetailViewDialog = ({ ngDialogData }) => {
|
||||
|
||||
<div style={{borderRadius: '8px', overflow: 'hidden', boxShadow: '0 4px 12px rgba(0, 0, 0, 0.08)'}}>
|
||||
<Collapse defaultActiveKey={['consumeMessageTrace']} expandIconPosition="right">
|
||||
<Panel header={<Typography.Title level={3} style={{ margin: 0, color: '#333' }}>{t.CONSUME_MESSAGE_TRACE}</Typography.Title>} key="consumeMessageTrace">
|
||||
<Panel header={<Typography.Title level={3} style={{
|
||||
margin: 0,
|
||||
color: '#333'
|
||||
}}>{t.CONSUME_MESSAGE_TRACE}</Typography.Title>} key="consumeMessageTrace">
|
||||
{subscriptionNodeList.length === 0 ? (
|
||||
<Paragraph style={{ padding: '16px', textAlign: 'center', color: '#666' }}>{t.NO_CONSUMER_TRACE_DATA}</Paragraph>
|
||||
<Paragraph style={{
|
||||
padding: '16px',
|
||||
textAlign: 'center',
|
||||
color: '#666'
|
||||
}}>{t.NO_CONSUMER_TRACE_DATA}</Paragraph>
|
||||
) : (
|
||||
<div style={{padding: '16px', backgroundColor: '#fff'}}>
|
||||
{subscriptionNodeList.map(subscriptionNode => (
|
||||
@@ -443,7 +538,9 @@ const MessageTraceDetailViewDialog = ({ ngDialogData }) => {
|
||||
ghost
|
||||
>
|
||||
<Panel
|
||||
header={<Typography.Title level={4} style={{ margin: 0 }}>{t.SUBSCRIPTION_GROUP}: <Text strong>{subscriptionNode.subscriptionGroup}</Text></Typography.Title>}
|
||||
header={<Typography.Title level={4}
|
||||
style={{margin: 0}}>{t.SUBSCRIPTION_GROUP}: <Text
|
||||
strong>{subscriptionNode.subscriptionGroup}</Text></Typography.Title>}
|
||||
key={subscriptionNode.subscriptionGroup}
|
||||
>
|
||||
<Table
|
||||
|
@@ -16,8 +16,8 @@
|
||||
*/
|
||||
|
||||
import React, {useEffect, useState} from 'react';
|
||||
import { Layout, Menu, Dropdown, Button, Drawer, Grid, Space } from 'antd';
|
||||
import {GlobalOutlined, DownOutlined, UserOutlined, MenuOutlined, BgColorsOutlined} from '@ant-design/icons';
|
||||
import {Button, Drawer, Dropdown, Grid, Layout, Menu, Space} from 'antd';
|
||||
import {BgColorsOutlined, DownOutlined, GlobalOutlined, MenuOutlined, UserOutlined} from '@ant-design/icons';
|
||||
import {useLocation, useNavigate} from 'react-router-dom';
|
||||
import {useLanguage} from '../i18n/LanguageContext';
|
||||
import {useTheme} from "../store/context/ThemeContext";
|
||||
@@ -102,7 +102,7 @@ const Navbar = ({ rmqVersion = true, showAcl = true}) => {
|
||||
{key: 'message', label: t.MESSAGE},
|
||||
{key: 'dlqMessage', label: t.DLQ_MESSAGE},
|
||||
{key: 'messageTrace', label: t.MESSAGETRACE},
|
||||
...(showAcl ? [{ key: 'acl', label: t.WHITE_LIST }] : []),
|
||||
...(showAcl ? [{key: 'acl', label: t.ACL_MANAGEMENT}] : []),
|
||||
];
|
||||
|
||||
// Determine if it's a small screen (e.g., less than md)
|
||||
|
@@ -15,7 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Input, Select, Tag, Space } from 'antd';
|
||||
import {Input, Select, Space, Tag} from 'antd';
|
||||
import {PlusOutlined} from '@ant-design/icons';
|
||||
import React, {useState} from 'react';
|
||||
|
||||
|
@@ -16,7 +16,7 @@
|
||||
*/
|
||||
|
||||
import {Input, Select} from 'antd';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import React, {useEffect, useState} from 'react';
|
||||
|
||||
const {Option} = Select;
|
||||
|
||||
|
@@ -15,8 +15,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Modal, Table, Spin } from 'antd';
|
||||
import React, {useEffect, useState} from 'react';
|
||||
import {Modal, Spin, Table} from 'antd';
|
||||
import {remoteApi} from '../../api/remoteApi/remoteApi';
|
||||
import {useLanguage} from '../../i18n/LanguageContext';
|
||||
|
||||
|
@@ -16,12 +16,22 @@
|
||||
*/
|
||||
|
||||
import React, {useEffect, useState} from 'react';
|
||||
import { Button, Descriptions, Form, Input, Select, Switch, message } from 'antd';
|
||||
import {Button, Descriptions, Form, Input, message, Select, Switch} from 'antd';
|
||||
import {remoteApi} from '../../api/remoteApi/remoteApi'; // 确保路径正确
|
||||
|
||||
const {Option} = Select;
|
||||
|
||||
const ConsumerConfigItem = ({ initialConfig, isAddConfig, group, brokerName, allBrokerList, allClusterNames,onCancel, onSuccess, t }) => {
|
||||
const ConsumerConfigItem = ({
|
||||
initialConfig,
|
||||
isAddConfig,
|
||||
group,
|
||||
brokerName,
|
||||
allBrokerList,
|
||||
allClusterNames,
|
||||
onCancel,
|
||||
onSuccess,
|
||||
t
|
||||
}) => {
|
||||
const [form] = Form.useForm();
|
||||
const [currentBrokerName, setCurrentBrokerName] = useState(brokerName);
|
||||
|
||||
|
@@ -15,8 +15,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Modal, Table, Spin } from 'antd';
|
||||
import React, {useEffect, useState} from 'react';
|
||||
import {Modal, Spin, Table} from 'antd';
|
||||
import {remoteApi} from '../../api/remoteApi/remoteApi';
|
||||
import {useLanguage} from '../../i18n/LanguageContext';
|
||||
|
||||
|
@@ -15,8 +15,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Modal, Spin, Checkbox, Button, notification } from 'antd';
|
||||
import React, {useEffect, useState} from 'react';
|
||||
import {Button, Checkbox, Modal, notification, Spin} from 'antd';
|
||||
import {remoteApi} from '../../api/remoteApi/remoteApi';
|
||||
import {useLanguage} from '../../i18n/LanguageContext';
|
||||
|
||||
|
@@ -63,7 +63,11 @@ const ConsumerViewDialog = ({ visible, onClose, topic, consumerData, consumerGro
|
||||
bordered
|
||||
pagination={false}
|
||||
showHeader={false}
|
||||
dataSource={[{ consumerGroup, diffTotal: consumeDetail.diffTotal, lastTimestamp: consumeDetail.lastTimestamp }]}
|
||||
dataSource={[{
|
||||
consumerGroup,
|
||||
diffTotal: consumeDetail.diffTotal,
|
||||
lastTimestamp: consumeDetail.lastTimestamp
|
||||
}]}
|
||||
columns={[
|
||||
{title: t.SUBSCRIPTION_GROUP, dataIndex: 'consumerGroup', key: 'consumerGroup'},
|
||||
{title: t.DELAY, dataIndex: 'diffTotal', key: 'diffTotal'},
|
||||
|
@@ -30,7 +30,11 @@ const RouterViewDialog = ({ visible, onClose, topic, routeData, t }) => {
|
||||
key: 'brokerAddrs',
|
||||
render: (_, record) => (
|
||||
<Table
|
||||
dataSource={Object.entries(record.brokerAddrs || []).map(([key, value]) => ({ key, idx: key, address: value }))}
|
||||
dataSource={Object.entries(record.brokerAddrs || []).map(([key, value]) => ({
|
||||
key,
|
||||
idx: key,
|
||||
address: value
|
||||
}))}
|
||||
columns={[
|
||||
{title: 'Index', dataIndex: 'idx', key: 'idx'},
|
||||
{title: 'Address', dataIndex: 'address', key: 'address'},
|
||||
|
@@ -61,5 +61,4 @@ const SendResultDialog = ({ visible, onClose, result, t }) => {
|
||||
};
|
||||
|
||||
|
||||
|
||||
export default SendResultDialog;
|
||||
|
@@ -18,7 +18,14 @@
|
||||
import {Button, Form, message, Modal, Select} from "antd";
|
||||
import React, {useEffect, useState} from "react";
|
||||
|
||||
const SkipMessageAccumulateDialog = ({ visible, onClose, topic, allConsumerGroupList, handleSkipMessageAccumulate, t }) => {
|
||||
const SkipMessageAccumulateDialog = ({
|
||||
visible,
|
||||
onClose,
|
||||
topic,
|
||||
allConsumerGroupList,
|
||||
handleSkipMessageAccumulate,
|
||||
t
|
||||
}) => {
|
||||
const [form] = Form.useForm();
|
||||
const [selectedConsumerGroup, setSelectedConsumerGroup] = useState([]);
|
||||
|
||||
|
@@ -17,7 +17,7 @@
|
||||
|
||||
// TopicSingleModifyForm.js
|
||||
import React, {useEffect} from "react";
|
||||
import {Button, Form, Input, Select, Divider, Row, Col} from "antd";
|
||||
import {Button, Col, Divider, Form, Input, Row, Select} from "antd";
|
||||
|
||||
const TopicSingleModifyForm = ({
|
||||
initialData,
|
||||
@@ -68,7 +68,8 @@ const TopicSingleModifyForm = ({
|
||||
|
||||
return (
|
||||
<div style={{paddingBottom: 24}}>
|
||||
{bIsUpdate && <Divider orientation="left">{`${t.TOPIC_CONFIG} - ${initialData.brokerNameList ? initialData.brokerNameList.join(', ') : t.UNKNOWN_BROKER}`}</Divider>}
|
||||
{bIsUpdate && <Divider
|
||||
orientation="left">{`${t.TOPIC_CONFIG} - ${initialData.brokerNameList ? initialData.brokerNameList.join(', ') : t.UNKNOWN_BROKER}`}</Divider>}
|
||||
<Row justify="center"> {/* 使用 Row 居中内容 */}
|
||||
<Col span={16}> {/* 表单内容占据 12 栅格宽度,并自动居中 */}
|
||||
<Form
|
||||
|
@@ -15,12 +15,13 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React, { createContext, useState, useContext } from 'react';
|
||||
import React, {createContext, useContext, useState} from 'react';
|
||||
import {translations} from '../i18n';
|
||||
|
||||
const LanguageContext = createContext({
|
||||
lang: 'en',
|
||||
setLang: () => {},
|
||||
setLang: () => {
|
||||
},
|
||||
t: translations['en'], // 当前语言的文本资源
|
||||
});
|
||||
|
||||
|
@@ -156,7 +156,6 @@ export const translations = {
|
||||
"NO_MATCH_RESULT": "没有查到符合条件的结果",
|
||||
"BATCH_RESEND": "批量重发",
|
||||
"BATCH_EXPORT": "批量导出",
|
||||
"WHITE_LIST":"白名单",
|
||||
"ACCOUNT_INFO": "账户信息",
|
||||
"IS_ADMIN": "是否管理员",
|
||||
"DEFAULT_TOPIC_PERM": "topic默认权限",
|
||||
@@ -325,7 +324,7 @@ export const translations = {
|
||||
"ADDRESS": "Address",
|
||||
"VERSION": "Version",
|
||||
"PRO_MSG_TPS": "Produce Message TPS",
|
||||
"CUS_MSG_TPS": "Consume Message TPS",
|
||||
"CUS_MSG_TPS": "Consumer Message TPS",
|
||||
"YESTERDAY_PRO_COUNT": "Yesterday Produce Count",
|
||||
"YESTERDAY_CUS_COUNT": "Yesterday Consume Count",
|
||||
"TODAY_PRO_COUNT": "Today Produce Count",
|
||||
@@ -421,7 +420,6 @@ export const translations = {
|
||||
"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",
|
||||
|
@@ -27,7 +27,6 @@ import store from './store';
|
||||
|
||||
const root = ReactDOM.createRoot(document.getElementById('root'));
|
||||
root.render(
|
||||
|
||||
<LanguageProvider>
|
||||
<React.StrictMode>
|
||||
<AntdApp>
|
||||
@@ -37,7 +36,6 @@ root.render(
|
||||
</AntdApp>
|
||||
</React.StrictMode>
|
||||
</LanguageProvider>
|
||||
|
||||
);
|
||||
|
||||
// If you want to start measuring performance in your app, pass a function
|
||||
|
@@ -16,7 +16,7 @@
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { Form, Input, Button, message, Typography } from 'antd';
|
||||
import {Button, Form, Input, message, Typography} from 'antd';
|
||||
import {remoteApi} from "../../api/remoteApi/remoteApi";
|
||||
|
||||
const {Title} = Typography;
|
||||
|
@@ -15,8 +15,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Select, Button, Switch, Input, Typography, Space, message } from 'antd';
|
||||
import React, {useEffect, useState} from 'react';
|
||||
import {Button, Input, message, Select, Space, Switch, Typography} from 'antd';
|
||||
import {remoteApi} from '../../api/remoteApi/remoteApi';
|
||||
|
||||
const {Title} = Typography;
|
||||
|
@@ -15,8 +15,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Modal, Button, Select, Input, Card, Row, Col, notification, Spin } from 'antd';
|
||||
import React, {useEffect, useState} from 'react';
|
||||
import {Button, Card, Col, Input, Modal, notification, Row, Select, Spin} from 'antd';
|
||||
import {useLanguage} from '../../i18n/LanguageContext';
|
||||
import {remoteApi} from "../../api/remoteApi/remoteApi";
|
||||
|
||||
@@ -71,7 +71,10 @@ const ProxyManager = () => {
|
||||
|
||||
const handleAddProxyAddr = () => {
|
||||
if (!newProxyAddr.trim()) {
|
||||
notificationApi.warning({ message: t.INPUT_PROXY_ADDR_REQUIRED || "Please input a new proxy address.", duration: 2 });
|
||||
notificationApi.warning({
|
||||
message: t.INPUT_PROXY_ADDR_REQUIRED || "Please input a new proxy address.",
|
||||
duration: 2
|
||||
});
|
||||
return;
|
||||
}
|
||||
setLoading(true);
|
||||
@@ -167,7 +170,8 @@ const ProxyManager = () => {
|
||||
))
|
||||
) : (
|
||||
<tr>
|
||||
<td colSpan="2" style={{ textAlign: 'center' }}>{t.NO_CONFIG_DATA || "No configuration data available."}</td>
|
||||
<td colSpan="2"
|
||||
style={{textAlign: 'center'}}>{t.NO_CONFIG_DATA || "No configuration data available."}</td>
|
||||
</tr>
|
||||
)}
|
||||
</tbody>
|
||||
|
@@ -173,7 +173,6 @@ const DeployHistoryList = () => {
|
||||
};
|
||||
|
||||
|
||||
|
||||
const filterList = (currentPage) => {
|
||||
const lowExceptStr = filterStr.toLowerCase();
|
||||
const canShowList = allTopicList.filter((topic, index) => {
|
||||
|
@@ -15,8 +15,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
import {useEffect} from 'react';
|
||||
import { useSelector, useDispatch } from 'react-redux';
|
||||
import { themes, defaultTheme } from '../../assets/styles/theme';
|
||||
import {useDispatch, useSelector} from 'react-redux';
|
||||
import {defaultTheme, themes} from '../../assets/styles/theme';
|
||||
import {setTheme} from '../actions/themeActions';
|
||||
|
||||
export const useTheme = () => {
|
||||
|
@@ -14,7 +14,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { createStore,combineReducers } from 'redux';
|
||||
import {combineReducers, createStore} from 'redux';
|
||||
import themeReducer from './reducers/themeReducer';
|
||||
|
||||
// 组合所有的 reducers
|
||||
|
3
pom.xml
3
pom.xml
@@ -16,7 +16,8 @@
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
|
||||
<parent>
|
||||
<groupId>org.apache</groupId>
|
||||
|
@@ -1,25 +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.
|
||||
*/
|
||||
|
||||
package org.apache.rocketmq.dashboard.admin;
|
||||
|
||||
import org.apache.rocketmq.tools.admin.MQAdminExt;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface MQAdminExtCallback<T> {
|
||||
T doInMQAdminExt(MQAdminExt mqAdminExt) throws Exception;
|
||||
}
|
@@ -91,6 +91,10 @@ public class RMQConfigure {
|
||||
@Getter
|
||||
private Integer clientCallbackExecutorThreads = 4;
|
||||
|
||||
@Setter
|
||||
@Getter
|
||||
private String authMode = "file";
|
||||
|
||||
public void setProxyAddrs(List<String> proxyAddrs) {
|
||||
this.proxyAddrs = proxyAddrs;
|
||||
if (CollectionUtils.isNotEmpty(proxyAddrs)) {
|
||||
@@ -112,10 +116,12 @@ public class RMQConfigure {
|
||||
logger.info("setNameSrvAddrByProperty nameSrvAddr={}", namesrvAddr);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isACLEnabled() {
|
||||
return !(StringUtils.isAnyBlank(this.accessKey, this.secretKey) ||
|
||||
StringUtils.isAnyEmpty(this.accessKey, this.secretKey));
|
||||
}
|
||||
|
||||
public String getRocketMqDashboardDataPath() {
|
||||
return dataPath;
|
||||
}
|
||||
|
@@ -31,6 +31,7 @@ import org.springframework.web.bind.annotation.ResponseBody;
|
||||
public class ProxyController {
|
||||
@Resource
|
||||
private ProxyService proxyService;
|
||||
|
||||
@RequestMapping(value = "/homePage.query", method = RequestMethod.GET)
|
||||
@ResponseBody
|
||||
public Object homePage() {
|
||||
|
@@ -72,7 +72,8 @@ public class TestController {
|
||||
|
||||
new Thread(new Runnable() {
|
||||
|
||||
@Override public void run() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
int i = 0;
|
||||
while (true) {
|
||||
@@ -85,13 +86,11 @@ public class TestController {
|
||||
Thread.sleep(1000L);
|
||||
SendResult sendResult = producer.send(msg);
|
||||
logger.info("sendMessage={}", JsonUtil.obj2String(sendResult));
|
||||
}
|
||||
catch (Exception e) {
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
}
|
||||
catch (Exception ignore) {
|
||||
} catch (Exception ignore) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -29,7 +29,6 @@ import jakarta.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
|
||||
|
||||
|
||||
@WebFilter(urlPatterns = "/*", filterName = "httpBasicAuthorizedFilter")
|
||||
public class HttpBasicAuthorizedFilter implements Filter {
|
||||
|
||||
|
@@ -25,7 +25,9 @@ import java.util.Map;
|
||||
|
||||
public class MessageView {
|
||||
|
||||
/** from MessageExt **/
|
||||
/**
|
||||
* from MessageExt
|
||||
**/
|
||||
private int queueId;
|
||||
private int storeSize;
|
||||
private long queueOffset;
|
||||
@@ -41,13 +43,17 @@ public class MessageView {
|
||||
private long preparedTransactionOffset;
|
||||
/**from MessageExt**/
|
||||
|
||||
/** from Message **/
|
||||
/**
|
||||
* from Message
|
||||
**/
|
||||
private String topic;
|
||||
private int flag;
|
||||
private Map<String, String> properties;
|
||||
private String messageBody; // body
|
||||
|
||||
/** from Message **/
|
||||
/**
|
||||
* from Message
|
||||
**/
|
||||
|
||||
public static MessageView fromMessageExt(MessageExt messageExt) {
|
||||
MessageView messageView = new MessageView();
|
||||
|
@@ -15,6 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.rocketmq.dashboard.model.request;
|
||||
|
||||
import com.google.common.base.Objects;
|
||||
|
||||
import java.util.List;
|
||||
@@ -24,7 +25,9 @@ public class TopicConfigInfo {
|
||||
private List<String> clusterNameList;
|
||||
private List<String> brokerNameList;
|
||||
|
||||
/** topicConfig */
|
||||
/**
|
||||
* topicConfig
|
||||
*/
|
||||
private String topicName;
|
||||
private int writeQueueNums;
|
||||
private int readQueueNums;
|
||||
@@ -32,6 +35,7 @@ public class TopicConfigInfo {
|
||||
private boolean order;
|
||||
|
||||
private String messageType;
|
||||
|
||||
public List<String> getClusterNameList() {
|
||||
return clusterNameList;
|
||||
}
|
||||
@@ -40,8 +44,9 @@ public class TopicConfigInfo {
|
||||
this.clusterNameList = clusterNameList;
|
||||
}
|
||||
|
||||
/** topicConfig */
|
||||
|
||||
/**
|
||||
* topicConfig
|
||||
*/
|
||||
|
||||
|
||||
public List<String> getBrokerNameList() {
|
||||
@@ -102,8 +107,6 @@ public class TopicConfigInfo {
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o)
|
||||
|
@@ -29,6 +29,7 @@ import java.util.Set;
|
||||
public abstract class AbstractCommonService {
|
||||
@Resource
|
||||
protected MQAdminExt mqAdminExt;
|
||||
|
||||
protected final Set<String> changeToBrokerNameSet(Map<String, Set<String>> clusterAddrTable,
|
||||
List<String> clusterNameList, List<String> brokerNameList) {
|
||||
Set<String> finalBrokerNameList = Sets.newHashSet();
|
||||
@@ -37,8 +38,7 @@ public abstract class AbstractCommonService {
|
||||
for (String clusterName : clusterNameList) {
|
||||
finalBrokerNameList.addAll(clusterAddrTable.get(clusterName));
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
} catch (Exception e) {
|
||||
Throwables.throwIfUnchecked(e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
@@ -39,8 +39,7 @@ public interface MessageService {
|
||||
/**
|
||||
* @param topic
|
||||
* @param begin
|
||||
* @param end
|
||||
* org.apache.rocketmq.tools.command.message.PrintMessageSubCommand
|
||||
* @param end org.apache.rocketmq.tools.command.message.PrintMessageSubCommand
|
||||
*/
|
||||
List<MessageView> queryMessageByTopic(final String topic, final long begin,
|
||||
final long end);
|
||||
@@ -54,6 +53,4 @@ public interface MessageService {
|
||||
MessagePage queryMessageByPage(MessageQuery query);
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
@@ -94,7 +94,8 @@ public class MQAdminExtImpl implements MQAdminExt {
|
||||
private Logger logger = LoggerFactory.getLogger(MQAdminExtImpl.class);
|
||||
|
||||
|
||||
public MQAdminExtImpl() {}
|
||||
public MQAdminExtImpl() {
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@@ -134,8 +135,7 @@ public class MQAdminExtImpl implements MQAdminExt {
|
||||
RemotingCommand response = null;
|
||||
try {
|
||||
response = remotingClient.invokeSync(addr, request, 8000);
|
||||
}
|
||||
catch (Exception err) {
|
||||
} catch (Exception err) {
|
||||
Throwables.throwIfUnchecked(err);
|
||||
throw new RuntimeException(err);
|
||||
}
|
||||
@@ -566,17 +566,20 @@ public class MQAdminExtImpl implements MQAdminExt {
|
||||
}
|
||||
|
||||
// 4.0.0 added
|
||||
@Override public void updateNameServerConfig(Properties properties,
|
||||
@Override
|
||||
public void updateNameServerConfig(Properties properties,
|
||||
List<String> list) throws InterruptedException, RemotingConnectException, UnsupportedEncodingException, RemotingSendRequestException, RemotingTimeoutException, MQClientException, MQBrokerException {
|
||||
|
||||
}
|
||||
|
||||
@Override public Map<String, Properties> getNameServerConfig(
|
||||
@Override
|
||||
public Map<String, Properties> getNameServerConfig(
|
||||
List<String> list) throws InterruptedException, RemotingTimeoutException, RemotingSendRequestException, RemotingConnectException, MQClientException, UnsupportedEncodingException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override public QueryConsumeQueueResponseBody queryConsumeQueue(String brokerAddr, String topic,
|
||||
@Override
|
||||
public QueryConsumeQueueResponseBody queryConsumeQueue(String brokerAddr, String topic,
|
||||
int queueId, long index, int count,
|
||||
String consumerGroup) throws InterruptedException, RemotingTimeoutException, RemotingSendRequestException, RemotingConnectException, MQClientException {
|
||||
return null;
|
||||
@@ -588,7 +591,8 @@ public class MQAdminExtImpl implements MQAdminExt {
|
||||
}
|
||||
|
||||
|
||||
@Override public boolean resumeCheckHalfMessage(String topic,
|
||||
@Override
|
||||
public boolean resumeCheckHalfMessage(String topic,
|
||||
String msgId) throws RemotingException, MQClientException, InterruptedException, MQBrokerException {
|
||||
return false;
|
||||
}
|
||||
|
@@ -212,7 +212,6 @@ public class ConsumerServiceImpl extends AbstractCommonService implements Consum
|
||||
} else {
|
||||
consumeInfo.setSubGroupType(subscriptionGroupTable.get(consumerGroup).isConsumeMessageOrderly() ? "FIFO" : "NORMAL");
|
||||
}
|
||||
consumeInfo.setGroup(consumerGroup);
|
||||
consumeInfo.setUpdateTime(new Date());
|
||||
groupConsumeInfoList.add(consumeInfo);
|
||||
} catch (Exception e) {
|
||||
@@ -270,6 +269,7 @@ public class ConsumerServiceImpl extends AbstractCommonService implements Consum
|
||||
logger.warn("examineConsumeStats or examineConsumerConnectionInfo exception, "
|
||||
+ consumerGroup, e);
|
||||
}
|
||||
groupConsumeInfo.setGroup(consumerGroup);
|
||||
return groupConsumeInfo;
|
||||
}
|
||||
|
||||
|
@@ -96,6 +96,7 @@ public class DashboardCollectServiceImpl implements DashboardCollectService {
|
||||
public LoadingCache<String, List<String>> getBrokerMap() {
|
||||
return brokerMap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoadingCache<String, List<String>> getTopicMap() {
|
||||
return topicMap;
|
||||
@@ -106,8 +107,7 @@ public class DashboardCollectServiceImpl implements DashboardCollectService {
|
||||
List<String> strings;
|
||||
try {
|
||||
strings = Files.readLines(file, Charsets.UTF_8);
|
||||
}
|
||||
catch (IOException e) {
|
||||
} catch (IOException e) {
|
||||
Throwables.throwIfUnchecked(e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
@@ -34,6 +34,7 @@ public class DashboardServiceImpl implements DashboardService {
|
||||
|
||||
@Resource
|
||||
private DashboardCollectService dashboardCollectService;
|
||||
|
||||
/**
|
||||
* @param date format yyyy-MM-dd
|
||||
*/
|
||||
|
@@ -33,7 +33,6 @@ import org.apache.rocketmq.client.consumer.DefaultMQPullConsumer;
|
||||
import org.apache.rocketmq.client.consumer.PullResult;
|
||||
import org.apache.rocketmq.client.consumer.PullStatus;
|
||||
import org.apache.rocketmq.client.exception.MQClientException;
|
||||
import org.apache.rocketmq.common.MixAll;
|
||||
import org.apache.rocketmq.common.Pair;
|
||||
import org.apache.rocketmq.common.message.MessageClientIDSetter;
|
||||
import org.apache.rocketmq.common.message.MessageExt;
|
||||
@@ -74,6 +73,9 @@ import java.util.stream.Collectors;
|
||||
@Service
|
||||
public class MessageServiceImpl implements MessageService {
|
||||
|
||||
@Resource
|
||||
private AutoCloseConsumerWrapper autoCloseConsumerWrapper;
|
||||
|
||||
private Logger logger = LoggerFactory.getLogger(MessageServiceImpl.class);
|
||||
|
||||
private static final Cache<String, List<QueueOffsetInfo>> CACHE = CacheBuilder.newBuilder()
|
||||
@@ -128,8 +130,8 @@ public class MessageServiceImpl implements MessageService {
|
||||
if (isEnableAcl) {
|
||||
rpcHook = new AclClientRPCHook(new SessionCredentials(configure.getAccessKey(), configure.getSecretKey()));
|
||||
}
|
||||
AutoCloseConsumerWrapper consumerWrapper = new AutoCloseConsumerWrapper();
|
||||
DefaultMQPullConsumer consumer = consumerWrapper.getConsumer(rpcHook, configure.isUseTLS());
|
||||
|
||||
DefaultMQPullConsumer consumer = autoCloseConsumerWrapper.getConsumer(rpcHook, configure.isUseTLS());
|
||||
List<MessageView> messageViewList = Lists.newArrayList();
|
||||
try {
|
||||
String subExpression = "*";
|
||||
@@ -262,8 +264,8 @@ public class MessageServiceImpl implements MessageService {
|
||||
if (isEnableAcl) {
|
||||
rpcHook = new AclClientRPCHook(new SessionCredentials(configure.getAccessKey(), configure.getSecretKey()));
|
||||
}
|
||||
AutoCloseConsumerWrapper consumerWrapper = new AutoCloseConsumerWrapper();
|
||||
DefaultMQPullConsumer consumer = consumerWrapper.getConsumer(rpcHook, configure.isUseTLS());
|
||||
|
||||
DefaultMQPullConsumer consumer = autoCloseConsumerWrapper.getConsumer(rpcHook, configure.isUseTLS());
|
||||
|
||||
long total = 0;
|
||||
List<QueueOffsetInfo> queueOffsetInfos = new ArrayList<>();
|
||||
@@ -402,8 +404,8 @@ public class MessageServiceImpl implements MessageService {
|
||||
if (isEnableAcl) {
|
||||
rpcHook = new AclClientRPCHook(new SessionCredentials(configure.getAccessKey(), configure.getSecretKey()));
|
||||
}
|
||||
AutoCloseConsumerWrapper consumerWrapper = new AutoCloseConsumerWrapper();
|
||||
DefaultMQPullConsumer consumer = consumerWrapper.getConsumer(rpcHook, configure.isUseTLS());
|
||||
|
||||
DefaultMQPullConsumer consumer = autoCloseConsumerWrapper.getConsumer(rpcHook, configure.isUseTLS());
|
||||
List<MessageView> messageViews = new ArrayList<>();
|
||||
|
||||
long offset = query.getPageNum() * query.getPageSize();
|
||||
@@ -541,9 +543,9 @@ public class MessageServiceImpl implements MessageService {
|
||||
}
|
||||
}
|
||||
|
||||
public DefaultMQPullConsumer buildDefaultMQPullConsumer(RPCHook rpcHook, boolean useTLS) {
|
||||
DefaultMQPullConsumer consumer = new DefaultMQPullConsumer(MixAll.TOOLS_CONSUMER_GROUP, rpcHook);
|
||||
consumer.setUseTLS(useTLS);
|
||||
return consumer;
|
||||
}
|
||||
// public DefaultMQPullConsumer buildDefaultMQPullConsumer(RPCHook rpcHook, boolean useTLS) {
|
||||
// DefaultMQPullConsumer consumer = new DefaultMQPullConsumer(MixAll.TOOLS_CONSUMER_GROUP, rpcHook);
|
||||
// consumer.setUseTLS(useTLS);
|
||||
// return consumer;
|
||||
// }
|
||||
}
|
||||
|
@@ -81,8 +81,7 @@ public class MonitorServiceImpl implements MonitorService {
|
||||
private void writeDataJsonToFile(String path, String dataStr) {
|
||||
try {
|
||||
MixAll.string2File(dataStr, path);
|
||||
}
|
||||
catch (Exception e) {
|
||||
} catch (Exception e) {
|
||||
Throwables.throwIfUnchecked(e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
@@ -33,8 +33,7 @@ public class ProducerServiceImpl implements ProducerService {
|
||||
public ProducerConnection getProducerConnection(String producerGroup, String topic) {
|
||||
try {
|
||||
return mqAdminExt.examineProducerConnectionInfo(producerGroup, topic);
|
||||
}
|
||||
catch (Exception e) {
|
||||
} catch (Exception e) {
|
||||
Throwables.throwIfUnchecked(e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
@@ -17,29 +17,26 @@
|
||||
|
||||
package org.apache.rocketmq.dashboard.service.impl;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import org.apache.rocketmq.auth.authentication.enums.UserType;
|
||||
import org.apache.rocketmq.dashboard.admin.UserMQAdminPoolManager;
|
||||
import org.apache.rocketmq.dashboard.config.RMQConfigure;
|
||||
import org.apache.rocketmq.dashboard.model.User;
|
||||
import org.apache.rocketmq.dashboard.service.UserService;
|
||||
import org.apache.rocketmq.dashboard.service.provider.UserInfoProvider;
|
||||
import org.apache.rocketmq.dashboard.service.strategy.UserContext;
|
||||
import org.apache.rocketmq.remoting.protocol.body.UserInfo;
|
||||
import org.apache.rocketmq.tools.admin.MQAdminExt;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class UserServiceImpl implements UserService {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(UserServiceImpl.class);
|
||||
|
||||
@Resource
|
||||
private RMQConfigure configure;
|
||||
|
||||
@Autowired
|
||||
private UserInfoProvider userInfoProvider;
|
||||
private UserContext userContext;
|
||||
|
||||
@Autowired
|
||||
private UserMQAdminPoolManager userMQAdminPoolManager;
|
||||
@@ -47,7 +44,7 @@ public class UserServiceImpl implements UserService {
|
||||
|
||||
@Override
|
||||
public User queryByName(String name) {
|
||||
UserInfo userInfo = userInfoProvider.getUserInfoByUsername(name);
|
||||
UserInfo userInfo = userContext.queryByUsername(name);
|
||||
if (userInfo == null) {
|
||||
return null;
|
||||
}
|
||||
|
@@ -23,6 +23,7 @@ import org.apache.rocketmq.common.MixAll;
|
||||
import org.apache.rocketmq.remoting.RPCHook;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
@@ -32,9 +33,10 @@ import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
@Component
|
||||
public class AutoCloseConsumerWrapper {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(GlobalRestfulResponseBodyAdvice.class);
|
||||
private final Logger logger = LoggerFactory.getLogger(AutoCloseConsumerWrapper.class);
|
||||
|
||||
private static final AtomicReference<DefaultMQPullConsumer> CONSUMER_REF = new AtomicReference<>();
|
||||
private final AtomicBoolean isTaskScheduled = new AtomicBoolean(false);
|
||||
@@ -77,7 +79,10 @@ public class AutoCloseConsumerWrapper {
|
||||
|
||||
protected DefaultMQPullConsumer createNewConsumer(RPCHook rpcHook, Boolean useTLS) {
|
||||
return new DefaultMQPullConsumer(MixAll.TOOLS_CONSUMER_GROUP, rpcHook) {
|
||||
{ setUseTLS(useTLS); } };
|
||||
{
|
||||
setUseTLS(useTLS);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void startIdleCheckTask() {
|
||||
|
@@ -37,8 +37,7 @@ public class GlobalExceptionHandler {
|
||||
if (ex instanceof ServiceException) {
|
||||
logger.error("Occur service exception: {}", ex.getMessage());
|
||||
value = new JsonResult<Object>(((ServiceException) ex).getCode(), ex.getMessage());
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
logger.error("op=global_exception_handler_print_error", ex);
|
||||
value = new JsonResult<Object>(-1, ex.getMessage() == null ? ex.toString() : ex.getMessage());
|
||||
}
|
||||
|
@@ -47,8 +47,7 @@ public class GlobalRestfulResponseBodyAdvice implements ResponseBodyAdvice<Objec
|
||||
JsonResult value;
|
||||
if (obj instanceof JsonResult) {
|
||||
value = (JsonResult) obj;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
value = new JsonResult(obj);
|
||||
}
|
||||
return value;
|
||||
|
@@ -87,8 +87,7 @@ public class DashboardCollectTask {
|
||||
CollectTaskRunnble collectTask = new CollectTaskRunnble(topic, mqAdminExt, dashboardCollectService);
|
||||
collectExecutor.submit(collectTask);
|
||||
}
|
||||
}
|
||||
catch (Exception err) {
|
||||
} catch (Exception err) {
|
||||
Throwables.throwIfUnchecked(err);
|
||||
throw new RuntimeException(err);
|
||||
}
|
||||
@@ -100,7 +99,6 @@ public class DashboardCollectTask {
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Scheduled(cron = "0 0/1 * * * ?")
|
||||
public void collectBroker() {
|
||||
if (!rmqConfigure.isEnableDashBoardCollect()) {
|
||||
@@ -139,8 +137,7 @@ public class DashboardCollectTask {
|
||||
dashboardCollectService.getBrokerMap().put(entry.getValue(), list);
|
||||
}
|
||||
log.debug("Broker Collected Data in memory = {}" + JsonUtil.obj2String(dashboardCollectService.getBrokerMap().asMap()));
|
||||
}
|
||||
catch (Exception e) {
|
||||
} catch (Exception e) {
|
||||
Throwables.throwIfUnchecked(e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
@@ -152,12 +149,10 @@ public class DashboardCollectTask {
|
||||
}
|
||||
try {
|
||||
return mqAdminExt.fetchBrokerRuntimeStats(brokerAddr);
|
||||
}
|
||||
catch (Exception e) {
|
||||
} catch (Exception e) {
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
}
|
||||
catch (InterruptedException e1) {
|
||||
} catch (InterruptedException e1) {
|
||||
Throwables.throwIfUnchecked(e1);
|
||||
throw new RuntimeException(e1);
|
||||
}
|
||||
@@ -189,16 +184,14 @@ public class DashboardCollectTask {
|
||||
Map<String, List<String>> topicFileMap;
|
||||
if (brokerFile.exists()) {
|
||||
brokerFileMap = dashboardCollectService.jsonDataFile2map(brokerFile);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
brokerFileMap = Maps.newHashMap();
|
||||
Files.createParentDirs(brokerFile);
|
||||
}
|
||||
|
||||
if (topicFile.exists()) {
|
||||
topicFileMap = dashboardCollectService.jsonDataFile2map(topicFile);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
topicFileMap = Maps.newHashMap();
|
||||
Files.createParentDirs(topicFile);
|
||||
}
|
||||
@@ -211,8 +204,7 @@ public class DashboardCollectTask {
|
||||
log.debug("Broker Collected Data in memory = {}" + JsonUtil.obj2String(dashboardCollectService.getBrokerMap().asMap()));
|
||||
log.debug("Topic Collected Data in memory = {}" + JsonUtil.obj2String(dashboardCollectService.getTopicMap().asMap()));
|
||||
|
||||
}
|
||||
catch (IOException e) {
|
||||
} catch (IOException e) {
|
||||
Throwables.throwIfUnchecked(e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
@@ -224,8 +216,7 @@ public class DashboardCollectTask {
|
||||
Map<String, List<String>> resultMap = Maps.newHashMap();
|
||||
if (fileMap.size() == 0) {
|
||||
resultMap = newMap;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
for (Map.Entry<String, List<String>> entry : fileMap.entrySet()) {
|
||||
List<String> oldList = entry.getValue();
|
||||
List<String> newList = newMap.get(entry.getKey());
|
||||
|
@@ -55,8 +55,7 @@ public class JsonUtil {
|
||||
public static void writeValue(Writer writer, Object obj) {
|
||||
try {
|
||||
objectMapper.writeValue(writer, obj);
|
||||
}
|
||||
catch (IOException e) {
|
||||
} catch (IOException e) {
|
||||
Throwables.propagateIfPossible(e);
|
||||
}
|
||||
}
|
||||
@@ -68,8 +67,7 @@ public class JsonUtil {
|
||||
|
||||
try {
|
||||
return src instanceof String ? (String) src : objectMapper.writeValueAsString(src);
|
||||
}
|
||||
catch (Exception e) {
|
||||
} catch (Exception e) {
|
||||
logger.error("Parse Object to String error src=" + src, e);
|
||||
return null;
|
||||
}
|
||||
@@ -82,8 +80,7 @@ public class JsonUtil {
|
||||
|
||||
try {
|
||||
return src instanceof byte[] ? (byte[]) src : objectMapper.writeValueAsBytes(src);
|
||||
}
|
||||
catch (Exception e) {
|
||||
} catch (Exception e) {
|
||||
logger.error("Parse Object to byte[] error", e);
|
||||
return null;
|
||||
}
|
||||
@@ -96,8 +93,7 @@ public class JsonUtil {
|
||||
str = escapesSpecialChar(str);
|
||||
try {
|
||||
return clazz.equals(String.class) ? (T) str : objectMapper.readValue(str, clazz);
|
||||
}
|
||||
catch (Exception e) {
|
||||
} catch (Exception e) {
|
||||
logger.error("Parse String to Object error\nString: {}\nClass<T>: {}\nError: {}", str, clazz.getName(), e);
|
||||
return null;
|
||||
}
|
||||
@@ -109,8 +105,7 @@ public class JsonUtil {
|
||||
}
|
||||
try {
|
||||
return clazz.equals(byte[].class) ? (T) bytes : objectMapper.readValue(bytes, clazz);
|
||||
}
|
||||
catch (Exception e) {
|
||||
} catch (Exception e) {
|
||||
logger.error("Parse byte[] to Object error\nbyte[]: {}\nClass<T>: {}\nError: {}", bytes, clazz.getName(), e);
|
||||
return null;
|
||||
}
|
||||
@@ -123,8 +118,7 @@ public class JsonUtil {
|
||||
str = escapesSpecialChar(str);
|
||||
try {
|
||||
return (T) (typeReference.getType().equals(String.class) ? str : objectMapper.readValue(str, typeReference));
|
||||
}
|
||||
catch (Exception e) {
|
||||
} catch (Exception e) {
|
||||
logger.error("Parse String to Object error\nString: {}\nTypeReference<T>: {}\nError: {}", str,
|
||||
typeReference.getType(), e);
|
||||
return null;
|
||||
@@ -138,8 +132,7 @@ public class JsonUtil {
|
||||
try {
|
||||
return (T) (typeReference.getType().equals(byte[].class) ? bytes : objectMapper.readValue(bytes,
|
||||
typeReference));
|
||||
}
|
||||
catch (Exception e) {
|
||||
} catch (Exception e) {
|
||||
logger.error("Parse byte[] to Object error\nbyte[]: {}\nTypeReference<T>: {}\nError: {}", bytes,
|
||||
typeReference.getType(), e);
|
||||
return null;
|
||||
|
@@ -14,13 +14,10 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
# This file supports hot change, any change will be auto-reloaded without Dashboard restarting.
|
||||
# Format: a user per line, username=password[,N] #N is optional, 0 (Normal User); 1 (Admin)
|
||||
|
||||
# Define Admin
|
||||
admin=admin,1
|
||||
|
||||
super=admin,1
|
||||
# Define Users
|
||||
user1=user1
|
||||
user2=user2
|
||||
user1=user
|
||||
user2=user
|
||||
|
Reference in New Issue
Block a user