diff --git a/frontend/src/views/components/ConfigSettings/ConfigForm/ConfigForm.js b/frontend/src/views/components/ConfigSettings/ConfigForm/ConfigForm.js index 7a643c8..bd6cafb 100644 --- a/frontend/src/views/components/ConfigSettings/ConfigForm/ConfigForm.js +++ b/frontend/src/views/components/ConfigSettings/ConfigForm/ConfigForm.js @@ -72,8 +72,6 @@ const baseGroups = [ field('logLevel', '日志级别', 'select', { options: options([['', '默认级别'], ['debug', '调试'], ['info', '信息'], ['warn', '警告'], ['error', '错误']]) }) ]), group('database', '数据库连接', '修改数据库类型、名称或文件请使用右侧“数据库迁移”,支持 SQLite、MySQL、PostgreSQL,避免切换到空数据库。', [ - field('database.type', '数据库类型', 'select', { locked: true, options: options([['sqlite', 'SQLite(本地文件)'], ['mysql', 'MySQL'], ['postgresql', 'PostgreSQL']]) }), - field('database.localRef', '数据库文件', 'text', { locked: true, show: config => !networkDatabase(config) }), field('database.name', '数据库名称', 'text', { locked: true, show: networkDatabase }), field('database.host', '数据库主机', 'text', { required: true, show: networkDatabase, placeholder: '127.0.0.1' }), port('database.port', '数据库端口', { min: 1, show: networkDatabase }), @@ -95,11 +93,6 @@ const baseGroups = [ min: 1, show: config => config.http?.tlsMode !== 'proxy' && !!config.http?.httpsEnabled, hint: '默认使用 443;需要避开端口占用时可手动修改。' - }), - field('http.trustedProxyCidrs', '可信代理网段', 'list', { - show: config => config.http?.tlsMode === 'proxy', - placeholder: '127.0.0.1/32', - hint: '仅填写实际代理的 CIDR 网段,不要无条件信任所有来源。' }) ]), group('security', '全局安全选项', '只调整功能开关与站点信息,密码算法和加密密钥由系统维护。', [ @@ -189,6 +182,7 @@ export function getFieldGroups(section, config) { .filter(input => !input.show || input.show(config)) .map(input => ({ ...input, recommendedPort: getRecommendedPort(config, input.path) })) })) + .filter(item => item.fields.length > 0) } return Object.entries(config.plugins || {}).map(([id, plugin]) => { const definition = pluginDefinitions[id] diff --git a/test/frontend/config-form.test.mjs b/test/frontend/config-form.test.mjs index 892632c..9e4f829 100644 --- a/test/frontend/config-form.test.mjs +++ b/test/frontend/config-form.test.mjs @@ -69,6 +69,38 @@ test('单字段更新完整保留内部数据,锁定字段和污染路径无 assert.equal({}.polluted, undefined) }) +test('基础配置隐藏代理网段和数据库只读信息,不留下空分组且保留内部值', () => { + for (const type of ['sqlite', 'mysql', 'postgresql']) { + for (const tlsMode of ['direct', 'proxy']) { + const config = { + database: { type, localRef: 'preserved.db', name: 'smail', host: 'db.example.com', port: type === 'postgresql' ? 5432 : 3306, username: 'smail', password: null }, + http: { tlsMode, port: 8080, trustedProxyCidrs: ['127.0.0.1/32', '192.0.2.10/32'] }, + security: { externalEmailEnabled: false, webAuthnEnabled: false } + } + const text = JSON.stringify(config) + const groups = getFieldGroups('base', config) + const keys = groups.flatMap(item => item.fields.map(input => input.path.join('.'))) + const hidden = ['database.type', 'database.localRef', 'http.trustedProxyCidrs'] + assert.ok(groups.every(item => item.fields.length > 0)) + assert.ok(hidden.every(key => !keys.includes(key))) + assert.equal(groups.some(item => item.id === 'database'), type !== 'sqlite') + for (const key of hidden) { + assert.equal(updateFormConfig(text, 'base', key.split('.'), 'changed'), text) + } + for (const key of ['security.externalEmailEnabled', 'security.webAuthnEnabled']) { + assert.ok(keys.includes(key)) + } + const updated = readFormConfig(updateFormConfig(text, 'base', ['http', 'port'], 9080)) + assert.deepEqual(updated, { ...config, http: { ...config.http, port: 9080 } }) + if (type !== 'sqlite') { + for (const key of ['database.name', 'database.host', 'database.port', 'database.username']) { + assert.ok(keys.includes(key)) + } + } + } + } +}) + test('开关、数字、空数字和列表保持真实类型,代理 HTTPS 正确联动', () => { const modeField = getFieldGroups('base', {}).flatMap(group => group.fields).find(field => field.path.join('.') === 'http.tlsMode') assert.match(modeField.hint, /不自动申请或续期 SSL 证书/) diff --git a/test/frontend/config-settings.test.mjs b/test/frontend/config-settings.test.mjs index d68262b..24985a6 100644 --- a/test/frontend/config-settings.test.mjs +++ b/test/frontend/config-settings.test.mjs @@ -557,6 +557,25 @@ test('SQLite 不显示或发送密码,网络数据库和中继密码留空保 assert.equal(request.secretChanges['relay.password'].value.plaintext, ' smtp-test-only ') }) +test('基础配置保存完整保留隐藏的数据库和代理信任配置', async () => { + const config = { + database: { type: 'sqlite', localRef: 'preserved.db' }, + domains: { primary: 'example.com', web: 'mail.example.com', accepted: ['example.com'] }, + http: { tlsMode: 'proxy', port: 8080, httpsEnabled: false, trustedProxyCidrs: ['127.0.0.1/32', '192.0.2.10/32'] }, + security: { externalEmailEnabled: false, webAuthnEnabled: false } + } + let request + const { page } = fixture(async () => ({ errorNo: 0, data: config }), 'base', { + http: { put: async (path, body) => { request = { path, body }; return { errorNo: 0, data: { generation: 2 } } } } + }) + await page.loadSection() + page.updateField(['http', 'port'], 9080) + await page.saveConfig() + assert.equal(request.path, '/api/v1/config/base') + assert.deepEqual(request.body.payload, { ...config, http: { ...config.http, port: 9080 } }) + assert.deepEqual(request.body.secretChanges, {}) +}) + test('保存后更改密码或填写内容不能应用旧候选', async () => { const requests = [] const { page } = fixture(async () => ({ errorNo: 0, data: {} }), 'smtp', {