69 lines
4.5 KiB
TypeScript
69 lines
4.5 KiB
TypeScript
import { useEffect, useState } from 'react';
|
||
import { getMindSearchConfig, updateMindSearchConfig } from '../../api/client';
|
||
import type { MindSearchConfig } from '../../types';
|
||
|
||
const initial: MindSearchConfig = {
|
||
enabled: false,
|
||
mode: 'off',
|
||
providers: { searxng: false, github: false, reader: false },
|
||
settings: { searxngEndpoint: '', maxResults: 10, timeoutMs: 8000, readerMaxChars: 12000 },
|
||
};
|
||
|
||
export function MindSearchPage() {
|
||
const [config, setConfig] = useState(initial);
|
||
const [meta, setMeta] = useState<{ source?: string; updatedAt?: number | null; updatedBy?: string | null }>({});
|
||
const [message, setMessage] = useState('加载配置中…');
|
||
|
||
useEffect(() => {
|
||
getMindSearchConfig()
|
||
.then((result) => {
|
||
setConfig({ ...initial, ...result.config, settings: { ...initial.settings, ...result.config.settings } });
|
||
setMeta(result);
|
||
setMessage('');
|
||
})
|
||
.catch((error) => setMessage(error instanceof Error ? error.message : '配置加载失败'));
|
||
}, []);
|
||
|
||
const save = async () => {
|
||
setMessage('保存中…');
|
||
try {
|
||
const result = await updateMindSearchConfig(config);
|
||
setConfig({ ...initial, ...result.config, settings: { ...initial.settings, ...result.config.settings } });
|
||
setMeta(result);
|
||
setMessage('已保存到数据库;新会话生效,旧会话不受影响');
|
||
} catch (error) {
|
||
setMessage(error instanceof Error ? error.message : '保存失败');
|
||
}
|
||
};
|
||
|
||
const toggleProvider = (key: keyof MindSearchConfig['providers']) =>
|
||
setConfig((current) => ({ ...current, providers: { ...current.providers, [key]: !current.providers[key] } }));
|
||
const updateSetting = (key: keyof MindSearchConfig['settings'], value: string | number) =>
|
||
setConfig((current) => ({ ...current, settings: { ...current.settings, [key]: value } }));
|
||
|
||
return (
|
||
<section className="admin-page">
|
||
<div className="admin-page-header"><div><h1>MindSearch</h1><p>可插拔外部搜索增强。关闭时完全保持原有搜索、记忆和上下文链路。</p></div></div>
|
||
<div className="admin-card">
|
||
<h2>总开关与模式</h2>
|
||
<label><input type="checkbox" checked={config.enabled} onChange={(event) => setConfig({ ...config, enabled: event.target.checked, mode: event.target.checked ? (config.mode === 'off' ? 'shadow' : config.mode) : 'off' })} /> 启用 MindSearch</label>
|
||
<label>运行模式 <select value={config.mode} onChange={(event) => setConfig({ ...config, mode: event.target.value as MindSearchConfig['mode'] })}><option value="off">关闭</option><option value="shadow">Shadow(只记录,不注入)</option><option value="assist">Assist(补充工具)</option></select></label>
|
||
<h2>Provider</h2>
|
||
<fieldset><legend>可用 Provider</legend>
|
||
<label><input type="checkbox" checked={config.providers.searxng} onChange={() => toggleProvider('searxng')} /> SearXNG Web/News</label>
|
||
<label><input type="checkbox" checked={config.providers.github} onChange={() => toggleProvider('github')} /> GitHub Code(Token 通过环境变量配置,不在页面回显)</label>
|
||
<label><input type="checkbox" checked={config.providers.reader} onChange={() => toggleProvider('reader')} /> Reader(阻止 localhost/内网地址)</label>
|
||
</fieldset>
|
||
<h2>运行参数</h2>
|
||
<label>SearXNG 地址 <input value={config.settings.searxngEndpoint} placeholder="http://127.0.0.1:8080/search" onChange={(event) => updateSetting('searxngEndpoint', event.target.value)} /></label>
|
||
<label>最大结果数 <input type="number" min={1} max={20} value={config.settings.maxResults} onChange={(event) => updateSetting('maxResults', Number(event.target.value))} /></label>
|
||
<label>Provider 超时(毫秒) <input type="number" min={1000} max={30000} value={config.settings.timeoutMs} onChange={(event) => updateSetting('timeoutMs', Number(event.target.value))} /></label>
|
||
<label>Reader 最大字符数 <input type="number" min={1000} max={50000} value={config.settings.readerMaxChars} onChange={(event) => updateSetting('readerMaxChars', Number(event.target.value))} /></label>
|
||
<button type="button" onClick={save}>保存配置</button>
|
||
{message && <p role="status">{message}</p>}
|
||
<p>配置来源:{meta.source === 'admin' ? '数据库' : '环境变量默认值'};最后更新:{meta.updatedAt ? new Date(meta.updatedAt).toLocaleString() : '尚未保存'}{meta.updatedBy ? `;操作人:${meta.updatedBy}` : ''}</p>
|
||
</div>
|
||
</section>
|
||
);
|
||
}
|