移除验证邮件策略分组并将中继密码合并入表单
Some checks are pending
Security Checks / security (push) Waiting to run

This commit is contained in:
smail 2026-09-07 11:42:59 +08:00
parent 1faecab9ae
commit 141e846918
5 changed files with 18 additions and 16 deletions

View File

@ -121,7 +121,9 @@
:placeholder="field.placeholder || '填写内容'"
:aria-describedby="descriptionId"
:aria-invalid="!!error"
autocomplete="off"
:type="field.type === 'password' ? 'password' : 'text'"
:show-password="field.type === 'password'"
:autocomplete="field.type === 'password' ? 'new-password' : 'off'"
@update:model-value="update"
/>
<ConfigPagination

View File

@ -128,12 +128,9 @@ const smtpGroups = [
}),
port('relay.relayPort', 'SMTP 发信中继端口', { hint: '外部 SMTP 服务器端口,与本机监听无关;选择加密方式后自动填写。' }),
field('relay.username', '认证用户名', 'text', { placeholder: '需要认证时填写' }),
field('relay.password', '中继密码', 'password', { placeholder: '留空保持原值' }),
field('relay.from', '发件邮箱', 'text', { placeholder: 'noreply@example.com' }),
field('relay.fromDomain', '发件域名', 'text', { placeholder: 'example.com' })
]),
group('verification', '验证邮件策略', '', [
field('relay.verificationEnabled', '启用验证邮件发送', 'switch'),
field('relay.loginPolicy', '中继登录验证策略', 'select', { options: loginOptions })
])
]
@ -296,7 +293,7 @@ export function validateFormConfig(section, config) {
}
if (section === 'smtp') {
const relay = config.relay || {}
if (relay.verificationEnabled || (relay.relayHost && relay.security)) {
if (relay.relayHost && relay.security) {
if (!['starttls', 'implicit_tls', 'tls', 'plain'].includes(relay.security)) {
errors['relay.security'] = '请选择明确的连接加密方式,系统会自动匹配端口'
}

View File

@ -180,9 +180,6 @@ export function usePage(pageKey = 'base') {
if (selectedSection.value === 'base' && ['mysql', 'postgresql'].includes(formConfig.value.database?.type)) {
return { label: '数据库密码', description: '只在本次请求内传输,保存后仅保留密文状态。留空表示保持原值。', placeholder: '输入新数据库密码(可选)' }
}
if (selectedSection.value === 'smtp') {
return { label: 'SMTP Relay 密码', description: '只在本次请求内传输,保存后仅保留密文状态。留空表示保持原值。', placeholder: '输入新的 Relay 密码(可选)' }
}
return null
})
const sensitiveHint = computed(() => secretField.value ? '密码不会回填,修改请使用下方独立输入框,留空保持原值' : '')
@ -524,6 +521,12 @@ export function usePage(pageKey = 'base') {
}
if (selectedSection.value === 'smtp') {
delete payload.relayPasswordStatus
if (payload.relay && 'password' in payload.relay) {
delete payload.relay.password
if (Object.keys(payload.relay).length === 0) {
delete payload.relay
}
}
}
if (apiSection() === 'plugins' && payload.plugins && typeof payload.plugins === 'object') {
Object.values(payload.plugins).forEach(plugin => {
@ -545,8 +548,9 @@ export function usePage(pageKey = 'base') {
if (value && selectedSection.value === 'base' && secretField.value) {
secretChanges['database.password'] = { operation: 'replace', value: { plaintext: value } }
}
if (value && selectedSection.value === 'smtp') {
secretChanges['relay.password'] = { operation: 'replace', value: { plaintext: value } }
const relayPassword = formConfig.value?.relay?.password
if (relayPassword && selectedSection.value === 'smtp') {
secretChanges['relay.password'] = { operation: 'replace', value: { plaintext: relayPassword } }
}
return {
expectedGeneration: Number(status.activeGeneration),

View File

@ -40,7 +40,7 @@ test('七个页面使用分组表单,控件和样式能编译且不再提供 J
const keys = groups.flatMap(group => group.fields.map(field => field.path.join('.')))
assert.equal(keys.length, new Set(keys).size)
assert.ok(groups.every(group => /[\u4e00-\u9fff]/.test(group.title)))
assert.ok(!keys.some(key => /generation|password|encryptionKey|capabilities/.test(key)))
assert.ok(!keys.some(key => /generation|database\.password|encryptionKey|capabilities/.test(key)))
}
})
@ -320,8 +320,7 @@ test('加密配置校验拦截监听冲突、远程明文和已启用但未选
for (const relayHost of ['smtp.example.com', '127.0.0.999', '127.1', '127.0.0.1.evil.com', '192.168.1.1']) {
assert.ok(validateFormConfig('smtp', { relay: { relayHost, security: 'plain', relayPort: 25 } })['relay.relayHost'])
}
const errors = validateFormConfig('smtp', { relay: { verificationEnabled: true, relayHost: 'smtp.example.com', relayPort: 0 } })
assert.ok(errors['relay.security'])
const errors = validateFormConfig('smtp', { relay: { relayHost: 'smtp.example.com', security: 'starttls', relayPort: 0 } })
assert.ok(errors['relay.relayPort'])
assert.deepEqual(validateFormConfig('smtp', { relay: {} }), {})
assert.deepEqual(validateFormConfig('smtp', { relay: { security: 'starttls', relayPort: 2525, relayHost: 'smtp.example.com' } }), {})

View File

@ -512,7 +512,7 @@ test('独立页保持验证、保存、应用端点和代次协议,敏感值
}
})
await page.loadAll()
page.secretValue.value = 'test-only-value'
page.updateField(['relay', 'password'], 'test-only-value')
page.confirmed.value = true
await page.validateConfig()
await page.saveConfig()
@ -618,7 +618,7 @@ test('SQLite 不显示或发送密码,网络数据库和中继密码留空保
http: { post: async (path, body) => { request = body; return { errorNo: 0, data: { valid: true } } } }
})
await page.loadSection()
page.updateSecret(' smtp-test-only ')
page.updateField(['relay', 'password'], ' smtp-test-only ')
await page.validateConfig()
assert.equal(request.secretChanges['relay.password'].value.plaintext, ' smtp-test-only ')
})