修复配置保存覆盖服务选择

This commit is contained in:
QLHazyCoder
2026-05-20 13:18:20 +08:00
parent e849c8800f
commit 752e0d3aaf
2 changed files with 271 additions and 0 deletions
+95
View File
@@ -3574,6 +3574,97 @@ function projectSettingsSchemaView(settingsSchemaApi, normalizedInput = {}, payl
return settingsSchemaApi.buildSettingsView(normalizedSettingsState, payload);
}
function setSettingsStatePatchValue(patch, path, value) {
let cursor = patch;
for (let index = 0; index < path.length - 1; index += 1) {
const key = path[index];
if (!isPlainObjectValue(cursor[key])) {
cursor[key] = {};
}
cursor = cursor[key];
}
cursor[path[path.length - 1]] = value;
}
function mergeSettingsStatePatch(baseValue = {}, patchValue = {}) {
if (!isPlainObjectValue(patchValue)) {
return isPlainObjectValue(baseValue) ? { ...baseValue } : {};
}
const next = {
...(isPlainObjectValue(baseValue) ? baseValue : {}),
};
Object.entries(patchValue).forEach(([key, value]) => {
next[key] = isPlainObjectValue(value)
? mergeSettingsStatePatch(next[key], value)
: value;
});
return next;
}
function buildSettingsStatePatchFromFlatUpdates(updates = {}) {
const patch = {};
const hasUpdate = (key) => Object.prototype.hasOwnProperty.call(updates, key);
const assignIfUpdated = (key, path) => {
if (hasUpdate(key)) {
setSettingsStatePatchValue(patch, path, updates[key]);
}
};
assignIfUpdated('activeFlowId', ['activeFlowId']);
if (hasUpdate('openaiIntegrationTargetId') || hasUpdate('panelMode')) {
setSettingsStatePatchValue(
patch,
['flows', 'openai', 'integrationTargetId'],
hasUpdate('openaiIntegrationTargetId') ? updates.openaiIntegrationTargetId : updates.panelMode
);
}
assignIfUpdated('kiroTargetId', ['flows', 'kiro', 'targetId']);
assignIfUpdated('vpsUrl', ['flows', 'openai', 'integrationTargets', 'cpa', 'vpsUrl']);
assignIfUpdated('vpsPassword', ['flows', 'openai', 'integrationTargets', 'cpa', 'vpsPassword']);
assignIfUpdated('localCpaStep9Mode', ['flows', 'openai', 'integrationTargets', 'cpa', 'localCpaStep9Mode']);
assignIfUpdated('sub2apiUrl', ['flows', 'openai', 'integrationTargets', 'sub2api', 'sub2apiUrl']);
assignIfUpdated('sub2apiEmail', ['flows', 'openai', 'integrationTargets', 'sub2api', 'sub2apiEmail']);
assignIfUpdated('sub2apiPassword', ['flows', 'openai', 'integrationTargets', 'sub2api', 'sub2apiPassword']);
assignIfUpdated('sub2apiGroupName', ['flows', 'openai', 'integrationTargets', 'sub2api', 'sub2apiGroupName']);
assignIfUpdated('sub2apiGroupNames', ['flows', 'openai', 'integrationTargets', 'sub2api', 'sub2apiGroupNames']);
assignIfUpdated('sub2apiAccountPriority', ['flows', 'openai', 'integrationTargets', 'sub2api', 'sub2apiAccountPriority']);
assignIfUpdated('sub2apiDefaultProxyName', ['flows', 'openai', 'integrationTargets', 'sub2api', 'sub2apiDefaultProxyName']);
assignIfUpdated('codex2apiUrl', ['flows', 'openai', 'integrationTargets', 'codex2api', 'codex2apiUrl']);
assignIfUpdated('codex2apiAdminKey', ['flows', 'openai', 'integrationTargets', 'codex2api', 'codex2apiAdminKey']);
assignIfUpdated('customPassword', ['services', 'account', 'customPassword']);
assignIfUpdated('signupMethod', ['flows', 'openai', 'signup', 'signupMethod']);
assignIfUpdated('phoneVerificationEnabled', ['flows', 'openai', 'signup', 'phoneVerificationEnabled']);
assignIfUpdated('phoneSignupReloginAfterBindEmailEnabled', ['flows', 'openai', 'signup', 'phoneSignupReloginAfterBindEmailEnabled']);
assignIfUpdated('plusModeEnabled', ['flows', 'openai', 'plus', 'plusModeEnabled']);
assignIfUpdated('plusPaymentMethod', ['flows', 'openai', 'plus', 'plusPaymentMethod']);
assignIfUpdated('plusAccountAccessStrategy', ['flows', 'openai', 'plus', 'plusAccountAccessStrategy']);
assignIfUpdated('mailProvider', ['services', 'email', 'provider']);
assignIfUpdated('ipProxyEnabled', ['services', 'proxy', 'enabled']);
assignIfUpdated('ipProxyService', ['services', 'proxy', 'provider']);
assignIfUpdated('ipProxyMode', ['services', 'proxy', 'mode']);
assignIfUpdated('kiroRsUrl', ['flows', 'kiro', 'targets', 'kiro-rs', 'baseUrl']);
assignIfUpdated('kiroRsKey', ['flows', 'kiro', 'targets', 'kiro-rs', 'apiKey']);
if (hasUpdate('stepExecutionRangeByFlow') && isPlainObjectValue(updates.stepExecutionRangeByFlow)) {
if (isPlainObjectValue(updates.stepExecutionRangeByFlow.openai)) {
setSettingsStatePatchValue(
patch,
['flows', 'openai', 'autoRun', 'stepExecutionRange'],
updates.stepExecutionRangeByFlow.openai
);
}
if (isPlainObjectValue(updates.stepExecutionRangeByFlow.kiro)) {
setSettingsStatePatchValue(
patch,
['flows', 'kiro', 'autoRun', 'stepExecutionRange'],
updates.stepExecutionRangeByFlow.kiro
);
}
}
return patch;
}
function buildPersistedSettingsStoragePayload(payload = {}) {
const storagePayload = {};
Object.entries(payload || {}).forEach(([key, value]) => {
@@ -3924,6 +4015,10 @@ async function setPersistentSettings(updates) {
? settingsSchemaApi.mergeSettingsState(currentSettingsState, nextUpdates.settingsState)
: nextUpdates.settingsState)
: currentSettingsState;
mergedSettingsState = mergeSettingsStatePatch(
mergedSettingsState,
buildSettingsStatePatchFromFlatUpdates(explicitFlatUpdates)
);
}
const nextPayloadInput = {
@@ -174,6 +174,9 @@ function resolveLegacyAutoStepDelaySeconds() { return undefined; }
${extractFunction('normalizePersistentSettingValue')}
${extractFunction('getSettingsSchemaApi')}
${extractFunction('projectSettingsSchemaView')}
${extractFunction('setSettingsStatePatchValue')}
${extractFunction('mergeSettingsStatePatch')}
${extractFunction('buildSettingsStatePatchFromFlatUpdates')}
${extractFunction('buildPersistedSettingsStoragePayload')}
${extractFunction('buildPersistentSettingsPayload')}
${extractFunction('getPersistedSettings')}
@@ -513,3 +516,176 @@ function getRemovedKeys() {
assert.ok(api.getRemovedKeys().includes('panelMode'));
assert.ok(api.getRemovedKeys().includes('kiroRsUrl'));
});
test('setPersistentSettings mirrors flat mail provider updates into canonical settingsState', async () => {
const api = buildHarness(`
const persistedWrites = [];
const removedKeys = [];
const chrome = {
storage: {
local: {
async get() {
return {
settingsSchemaVersion: 4,
settingsState: {
activeFlowId: 'openai',
services: {
account: { customPassword: '' },
email: { provider: '163' },
proxy: { enabled: false, provider: '711proxy', mode: 'account' },
},
flows: {},
},
};
},
async remove(keys) {
removedKeys.push(...(Array.isArray(keys) ? keys : [keys]));
},
async set(payload) {
persistedWrites.push(JSON.parse(JSON.stringify(payload)));
},
},
},
};
function getPersistedWrites() {
return persistedWrites;
}
function getRemovedKeys() {
return removedKeys;
}
`);
const persisted = await api.setPersistentSettings({
mailProvider: 'cloudflare-temp-email',
});
const write = api.getPersistedWrites().at(-1);
assert.equal(persisted.mailProvider, 'cloudflare-temp-email');
assert.equal(persisted.settingsState.services.email.provider, 'cloudflare-temp-email');
assert.equal(write.settingsState.services.email.provider, 'cloudflare-temp-email');
assert.equal(Object.prototype.hasOwnProperty.call(write, 'mailProvider'), false);
});
test('setPersistentSettings mirrors flat schema updates without resetting other canonical settings', async () => {
const api = buildHarness(`
const persistedWrites = [];
const removedKeys = [];
const chrome = {
storage: {
local: {
async get() {
return {
settingsSchemaVersion: 4,
settingsState: {
activeFlowId: 'openai',
services: {
account: { customPassword: 'old-password' },
email: { provider: '163' },
proxy: { enabled: false, provider: '711proxy', mode: 'account' },
},
flows: {
openai: {
integrationTargetId: 'cpa',
integrationTargets: {
cpa: {
vpsUrl: 'https://old-cpa.example.com',
vpsPassword: 'old-vps-password',
localCpaStep9Mode: 'submit',
},
sub2api: {
sub2apiUrl: 'https://sub2api.example.com',
sub2apiEmail: 'owner@example.com',
sub2apiPassword: 'sub2api-secret',
sub2apiGroupName: 'kept-group',
sub2apiGroupNames: ['kept-group'],
sub2apiAccountPriority: 7,
sub2apiDefaultProxyName: 'proxy-a',
},
codex2api: {
codex2apiUrl: 'https://codex2api.example.com',
codex2apiAdminKey: 'codex-key',
},
},
signup: {
signupMethod: 'email',
phoneVerificationEnabled: false,
phoneSignupReloginAfterBindEmailEnabled: false,
},
plus: {
plusModeEnabled: false,
plusPaymentMethod: 'paypal',
plusAccountAccessStrategy: 'oauth',
},
autoRun: {
stepExecutionRange: { enabled: false, fromStep: 1, toStep: 11 },
},
},
kiro: {
targetId: 'kiro-rs',
targets: {
'kiro-rs': {
baseUrl: 'https://kiro.example.com/admin',
apiKey: 'kiro-key',
},
},
autoRun: {
stepExecutionRange: { enabled: false, fromStep: 1, toStep: 9 },
},
},
},
},
};
},
async remove(keys) {
removedKeys.push(...(Array.isArray(keys) ? keys : [keys]));
},
async set(payload) {
persistedWrites.push(JSON.parse(JSON.stringify(payload)));
},
},
},
};
function getPersistedWrites() {
return persistedWrites;
}
function getRemovedKeys() {
return removedKeys;
}
`);
const persisted = await api.setPersistentSettings({
panelMode: 'sub2api',
mailProvider: 'cloudflare-temp-email',
ipProxyEnabled: true,
ipProxyMode: 'api',
stepExecutionRangeByFlow: {
openai: { enabled: true, fromStep: 2, toStep: 4 },
},
});
const write = api.getPersistedWrites().at(-1);
assert.equal(persisted.panelMode, 'sub2api');
assert.equal(persisted.mailProvider, 'cloudflare-temp-email');
assert.equal(persisted.ipProxyEnabled, true);
assert.equal(persisted.ipProxyMode, 'api');
assert.deepEqual(persisted.stepExecutionRangeByFlow.openai, {
enabled: true,
fromStep: 2,
toStep: 4,
});
assert.equal(write.settingsState.flows.openai.integrationTargetId, 'sub2api');
assert.equal(write.settingsState.services.email.provider, 'cloudflare-temp-email');
assert.equal(write.settingsState.services.proxy.enabled, true);
assert.equal(write.settingsState.services.proxy.mode, 'api');
assert.deepEqual(write.settingsState.flows.openai.autoRun.stepExecutionRange, {
enabled: true,
fromStep: 2,
toStep: 4,
});
assert.equal(write.settingsState.flows.openai.integrationTargets.sub2api.sub2apiUrl, 'https://sub2api.example.com');
assert.equal(write.settingsState.flows.openai.integrationTargets.sub2api.sub2apiEmail, 'owner@example.com');
assert.equal(write.settingsState.flows.kiro.targets['kiro-rs'].apiKey, 'kiro-key');
assert.equal(Object.prototype.hasOwnProperty.call(write, 'mailProvider'), false);
assert.equal(Object.prototype.hasOwnProperty.call(write, 'panelMode'), false);
assert.equal(Object.prototype.hasOwnProperty.call(write, 'ipProxyMode'), false);
});