From fe3e330577bb506356d80ee85661e2e9c9995273 Mon Sep 17 00:00:00 2001 From: smail Date: Tue, 6 Oct 2026 00:50:19 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=89=E5=85=A8=E7=A0=81=E6=98=BE=E7=A4=BA?= =?UTF-8?q?=E5=85=AD=E5=8D=81=E7=A7=92=E5=90=8E=E8=87=AA=E5=8A=A8=E9=9A=90?= =?UTF-8?q?=E8=97=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/utils/recoveryCodeDisplay.js | 1 + .../components/SecuritySettings/SecuritySettings.js | 3 ++- .../RecoveryCodeDialog/RecoveryCodeDialog.js | 3 ++- test/frontend/security-code-save.test.mjs | 11 +++++++++++ test/frontend/user-management.test.mjs | 1 + 5 files changed, 17 insertions(+), 2 deletions(-) create mode 100644 frontend/src/utils/recoveryCodeDisplay.js diff --git a/frontend/src/utils/recoveryCodeDisplay.js b/frontend/src/utils/recoveryCodeDisplay.js new file mode 100644 index 0000000..7dfe142 --- /dev/null +++ b/frontend/src/utils/recoveryCodeDisplay.js @@ -0,0 +1 @@ +export const RECOVERY_CODE_DISPLAY_MS = 60 * 1000 diff --git a/frontend/src/views/components/SecuritySettings/SecuritySettings.js b/frontend/src/views/components/SecuritySettings/SecuritySettings.js index 655602e..a1789d2 100644 --- a/frontend/src/views/components/SecuritySettings/SecuritySettings.js +++ b/frontend/src/views/components/SecuritySettings/SecuritySettings.js @@ -12,6 +12,7 @@ import { import { useGlobalStatusStore } from '@/stores/useGlobalStatusStore' import { normalizeCreationOptions, serializeCredential, webAuthnSupported } from '@/utils/webauthn' import { downloadRecoveryCodes } from '@/utils/recoveryCodeDownload' +import { RECOVERY_CODE_DISPLAY_MS } from '@/utils/recoveryCodeDisplay' export function usePage() { const globalStatus = useGlobalStatusStore() @@ -270,7 +271,7 @@ export function usePage() { const scheduleRecoveryCodeHide = () => { clearTimeout(recoveryCodeHideTimer) - recoveryCodeHideTimer = setTimeout(clearRecoveryCodes, 60000) + recoveryCodeHideTimer = setTimeout(clearRecoveryCodes, RECOVERY_CODE_DISPLAY_MS) } const revealRecoveryCodes = async () => { diff --git a/frontend/src/views/components/UserManagement/RecoveryCodeDialog/RecoveryCodeDialog.js b/frontend/src/views/components/UserManagement/RecoveryCodeDialog/RecoveryCodeDialog.js index 960ad3a..279c2cf 100644 --- a/frontend/src/views/components/UserManagement/RecoveryCodeDialog/RecoveryCodeDialog.js +++ b/frontend/src/views/components/UserManagement/RecoveryCodeDialog/RecoveryCodeDialog.js @@ -7,6 +7,7 @@ import { isRecentAuthenticationCancelled, requestWithRecentAuthentication, } from '@/utils/recentAuthentication' +import { RECOVERY_CODE_DISPLAY_MS } from '@/utils/recoveryCodeDisplay' export const recoveryCodeDialogProps = { modelValue: { @@ -52,7 +53,7 @@ export function useRecoveryCodeDialog(props, emit) { const scheduleHide = () => { clearTimeout(hideTimer) - hideTimer = setTimeout(clearCodes, 60000) + hideTimer = setTimeout(clearCodes, RECOVERY_CODE_DISPLAY_MS) } const isCurrentView = (version, userID) => ( diff --git a/test/frontend/security-code-save.test.mjs b/test/frontend/security-code-save.test.mjs index 220c491..be1e2c9 100644 --- a/test/frontend/security-code-save.test.mjs +++ b/test/frontend/security-code-save.test.mjs @@ -1,6 +1,17 @@ import assert from 'node:assert/strict' import test from 'node:test' +import { readFileSync } from 'node:fs' import { downloadRecoveryCodes } from '../../frontend/src/utils/recoveryCodeDownload.js' +import { RECOVERY_CODE_DISPLAY_MS } from '../../frontend/src/utils/recoveryCodeDisplay.js' + +const securitySettingsSource = readFileSync(new URL('../../frontend/src/views/components/SecuritySettings/SecuritySettings.js', import.meta.url), 'utf8') +const securitySettingsTemplate = readFileSync(new URL('../../frontend/src/views/components/SecuritySettings/SecuritySettings.vue', import.meta.url), 'utf8') + +test('security codes use a shared 60-second display timeout', () => { + assert.equal(RECOVERY_CODE_DISPLAY_MS, 60000) + assert.match(securitySettingsSource, /setTimeout\(clearRecoveryCodes, RECOVERY_CODE_DISPLAY_MS\)/) + assert.match(securitySettingsTemplate, /one-time-tip/) +}) function createDocumentFixture() { const anchors = [] diff --git a/test/frontend/user-management.test.mjs b/test/frontend/user-management.test.mjs index 63df167..2f9ec6a 100644 --- a/test/frontend/user-management.test.mjs +++ b/test/frontend/user-management.test.mjs @@ -70,6 +70,7 @@ test('用户安全信息使用独立弹窗并调用固定管理接口', () => { const recoverySource = read('RecoveryCodeDialog/RecoveryCodeDialog.js') assert.match(recoverySource, /\/api\/v1\/user\/security\/recovery-codes\/generate/) assert.match(recoverySource, /shown_once|codes/) + assert.match(recoverySource, /setTimeout\(clearCodes, RECOVERY_CODE_DISPLAY_MS\)/) const externalSource = read('UserExternalEmailDialog/UserExternalEmailDialog.js') assert.match(externalSource, /\/api\/v1\/user\/security\/external-email\/change/) assert.match(externalSource, /\/api\/v1\/user\/security\/external-email\/verify/)