This commit is contained in:
parent
659d98b589
commit
a7ad2bc32e
@ -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)
|
||||
}
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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 || '请输入当前密码'
|
||||
|
||||
@ -78,7 +78,7 @@
|
||||
<div v-if="canWrite" class="editor-footer">
|
||||
<el-alert
|
||||
v-if="pendingCandidate"
|
||||
title="有一项配置尚未生效,可以继续应用或放弃更改。"
|
||||
title="上次保存尚未完成,可以继续保存或放弃本次更改。"
|
||||
type="warning"
|
||||
:closable="false"
|
||||
show-icon
|
||||
@ -98,7 +98,7 @@
|
||||
:disabled="sectionLoading || !!working || !!sectionError || !hasChanges"
|
||||
@click="saveAndApply"
|
||||
>
|
||||
保存并应用
|
||||
保存配置
|
||||
</el-button>
|
||||
<el-button
|
||||
v-else
|
||||
@ -107,7 +107,7 @@
|
||||
:disabled="sectionLoading || !!working || !!sectionError"
|
||||
@click="continuePending"
|
||||
>
|
||||
继续应用
|
||||
继续保存
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="pendingCandidate"
|
||||
|
||||
@ -82,7 +82,7 @@ export function usePage(pageKey = 'base') {
|
||||
const scope = pending.payload && loadedText.value
|
||||
? (changed.length ? `本次候选涉及:${changed.join('、')}。` : '本次候选的插件运行配置未变化。')
|
||||
: '候选内容尚未完整读取,请刷新后核对。'
|
||||
return `${scope}三个插件共用一个配置分区,请在对应三级菜单核对;应用时整个候选一起生效,其他插件的原有配置会保留。`
|
||||
return `${scope}三个插件共用一组运行配置,请在对应三级菜单核对;完成保存时会统一生效,其他插件的原有配置会保留。`
|
||||
}
|
||||
if (pending?.section !== 'mail' || apiSection() !== 'mail') return ''
|
||||
const active = readFormConfig(loadedText.value)
|
||||
@ -101,7 +101,7 @@ export function usePage(pageKey = 'base') {
|
||||
const scope = pending.payload && loadedText.value
|
||||
? (changed.length ? `本次候选涉及:${changed.join('、')}。` : '本次候选的邮件服务与安全策略未变化。')
|
||||
: '候选内容尚未完整读取,请刷新后核对。'
|
||||
return `${scope}邮件服务页和安全策略页共用一个候选配置;应用时全部变更一起生效,监听端口变更仍需重启后端。`
|
||||
return `${scope}邮件服务页和安全策略页共用一组运行配置;完成保存时全部变更一起生效,监听端口变更仍需重启后端。`
|
||||
})
|
||||
let savedInput = null
|
||||
const validationResult = ref(null)
|
||||
@ -577,7 +577,7 @@ export function usePage(pageKey = 'base') {
|
||||
validationResult.value = { valid: true, generation: savedGeneration.value, issues: [] }
|
||||
await loadStatus()
|
||||
await loadPending()
|
||||
if (!quiet) ElMessage.success('配置已保存,等待应用')
|
||||
if (!quiet) ElMessage.success('配置已暂存,正在完成保存')
|
||||
return true
|
||||
} catch (error) {
|
||||
if (isRecentAuthenticationCancelled(error)) return false
|
||||
@ -625,7 +625,7 @@ export function usePage(pageKey = 'base') {
|
||||
return
|
||||
}
|
||||
const saved = await saveConfig({ quiet: true })
|
||||
if (saved) await applyConfig({ successMessage: '配置已保存并应用' })
|
||||
if (saved) await applyConfig({ successMessage: '配置已保存' })
|
||||
}
|
||||
|
||||
const continuePending = async () => {
|
||||
@ -716,7 +716,7 @@ export function usePage(pageKey = 'base') {
|
||||
if (!hasChanges.value && !migrationToken.value && !rollbackPreview.value) return true
|
||||
try {
|
||||
await ElMessageBox.confirm(
|
||||
'当前页有编辑内容、待应用候选配置或操作预览。离开会清除本页状态,但不会撤销已保存到服务器的候选配置。确认离开吗?',
|
||||
'当前页有编辑内容、未完成的保存或操作预览。离开会清除本页状态,但不会撤销已暂存到服务器的更改。确认离开吗?',
|
||||
'确认离开当前页',
|
||||
{ type: 'warning', confirmButtonText: '离开页面', cancelButtonText: '继续编辑' }
|
||||
)
|
||||
|
||||
@ -131,7 +131,7 @@ export function useSpamBlockSettings() {
|
||||
savedInput = JSON.stringify(settings)
|
||||
validationResult.value = { valid: true, generation: savedGeneration.value, issues: [] }
|
||||
await load()
|
||||
if (!quiet) ElMessage.success('垃圾邮件插件配置已保存,等待应用')
|
||||
if (!quiet) ElMessage.success('垃圾邮件插件配置已暂存,正在完成保存')
|
||||
return true
|
||||
} catch (error) {
|
||||
if (isRecentAuthenticationCancelled(error)) return false
|
||||
@ -201,7 +201,7 @@ export function useSpamBlockSettings() {
|
||||
return
|
||||
}
|
||||
const saved = await save({ quiet: true })
|
||||
if (saved) await apply({ successMessage: '垃圾邮件插件配置已保存并应用' })
|
||||
if (saved) await apply({ successMessage: '垃圾邮件插件配置已保存' })
|
||||
}
|
||||
|
||||
const continuePending = async () => {
|
||||
|
||||
@ -25,7 +25,7 @@
|
||||
show-icon
|
||||
/>
|
||||
|
||||
<el-alert v-if="pendingCandidate" title="有一项配置尚未生效,可以继续应用或放弃更改。" type="warning" :closable="false" show-icon />
|
||||
<el-alert v-if="pendingCandidate" title="上次保存尚未完成,可以继续保存或放弃本次更改。" type="warning" :closable="false" show-icon />
|
||||
<el-form label-position="top" class="spam-form">
|
||||
<el-form-item label="模型 API 接口地址">
|
||||
<el-input
|
||||
@ -68,7 +68,7 @@
|
||||
:disabled="!canWrite() || !!working || loading || !hasChanges"
|
||||
@click="saveAndApply"
|
||||
>
|
||||
保存并应用
|
||||
保存配置
|
||||
</el-button>
|
||||
<el-button
|
||||
v-else
|
||||
@ -77,7 +77,7 @@
|
||||
:disabled="!canWrite() || !!working || loading"
|
||||
@click="continuePending"
|
||||
>
|
||||
继续应用
|
||||
继续保存
|
||||
</el-button>
|
||||
<el-button v-if="canWrite() && pendingCandidate" type="danger" plain :disabled="!!working || pendingCandidate.committed" @click="managePending('cancel')">放弃更改</el-button>
|
||||
</div>
|
||||
|
||||
@ -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'))
|
||||
|
||||
Loading…
Reference in New Issue
Block a user