修复配置保存覆盖服务选择
This commit is contained in:
@@ -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);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user