Files
wxauto_api/services/node_manager.py
T
2026-04-07 14:11:45 +08:00

226 lines
8.1 KiB
Python

# ///
# node_manager.py
# 描述:节点管理服务,负责与WXAUTO-HTTP-API节点通信
# 作者:AI Generated
# 创建日期:2026-04-05
# ///
import asyncio
from typing import Dict, List, Optional, Any
import httpx
from services.log_service import log_service
from database_service import get_db_service
class NodeStatus:
ACTIVE = "active"
INACTIVE = "inactive"
ERROR = "error"
class Node:
def __init__(self, node_id: str, name: str, api_url: str, api_key: str, description: str = "", group: str = "default"):
self.node_id = node_id
self.name = name
self.api_url = api_url.rstrip("/")
self.api_key = api_key
self.description = description
self.group = group
self.enabled = True
self.status = NodeStatus.INACTIVE
self.wechat_status = "online"
self.is_healthy = False
def to_dict(self) -> Dict[str, Any]:
return {
"node_id": self.node_id,
"name": self.name,
"api_url": self.api_url,
"enabled": self.enabled,
"description": self.description,
"group": self.group,
"status": self.status,
"wechat_status": self.wechat_status,
"is_healthy": self.is_healthy
}
class NodeManager:
def __init__(self):
self.nodes: Dict[str, Node] = {}
async def add_node(self, node_id: str, name: str, api_url: str, api_key: str, description: str = "", group: str = "default") -> bool:
if node_id in self.nodes:
return False
self.nodes[node_id] = Node(node_id, name, api_url, api_key, description, group)
await self.check_node_status(node_id)
return True
async def remove_node(self, node_id: str) -> bool:
if node_id not in self.nodes:
return False
del self.nodes[node_id]
return True
async def get_node(self, node_id: str) -> Optional[Node]:
return self.nodes.get(node_id)
async def get_all_nodes(self) -> List[Node]:
return list(self.nodes.values())
async def get_enabled_nodes(self) -> List[Node]:
return [n for n in self.nodes.values() if n.enabled]
async def get_nodes_by_group(self, group: str) -> List[Node]:
return [n for n in self.nodes.values() if n.group == group]
def to_config(self) -> List[Dict[str, Any]]:
return [{
"node_id": n.node_id,
"name": n.name,
"api_url": n.api_url,
"api_key": n.api_key,
"enabled": n.enabled,
"description": n.description,
"group": n.group
} for n in self.nodes.values()]
async def update_node(self, node_id: str, **kwargs) -> bool:
node = self.nodes.get(node_id)
if not node:
return False
for key, value in kwargs.items():
if hasattr(node, key):
setattr(node, key, value)
if "api_url" in kwargs or "api_key" in kwargs:
await self.check_node_status(node_id)
return True
async def check_node_status(self, node_id: str) -> str:
node = self.nodes.get(node_id)
if not node or not node.enabled:
return NodeStatus.INACTIVE
try:
async with httpx.AsyncClient(timeout=5.0) as client:
response = await client.get(
f"{node.api_url}/health",
headers={"X-API-Key": node.api_key}
)
if response.status_code == 200:
node.status = NodeStatus.ACTIVE
else:
node.status = NodeStatus.ERROR
except Exception:
node.status = NodeStatus.ERROR
return node.status
async def is_node_healthy(self, node_id: str) -> tuple[bool, str]:
node = self.nodes.get(node_id)
if not node or not node.enabled:
return False, "Node not found or disabled"
api_status = await self.check_node_status(node_id)
if api_status != NodeStatus.ACTIVE:
return False, f"API status: {api_status}"
return True, "OK"
async def call_node_api(self, node_id: str, endpoint: str, method: str = "GET", data: Optional[Dict] = None) -> Dict[str, Any]:
node = self.nodes.get(node_id)
if not node or not node.enabled:
return {"success": False, "error": f"Node '{node_id}' not found or disabled"}
url = f"{node.api_url}{endpoint}"
headers = {"X-API-Key": node.api_key}
try:
async with httpx.AsyncClient(timeout=10.0) as client:
if method.upper() == "GET":
response = await client.get(url, headers=headers)
elif method.upper() == "POST":
response = await client.post(url, headers=headers, json=data)
else:
return {"success": False, "error": f"Unsupported method: {method}"}
if response.status_code == 200:
return {"success": True, "data": response.json()}
else:
return {"success": False, "error": f"HTTP {response.status_code}", "url": url}
except Exception as e:
return {"success": False, "error": str(e), "url": url}
async def send_message(self, node_id: str, who: str, msg: str, msg_type: str = "text") -> Dict[str, Any]:
node = self.nodes.get(node_id)
await self.call_node_api(node_id, "/api/wechat/initialize", method="POST")
result = await self.call_node_api(
node_id,
"/api/message/send",
method="POST",
data={"receiver": who, "message": msg}
)
if node:
node.wechat_status = "online" if result.get("success") else "offline"
db_service = get_db_service()
if db_service:
db_service.update_node(node_id, wechat_status=node.wechat_status)
return result
async def send_message_to_group(self, node_id: str, group_name: str, msg: str, msg_type: str = "text") -> Dict[str, Any]:
node = self.nodes.get(node_id)
await self.call_node_api(node_id, "/api/wechat/initialize", method="POST")
result = await self.call_node_api(
node_id,
"/api/message/send",
method="POST",
data={"receiver": group_name, "message": msg}
)
if node:
node.wechat_status = "online" if result.get("success") else "offline"
db_service = get_db_service()
if db_service:
db_service.update_node(node_id, wechat_status=node.wechat_status)
return result
async def get_friends_list(self, node_id: str) -> Dict[str, Any]:
return await self.call_node_api(node_id, "/api/friend/list", method="GET")
async def get_groups_list(self, node_id: str) -> Dict[str, Any]:
return await self.call_node_api(node_id, "/api/group/list", method="GET")
async def get_messages(self, node_id: str, chat_name: str, limit: int = 20) -> Dict[str, Any]:
return await self.call_node_api(
node_id,
"/api/message/get-history",
method="POST",
data={"chat_name": chat_name, "save_pic": False, "save_video": False, "save_file": False, "save_voice": False}
)
async def reset_wechat_status(self, node_id: str) -> Dict[str, Any]:
node = self.nodes.get(node_id)
if not node:
return {"success": False, "error": f"Node '{node_id}' not found"}
await self.call_node_api(node_id, "/api/wechat/initialize", method="POST")
result = await self.call_node_api(
node_id,
"/api/message/send",
method="POST",
data={"receiver": "文件传输助手", "message": "WXAuto Center 状态检测消息"}
)
if result.get("success"):
node.wechat_status = "online"
else:
node.wechat_status = "offline"
db_service = get_db_service()
if db_service:
db_service.update_node(node_id, wechat_status=node.wechat_status)
return result
node_manager = NodeManager()