Merge pull request 'fix(admin): AI 状态接口使用共享 API 客户端 nonce' (#81) from worktree-fix-ai-status-403 into main
Reviewed-on: CYL/wordpress-theme-aksrt#81
This commit is contained in:
@@ -5,4 +5,8 @@ export const extensionsApiMethods = {
|
||||
|
||||
testIpGeo(ip) { return this.request('/extensions/ip-test', { method: 'POST', body: JSON.stringify({ ip }) }) },
|
||||
testContentAudit(text) { return this.request('/extensions/audit-test', { method: 'POST', body: JSON.stringify({ text }) }) },
|
||||
|
||||
/* AI 辅助(兼容页) */
|
||||
getAiStatus() { return this.request('/ai/status') },
|
||||
updateAiSettings(data) { return this.request('/ai/settings', { method: 'POST', data }) },
|
||||
}
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
<div class="aksrt-page-container">
|
||||
<PageHeader title="AI 辅助(兼容)" desc="兼容保留的 AI 内容辅助能力,包括 SEO 建议、摘要生成、标签建议等。" />
|
||||
|
||||
<div v-if="loadError" class="aksrt-ai__form-card aksrt-ai__load-error" role="alert">
|
||||
<strong>加载失败:</strong>{{ loadError }}
|
||||
</div>
|
||||
|
||||
<!-- 状态卡片 -->
|
||||
<div class="aksrt-ai__status">
|
||||
<div class="aksrt-ai__status-card">
|
||||
@@ -131,8 +135,7 @@
|
||||
<script>
|
||||
import { ref, reactive, onMounted } from 'vue';
|
||||
import PageHeader from '@/components/PageHeader.vue';
|
||||
|
||||
const API = '/wp-json/aksrt/v1/ai';
|
||||
import api from '@/api';
|
||||
|
||||
export default {
|
||||
name: 'ExtensionAI',
|
||||
@@ -159,6 +162,7 @@ export default {
|
||||
});
|
||||
|
||||
const saving = ref(false);
|
||||
const loadError = ref('');
|
||||
|
||||
const features = [
|
||||
{ key: 'seo', name: 'SEO 建议', desc: '生成标题、描述、关键词建议', scene: '文章编辑' },
|
||||
@@ -167,22 +171,38 @@ export default {
|
||||
{ key: 'excerpt', name: '摘要建议', desc: '生成吸引人的列表摘要', scene: '文章编辑' },
|
||||
];
|
||||
|
||||
function hydrateFromStatus(data) {
|
||||
if (!data || typeof data !== 'object') return;
|
||||
status.value = {
|
||||
enabled: !!data.enabled,
|
||||
provider: data.provider || 'none',
|
||||
configured: !!data.configured,
|
||||
model: data.model || '',
|
||||
api_key: '',
|
||||
rate_limit: data.rate_limit || 10,
|
||||
available: !!data.available,
|
||||
};
|
||||
form.enabled = !!data.enabled;
|
||||
form.provider = data.provider || 'none';
|
||||
form.model = data.model || 'gpt-3.5-turbo';
|
||||
form.rate_limit = data.rate_limit || 10;
|
||||
if (data.api_base) {
|
||||
form.api_base = data.api_base;
|
||||
}
|
||||
if (typeof data.max_tokens === 'number' && data.max_tokens > 0) {
|
||||
form.max_tokens = data.max_tokens;
|
||||
}
|
||||
// 不回显 api_key
|
||||
}
|
||||
|
||||
async function loadStatus() {
|
||||
loadError.value = '';
|
||||
try {
|
||||
const res = await fetch(`${API}/status`, {
|
||||
headers: { 'X-WP-Nonce': window.aksrtAdmin?.nonce || '' },
|
||||
});
|
||||
const json = await res.json();
|
||||
if (json.success) {
|
||||
status.value = json.data;
|
||||
form.enabled = json.data.enabled || false;
|
||||
form.provider = json.data.provider || 'none';
|
||||
form.model = json.data.model || 'gpt-3.5-turbo';
|
||||
form.rate_limit = json.data.rate_limit || 10;
|
||||
// 不回显 api_key
|
||||
}
|
||||
const json = await api.getAiStatus();
|
||||
hydrateFromStatus(json.data || {});
|
||||
} catch (e) {
|
||||
console.error('Load AI status failed:', e);
|
||||
loadError.value = e?.message || '加载 AI 状态失败';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,27 +210,16 @@ export default {
|
||||
saving.value = true;
|
||||
try {
|
||||
const payload = { ...form };
|
||||
// 如果密钥为空且已配置,发送掩码
|
||||
// 如果密钥为空且已配置,发送掩码(后端应忽略 *** 保留原密钥)
|
||||
if (!payload.api_key && status.value.configured) {
|
||||
payload.api_key = '***';
|
||||
}
|
||||
const res = await fetch(`${API}/settings`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-WP-Nonce': window.aksrtAdmin?.nonce || '',
|
||||
},
|
||||
body: JSON.stringify(payload),
|
||||
});
|
||||
const json = await res.json();
|
||||
if (json.success) {
|
||||
alert('设置已保存');
|
||||
loadStatus();
|
||||
} else {
|
||||
alert(json.message || '保存失败');
|
||||
}
|
||||
const json = await api.updateAiSettings(payload);
|
||||
alert(json.message || '设置已保存');
|
||||
await loadStatus();
|
||||
} catch (e) {
|
||||
console.error('Save failed:', e);
|
||||
alert(e?.message || '保存失败');
|
||||
} finally {
|
||||
saving.value = false;
|
||||
}
|
||||
@@ -218,7 +227,7 @@ export default {
|
||||
|
||||
onMounted(() => loadStatus());
|
||||
|
||||
return { status, form, saving, features, saveSettings };
|
||||
return { status, form, saving, loadError, features, saveSettings };
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -290,6 +299,12 @@ export default {
|
||||
color: #888;
|
||||
margin-top: 4px;
|
||||
}
|
||||
.aksrt-ai__load-error {
|
||||
background: #fdecea;
|
||||
border-color: #f5c2c0;
|
||||
color: #842029;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.aksrt-ai__notice {
|
||||
background: #fff8e1;
|
||||
border-color: #ffe082;
|
||||
|
||||
Reference in New Issue
Block a user