Add SSE second-level dashboard stream and 2s client heartbeat.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}总览 · {{ app_name }}{% endblock %}
|
||||
{% block content %}
|
||||
<h2 style="margin:0 0 14px">总览</h2>
|
||||
<h2 style="margin:0 0 14px">总览 <span class="muted" style="font-size:13px;font-weight:400" id="live-hint">· 秒级推送</span></h2>
|
||||
<div class="grid cols-3">
|
||||
<div class="card"><div class="muted">账户</div><div class="stat">{{ accounts|length }}</div></div>
|
||||
<div class="card">
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
<div class="grid cols-2" style="margin-top:14px">
|
||||
<div class="card">
|
||||
<h3>在线机器 <span class="muted" style="font-weight:400;font-size:12px">· 约 5 秒刷新</span></h3>
|
||||
<h3>在线机器</h3>
|
||||
<table>
|
||||
<thead><tr><th>机器</th><th>状态</th><th>最近心跳</th></tr></thead>
|
||||
<tbody id="online-machines-body">
|
||||
@@ -24,7 +24,7 @@
|
||||
<td class="muted">{{ m.last_seen_fmt }}</td>
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr><td colspan="3" class="muted">当前无在线机器(客户端运行中会每分钟心跳)</td></tr>
|
||||
<tr><td colspan="3" class="muted">当前无在线机器(客户端约每 2 秒心跳)</td></tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -41,7 +41,7 @@
|
||||
</div>
|
||||
|
||||
<div class="card" style="margin-top:14px">
|
||||
<h3>机器实时交易记录 <span class="muted" style="font-weight:400;font-size:12px">· 自动刷新</span></h3>
|
||||
<h3>机器实时交易记录</h3>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
@@ -74,8 +74,10 @@
|
||||
{% block scripts %}
|
||||
<script>
|
||||
(function () {
|
||||
var streamUrl = {{ url_for('dashboard_stream')|tojson }};
|
||||
var liveUrl = {{ url_for('dashboard_live')|tojson }};
|
||||
var detailBase = {{ url_for('record_detail', tid=0)|tojson }}.replace(/0$/, '');
|
||||
var hint = document.getElementById('live-hint');
|
||||
|
||||
function esc(s) {
|
||||
return String(s == null ? '' : s)
|
||||
@@ -87,7 +89,7 @@
|
||||
var body = document.getElementById('online-machines-body');
|
||||
if (!body) return;
|
||||
if (!list.length) {
|
||||
body.innerHTML = '<tr><td colspan="3" class="muted">当前无在线机器(客户端运行中会每分钟心跳)</td></tr>';
|
||||
body.innerHTML = '<tr><td colspan="3" class="muted">当前无在线机器(客户端约每 2 秒心跳)</td></tr>';
|
||||
return;
|
||||
}
|
||||
body.innerHTML = list.map(function (m) {
|
||||
@@ -129,19 +131,36 @@
|
||||
}).join('');
|
||||
}
|
||||
|
||||
function tick() {
|
||||
fetch(liveUrl, { credentials: 'same-origin' })
|
||||
.then(function (r) { return r.json(); })
|
||||
.then(function (data) {
|
||||
if (!data || !data.ok) return;
|
||||
document.getElementById('stat-online').textContent = String(data.online_count);
|
||||
renderMachines(data.online_machines || []);
|
||||
renderTrades(data.trades || []);
|
||||
})
|
||||
.catch(function () {});
|
||||
function apply(data) {
|
||||
if (!data || !data.ok) return;
|
||||
document.getElementById('stat-online').textContent = String(data.online_count);
|
||||
renderMachines(data.online_machines || []);
|
||||
renderTrades(data.trades || []);
|
||||
}
|
||||
|
||||
setInterval(tick, 5000);
|
||||
function startPollFallback() {
|
||||
if (hint) hint.textContent = '· 轮询 1s(SSE 不可用)';
|
||||
setInterval(function () {
|
||||
fetch(liveUrl, { credentials: 'same-origin' })
|
||||
.then(function (r) { return r.json(); })
|
||||
.then(apply)
|
||||
.catch(function () {});
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
if (typeof EventSource === 'undefined') {
|
||||
startPollFallback();
|
||||
return;
|
||||
}
|
||||
|
||||
var es = new EventSource(streamUrl);
|
||||
es.onmessage = function (ev) {
|
||||
try { apply(JSON.parse(ev.data)); } catch (e) {}
|
||||
};
|
||||
es.onerror = function () {
|
||||
es.close();
|
||||
startPollFallback();
|
||||
};
|
||||
})();
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
Reference in New Issue
Block a user