WechatOnCloud/bridge/woc_bridge/routes/status.py

117 lines
4.8 KiB
Python
Raw Permalink Normal View History

from __future__ import annotations
import asyncio
import logging
import time
from fastapi import APIRouter
from woc_bridge.config import _state, _require_xdotool, _require_db_reader
from woc_bridge.db.coordinator import with_db_retry, _resolve_db_state
from woc_bridge.models import StatusResponse
from woc_bridge.ui.metrics import send_queue_pending as send_queue_pending_gauge
from woc_bridge.version import BRIDGE_VERSION, BRIDGE_CAPABILITIES, MEDIA_SUPPORTED
logger = logging.getLogger("woc-bridge")
router = APIRouter()
@router.get("/api/status", response_model=StatusResponse)
@with_db_retry
async def get_status() -> StatusResponse:
"""聚合返回 bridge 运行状态。
一次请求内完成 4 类检测进程运行窗口存在登录态DB 可达性
并返回当前账号信息与客户端轮询建议参数所有调用方channels 适配器
面板健康检查诊断 autofix都应优先调本接口判断前置条件
Returns:
StatusResponse bridge_version / wechat_running / wechat_window_found
/ login_state / db_accessible / current_wxid / current_nickname
/ uptime_seconds / display / max_batch_size / poll_interval_ms
Notes:
- 不抛业务错误即使微信未运行 / DB 加密也返回 200 + 对应 false 字段
让调用方按字段判定下一步动作而非按 HTTP 状态码
- login_state 内联判定不调 detect_login_state避免重复 pgrep/xdotool
- DB 加密时细分 key 且验证过 encrypted_key_ok + db_accessible=true
key 且自动提取未启用 encrypted_no_key
自动提取已尝试但失败 key_extract_failed
二者 db_accessible 均为 false
- current_wxid/nickname 仅在 db_accessible=true login_state=logged_in
时才尝试读 DB失败静默为空字符串
"""
xdotool = _require_xdotool()
db_reader = _require_db_reader()
# 一次 status 请求内复用 pgrep/xdotool 结果,避免重复调用
wechat_running = await xdotool.is_wechat_running()
window_id = await xdotool.find_wechat_window() if wechat_running else None
wechat_window_found = window_id is not None
# 登录态内联判定,避免 detect_login_state 内部再次 pgrep/xdotool search
if not wechat_running:
login_state = "not_running"
elif not wechat_window_found:
login_state = "not_logged_in"
else:
login_state = "logged_in"
# DB 状态:统一走 _resolve_db_state不触发自动提取仅解析
db_state = await _resolve_db_state()
db_accessible = db_state.db_accessible
db_error_code = db_state.db_error_code
init_in_progress = _state.init_state.state == "running"
init_progress_pct = _state.init_state.progress_pct
init_message = _state.init_state.message
# current_wxid / current_nickname从 DB 读,失败为空
current_wxid = ""
current_nickname = ""
if db_accessible and login_state == "logged_in":
try:
self_info = await asyncio.to_thread(db_reader.get_self_info)
current_wxid = self_info.get("wxid", "")
current_nickname = self_info.get("nickname", "")
except Exception:
current_wxid = ""
current_nickname = ""
uptime_seconds = time.monotonic() - _state.start_time
send_queue = _state.send_queue
send_queue_pending = send_queue.pending_count() if send_queue else 0
ui_scheduler_metrics = None
if send_queue is not None and hasattr(send_queue, "get_metrics"):
try:
ui_scheduler_metrics = send_queue.get_metrics()
except Exception as exc:
logger.warning("[status] get_metrics failed: %s", exc)
# 修复既有 Prometheus Gauge bug每次 status 请求时 set 当前 pending 数
try:
send_queue_pending_gauge.set(send_queue_pending)
except Exception as exc:
logger.debug("[status] send_queue_pending_gauge.set failed: %s", exc)
return StatusResponse(
bridge_version=BRIDGE_VERSION,
wechat_running=wechat_running,
wechat_window_found=wechat_window_found,
login_state=login_state,
db_accessible=db_accessible,
db_error_code=db_error_code,
init_in_progress=init_in_progress,
init_progress_pct=init_progress_pct,
init_message=init_message,
current_wxid=current_wxid,
current_nickname=current_nickname,
uptime_seconds=uptime_seconds,
display=_state.config.display,
max_batch_size=_state.config.max_batch_size,
poll_interval_ms=_state.config.poll_interval_ms,
send_queue_pending=send_queue_pending,
media_supported=MEDIA_SUPPORTED,
bridge_capabilities=list(BRIDGE_CAPABILITIES),
ui_scheduler=ui_scheduler_metrics,
)