fix(ai): treat unchanged AI settings as successful save

update_option returns false when values are identical, which made
POST /ai/settings respond 400 with "保存失败" on no-op saves and
masked-key re-saves. Normalize config, accept no-op as success, and
only send a new API key from the admin form when entered.
This commit is contained in:
2026-07-13 08:08:42 +08:00
parent 9c19bc250d
commit 7ced67b2d3
3 changed files with 65 additions and 5 deletions
@@ -216,9 +216,17 @@ export default {
async function saveSettings() { async function saveSettings() {
saving.value = true; saving.value = true;
try { try {
const payload = { ...form }; const payload = {
if (!payload.api_key && status.value.configured) { enabled: form.enabled,
payload.api_key = '***'; provider: form.provider,
api_base: form.api_base,
model: form.model,
max_tokens: form.max_tokens,
rate_limit: form.rate_limit,
};
// Only send api_key when the user entered a new one.
if (form.api_key && form.api_key !== '***') {
payload.api_key = form.api_key;
} }
const json = await api.updateAiSettings(payload); const json = await api.updateAiSettings(payload);
alert(json.message || '设置已保存'); alert(json.message || '设置已保存');
@@ -130,7 +130,10 @@ class AKSRT_Admin_AI_Controller extends AKSRT_Admin_Controller_Base {
$success = AKSRT_AI::get_instance()->update_config($config); $success = AKSRT_AI::get_instance()->update_config($config);
if ($success) { if ($success) {
return $this->success(['message' => __('AI 设置已保存', 'aksrt')]); return $this->success([
'message' => __('AI 设置已保存', 'aksrt'),
'data' => AKSRT_AI::get_instance()->get_status(),
]);
} }
return $this->error(__('保存失败', 'aksrt')); return $this->error(__('保存失败', 'aksrt'));
} }
+50 -1
View File
@@ -54,11 +54,60 @@ class AKSRT_AI {
$current = $this->get_config(); $current = $this->get_config();
$merged = array_merge($current, $config); $merged = array_merge($current, $config);
// Preserve existing key when client sends the masked placeholder.
if (isset($config['api_key']) && $config['api_key'] === '***') { if (isset($config['api_key']) && $config['api_key'] === '***') {
$merged['api_key'] = $current['api_key']; $merged['api_key'] = $current['api_key'];
} }
return update_option('aksrt_ai_settings', $merged); $merged['enabled'] = !empty($merged['enabled']);
$merged['provider'] = sanitize_key((string) ($merged['provider'] ?? self::PROVIDER_NONE));
$merged['api_key'] = (string) ($merged['api_key'] ?? '');
$merged['api_base'] = esc_url_raw((string) ($merged['api_base'] ?? 'https://api.openai.com/v1'));
$merged['model'] = sanitize_text_field((string) ($merged['model'] ?? 'gpt-3.5-turbo'));
$merged['max_tokens'] = max(100, min(4000, (int) ($merged['max_tokens'] ?? 1000)));
$merged['rate_limit'] = max(1, min(100, (int) ($merged['rate_limit'] ?? 10)));
// Persist only the known keys so shape stays stable.
$to_store = [
'enabled' => $merged['enabled'],
'provider' => $merged['provider'],
'api_key' => $merged['api_key'],
'api_base' => $merged['api_base'],
'model' => $merged['model'],
'max_tokens' => $merged['max_tokens'],
'rate_limit' => $merged['rate_limit'],
];
$before = get_option('aksrt_ai_settings', []);
$updated = update_option('aksrt_ai_settings', $to_store);
if ($updated) {
return true;
}
// update_option returns false when the value is unchanged — treat as success.
if (is_array($before)) {
$before_normalized = array_merge([
'enabled' => false,
'provider' => self::PROVIDER_NONE,
'api_key' => '',
'api_base' => 'https://api.openai.com/v1',
'model' => 'gpt-3.5-turbo',
'max_tokens' => 1000,
'rate_limit' => 10,
], $before);
$before_normalized['enabled'] = !empty($before_normalized['enabled']);
$before_normalized['provider'] = sanitize_key((string) ($before_normalized['provider'] ?? self::PROVIDER_NONE));
$before_normalized['api_key'] = (string) ($before_normalized['api_key'] ?? '');
$before_normalized['api_base'] = esc_url_raw((string) ($before_normalized['api_base'] ?? 'https://api.openai.com/v1'));
$before_normalized['model'] = sanitize_text_field((string) ($before_normalized['model'] ?? 'gpt-3.5-turbo'));
$before_normalized['max_tokens'] = max(100, min(4000, (int) ($before_normalized['max_tokens'] ?? 1000)));
$before_normalized['rate_limit'] = max(1, min(100, (int) ($before_normalized['rate_limit'] ?? 10)));
return maybe_serialize($before_normalized) === maybe_serialize($to_store)
|| $before_normalized == $to_store;
}
return maybe_serialize($before) === maybe_serialize($to_store);
} }
/** /**