From a7ad2bc32ea87fbd5e1eb1cc9c8a59cb91f96f33 Mon Sep 17 00:00:00 2001 From: smail Date: Mon, 7 Sep 2026 17:50:06 +0800 Subject: [PATCH] =?UTF-8?q?=E8=BF=9B=E4=B8=80=E6=AD=A5=E7=B2=BE=E7=AE=80?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E4=BF=9D=E5=AD=98=E6=B5=81=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/controllers/reauth_test.go | 3 ++- frontend/src/utils/axios.js | 3 +++ frontend/src/utils/recentAuthentication.js | 4 ++-- .../ConfigSettings/ConfigEditor/ConfigEditor.vue | 6 +++--- .../views/components/ConfigSettings/ConfigSettings.js | 10 +++++----- .../SpamBlockSettings/SpamBlockSettings.js | 4 ++-- .../SpamBlockSettings/SpamBlockSettings.vue | 6 +++--- test/frontend/config-settings.test.mjs | 11 ++++++----- 8 files changed, 26 insertions(+), 21 deletions(-) diff --git a/backend/controllers/reauth_test.go b/backend/controllers/reauth_test.go index f3ed9d5..1e6d4bd 100644 --- a/backend/controllers/reauth_test.go +++ b/backend/controllers/reauth_test.go @@ -25,6 +25,7 @@ func TestReauthenticateCurrentUser(t *testing.T) { oldSession := session.Instance oldUserAttempts := reauthUserAttempts oldIPAttempts := reauthIPAttempts + testRoot := t.TempDir() t.Cleanup(func() { if db.Instance != nil && db.Instance != oldDB { _ = db.Instance.Close() @@ -36,7 +37,7 @@ func TestReauthenticateCurrentUser(t *testing.T) { reauthIPAttempts = oldIPAttempts }) - config.Instance = &config.Config{DbType: config.DBTypeSQLite, DbDSN: filepath.Join(t.TempDir(), "reauth.db")} + config.Instance = &config.Config{DbType: config.DBTypeSQLite, DbDSN: filepath.Join(testRoot, "reauth.db")} if err := db.Init(""); err != nil { t.Fatal(err) } diff --git a/frontend/src/utils/axios.js b/frontend/src/utils/axios.js index 3a6e465..872beca 100644 --- a/frontend/src/utils/axios.js +++ b/frontend/src/utils/axios.js @@ -64,6 +64,8 @@ function isAuthenticationRefreshRequest(config) { } function isAuthenticationFailureError(error) { + const errorCode = error?.response?.data?.data?.code + if (errorCode === 'RECENT_AUTH_REQUIRED') return false return error?.response?.data?.errorNo === 403 || (error?.response?.status === 403 && isAuthenticationRefreshRequest(error.config)) } @@ -71,6 +73,7 @@ function isAuthenticationFailureError(error) { http.interceptors.response.use(async (response) => { //响应成功 if (response.data?.errorNo === 403 + && response.data?.data?.code !== 'RECENT_AUTH_REQUIRED' && !isSetupRequest(response.config) && !isAuthenticationRefreshRequest(response.config)) { await handleLoginRedirect(); diff --git a/frontend/src/utils/recentAuthentication.js b/frontend/src/utils/recentAuthentication.js index 92c919f..548dda3 100644 --- a/frontend/src/utils/recentAuthentication.js +++ b/frontend/src/utils/recentAuthentication.js @@ -18,12 +18,12 @@ export async function requestWithRecentAuthentication(request) { let password = '' try { const result = await ElMessageBox.prompt( - '为保护敏感配置,请确认当前登录密码。确认后将自动继续应用。', + '为保护敏感配置,请确认当前登录密码。确认后将自动完成保存。', '确认当前密码', { inputType: 'password', inputPlaceholder: '请输入当前密码', - confirmButtonText: '确认并继续', + confirmButtonText: '确认并保存', cancelButtonText: '取消', closeOnClickModal: false, inputValidator: value => String(value || '').length > 0 || '请输入当前密码' diff --git a/frontend/src/views/components/ConfigSettings/ConfigEditor/ConfigEditor.vue b/frontend/src/views/components/ConfigSettings/ConfigEditor/ConfigEditor.vue index 2a92deb..85ba13c 100644 --- a/frontend/src/views/components/ConfigSettings/ConfigEditor/ConfigEditor.vue +++ b/frontend/src/views/components/ConfigSettings/ConfigEditor/ConfigEditor.vue @@ -78,7 +78,7 @@ diff --git a/test/frontend/config-settings.test.mjs b/test/frontend/config-settings.test.mjs index 44d98e7..651d117 100644 --- a/test/frontend/config-settings.test.mjs +++ b/test/frontend/config-settings.test.mjs @@ -9,6 +9,7 @@ const formSource = readFileSync(new URL('../../frontend/src/views/components/Con const formHelpers = compileFunction(formSource.replaceAll('export function ', 'function ') + '\nreturn { readFormConfig, updateFormConfig, validateFormConfig }')() const recentAuthenticationSource = readFileSync(new URL('../../frontend/src/utils/recentAuthentication.js', import.meta.url), 'utf8') .replace(/^import .*$/gm, '') + .replaceAll('export async function ', 'async function ') .replaceAll('export function ', 'function ') const compileRecentAuthentication = compileFunction( recentAuthenticationSource + '\nreturn { isRecentAuthenticationCancelled, requestWithRecentAuthentication }', @@ -429,7 +430,7 @@ test('独立页模板可编译,编辑器不再连接维护面板且权限绑 assert.doesNotMatch(template, /&#x[0-9a-f]+;/i) }) -test('配置编辑页将验证、候选保存和应用精简为单一操作', () => { +test('配置编辑页将验证、候选保存和应用精简为单一保存操作', () => { const root = new URL('../../frontend/src/views/components/ConfigSettings/', import.meta.url) const template = readFileSync(new URL('ConfigEditor/ConfigEditor.vue', root), 'utf8') const sharedCss = readFileSync(new URL('ConfigSettings.css', root), 'utf8') @@ -437,9 +438,9 @@ test('配置编辑页将验证、候选保存和应用精简为单一操作', () assert.doesNotMatch(template, /change-workflow|step-number|配置变更流程/) assert.doesNotMatch(sharedCss + editorCss, /change-workflow|step-number/) assert.match(template, /@click="saveAndApply"/) - assert.match(template, /保存并应用/) + assert.match(template, /保存配置/) assert.match(template, /@click="continuePending"/) - assert.match(template, /继续应用/) + assert.match(template, /继续保存/) assert.match(template, /v-if="canWrite" class="editor-footer"/) assert.match(template, /@click="managePending\('cancel'\)"/) assert.match(template, /放弃更改/) @@ -857,7 +858,7 @@ test('邮件页与安全策略页共享候选并明确提示改动范围,忽 assert.match(page.pendingScopeHint.value, /邮件服务与安全策略未变化/) page.pendingCandidate.value.payload = { ...active, pop3: { port: 1110, tlsPort: 1995 }, policy: { maxMessageBytes: 5678 } } assert.match(page.pendingScopeHint.value, /本次候选涉及:POP3 监听、邮件与账号策略。/) - assert.match(page.pendingScopeHint.value, /邮件服务页和安全策略页共用一个候选配置/) + assert.match(page.pendingScopeHint.value, /邮件服务页和安全策略页共用一组运行配置/) page.pendingCandidate.value = { section: 'smtp' } assert.equal(page.pendingScopeHint.value, '') page.pendingCandidate.value = { section: 'mail' } @@ -924,7 +925,7 @@ for (const key of ['spam_block', 'telegram_push', 'wechat_push']) { const restored = fixture(() => {}, other, { http }).page await restored.loadAll() assert.equal(restored.savedGeneration.value, 2) - assert.match(restored.pendingScopeHint.value, /三个插件共用一个配置分区/) + assert.match(restored.pendingScopeHint.value, /三个插件共用一组运行配置/) assert.ok(restored.pendingScopeHint.value.includes(page.currentSection.value.label)) await restored.applyConfig() assert.ok(requests.includes('/api/v1/config/plugins/apply'))