diff --git a/gateway/scripts/dashboard.py b/gateway/scripts/dashboard.py index fb02e75..90df577 100644 --- a/gateway/scripts/dashboard.py +++ b/gateway/scripts/dashboard.py @@ -613,7 +613,7 @@ def _augment_wechat_status(entry): r = subprocess.run(["tail", "-200", webhook_log], capture_output=True, text=True, timeout=5) lines = r.stdout.strip().split("\n") if r.stdout else [] - last_ok, last_err, last_login = None, None, None + last_ok, last_err, last_login, last_logout = None, None, None, None for line in reversed(lines): ts_match = re.match(r"^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})", line) ts = ts_match.group(1) if ts_match else None @@ -621,17 +621,32 @@ def _augment_wechat_status(entry): last_ok = ts elif "WeChat send error" in line and ts and not last_err: last_err = ts - if "system_event_login" in line and ts and not last_login: - last_login = ts - if last_ok and last_err and last_login: + if last_ok and last_err: break + # Login/logout events: search full log (not just tail -200) + try: + for ev, key in (("system_event_logout", "last_logout"), ("system_event_login", "last_login")): + er = subprocess.run(["grep", ev, webhook_log], + capture_output=True, text=True, timeout=5) + ev_lines = [l for l in er.stdout.strip().split("\n") if l.strip()] + if ev_lines: + m = re.match(r"^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})", ev_lines[-1]) + if m: + if key == "last_login": + last_login = m.group(1) + else: + last_logout = m.group(1) + except Exception: + pass - ok_dt = err_dt = None + ok_dt = err_dt = logout_dt = None if last_ok: ok_dt = datetime.strptime(last_ok, "%Y-%m-%d %H:%M:%S") entry["session_age_hours"] = round((now - ok_dt).total_seconds() / 3600, 1) if last_err: err_dt = datetime.strptime(last_err, "%Y-%m-%d %H:%M:%S") + if last_logout: + logout_dt = datetime.strptime(last_logout, "%Y-%m-%d %H:%M:%S") # Check docker logs for recent login (catches fresh logins before webhook sees them) # Check webhook log for login event @@ -642,7 +657,18 @@ def _augment_wechat_status(entry): except ValueError: pass dlogin_dt = _docker_login_check() - if err_dt and login_dt and login_dt > err_dt: + # Logout is the most authoritative signal: if logout happened after the last login, session is dead + if logout_dt and login_dt and logout_dt > login_dt: + entry["login_ok"] = False + h = round((now - logout_dt).total_seconds() / 3600, 1) + entry["message"] = "已掉线" if h < 1 else f"已掉线 {h:.0f}h" + entry["status"] = "logged_out" + elif logout_dt and dlogin_dt and logout_dt > dlogin_dt: + entry["login_ok"] = False + h = round((now - logout_dt).total_seconds() / 3600, 1) + entry["message"] = "已掉线" if h < 1 else f"已掉线 {h:.0f}h" + entry["status"] = "logged_out" + elif err_dt and login_dt and login_dt > err_dt: entry["login_ok"] = True entry["session_age_hours"] = round((now - login_dt).total_seconds() / 3600, 1) h = entry["session_age_hours"] @@ -1757,10 +1783,20 @@ def api_wechat_status(): result["last_ok_at"] = ts elif "WeChat send error" in line and ts and not result["last_err_at"]: result["last_err_at"] = ts - if "system_event_login" in line and ts and not result.get("last_login_at"): - result["last_login_at"] = ts - if result["last_ok_at"] and result["last_err_at"] and result.get("last_login_at"): + if result["last_ok_at"] and result["last_err_at"]: break + # Login/logout events: search full log (not just tail -200) + try: + for ev, key in (("system_event_logout", "last_logout_at"), ("system_event_login", "last_login_at")): + er = subprocess.run(["grep", ev, webhook_log], + capture_output=True, text=True, timeout=5) + ev_lines = [l for l in er.stdout.strip().split("\n") if l.strip()] + if ev_lines: + m = re.match(r"^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})", ev_lines[-1]) + if m: + result[key] = m.group(1) + except Exception: + pass except Exception: pass @@ -1772,12 +1808,14 @@ def api_wechat_status(): except ValueError: pass - ok_dt = err_dt = None + ok_dt = err_dt = logout_dt = None try: if result["last_ok_at"]: ok_dt = datetime.strptime(result["last_ok_at"], "%Y-%m-%d %H:%M:%S") if result["last_err_at"]: err_dt = datetime.strptime(result["last_err_at"], "%Y-%m-%d %H:%M:%S") + if result.get("last_logout_at"): + logout_dt = datetime.strptime(result["last_logout_at"], "%Y-%m-%d %H:%M:%S") except ValueError: pass @@ -1790,7 +1828,15 @@ def api_wechat_status(): login_dt = datetime.strptime(result["last_login_at"], "%Y-%m-%d %H:%M:%S") except ValueError: pass - if err_dt and login_dt and login_dt > err_dt: + if logout_dt and login_dt and logout_dt > login_dt: + result["online"] = False + h = round((now - logout_dt).total_seconds() / 3600, 1) + result["message"] = "已掉线" if h < 1 else f"已掉线 {h:.0f}h 前" + elif logout_dt and dlogin_dt and logout_dt > dlogin_dt: + result["online"] = False + h = round((now - logout_dt).total_seconds() / 3600, 1) + result["message"] = "已掉线" if h < 1 else f"已掉线 {h:.0f}h 前" + elif err_dt and login_dt and login_dt > err_dt: result["online"] = True result["session_age_hours"] = round((now - login_dt).total_seconds() / 3600, 1) h = result["session_age_hours"]