feat(rdp): robust RDP enable — step-machine + deep health check + self-heal + end-to-end verify + live progress & structured log
This commit is contained in:
@@ -1376,6 +1376,27 @@ def api_rdp_toggle():
|
||||
return jsonify({"ok": False, "error": str(e)})
|
||||
|
||||
|
||||
@app.route("/api/rdp/progress")
|
||||
def api_rdp_progress():
|
||||
"""RDP enable 实时进度 — proxy to xmpp_bot on Windows (polls rdp_progress.json)."""
|
||||
try:
|
||||
data = _bridge_post("/rdp", {"action": "progress"}, timeout=8)
|
||||
return jsonify(data)
|
||||
except Exception as e:
|
||||
return jsonify({"ok": False, "state": "unknown", "message": "progress 查询失败", "error": str(e), "steps": []})
|
||||
|
||||
|
||||
@app.route("/api/rdp/enable_log")
|
||||
def api_rdp_enable_log():
|
||||
"""RDP enable 结构化日志 — proxy to xmpp_bot on Windows (for post-mortem debug)."""
|
||||
try:
|
||||
n = request.args.get("lines", 80, type=int)
|
||||
data = _bridge_post("/rdp", {"action": "enable_log", "lines": n}, timeout=8)
|
||||
return jsonify(data)
|
||||
except Exception as e:
|
||||
return jsonify({"ok": False, "error": str(e), "lines": []})
|
||||
|
||||
|
||||
# ════════════════════════════════════════════════════════════
|
||||
# OpenCode Go Usage Monitor — 4个账号用量配额监控 (246 本地采集)
|
||||
# See gateway/scripts/specs/usage_monitor.json
|
||||
|
||||
@@ -384,10 +384,11 @@ async function fI(){
|
||||
var rd=document.getElementById('rdp-section');
|
||||
if(!rd){
|
||||
rd=document.createElement('div');rd.id='rdp-section';rd.className='ps';rd.style.marginTop='16px';
|
||||
rd.innerHTML='<h2>RDP Remote Desktop<span class="help-btn" onclick="showModuleHelp(\'rdp\',\'human\')" title="使用说明">?</span><span class="help-btn ai" onclick="showModuleHelp(\'rdp\',\'ai\')" title="AI Spec">§</span></h2><div class=psv><div class=i><span class="d" id=rdp-st></span>RDP <span id=rdp-info></span></div><div class=i><span class="d" id=rdp-tun></span>Tunnel</div></div><div style="margin-top:8px;display:flex;gap:8px;align-items:center"><button class="btn s" id=btn-rdp-on>Enable</button><button class="btn x" id=btn-rdp-off>Disable</button><span id=rdp-status-txt style="font-size:12px;color:var(--dim)">-</span></div>';
|
||||
rd.innerHTML='<h2>RDP Remote Desktop<span class="help-btn" onclick="showModuleHelp(\'rdp\',\'human\')" title="使用说明">?</span><span class="help-btn ai" onclick="showModuleHelp(\'rdp\',\'ai\')" title="AI Spec">§</span></h2><div class=psv><div class=i><span class="d" id=rdp-st></span>RDP <span id=rdp-info></span></div><div class=i><span class="d" id=rdp-tun></span>Tunnel</div></div><div style="margin-top:8px;display:flex;gap:8px;align-items:center"><button class="btn s" id=btn-rdp-on>Enable</button><button class="btn x" id=btn-rdp-off>Disable</button><span id=rdp-status-txt style="font-size:12px;color:var(--dim)">-</span><a href="javascript:void(0)" id="rdp-log-link" style="font-size:11px;color:var(--dim);margin-left:auto">日志</a></div><div id="rdp-progress" style="margin-top:10px;display:none;font-size:12px"><div id="rdp-prog-msg" style="color:var(--accent);margin-bottom:6px;font-weight:600"></div><div id="rdp-prog-steps" style="line-height:1.7"></div></div><div id="rdp-logbox" style="margin-top:8px;display:none;font-size:11px;color:var(--dim);background:rgba(0,0,0,.2);padding:8px;border-radius:6px;max-height:180px;overflow:auto;white-space:pre-wrap;font-family:monospace"></div>';
|
||||
ci.appendChild(rd);
|
||||
document.getElementById('btn-rdp-on').onclick=function(){fetch('/api/rdp/toggle',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({action:'start'})}).then(function(){fI()}).catch(function(){toast('RDP Fail','err')})};
|
||||
document.getElementById('btn-rdp-on').onclick=function(){fetch('/api/rdp/toggle',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({action:'start'})}).then(function(){startRdpProgress()}).catch(function(){toast('RDP Fail','err')})};
|
||||
document.getElementById('btn-rdp-off').onclick=function(){fetch('/api/rdp/toggle',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({action:'stop'})}).then(function(){fI()}).catch(function(){toast('RDP Fail','err')})};
|
||||
document.getElementById('rdp-log-link').onclick=function(){var b=document.getElementById('rdp-logbox');if(b.style.display==='none'||!b.style.display){b.style.display='block';loadRdpLog()}else{b.style.display='none'}};
|
||||
}
|
||||
try{var e4=await fetch('/api/rdp').then(function(r){return r.json()});
|
||||
document.getElementById('rdp-st').className='d '+(e4.rdp_enabled?'ok':'stopped');
|
||||
@@ -399,6 +400,42 @@ async function fI(){
|
||||
document.getElementById('rdp-status-txt').textContent=fOn?'Connected':e4.rdp_enabled?'Tunnel pending':'Disconnected';
|
||||
}catch(e4){}
|
||||
|
||||
/* --- RDP enable 实时进度轮询 + 结构化日志 --- */
|
||||
var _rdpProgTimer=null;
|
||||
function startRdpProgress(){
|
||||
var box=document.getElementById('rdp-progress'); if(box) box.style.display='block';
|
||||
if(_rdpProgTimer) clearInterval(_rdpProgTimer);
|
||||
pollRdpProgress();
|
||||
_rdpProgTimer=setInterval(pollRdpProgress,1500);
|
||||
}
|
||||
async function pollRdpProgress(){
|
||||
try{
|
||||
var d=await fetch('/api/rdp/progress').then(function(r){return r.json()});
|
||||
var msg=document.getElementById('rdp-prog-msg');
|
||||
if(msg) msg.textContent=(d.state==='running'?'⏳ ':'')+(d.message||'');
|
||||
var st=document.getElementById('rdp-prog-steps');
|
||||
if(st && d.steps){
|
||||
st.innerHTML=d.steps.map(function(s){
|
||||
var icon=s.status==='ok'?'✓':(s.status==='fail'?'✗':(s.status==='warn'?'⚠':(s.status==='skip'?'—':'…')));
|
||||
var color=s.status==='ok'?'#4caf50':(s.status==='fail'?'#f44336':(s.status==='warn'?'#ff9800':'#9aa'));
|
||||
return '<div style="color:'+color+'">'+icon+' '+s.name+' <span style="color:#778;font-size:11px">'+(s.detail||'')+'</span></div>';
|
||||
}).join('');
|
||||
}
|
||||
if(d.state==='done'||d.state==='failed'){
|
||||
if(_rdpProgTimer){clearInterval(_rdpProgTimer);_rdpProgTimer=null;}
|
||||
if(msg) msg.textContent=(d.state==='done'?'✓ ':'✗ ')+(d.message||'');
|
||||
fI();
|
||||
}
|
||||
}catch(e){}
|
||||
}
|
||||
async function loadRdpLog(){
|
||||
try{
|
||||
var d=await fetch('/api/rdp/enable_log?lines=80').then(function(r){return r.json()});
|
||||
var b=document.getElementById('rdp-logbox');
|
||||
if(b) b.textContent=(d.lines&&d.lines.length)?d.lines.join('\n'):'(暂无日志)';
|
||||
}catch(e){var b=document.getElementById('rdp-logbox');if(b)b.textContent='日志加载失败';}
|
||||
}
|
||||
|
||||
/* --- OpenCode Go Usage section: create once, update state each cycle (no flicker) --- */
|
||||
var us=document.getElementById('usage-section');
|
||||
if(!us){
|
||||
|
||||
Reference in New Issue
Block a user