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={{

Reply via email to