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 b080ee60 [ISSUE #1616] Prevent duplicate instance submissions (#1626)
b080ee60 is described below
commit b080ee6073b45140c99b35bbe794f90ab6e60917
Author: 0 <[email protected]>
AuthorDate: Tue Aug 11 20:31:53 2026 +0800
[ISSUE #1616] Prevent duplicate instance submissions (#1626)
---
.../pages/instance/__tests__/InstancePage.test.tsx | 24 ++++++++++++++++++++++
web/src/pages/instance/index.tsx | 8 +++++++-
2 files changed, 31 insertions(+), 1 deletion(-)
diff --git a/web/src/pages/instance/__tests__/InstancePage.test.tsx
b/web/src/pages/instance/__tests__/InstancePage.test.tsx
index 2fd2442b..866d436c 100644
--- a/web/src/pages/instance/__tests__/InstancePage.test.tsx
+++ b/web/src/pages/instance/__tests__/InstancePage.test.tsx
@@ -186,6 +186,30 @@ describe('InstancePage', () => {
expect(screen.getByText('latest-instance')).toBeInTheDocument();
});
+ it('ignores duplicate create submissions while the first request is
pending', async () => {
+ const user = userEvent.setup();
+ vi.mocked(instanceService.createInstance).mockImplementation(() => new
Promise(() => {}));
+ renderPage();
+
+ expect(await screen.findByText('production-proxy')).toBeInTheDocument();
+ await user.click(screen.getByRole('button', { name: /添加实例/ }));
+ const dialog = await screen.findByRole('dialog');
+ await user.type(within(dialog).getByLabelText('实例名称'), 'new-proxy');
+ const createTypeSelect = within(dialog).getByRole('combobox');
+ fireEvent.mouseDown(createTypeSelect.parentElement!);
+ const proxyOptions = await screen.findAllByText('Proxy 模式', {
+ selector: '.ant-select-item-option-content',
+ });
+ await user.click(proxyOptions[proxyOptions.length - 1]);
+ await user.type(within(dialog).getByLabelText('接入地址'), 'proxy-new:8080');
+ const connect = within(dialog).getByRole('button', { name: /连\s*接/ });
+
+ fireEvent.click(connect);
+ fireEvent.click(connect);
+
+ await waitFor(() =>
expect(instanceService.createInstance).toHaveBeenCalledTimes(1));
+ });
+
it('reloads the current filters after creating an instance', async () => {
const user = userEvent.setup();
vi.mocked(instanceService.createInstance).mockResolvedValue(instance('created',
'new-proxy'));
diff --git a/web/src/pages/instance/index.tsx b/web/src/pages/instance/index.tsx
index 4eb346cd..0f507e28 100644
--- a/web/src/pages/instance/index.tsx
+++ b/web/src/pages/instance/index.tsx
@@ -98,6 +98,7 @@ const InstancePage = () => {
const [editForm] = Form.useForm();
const [submitting, setSubmitting] = useState(false);
const requestIdRef = useRef(0);
+ const mutationInFlightRef = useRef(false);
useEffect(() => {
const timer = window.setTimeout(() => setDebouncedSearch(search.trim()),
300);
@@ -276,6 +277,8 @@ const InstancePage = () => {
};
const handleCreate = async () => {
+ if (mutationInFlightRef.current) return;
+ mutationInFlightRef.current = true;
try {
const values = await addForm.validateFields();
setSubmitting(true);
@@ -301,12 +304,14 @@ const InstancePage = () => {
}
message.error('添加实例失败,请稍后重试');
} finally {
+ mutationInFlightRef.current = false;
setSubmitting(false);
}
};
const handleUpdate = async () => {
- if (!editingInstance) return;
+ if (!editingInstance || mutationInFlightRef.current) return;
+ mutationInFlightRef.current = true;
try {
const values = await editForm.validateFields();
setSubmitting(true);
@@ -325,6 +330,7 @@ const InstancePage = () => {
}
message.error('更新实例失败,请稍后重试');
} finally {
+ mutationInFlightRef.current = false;
setSubmitting(false);
}
};