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 40a8f6ae7 feat(cluster): clarify supported NameServer address formats
(#4383)
40a8f6ae7 is described below
commit 40a8f6ae78534039845a7297684e9b74407bc1e3
Author: Yanghanrui666 <[email protected]>
AuthorDate: Mon Sep 21 11:45:46 2026 +0800
feat(cluster): clarify supported NameServer address formats (#4383)
Add a neutral InfoBanner to the NameServer create/edit and connection-test
dialogs explaining that Studio connects from the server side, so the address
must be reachable from the Studio server, and listing the supported forms
(SLB/VIP, headless Service DNS, comma-separated list).
Adds both zh and en i18n entries and asserts the guidance renders and
localizes in ClusterPage tests.
---
web/src/i18n/translations.ts | 8 ++++++++
web/src/pages/cluster/__tests__/ClusterPage.test.tsx | 20 ++++++++++++++++++++
web/src/pages/cluster/index.tsx | 11 +++++++++++
3 files changed, 39 insertions(+)
diff --git a/web/src/i18n/translations.ts b/web/src/i18n/translations.ts
index 3f5c1105a..9a7bbe368 100644
--- a/web/src/i18n/translations.ts
+++ b/web/src/i18n/translations.ts
@@ -193,6 +193,14 @@ const translations: Record<string, Record<Lang, string>> =
{
'cluster.createCluster': { zh: '新建集群', en: 'New Cluster' },
'cluster.nsAddr': { zh: 'NameServer 地址', en: 'NameServer Address' },
'cluster.nsAddrPlaceholder': { zh: '例:10.0.1.1:9876', en: 'e.g.
10.0.1.1:9876' },
+ 'cluster.nsAddrGuidanceTitle': {
+ zh: 'NameServer 地址填写说明',
+ en: 'NameServer address guidance',
+ },
+ 'cluster.nsAddrGuidance': {
+ zh: 'Studio 会从服务器直接连接该地址,因此地址必须能被 Studio 服务器访问。支持 SLB/VIP、Headless Service
DNS(例如 namesrv.mq.svc.cluster.local:9876),也支持用英文逗号分隔的多个 NameServer 地址(例如
ns1:9876,ns2:9876)。',
+ en: 'Studio connects to this address directly from the server, so it must
be reachable from the Studio server. Supported forms include an SLB/VIP, a
headless Service DNS name (for example namesrv.mq.svc.cluster.local:9876), and
a comma-separated NameServer list (for example ns1:9876,ns2:9876).',
+ },
'cluster.k8sNamespace': { zh: 'K8s Namespace', en: 'K8s Namespace' },
'cluster.k8sId': { zh: 'k8s ID', en: 'k8s ID' },
'clients.selectClusterPlaceholder': { zh: '选择 Broker 集群', en: 'Select Broker
cluster' },
diff --git a/web/src/pages/cluster/__tests__/ClusterPage.test.tsx
b/web/src/pages/cluster/__tests__/ClusterPage.test.tsx
index 4be12b4f0..d5871f3c4 100644
--- a/web/src/pages/cluster/__tests__/ClusterPage.test.tsx
+++ b/web/src/pages/cluster/__tests__/ClusterPage.test.tsx
@@ -29,6 +29,7 @@ import type {
NameServerConfigDiffResult,
} from '../../../api/cluster';
import { LangProvider } from '../../../i18n/LangContext';
+import { LANGUAGE_STORAGE_KEY } from '../../../i18n/languagePreference';
const clusterServiceMocks = vi.hoisted(() => ({
createNameserverRegistry: vi.fn(),
@@ -527,6 +528,9 @@ describe('Cluster page', () => {
await user.click(screen.getByRole('button', { name: /新建 NameServer/ }));
const dialog = await screen.findByRole('dialog', { name: /新建 NameServer/
});
+
expect(within(dialog).getByTestId('nameserver-address-guidance')).toHaveTextContent(
+ 'Studio 会从服务器直接连接该地址',
+ );
await user.type(within(dialog).getByLabelText('名称'), 'rocketmq3');
await user.type(within(dialog).getByLabelText('NameServer 地址'),
'rocketmq3-nameserver:9876');
await user.click(within(dialog).getByRole('button', { name: /确\s*认/ }));
@@ -569,6 +573,22 @@ describe('Cluster page', () => {
confirmSpy.mockRestore();
});
+ it('localizes the NameServer address guidance', async () => {
+ localStorage.setItem(LANGUAGE_STORAGE_KEY, 'en');
+ const user = userEvent.setup();
+ renderWithProviders(<ClusterPage />);
+ await user.click(screen.getByRole('tab', { name: /NameServer/ }));
+ await user.click(screen.getByRole('button', { name: /New NameServer/ }));
+ const dialog = await screen.findByRole('dialog', { name: /New NameServer/
});
+
+
expect(within(dialog).getByTestId('nameserver-address-guidance')).toHaveTextContent(
+ 'Studio connects to this address directly from the server',
+ );
+
expect(within(dialog).getByTestId('nameserver-address-guidance')).toHaveTextContent(
+ 'ns1:9876,ns2:9876',
+ );
+ });
+
it('opens NameServer config drift details from a registry row', async () => {
const user = userEvent.setup();
instanceServiceMocks.listInstances.mockResolvedValue([
diff --git a/web/src/pages/cluster/index.tsx b/web/src/pages/cluster/index.tsx
index 5b83391dc..85b892d57 100644
--- a/web/src/pages/cluster/index.tsx
+++ b/web/src/pages/cluster/index.tsx
@@ -50,6 +50,7 @@ import {
} from '@ant-design/icons';
import { Cpu, HardDrives, Globe } from '@phosphor-icons/react';
import PageHeader from '../../components/PageHeader';
+import InfoBanner from '../../components/InfoBanner';
import { useLang } from '../../i18n/LangContext';
import { countClusterComponents } from './clusterStats';
import type {
@@ -1820,6 +1821,11 @@ const ClusterPage = () => {
cancelText={t('common.cancel')}
destroyOnHidden
>
+ <InfoBanner
+ title={t('cluster.nsAddrGuidanceTitle')}
+ description={t('cluster.nsAddrGuidance')}
+ data-testid="nameserver-address-guidance"
+ />
<Form form={nsCreateForm} layout="vertical" style={{ marginTop: 16 }}>
<Form.Item
name="name"
@@ -1915,6 +1921,11 @@ const ClusterPage = () => {
destroyOnHidden
>
<Text type="secondary">{t('cluster.testConnectionDesc')}</Text>
+ <InfoBanner
+ title={t('cluster.nsAddrGuidanceTitle')}
+ description={t('cluster.nsAddrGuidance')}
+ data-testid="nameserver-address-guidance"
+ />
<Form form={connectForm} layout="vertical" style={{ marginTop: 16 }}>
<Form.Item
name="namesrvAddr"