This is an automated email from the ASF dual-hosted git repository.
lizhimins pushed a commit to branch rocketmq-studio
in repository https://gitbox.apache.org/repos/asf/rocketmq-dashboard.git
The following commit(s) were added to refs/heads/rocketmq-studio by this push:
new 23d33f232 fix(web): say when the message body shown is truncated or
not text (#4872)
23d33f232 is described below
commit 23d33f232d00ca9e6ac1f31800059e70f3d4c1d4
Author: Wang1rrr <[email protected]>
AuthorDate: Thu Sep 24 10:48:18 2026 +0800
fix(web): say when the message body shown is truncated or not text (#4872)
`MessageRecordVO` carries `bodyEncoding` and `bodyTruncated`, api-spec 8.1
documents both, and `RocketMQMessageProvider.displayBody` sets them when it
cuts a UTF-8 body at 64 KiB or base64-encodes a body it could not decode at 48
KiB — but the web `MessageRecord` type declared neither, so the detail panel
presented a partial or base64 body as the whole message and the download wrote
it to `<msgId>.json`. Both fields are now declared and a truncation warning
plus a binary-body notice r [...]
---
web/src/api/message.ts | 4 ++
web/src/i18n/translations.ts | 8 ++++
.../pages/instance/__tests__/MessagePage.test.tsx | 50 ++++++++++++++++++++++
web/src/pages/instance/message.tsx | 16 +++++++
4 files changed, 78 insertions(+)
diff --git a/web/src/api/message.ts b/web/src/api/message.ts
index fdf1ec74a..b8b4c4ea9 100644
--- a/web/src/api/message.ts
+++ b/web/src/api/message.ts
@@ -10,6 +10,10 @@ export interface MessageRecord {
queueId: number | null;
queueOffset: number | null;
body: string;
+ /** 'UTF-8' for a text body, 'BASE64' when the server could not decode the
bytes as text. */
+ bodyEncoding?: string | null;
+ /** True when the server cut the body short for display (64 KiB text / 48
KiB base64). */
+ bodyTruncated?: boolean;
storeTime: number | string;
bornHost: string;
storeHost: string;
diff --git a/web/src/i18n/translations.ts b/web/src/i18n/translations.ts
index f907b0977..b78d1c285 100644
--- a/web/src/i18n/translations.ts
+++ b/web/src/i18n/translations.ts
@@ -560,6 +560,14 @@ const translations: Record<string, Record<Lang, string>> =
{
zh: '查询结果达到服务端扫描上限,当前总数可能不完整。',
en: 'The query reached the server-side scan limit; the total count may be
incomplete.',
},
+ 'messagePage.bodyTruncatedWarning': {
+ zh: '消息体超过服务端展示上限,已被截断;此处展示与下载的内容都不完整。',
+ en: 'The body exceeds the server display limit and was truncated, so what
is shown and downloaded here is incomplete.',
+ },
+ 'messagePage.bodyBinaryWarning': {
+ zh: '消息体不是 UTF-8 文本,服务端以 BASE64 返回;下方展示的是编码后的内容。',
+ en: 'The body is not UTF-8 text and the server returned it BASE64-encoded;
below is the encoded form.',
+ },
'messagePage.queryFailed': {
zh: '消息查询失败,请稍后重试',
en: 'Failed to query messages, please try again later',
diff --git a/web/src/pages/instance/__tests__/MessagePage.test.tsx
b/web/src/pages/instance/__tests__/MessagePage.test.tsx
index 963f0a79f..8aa82669b 100644
--- a/web/src/pages/instance/__tests__/MessagePage.test.tsx
+++ b/web/src/pages/instance/__tests__/MessagePage.test.tsx
@@ -471,6 +471,56 @@ describe('Message page query history', () => {
expect(locationItems[2]).toHaveTextContent('0');
});
+ it('warns on the detail panel when the body was truncated or is not text',
async () => {
+ const user = userEvent.setup();
+ messageServiceMocks.queryMessages.mockResolvedValue([
+ {
+ ...createMessage('MID-BODY-FLAGS'),
+ body: 'AAEC',
+ bodyEncoding: 'BASE64',
+ bodyTruncated: true,
+ },
+ ]);
+ renderWithProviders(<MessagePage />);
+
+ await user.click(screen.getByText('按 Message ID'));
+ await user.click(lastElement(screen.getAllByRole('combobox')));
+ await user.click(lastElement(await screen.findAllByText('order-create')));
+ await user.type(screen.getByPlaceholderText('输入 Message ID'),
'MID-BODY-FLAGS');
+ await user.click(screen.getByRole('button', { name: /^search查询$/ }));
+
+ expect(await screen.findByText('MID-BODY-FLAGS')).toBeInTheDocument();
+ await user.click(screen.getByRole('button', { name: /详情/ }));
+
+ expect(await screen.findByText('消息体')).toBeInTheDocument();
+ expect(
+ screen.getByText('消息体超过服务端展示上限,已被截断;此处展示与下载的内容都不完整。'),
+ ).toBeInTheDocument();
+ expect(
+ screen.getByText('消息体不是 UTF-8 文本,服务端以 BASE64 返回;下方展示的是编码后的内容。'),
+ ).toBeInTheDocument();
+ });
+
+ it('does not warn on the detail panel when the body is complete UTF-8 text',
async () => {
+ const user = userEvent.setup();
+ messageServiceMocks.queryMessages.mockResolvedValue([
+ { ...createMessage('MID-BODY-PLAIN'), bodyEncoding: 'UTF-8',
bodyTruncated: false },
+ ]);
+ renderWithProviders(<MessagePage />);
+
+ await user.click(screen.getByText('按 Message ID'));
+ await user.click(lastElement(screen.getAllByRole('combobox')));
+ await user.click(lastElement(await screen.findAllByText('order-create')));
+ await user.type(screen.getByPlaceholderText('输入 Message ID'),
'MID-BODY-PLAIN');
+ await user.click(screen.getByRole('button', { name: /^search查询$/ }));
+
+ expect(await screen.findByText('MID-BODY-PLAIN')).toBeInTheDocument();
+ await user.click(screen.getByRole('button', { name: /详情/ }));
+
+ expect(await screen.findByText('消息体')).toBeInTheDocument();
+ expect(screen.queryAllByRole('alert')).toHaveLength(0);
+ });
+
it('renders trace diagnostics in English when the UI language is English',
async () => {
localStorage.setItem('rocketmq-studio-language', 'en');
messageServiceMocks.queryMessages.mockResolvedValue([createMessage('MID-TRACE-EN')]);
diff --git a/web/src/pages/instance/message.tsx
b/web/src/pages/instance/message.tsx
index 921f42dd6..0b85eb017 100644
--- a/web/src/pages/instance/message.tsx
+++ b/web/src/pages/instance/message.tsx
@@ -942,6 +942,22 @@ const MessagePageContent = ({
<Typography.Title level={5} style={{ marginBottom: 8 }}>
{t('topic.messageBody')}
</Typography.Title>
+ {selectedMsg.bodyTruncated && (
+ <Alert
+ showIcon
+ type="warning"
+ message={t('messagePage.bodyTruncatedWarning')}
+ style={{ marginBottom: 8 }}
+ />
+ )}
+ {selectedMsg.bodyEncoding === 'BASE64' && (
+ <Alert
+ showIcon
+ type="info"
+ message={t('messagePage.bodyBinaryWarning')}
+ style={{ marginBottom: 8 }}
+ />
+ )}
<Paragraph
copyable={{ text: selectedMsg.body }}
style={{