200 lines
8.1 KiB
Python
200 lines
8.1 KiB
Python
# -*- coding: utf-8 -*-
|
||
"""
|
||
统计看板服务器 - 使用Flask蓝图重构版
|
||
"""
|
||
import logging
|
||
import os
|
||
import sys
|
||
import threading
|
||
import time
|
||
from datetime import datetime
|
||
from db.message_storage import MessageStorageDB
|
||
from db.stats_db import StatsDBOperator
|
||
from flask import Flask, send_from_directory
|
||
import toml
|
||
|
||
# 添加项目根目录到系统路径,确保可以导入项目模块
|
||
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..')))
|
||
|
||
class DashboardServer:
|
||
"""统计看板服务器"""
|
||
|
||
def __init__(self, host: str = None, port: int = None,
|
||
username: str = None, password: str = None,
|
||
robot_instance=None):
|
||
# 加载配置文件
|
||
self.config = self._load_dashboard_config()
|
||
|
||
# 优先使用传入的参数,其次使用配置文件中的参数
|
||
self.host = host or self.config.get("server", {}).get("host", "0.0.0.0")
|
||
self.port = port or self.config.get("server", {}).get("port", 8888)
|
||
self.username = username or self.config.get("auth", {}).get("username", "admin")
|
||
self.password = password or self.config.get("auth", {}).get("password", "admin123")
|
||
|
||
self.logger = logging.getLogger("DashboardServer")
|
||
self.logger.info(f"Dashboard配置加载完成: 服务器将运行在 {self.host}:{self.port}")
|
||
logging.getLogger("werkzeug").setLevel(logging.WARNING)
|
||
|
||
# 如果提供了robot实例,则使用其对象
|
||
if robot_instance:
|
||
self.db_manager = robot_instance.db_manager
|
||
self.stats_db = StatsDBOperator(self.db_manager)
|
||
self.message_storage = MessageStorageDB(self.db_manager)
|
||
# 获取联系人管理器实例
|
||
self.contact_manager = robot_instance.contact_manager
|
||
self.plugin_manager = robot_instance.plugin_manager
|
||
self.plugin_registry = robot_instance.plugin_registry
|
||
# 获取WCF实例
|
||
self.wcf = robot_instance.wcf
|
||
self.logger.info("使用Robot实例的对象进行初始化")
|
||
else:
|
||
self.logger.error("未提供Robot实例,Dashboard无法正常工作")
|
||
raise ValueError("必须提供Robot实例")
|
||
|
||
self.app = self._create_app()
|
||
self._stop_event = threading.Event()
|
||
self._server = None # 存储服务器实例
|
||
|
||
def _load_dashboard_config(self):
|
||
"""加载Dashboard配置文件"""
|
||
try:
|
||
config_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'config.toml')
|
||
if os.path.exists(config_path):
|
||
with open(config_path, 'r', encoding='utf-8') as f:
|
||
return toml.load(f)
|
||
else:
|
||
# 如果配置文件不存在,创建默认配置
|
||
default_config = {
|
||
"server": {"host": "0.0.0.0", "port": 8888},
|
||
"auth": {"username": "admin", "password": "admin123"}
|
||
}
|
||
with open(config_path, 'w', encoding='utf-8') as f:
|
||
toml.dump(default_config, f)
|
||
return default_config
|
||
except Exception as e:
|
||
self.logger.error(f"加载Dashboard配置文件失败: {e}")
|
||
# 返回默认配置
|
||
return {
|
||
"server": {"host": "0.0.0.0", "port": 8888},
|
||
"auth": {"username": "admin", "password": "admin123"}
|
||
}
|
||
|
||
def _create_app(self) -> Flask:
|
||
"""创建Flask应用"""
|
||
# 指定模板文件夹路径
|
||
template_folder = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'templates')
|
||
app = Flask(__name__, template_folder=template_folder)
|
||
app.secret_key = "stats_dashboard_secret_key"
|
||
# 禁用模板缓存,使修改HTML文件后立即生效
|
||
app.config['TEMPLATES_AUTO_RELOAD'] = True
|
||
|
||
# 配置静态文件访问
|
||
static_folder = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'static')
|
||
|
||
@app.route('/static/<path:filename>')
|
||
def serve_static(filename):
|
||
return send_from_directory(static_folder, filename)
|
||
|
||
# 获取项目根目录下的static/images目录
|
||
project_root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||
images_dir = os.path.join(project_root, "static", "images")
|
||
|
||
# 确保目录存在
|
||
os.makedirs(images_dir, exist_ok=True)
|
||
|
||
@app.route('/static/images/<path:filename>')
|
||
def serve_images(filename):
|
||
return send_from_directory(images_dir, filename)
|
||
|
||
# 添加一个路由处理favicon请求
|
||
@app.route('/favicon.ico')
|
||
def favicon():
|
||
return send_from_directory(os.path.join(app.root_path, 'static'),
|
||
'favicon.ico', mimetype='image/vnd.microsoft.icon')
|
||
|
||
# 注册蓝图
|
||
self._register_blueprints(app)
|
||
|
||
return app
|
||
|
||
def _register_blueprints(self, app):
|
||
"""注册所有蓝图"""
|
||
# 在函数内部导入蓝图,避免循环导入
|
||
from admin.dashboard.blueprints.auth import auth_bp
|
||
from admin.dashboard.blueprints.contacts import contacts_bp
|
||
from admin.dashboard.blueprints.robot import robot_bp
|
||
from admin.dashboard.blueprints.messages import messages_bp
|
||
from admin.dashboard.blueprints.stats import stats_bp
|
||
from admin.dashboard.blueprints.system import system_bp
|
||
from admin.dashboard.blueprints.main import main_bp
|
||
from admin.dashboard.blueprints.plugin_routes import plugin_routes
|
||
|
||
# 将服务器实例存储在应用上下文中
|
||
app.dashboard_server = self
|
||
|
||
# 注册蓝图
|
||
app.register_blueprint(auth_bp)
|
||
app.register_blueprint(main_bp)
|
||
app.register_blueprint(contacts_bp)
|
||
app.register_blueprint(robot_bp)
|
||
app.register_blueprint(messages_bp)
|
||
app.register_blueprint(stats_bp)
|
||
app.register_blueprint(system_bp)
|
||
app.register_blueprint(plugin_routes)
|
||
|
||
self.logger.info("所有蓝图已注册")
|
||
|
||
def run(self):
|
||
"""运行服务器"""
|
||
from werkzeug.serving import make_server
|
||
|
||
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:
|
||
self.logger.error(f"服务器运行失败: {e}")
|
||
self._stop_event.set()
|
||
|
||
def stop(self):
|
||
"""停止服务器"""
|
||
self.logger.info("正在停止服务器...")
|
||
self._stop_event.set()
|
||
|
||
# 使用werkzeug服务器的关闭方法
|
||
if self._server:
|
||
self._server.shutdown()
|
||
|
||
self.logger.info("服务器已停止")
|
||
|
||
def get_current_user_info(self):
|
||
"""获取当前登录的微信用户信息"""
|
||
try:
|
||
if not self.wcf:
|
||
self.logger.error("WCF实例不可用,无法获取当前用户信息")
|
||
return {"success": False, "message": "WCF实例不可用"}
|
||
|
||
# 获取当前登录的微信ID
|
||
wx_id = self.wcf.get_self_wxid()
|
||
if not wx_id:
|
||
return {"success": False, "message": "未获取到微信ID"}
|
||
|
||
# 获取用户详细信息
|
||
user_info = self.wcf.get_user_info()
|
||
self.logger.info(f"获取用户信息:{user_info}")
|
||
|
||
return {
|
||
"success": True,
|
||
"data": {
|
||
"wx_id": user_info.get("wxid", wx_id),
|
||
"nickname": user_info.get("name", "未知用户"),
|
||
"avatar": "logo.png", # 使用默认LOGO
|
||
"mobile": user_info.get("mobile", ""),
|
||
"home": user_info.get("home", "")
|
||
}
|
||
}
|
||
except Exception as e:
|
||
self.logger.error(f"获取当前用户信息失败: {e}")
|
||
return {"success": False, "message": f"获取用户信息出错: {str(e)}"}
|