2026-06-18 22:48:16 +08:00
|
|
|
<!DOCTYPE html>
|
|
|
|
|
<html lang="zh-CN" class="h-full">
|
|
|
|
|
{{template "head" .}}
|
|
|
|
|
<body class="h-full bg-gray-50 text-gray-900">
|
|
|
|
|
<div class="min-h-full flex flex-col">
|
|
|
|
|
{{template "nav" .}}
|
|
|
|
|
<main class="flex-1">
|
|
|
|
|
<div class="max-w-2xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
|
|
|
|
<h1 class="text-2xl font-bold mb-6">设置</h1>
|
|
|
|
|
<form id="settings-form" class="space-y-6">
|
|
|
|
|
<!-- LLM Settings -->
|
|
|
|
|
<div class="bg-white rounded-lg shadow-sm border border-gray-200 p-6">
|
|
|
|
|
<h2 class="text-lg font-semibold mb-4">LLM 配置</h2>
|
|
|
|
|
<div class="space-y-4">
|
|
|
|
|
<div>
|
|
|
|
|
<label class="block text-sm font-medium text-gray-700 mb-1">API Endpoint</label>
|
|
|
|
|
<input type="text" name="llm.endpoint" value="{{index .Settings "llm.endpoint"}}"
|
|
|
|
|
class="w-full rounded-md border border-gray-300 px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-indigo-500">
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label class="block text-sm font-medium text-gray-700 mb-1">API Key</label>
|
|
|
|
|
<input type="password" name="llm.api_key" value="{{index .Settings "llm.api_key"}}"
|
|
|
|
|
class="w-full rounded-md border border-gray-300 px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-indigo-500" placeholder="sk-...">
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label class="block text-sm font-medium text-gray-700 mb-1">Model</label>
|
|
|
|
|
<input type="text" name="llm.model" value="{{index .Settings "llm.model"}}"
|
|
|
|
|
class="w-full rounded-md border border-gray-300 px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-indigo-500">
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<!-- Review Settings -->
|
|
|
|
|
<div class="bg-white rounded-lg shadow-sm border border-gray-200 p-6">
|
|
|
|
|
<h2 class="text-lg font-semibold mb-4">审查配置</h2>
|
2026-06-19 23:40:13 +08:00
|
|
|
<div class="space-y-4">
|
|
|
|
|
<div>
|
|
|
|
|
<label class="block text-sm font-medium text-gray-700 mb-1">Top-N 文件数</label>
|
|
|
|
|
<p class="text-xs text-gray-400 mb-2">大 diff 时优先分析变更最大的 N 个文件,设为 0 分析全部</p>
|
|
|
|
|
<input type="number" name="review.top_n" value="{{index .Settings "review.top_n"}}" min="0"
|
|
|
|
|
class="w-full rounded-md border border-gray-300 px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-indigo-500">
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label class="block text-sm font-medium text-gray-700 mb-1">并发分析数</label>
|
|
|
|
|
<p class="text-xs text-gray-400 mb-2">同时向 LLM 发送的文件评审请求数,越大越快但占用更多 API 额度</p>
|
|
|
|
|
<input type="number" name="review.concurrency" value="{{index .Settings "review.concurrency"}}" min="1" max="20"
|
|
|
|
|
class="w-full rounded-md border border-gray-300 px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-indigo-500">
|
|
|
|
|
</div>
|
2026-06-18 22:48:16 +08:00
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<!-- Cache Settings -->
|
|
|
|
|
<div class="bg-white rounded-lg shadow-sm border border-gray-200 p-6">
|
|
|
|
|
<h2 class="text-lg font-semibold mb-4">缓存配置</h2>
|
|
|
|
|
<div class="space-y-4">
|
|
|
|
|
<div>
|
|
|
|
|
<label class="block text-sm font-medium text-gray-700 mb-1">缓存过期天数</label>
|
|
|
|
|
<input type="number" name="cache.max_age_days" value="{{index .Settings "cache.max_age_days"}}" min="1"
|
|
|
|
|
class="w-full rounded-md border border-gray-300 px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-indigo-500">
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label class="block text-sm font-medium text-gray-700 mb-1">最大缓存大小 (MB)</label>
|
|
|
|
|
<input type="number" name="cache.max_size_mb" value="{{index .Settings "cache.max_size_mb"}}" min="100"
|
|
|
|
|
class="w-full rounded-md border border-gray-300 px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-indigo-500">
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<!-- Submit -->
|
|
|
|
|
<div class="flex items-center gap-3">
|
|
|
|
|
<button type="submit"
|
|
|
|
|
class="bg-indigo-600 text-white px-5 py-2 rounded-md text-sm font-medium hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2">
|
|
|
|
|
保存设置
|
|
|
|
|
</button>
|
|
|
|
|
<span id="save-status" class="text-sm hidden"></span>
|
|
|
|
|
</div>
|
|
|
|
|
</form>
|
|
|
|
|
</div>
|
|
|
|
|
</main>
|
|
|
|
|
{{template "footer" .}}
|
|
|
|
|
</div>
|
|
|
|
|
<script>
|
|
|
|
|
document.getElementById('settings-form').addEventListener('submit', async function(e) {
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
const form = new FormData(this);
|
|
|
|
|
const data = {};
|
|
|
|
|
for (const [key, val] of form.entries()) data[key] = val;
|
|
|
|
|
const status = document.getElementById('save-status');
|
|
|
|
|
try {
|
|
|
|
|
const resp = await fetch('/api/settings', {
|
|
|
|
|
method: 'PUT',
|
|
|
|
|
headers: {'Content-Type': 'application/json'},
|
2026-06-20 21:57:46 +08:00
|
|
|
credentials: 'same-origin',
|
2026-06-18 22:48:16 +08:00
|
|
|
body: JSON.stringify(data)
|
|
|
|
|
});
|
|
|
|
|
if (!resp.ok) throw new Error('保存失败');
|
|
|
|
|
status.textContent = '✓ 已保存';
|
|
|
|
|
status.className = 'text-sm text-green-600';
|
|
|
|
|
} catch (err) {
|
|
|
|
|
status.textContent = '✗ ' + err.message;
|
|
|
|
|
status.className = 'text-sm text-red-600';
|
|
|
|
|
}
|
|
|
|
|
status.classList.remove('hidden');
|
|
|
|
|
setTimeout(() => status.classList.add('hidden'), 3000);
|
|
|
|
|
});
|
|
|
|
|
</script>
|
|
|
|
|
</body>
|
|
|
|
|
</html>
|