diff --git a/assets/admin/src/pages/extensions/ExtensionAI.vue b/assets/admin/src/pages/extensions/ExtensionAI.vue index 93943e72..11434b2e 100644 --- a/assets/admin/src/pages/extensions/ExtensionAI.vue +++ b/assets/admin/src/pages/extensions/ExtensionAI.vue @@ -216,9 +216,17 @@ export default { async function saveSettings() { saving.value = true; try { - const payload = { ...form }; - if (!payload.api_key && status.value.configured) { - payload.api_key = '***'; + const payload = { + enabled: form.enabled, + 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); alert(json.message || '设置已保存'); diff --git a/inc/classes/admin/class-aksrt-admin-ai-controller.php b/inc/classes/admin/class-aksrt-admin-ai-controller.php index 801890e0..801f5716 100644 --- a/inc/classes/admin/class-aksrt-admin-ai-controller.php +++ b/inc/classes/admin/class-aksrt-admin-ai-controller.php @@ -130,7 +130,10 @@ class AKSRT_Admin_AI_Controller extends AKSRT_Admin_Controller_Base { $success = AKSRT_AI::get_instance()->update_config($config); 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')); } diff --git a/inc/classes/class-aksrt-ai.php b/inc/classes/class-aksrt-ai.php index 28ca977c..f3402471 100644 --- a/inc/classes/class-aksrt-ai.php +++ b/inc/classes/class-aksrt-ai.php @@ -54,11 +54,60 @@ class AKSRT_AI { $current = $this->get_config(); $merged = array_merge($current, $config); + // Preserve existing key when client sends the masked placeholder. if (isset($config['api_key']) && $config['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); } /**