diff --git a/server.py b/server.py index 904be14e..83bc5684 100644 --- a/server.py +++ b/server.py @@ -2472,6 +2472,40 @@ def api_broadcast_archive(): return jsonify({"ok": True}) + +@app.route("/api/broadcast/toggle_delivery", methods=["POST"]) +def api_broadcast_toggle_delivery(): + """切换 cron job 的消息通道(broadcast/xmpp/both) + payload: {"name": "job名称"} 或 {"script": "脚本名"} + """ + import json as _json + data = request.get_json(force=True) or {} + jobs_path = "/home/hmo/.hermes/profiles/position-analyst/cron/jobs.json" + with open(jobs_path, encoding="utf-8") as f: + jobs = _json.load(f) + update = None + for j in jobs.get("jobs", []): + if not isinstance(j, dict): + continue + match = False + if data.get("name") and j.get("name") == data.get("name"): + match = True + elif data.get("script") and str(j.get("script","")) == data.get("script"): + match = True + if match: + # 循环切换 + cur = j.get("delivery", "broadcast") + nxt = {"broadcast":"xmpp", "xmpp":"both", "both":"broadcast"}.get(cur, "broadcast") + j["delivery"] = nxt + update = {"name": j.get("name"), "script": j.get("script"), "delivery": nxt} + break + if update: + with open(jobs_path, "w", encoding="utf-8") as f: + _json.dump(jobs, f, ensure_ascii=False, indent=2) + return jsonify({"ok": True, "job": update}) + return jsonify({"ok": False, "error": "job not found"}) + + if __name__ == "__main__": port = int(os.environ.get("PORT", 8899)) print(f"🚀 MoFin Dashboard → http://0.0.0.0:{port}") diff --git a/static/mofin_health.html b/static/mofin_health.html index 84923ce8..dbbebd86 100644 --- a/static/mofin_health.html +++ b/static/mofin_health.html @@ -390,7 +390,7 @@ function renderPipelineTable(pipelines) { const layerColor = {collect:'#3fb950', process:'#d29922', use:'#58a6ff', monitor:'#bc8cff'}[layer]; html += `
`; html += `${title} ${desc} · ${group.length}任务 ✅${ok} ❌${err}
`; - html += ''; + html += '
名称来源脚本/LLM类型调度状态最后运行最后十次
'; group.forEach((p) => { const tagCls = p.status==='ok'?'ok':p.status==='error'?'error':'warn'; const statusDisplay = p.last_run ? p.status : '待首次运行'; @@ -408,6 +408,9 @@ function renderPipelineTable(pipelines) { html += ``; html += ``; html += ``; + const delColor = p.delivery==='xmpp'?'#3fb950':p.delivery==='both'?'#d29922':'#8b949e'; + const delLabel = p.delivery==='xmpp'?'📣 xmpp':p.delivery==='both'?'🔄 both':'📡 broadcast'; + html += ``; html += ``; html += ``; html += ``; @@ -581,5 +584,24 @@ function viewReportDetail(reportId) { list.innerHTML = '
加载失败: ' + e.message + '
'; }); } + +function cycDelivery(jobName) { + var uname = decodeURIComponent(jobName); + var row = document.querySelector('[data-name="'+uname.toLowerCase()+'"]'); + if (!row) { alert('job not found'); return; } + var cur = row.querySelector('.pipe-row span') ? row.querySelector('.pipe-row span').textContent : ''; + // 找到该行的delivery span + var spans = row.querySelectorAll('td:nth-child(6) span'); + var curDel = spans[0] ? spans[0].textContent.replace(/[^-]/g,'').trim() : ''; + // 简化:点击后调API toggle + fetch('/api/broadcast/toggle_delivery', { + method:'POST', + headers:{'Content-Type':'application/json'}, + body: JSON.stringify({name: uname}) + }).then(r=>r.json()).then(d=>{ + location.reload(); + }).catch(e=>alert('切换失败:'+e.message)); +} +
名称来源脚本/LLM类型调度消息通道状态最后运行最后十次
${p.script||'LLM'}${p.type||'cron'}${p.schedule||'-'}${delLabel}${statusDisplay}${p.last_run||'-'}