v1.4.0: 通知日志分页+筛选 / 仪表盘TOP10+更多链接 / 数据源走web-capture-api抓取(获取方式与参数可编辑) / 系统错误邮件通知(频率+静默时段)

This commit is contained in:
2026-08-30 19:03:50 +08:00
parent de08530958
commit e8c8889460
14 changed files with 812 additions and 57 deletions
+96 -2
View File
@@ -98,7 +98,72 @@
</section>
<section class="card">
<h3>🤖 大模型接口
<h3>🌐 网页提取服务(web-capture-api
<span class="muted small">(数据源真实网页默认通过该服务抓取,每源可单独选获取方式与参数)</span>
</h3>
<div class="setting-row">
<label>启用 web-capture-api 抓取</label>
<input type="checkbox" id="w_enabled" {{ 'checked' if auto.webcapture.enabled }}>
</div>
<div class="setting-row">
<label>API 地址</label>
<input id="w_api_url" value="{{ auto.webcapture.api_url }}" placeholder="http://121.40.164.32:16025">
</div>
<div class="setting-row">
<label>抓取超时(秒)</label>
<input type="number" id="w_timeout" value="{{ auto.webcapture.timeout }}">
</div>
<div class="setting-row">
<label>连通性测试</label>
<span style="display:flex;gap:8px;align-items:center;">
<button class="btn mini" type="button" onclick="testWebcapture()">🧪 测试接口</button>
<span id="wc_test_result" class="muted small"></span>
</span>
</div>
</section>
<section class="card">
<h3>⚠️ 系统错误邮件通知
<span class="muted small">(采集/分析/通知/汇总/系统异常自动邮件通知,可设通知频率与静默时段)</span>
</h3>
<div class="setting-row">
<label>启用错误邮件通知</label>
<input type="checkbox" id="e_enabled" {{ 'checked' if auto.errnotify.enabled }}>
</div>
<div class="setting-row">
<label>通知方式</label>
<select id="e_mode">
<option value="immediate" {{ 'selected' if auto.errnotify.mode=='immediate' }}>⚡ 立即(出现即尝试发,仍受冷却限制)</option>
<option value="cooldown" {{ 'selected' if auto.errnotify.mode=='cooldown' }}>⏱ 按冷却聚合(到冷却时间集中发一封)</option>
<option value="off" {{ 'selected' if auto.errnotify.mode=='off' }}>🚫 关闭(仅记录不发送)</option>
</select>
</div>
<div class="setting-row">
<label>通知频率(冷却分钟)<span class="muted small">— 两次错误邮件最小间隔</span></label>
<input type="number" id="e_cooldown_min" value="{{ auto.errnotify.cooldown_min }}">
</div>
<div class="setting-row">
<label>单封最多错误条数</label>
<input type="number" id="e_max_items" value="{{ auto.errnotify.max_items }}">
</div>
<div class="setting-row">
<label>启用静默时段</label>
<input type="checkbox" id="e_quiet_enabled" {{ 'checked' if auto.errnotify.quiet_enabled }}>
</div>
<div style="margin-top:10px;">
<label style="font-size:12px;color:#6b7280;">静默时段(每行一段 <b>HH:MM-HH:MM</b>,支持跨午夜、多段;静默期不发送,结束后自动补发)</label>
<textarea id="e_quiet_periods" rows="3" style="margin-top:4px;">{{ '\n'.join(auto.errnotify.quiet_periods) }}</textarea>
</div>
<div class="setting-row">
<label>测试</label>
<span style="display:flex;gap:8px;align-items:center;">
<button class="btn mini" type="button" onclick="testErrorMail()">📨 发一封测试错误通知</button>
<span id="e_test_result" class="muted small"></span>
</span>
</div>
</section>
<section class="card">
<span class="muted small">(切换后即时生效,智能分析将使用当前接口;失败自动切换下一个可用接口)</span>
</h3>
<div class="form-row" style="margin-bottom:12px;">
@@ -170,10 +235,39 @@ async function saveAll(){
custom_summary_window_hours: parseInt(G('c_custom_summary_window_hours'))||24,
custom_max_summary_items: parseInt(G('c_custom_max_summary_items'))||20,
};
const r = await API.json('/api/settings', { ...auto, mail, custom });
const webcapture = {
enabled: document.getElementById('w_enabled').checked ? 1 : 0,
api_url: G('w_api_url').trim() || 'http://121.40.164.32:16025',
timeout: parseInt(G('w_timeout')) || 60,
};
const errnotify = {
enabled: document.getElementById('e_enabled').checked ? 1 : 0,
mode: G('e_mode'),
cooldown_min: parseInt(G('e_cooldown_min')) || 60,
max_items: parseInt(G('e_max_items')) || 10,
quiet_enabled: document.getElementById('e_quiet_enabled').checked ? 1 : 0,
quiet_periods: G('e_quiet_periods').split(/[\n,;,;]/).map(s=>s.trim()).filter(Boolean),
};
const r = await API.json('/api/settings', { ...auto, mail, custom, webcapture, errnotify });
toast(r.ok ? '✅ 设置已保存' : '❌ 保存失败', r.ok);
}
async function testWebcapture(){
const el = document.getElementById('wc_test_result');
el.textContent = '⏳ 测试中...';
const r = await API.json('/api/actions', {action:'test_webcapture'});
if (r.ok) el.textContent = `${r.api_url} 正常,抓取到:${r.title}${(r.text||'').trim()}`;
else el.textContent = '❌ ' + (r.error || '测试失败');
}
async function testErrorMail(){
const el = document.getElementById('e_test_result');
el.textContent = '⏳ 发送中...';
const r = await API.json('/api/actions', {action:'test_error_mail'});
if (r.ok) el.textContent = '✅ 测试错误通知邮件已发送';
else el.textContent = '❌ ' + (r.error || '发送失败');
}
async function addProvider(){
const r = await API.json('/api/llm', {
action:'add',