🎯 重构目标: - 将 3700+ 行单文件拆分为多个模块 - 清晰的职责分离,便于维护和扩展 📁 模块结构: - config.py: 配置常量和路径 - database.py: 数据库操作 - utils.py: 工具函数(日志、进程、cron等) - routes_cron.py: Cron管理API - routes_system.py: 系统监控API - routes_config.py: 配置管理API - routes_email.py: 邮件通知API - routes_project.py: 项目管理API - templates.py: HTML模板 - app.py: 主入口文件 ✨ 优势: - 易于维护:每个模块职责明确 - 易于扩展:新增功能只需添加对应模块 - 易于测试:模块独立,便于单元测试 - 易于理解:新人能快速上手 📝 更新 README.md,详细说明项目结构和API接口
188 lines
6.8 KiB
Python
188 lines
6.8 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
系统资源监控 API 路由
|
|
"""
|
|
|
|
import os
|
|
import subprocess
|
|
import time
|
|
from flask import jsonify
|
|
from utils import log_message, get_cpu_temperatures, load_alert_config
|
|
|
|
try:
|
|
import psutil
|
|
HAS_PSUTIL = True
|
|
_last_net_stats = {'bytes_sent': 0, 'bytes_recv': 0, 'time': 0}
|
|
except ImportError:
|
|
HAS_PSUTIL = False
|
|
|
|
|
|
def register_system_routes(app):
|
|
"""注册系统监控相关路由"""
|
|
|
|
@app.route('/api/system/stats')
|
|
def api_system_stats():
|
|
"""获取系统资源信息"""
|
|
if not HAS_PSUTIL:
|
|
return jsonify({'error': 'psutil未安装', 'available': False})
|
|
|
|
try:
|
|
# CPU
|
|
cpu_percent = psutil.cpu_percent(interval=0.5)
|
|
cpu_count = psutil.cpu_count(logical=False) or psutil.cpu_count()
|
|
cpu_count_logical = psutil.cpu_count(logical=True)
|
|
|
|
# CPU 温度
|
|
cpu_temps = get_cpu_temperatures()
|
|
|
|
# 内存
|
|
mem = psutil.virtual_memory()
|
|
mem_total_gb = round(mem.total / (1024**3), 2)
|
|
mem_used_gb = round(mem.used / (1024**3), 2)
|
|
mem_available_gb = round(mem.available / (1024**3), 2)
|
|
mem_percent = mem.percent
|
|
|
|
# 磁盘
|
|
disk = psutil.disk_usage('/')
|
|
disk_total_gb = round(disk.total / (1024**3), 2)
|
|
disk_used_gb = round(disk.used / (1024**3), 2)
|
|
disk_percent = disk.percent
|
|
|
|
# 网速(需要计算差值)
|
|
global _last_net_stats
|
|
current_time = time.time()
|
|
net = psutil.net_io_counters()
|
|
|
|
if _last_net_stats['time'] > 0:
|
|
time_diff = current_time - _last_net_stats['time']
|
|
bytes_sent_diff = net.bytes_sent - _last_net_stats['bytes_sent']
|
|
bytes_recv_diff = net.bytes_recv - _last_net_stats['bytes_recv']
|
|
|
|
upload_speed = (bytes_sent_diff / time_diff) / 1024 # KB/s
|
|
download_speed = (bytes_recv_diff / time_diff) / 1024 # KB/s
|
|
else:
|
|
upload_speed = 0
|
|
download_speed = 0
|
|
|
|
_last_net_stats = {
|
|
'bytes_sent': net.bytes_sent,
|
|
'bytes_recv': net.bytes_recv,
|
|
'time': current_time
|
|
}
|
|
|
|
# 进程数量
|
|
process_count = len(psutil.pids())
|
|
|
|
# 系统启动时间
|
|
boot_time = psutil.boot_time()
|
|
uptime_seconds = time.time() - boot_time
|
|
uptime_hours = int(uptime_seconds // 3600)
|
|
uptime_days = int(uptime_hours // 24)
|
|
uptime_str = f"{uptime_days}天 {uptime_hours % 24}小时"
|
|
|
|
return jsonify({
|
|
'available': True,
|
|
'cpu': {
|
|
'percent': cpu_percent,
|
|
'count_physical': cpu_count,
|
|
'count_logical': cpu_count_logical,
|
|
'temp_package_0': cpu_temps.get('package_0'),
|
|
'temp_package_1': cpu_temps.get('package_1')
|
|
},
|
|
'memory': {
|
|
'total_gb': mem_total_gb,
|
|
'used_gb': mem_used_gb,
|
|
'available_gb': mem_available_gb,
|
|
'percent': mem_percent
|
|
},
|
|
'disk': {
|
|
'total_gb': disk_total_gb,
|
|
'used_gb': disk_used_gb,
|
|
'percent': disk_percent
|
|
},
|
|
'network': {
|
|
'upload_speed_kb': round(upload_speed, 2),
|
|
'download_speed_kb': round(download_speed, 2)
|
|
},
|
|
'process_count': process_count,
|
|
'uptime': uptime_str
|
|
})
|
|
except Exception as e:
|
|
return jsonify({'error': str(e)}), 500
|
|
|
|
@app.route('/api/system/processes')
|
|
def api_system_processes():
|
|
"""获取进程列表"""
|
|
if not HAS_PSUTIL:
|
|
return jsonify({'error': 'psutil未安装'}), 500
|
|
|
|
try:
|
|
processes = []
|
|
for proc in psutil.process_iter(['pid', 'name', 'cpu_percent', 'memory_percent', 'status']):
|
|
try:
|
|
processes.append({
|
|
'pid': proc.info['pid'],
|
|
'name': proc.info['name'],
|
|
'cpu': proc.info['cpu_percent'] or 0,
|
|
'memory': proc.info['memory_percent'] or 0,
|
|
'status': proc.info['status']
|
|
})
|
|
except:
|
|
pass
|
|
|
|
# 按CPU排序
|
|
processes.sort(key=lambda x: x['cpu'], reverse=True)
|
|
processes = processes[:50] # 只返回前50个
|
|
|
|
return jsonify({'processes': processes})
|
|
except Exception as e:
|
|
return jsonify({'error': str(e)}), 500
|
|
|
|
@app.route('/api/system/docker')
|
|
def api_system_docker():
|
|
"""获取 Docker 容器状态"""
|
|
try:
|
|
result = subprocess.run(['docker', 'ps', '--format', '{{.Names}}|{{.Status}}|{{.Ports}}'],
|
|
capture_output=True, text=True, timeout=5)
|
|
containers = []
|
|
for line in result.stdout.strip().split('\n'):
|
|
if line:
|
|
parts = line.split('|')
|
|
if len(parts) >= 2:
|
|
containers.append({
|
|
'name': parts[0],
|
|
'status': parts[1],
|
|
'ports': parts[2] if len(parts) > 2 else ''
|
|
})
|
|
return jsonify({'containers': containers})
|
|
except Exception as e:
|
|
return jsonify({'error': str(e), 'containers': []})
|
|
|
|
@app.route('/api/alerts', methods=['GET'])
|
|
def api_get_alerts():
|
|
"""获取预警配置"""
|
|
config = load_alert_config()
|
|
return jsonify(config)
|
|
|
|
@app.route('/api/alerts', methods=['POST'])
|
|
def api_save_alerts():
|
|
"""保存预警配置"""
|
|
from flask import request
|
|
import json
|
|
from config import ALERT_CONFIG_FILE
|
|
|
|
try:
|
|
data = request.get_json()
|
|
config = load_alert_config()
|
|
|
|
if 'thresholds' in data:
|
|
config['thresholds'] = data['thresholds']
|
|
if 'web_settings' in data:
|
|
config['web_settings'] = data['web_settings']
|
|
|
|
with open(ALERT_CONFIG_FILE, 'w', encoding='utf-8') as f:
|
|
json.dump(config, f, ensure_ascii=False, indent=2)
|
|
|
|
return jsonify({'message': '保存成功'})
|
|
except Exception as e:
|
|
return jsonify({'error': str(e)}), 500 |