From 131a46f4b8511bff0ae5ff6472547eb8fe7429c4 Mon Sep 17 00:00:00 2001 From: liuwei Date: Thu, 27 Mar 2025 12:05:36 +0800 Subject: [PATCH] =?UTF-8?q?=E8=BF=98=E6=98=AF=E9=9A=8F=E9=A1=B9=E7=9B=AE?= =?UTF-8?q?=E5=90=AF=E5=8A=A8=E3=80=82=E8=A7=A3=E5=86=B3=E5=AF=B9=E8=B1=A1?= =?UTF-8?q?=E4=BC=A0=E9=80=92=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- admin/dashboard/server.py | 318 ++++++++++++++++++-------------------- 1 file changed, 151 insertions(+), 167 deletions(-) diff --git a/admin/dashboard/server.py b/admin/dashboard/server.py index 7eefbfb..b1bdcc3 100644 --- a/admin/dashboard/server.py +++ b/admin/dashboard/server.py @@ -2,24 +2,17 @@ import logging import os import sys import threading -import time -import yaml from datetime import datetime +import time # 添加项目根目录到系统路径,确保可以导入项目模块 sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..'))) from flask import Flask, render_template, request, jsonify, redirect, url_for, session, send_from_directory - -from db.connection import DBConnectionManager -from db.message_storage import MessageStorageDB -from db.stats_db import StatsDBOperator -from utils.wechat.contact_manager import ContactManager from robot_cmd.robot_command import GroupBotManager, Feature, PermissionStatus -# 在导入部分添加 +# 导入toml用于配置文件 import toml -import os.path class DashboardServer: @@ -44,15 +37,12 @@ class DashboardServer: if robot_instance: self.db_manager = robot_instance.db_manager self.contact_manager = robot_instance.contact_manager - # 使用robot的GroupBotManager实例 - self.gbm = robot_instance.gbm + self.stats_db = robot_instance.stats_db + self.message_storage = robot_instance.message_storage self.logger.info("使用Robot实例的对象进行初始化") else: - # 初始化数据库连接 - self._init_database() - # 获取联系人管理器实例 - self.contact_manager = ContactManager.get_instance() - self.logger.info("独立初始化数据库和联系人管理器") + self.logger.error("未提供Robot实例,Dashboard无法正常工作") + raise ValueError("必须提供Robot实例") self.app = self._create_app() self._stop_event = threading.Event() @@ -75,88 +65,18 @@ class DashboardServer: toml.dump(default_config, f) return default_config except Exception as e: - print(f"加载Dashboard配置文件失败: {e}") + self.logger.error(f"加载Dashboard配置文件失败: {e}") # 返回默认配置 return { "server": {"host": "0.0.0.0", "port": 8888}, "auth": {"username": "admin", "password": "admin123"} } - def _load_config(self): - """从配置文件加载配置""" - try: - # 获取项目根目录的config.yaml文件路径 - config_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', 'config.yaml')) - self.logger.info(f"正在加载配置文件: {config_path}") - - with open(config_path, 'r', encoding='utf-8') as f: - config = yaml.safe_load(f) - - return config - except Exception as e: - self.logger.error(f"加载配置文件失败: {e}") - # 返回默认配置 - return { - "db_config": { - "pool_name": "wechat_boot_pool", - "pool_size": 10, - "host": "localhost", - "user": "root", - "password": "password", - "database": "message_archive", - "charset": "utf8mb4", - "use_unicode": True, - "get_warnings": True, - "pool_reset_session": True - }, - "redis_config": { - "host": "localhost", - "port": 6379, - "db": 0, - "decode_responses": True - } - } - - def _init_database(self): - """初始化数据库连接""" - try: - # 加载配置 - config = self._load_config() - - # 获取数据库配置 - db_config = config.get("db_config", {}) - redis_config = config.get("redis_config", {}) - - self.logger.info("正在初始化数据库连接...") - self.logger.info(f"数据库主机: {db_config.get('host')}, 数据库: {db_config.get('database')}") - - # 初始化数据库连接管理器 - 按照DBConnectionManager的接口要求传递参数 - self.db_manager = DBConnectionManager.get_instance( - mysql_config=db_config, - redis_config=redis_config - ) - - # 初始化数据库操作类 - self.stats_db = StatsDBOperator(self.db_manager) - self.message_storage = MessageStorageDB(self.db_manager) - - self.logger.info("数据库连接初始化成功") - except Exception as e: - self.logger.error(f"初始化数据库连接失败: {e}") - raise - def _create_app(self) -> Flask: """创建Flask应用""" app = Flask(__name__) app.secret_key = "stats_dashboard_secret_key" - # 实现基本的身份验证 - def check_auth(): - auth = request.authorization - if not auth or auth.username != self.username or auth.password != self.password: - return False - return True - # 静态文件目录 static_folder = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'static') @@ -236,36 +156,72 @@ class DashboardServer: @app.route('/api/robot/groups') @login_required def api_robot_groups(): - # 获取所有群组列表 - groups = GroupBotManager.get_group_list() - group_data = [] - - for group_id in groups: - group_name = self.contact_manager.get_nickname(group_id) - robot_status = GroupBotManager.get_group_permission(group_id, Feature.ROBOT) - group_data.append({ - "group_id": group_id, - "group_name": group_name, - "robot_status": robot_status.value - }) - - return jsonify({"success": True, "data": group_data}) + try: + # 获取所有群组列表 + groups = GroupBotManager.get_group_list() + + # 如果方法返回None或发生异常,使用本地缓存 + if groups is None and hasattr(GroupBotManager, "local_cache"): + groups = GroupBotManager.local_cache.get("group_list", set()) + + # 如果仍然为None,则初始化为空集合 + if groups is None: + groups = set() + + self.logger.info(f"获取到 {len(groups)} 个群组") + group_data = [] + + for group_id in groups: + try: + # 获取群名称,如果失败则使用默认值 + group_name = self.contact_manager.get_nickname(group_id) + if not group_name: + group_name = f"未知群组({group_id})" + + # 获取机器人状态,如果失败则使用默认值 + try: + robot_status = GroupBotManager.get_group_permission(group_id, Feature.ROBOT) + except: + robot_status = PermissionStatus.UNKNOWN + + group_data.append({ + "group_id": group_id, + "group_name": group_name, + "robot_status": robot_status.value if robot_status else "unknown" + }) + except Exception as e: + self.logger.warning(f"处理群组 {group_id} 信息时出错: {e}") + # 添加基本信息,避免单个群组错误影响整个列表 + group_data.append({ + "group_id": group_id, + "group_name": "获取失败", + "robot_status": "unknown" + }) + + return jsonify({"success": True, "data": group_data}) + except Exception as e: + self.logger.error(f"获取群组列表失败: {e}") + return jsonify({"success": False, "error": str(e)}), 500 @app.route('/api/robot/group//permissions') @login_required def api_robot_group_permissions(group_id): - permissions = GroupBotManager.list_group_permissions(group_id) - permission_data = [] - - for feature, status in permissions.items(): - permission_data.append({ - "feature_id": feature.value, - "feature_name": feature.name, - "feature_description": feature.description, - "status": status.value - }) - - return jsonify({"success": True, "data": permission_data}) + try: + permissions = GroupBotManager.list_group_permissions(group_id) + permission_data = [] + + for feature, status in permissions.items(): + permission_data.append({ + "feature_id": feature.value, + "feature_name": feature.name, + "feature_description": feature.description, + "status": status.value + }) + + return jsonify({"success": True, "data": permission_data}) + except Exception as e: + self.logger.error(f"获取群组权限失败: {e}") + return jsonify({"success": False, "error": str(e)}), 500 @app.route('/api/robot/group//permissions', methods=['POST']) @login_required @@ -274,11 +230,11 @@ class DashboardServer: data = request.json feature_id = data.get('feature_id') status = data.get('status') - + try: feature = Feature(int(feature_id)) new_status = PermissionStatus(status) - + # 特殊处理ROBOT功能 if feature == Feature.ROBOT: r = self.db_manager.get_redis_connection() @@ -288,7 +244,7 @@ class DashboardServer: else: GroupBotManager.local_cache["group_list"].remove(group_id) r.srem("group:list", group_id) - + GroupBotManager.set_group_permission(group_id, feature, new_status) return jsonify({"success": True}) except Exception as e: @@ -302,15 +258,15 @@ class DashboardServer: data = request.json operation = data.get('operation') group_ids = data.get('group_ids', []) - + results = {} - + try: if operation == 'remove_groups': for group_id in group_ids: result = GroupBotManager.remove_group(group_id) results[group_id] = result - + return jsonify({"success": True, "results": results}) else: return jsonify({"success": False, "error": "不支持的操作类型"}), 400 @@ -321,67 +277,87 @@ class DashboardServer: @app.route('/api/user_stats') @login_required def api_user_stats(): - days = request.args.get('days', 7, type=int) - limit = request.args.get('limit', 10, type=int) - stats = self.stats_db.get_user_stats(days, limit) + try: + days = request.args.get('days', 7, type=int) + limit = request.args.get('limit', 10, type=int) + stats = self.stats_db.get_user_stats(days, limit) - # 将用户ID转换为名称 - for item in stats: - if 'user_id' in item: - user_id = item['user_id'] - item['user_name'] = self.contact_manager.get_nickname(user_id) + # 将用户ID转换为名称 + for item in stats: + if 'user_id' in item: + user_id = item['user_id'] + item['user_name'] = self.contact_manager.get_nickname(user_id) - return jsonify({"success": True, "data": stats}) + return jsonify({"success": True, "data": stats}) + except Exception as e: + self.logger.error(f"获取用户统计失败: {e}") + return jsonify({"success": False, "error": str(e)}), 500 @app.route('/api/group_stats') @login_required def api_group_stats(): - days = request.args.get('days', 7, type=int) - limit = request.args.get('limit', 10, type=int) - stats = self.stats_db.get_group_stats(days, limit) + try: + days = request.args.get('days', 7, type=int) + limit = request.args.get('limit', 10, type=int) + stats = self.stats_db.get_group_stats(days, limit) - # 将群ID转换为名称 - for item in stats: - if 'group_id' in item: - group_id = item['group_id'] - item['group_name'] = self.contact_manager.get_nickname(group_id) + # 将群ID转换为名称 + for item in stats: + if 'group_id' in item: + group_id = item['group_id'] + item['group_name'] = self.contact_manager.get_nickname(group_id) - return jsonify({"success": True, "data": stats}) + return jsonify({"success": True, "data": stats}) + except Exception as e: + self.logger.error(f"获取群组统计失败: {e}") + return jsonify({"success": False, "error": str(e)}), 500 @app.route('/api/plugin_stats') @login_required def api_plugin_stats(): - days = request.args.get('days', 7, type=int) - stats = self.stats_db.get_plugin_stats(days) - return jsonify({"success": True, "data": stats}) + try: + days = request.args.get('days', 7, type=int) + stats = self.stats_db.get_plugin_stats(days) + return jsonify({"success": True, "data": stats}) + except Exception as e: + self.logger.error(f"获取插件统计失败: {e}") + return jsonify({"success": False, "error": str(e)}), 500 @app.route('/api/error_logs') @login_required def api_error_logs(): - days = request.args.get('days', 7, type=int) - limit = request.args.get('limit', 100, type=int) - logs = self.stats_db.get_error_logs(days, limit) - return jsonify({"success": True, "data": logs}) + try: + days = request.args.get('days', 7, type=int) + limit = request.args.get('limit', 100, type=int) + logs = self.stats_db.get_error_logs(days, limit) + return jsonify({"success": True, "data": logs}) + except Exception as e: + self.logger.error(f"获取错误日志失败: {e}") + return jsonify({"success": False, "error": str(e)}), 500 @app.route('/api/system_info') @login_required def api_system_info(): - # 获取系统信息 - import platform - import psutil + try: + # 获取系统信息 + import platform + import psutil - system_info = { - "os": platform.system(), - "os_version": platform.version(), - "python_version": platform.python_version(), - "cpu_usage": psutil.cpu_percent(), - "memory_usage": psutil.virtual_memory().percent, - "disk_usage": psutil.disk_usage('/').percent, - "uptime": time.time() - psutil.boot_time(), - "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S") - } + system_info = { + "os": platform.system(), + "os_version": platform.version(), + "python_version": platform.python_version(), + "cpu_usage": psutil.cpu_percent(), + "memory_usage": psutil.virtual_memory().percent, + "disk_usage": psutil.disk_usage('/').percent, + "uptime": time.time() - psutil.boot_time(), + "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S") + } - return jsonify({"success": True, "data": system_info}) + return jsonify({"success": True, "data": system_info}) + except Exception as e: + self.logger.error(f"获取系统信息失败: {e}") + return jsonify({"success": False, "error": str(e)}), 500 @app.route('/api/dashboard_summary') @login_required @@ -409,10 +385,14 @@ class DashboardServer: @app.route('/api/plugin_trend') @login_required def api_plugin_trend(): - days = request.args.get('days', 7, type=int) - plugin_name = request.args.get('plugin_name', '') - trend = self.stats_db.get_plugin_trend(plugin_name, days) - return jsonify({"success": True, "data": trend}) + try: + days = request.args.get('days', 7, type=int) + plugin_name = request.args.get('plugin_name', '') + trend = self.stats_db.get_plugin_trend(plugin_name, days) + return jsonify({"success": True, "data": trend}) + except Exception as e: + self.logger.error(f"获取插件趋势失败: {e}") + return jsonify({"success": False, "error": str(e)}), 500 @app.route('/api/robot/group//message_trend') @login_required @@ -448,8 +428,12 @@ class DashboardServer: @app.route('/api/error_detail/') @login_required def api_error_detail(error_id): - detail = self.stats_db.get_error_detail(error_id) - return jsonify({"success": True, "data": detail}) + try: + detail = self.stats_db.get_error_detail(error_id) + return jsonify({"success": True, "data": detail}) + except Exception as e: + self.logger.error(f"获取错误详情失败: {e}") + return jsonify({"success": False, "error": str(e)}), 500 @app.route('/api/robot/add_group', methods=['POST']) @login_required @@ -499,7 +483,7 @@ class DashboardServer: self.logger.info(f"启动服务器: {self.host}:{self.port}") try: - # 修改:使用线程安全的方式运行服务器 + # 使用线程安全的方式运行服务器 self._server = make_server(self.host, self.port, self.app) self._server.serve_forever() except Exception as e: @@ -511,8 +495,8 @@ class DashboardServer: self.logger.info("正在停止服务器...") self._stop_event.set() - # 修改:使用werkzeug服务器的关闭方法 + # 使用werkzeug服务器的关闭方法 if self._server: self._server.shutdown() - self.logger.info("服务器已停止") + self.logger.info("服务器已停止") \ No newline at end of file