unbridled-41 opened a new pull request, #5092:
URL: https://github.com/apache/rocketmq-dashboard/pull/5092

   ## Problem
   
   The audit console's cleanup dialog (`web/src/pages/ops/audit.tsx`) confirms 
a **destructive** operation — `POST /api/audit-logs/cleanup` deletes every 
audit row older than N days — and nothing prevents a second submission while 
the first request is on the wire:
   
   - `handleCleanup` (`:293`) has no in-flight guard; it runs straight into 
`cleanupAuditLogs(cleanupDays)`.
   - The dialog stays open until the request resolves 
(`setCleanupModalOpen(false)` happens after the `await`), and the OK button is 
only given `okButtonProps={{ danger: true }}` — no `confirmLoading`.
   - antd v5's `Modal` does **not** disable the OK button for a 
promise-returning `onOk`; its `handleOk` is just `onOk === null || onOk === 
void 0 ? void 0 : onOk(e)` (`node_modules/antd/es/modal/Modal.js:88`), so the 
loading state must be passed in explicitly.
   
   Result: a second click on 确认清理 re-enters the handler and issues a second 
cleanup. The first response closes the dialog and toasts success; the second 
still runs and toasts success again (with its own count) after the dialog is 
gone, plus a second `setPage(1)`/refresh cycle and a second scan-and-delete 
over the same retention window.
   
   ## Evidence
   
   Reproduction, added as a regression test (`AuditPage.test.tsx`, `does not 
submit the cleanup again while the first request is in flight`): mock 
`cleanupAuditLogs` with a deferred promise, open 清理日志, click 确认清理 twice, assert 
`cleanupAuditLogs` was called once.
   
   Before the fix the test fails with `expected "vi.fn()" to be called 1 times, 
but got 2 times`. After the fix it passes, and the OK button carries 
`ant-btn-loading` while the request is pending.
   
   Same class and same fix as the NameServer registry dialog 
(`web/src/pages/cluster/index.tsx:288-329`), which was merged for exactly this 
reason: `nsSubmittingRef` + `nsSubmitting` + `confirmLoading`. 20 of the 42 
modals in the console already pass `confirmLoading`; this dialog was the one 
destructive confirm that did not.
   
   ## Root cause
   
   The confirm handler was written as a bare `async` function and the dialog 
was never given an in-flight state, so nothing — neither the component nor antd 
— blocks a second confirm click during the request.
   
   ## Fix
   
   Reuse the page's existing single-flight idiom: a `cleanupSubmittingRef` 
guard at the top of `handleCleanup` with a `cleanupSubmitting` state 
set/cleared in `finally`, and `confirmLoading={cleanupSubmitting}` on the 
dialog.
   
   ## Priority
   
   **54** = impact 16 (a duplicated destructive request and a duplicated 
success confirmation the operator reads as two separate deletions) + blast 
radius 6 (one dialog, but the operation it guards is irreversible) + 
reproducibility 18 (deterministic in RTL with a deferred promise) + maintenance 
14 (aligns the dialog with the pattern the rest of the console and the same 
page's request refs already use).
   
   `FIX_CONFIDENCE` **92**.
   
   ## Tests
   
   ```
   $ npx vitest run src/pages/ops/__tests__/AuditPage.test.tsx
    Test Files  1 passed (1)
         Tests  12 passed (12)      # the new test fails (2 calls) before the 
fix
   
   $ npx vitest run src/pages/ops src/services
    Test Files  23 passed (23)
         Tests  192 passed (192)
   
   $ npx eslint src/pages/ops/audit.tsx 
src/pages/ops/__tests__/AuditPage.test.tsx   # 0 problems
   $ npx prettier --check ...                                                   
     # clean
   $ npx tsc -b                                                                 
     # clean
   ```
   
   ## Risk
   
   Low. The guard only refuses a re-entry while a cleanup is pending; the 
confirm button now shows its loading state instead of appearing idle, and 
`finally` always releases the guard, so a failed cleanup can be retried 
immediately (covered by the existing failure-path behaviour of the dialog).


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to