diff --git a/gateway/scripts/dashboard.py b/gateway/scripts/dashboard.py index 89639a4..b3c57d0 100644 --- a/gateway/scripts/dashboard.py +++ b/gateway/scripts/dashboard.py @@ -1997,6 +1997,28 @@ def api_wechat_status(): return jsonify(result) +@app.route("/api/wechat/trigger-login", methods=["POST"]) +def api_wechat_trigger_login(): + """触发微信 bot 登录(获取二维码)——容器 DISABLE_AUTO_LOGIN=true 时需主动 POST /api/bot/login。 + + 返回 {ok, login_url} 或 {ok: false, error}。前端拿到 login_url 后轮询 /api/wechat/status 拿 qr_url。 + """ + import urllib.request as _ur + try: + req = _ur.Request( + "http://127.0.0.1:3001/api/bot/login", + data=b"", + headers={"Content-Type": "application/json"}, + method="POST", + ) + with _ur.urlopen(req, timeout=10) as resp: + data = json.loads(resp.read().decode("utf-8", errors="replace")) + if data.get("success"): + return jsonify({"ok": True, "login_url": data.get("loginUrl", "/login")}) + return jsonify({"ok": False, "error": data.get("error", "unknown")}) + except Exception as e: + return jsonify({"ok": False, "error": str(e)}) + @app.route("/api/platform") @app.route("/api/health") def api_health(): diff --git a/gateway/scripts/templates/dashboard.html b/gateway/scripts/templates/dashboard.html index eb1de4f..2f2020f 100644 --- a/gateway/scripts/templates/dashboard.html +++ b/gateway/scripts/templates/dashboard.html @@ -323,24 +323,35 @@ async function fI(){ if(s.status==='running'){dc='ok';dt=s.message?' '+esc(s.message)+'':'';} else if(s.status==='logged_out'){dc='ok';ds='background:var(--yellow)';dt=' '+esc(s.message||'\u9700\u626b\u7801')+'';} h+='
\u83ab\u8377\u5fae\u4fe1\u9700\u8981\u626b\u7801\u767b\u5f55
'; - h+=''+esc(s.message||'')+'\u3002\u7528\u5fae\u4fe1\u626b\u63cf\u5de6\u4fa7\u4e8c\u7ef4\u7801\u91cd\u65b0\u767b\u5f55\u3002
'; - h+='\u94fe\u63a5: '+esc(s.qr_url||'')+'
\u83ab\u8377\u5fae\u4fe1\u9700\u8981\u626b\u7801\u767b\u5f55
'; + h+=''+esc(s.message||'')+'\u3002\u7528\u5fae\u4fe1\u626b\u63cf\u5de6\u4fa7\u4e8c\u7ef4\u7801\u91cd\u65b0\u767b\u5f55\u3002
'; + h+='\u94fe\u63a5: '+esc(s.qr_url||'')+'
\u83ab\u8377\u5fae\u4fe1\u5df2\u6389\u7ebf
'; + h+=''+esc(s.message||'')+'\u3002\u70b9\u51fb\u5de6\u4fa7\u6309\u94ae\u89e6\u53d1\u767b\u5f55\uff0c\u751f\u6210\u4e8c\u7ef4\u7801\u540e\u7528\u5fae\u4fe1\u626b\u7801\u3002
'; + h+=''+inMd(block)+'
';} return h; function inMd(t){return t.replace(/\*\*([^*]+)\*\*/g,'$1').replace(/\*([^*]+)\*/g,'$1').replace(/`([^`]+)`/g,'$1').replace(/\[([^\]]+)\]\(([^)]+)\)/g,'$1')}}
init();loadAll();setInterval(loadAll,10000);setInterval(fK,15000);
+
+/* --- WeChat QR: 获取二维码(触发容器 /api/bot/login) + 刷新 --- */
+async function getWechatQR(){
+ var st=document.getElementById('wechat-qr-get-status');
+ var btn=document.getElementById('wechat-qr-get');
+ if(st)st.innerHTML='\u6b63\u5728\u83b7\u53d6\u4e8c\u7ef4\u7801...';
+ if(btn)btn.disabled=true;
+ try{
+ var r=await fetch('/api/wechat/trigger-login',{method:'POST'});
+ var d=await r.json();
+ if(d.ok){
+ if(st)st.innerHTML='\u5df2\u89e6\u53d1\uff0c\u6b63\u5728\u751f\u6210\u4e8c\u7ef4\u7801...';
+ // 触发后轮询 status 拿 qr_url(容器生成二维码需要几秒)
+ for(var i=0;i<10;i++){
+ await new Promise(res=>setTimeout(res,2000));
+ try{
+ var rs=await fetch('/api/wechat/status'),sd=await rs.json();
+ if(sd.qr_url){location.reload();return;}
+ }catch(e){}
+ }
+ if(st)st.innerHTML='\u4e8c\u7ef4\u7801\u672a\u751f\u6210\uff0c\u8bf7\u91cd\u8bd5';
+ if(btn)btn.disabled=false;
+ }else{
+ if(st)st.innerHTML='\u83b7\u53d6\u5931\u8d25: '+esc(d.error||'')+'';
+ if(btn)btn.disabled=false;
+ }
+ }catch(e){
+ if(st)st.innerHTML='\u83b7\u53d6\u5931\u8d25: '+esc(e.message||'')+'';
+ if(btn)btn.disabled=false;
+ }
+}
+async function refreshWechatQR(){
+ try{
+ await fetch('/api/wechat/trigger-login',{method:'POST'});
+ // 刷新后等几秒重新拉状态
+ setTimeout(function(){location.reload();},3000);
+ }catch(e){}
+}