Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ecbba836d9 | |||
| bb19141edc | |||
| c960048042 | |||
| 51f7ee9ad0 | |||
| ea904fb2b2 | |||
| 21240dc108 | |||
| 1d7f4c2bed | |||
| 88c11277cf | |||
| e5ae822ed2 |
10
README.md
10
README.md
@@ -4,7 +4,7 @@
|
||||
|
||||
## 版本信息
|
||||
|
||||
- **当前版本**: v3.4.0
|
||||
- **当前版本**: v3.4.1
|
||||
- **端口**: 16022
|
||||
- **更新日期**: 2026-06-01
|
||||
|
||||
@@ -158,7 +158,13 @@ python3 app_main.py
|
||||
|
||||
## 更新日志
|
||||
|
||||
### v3.4.0 (2026-06-01)
|
||||
### v3.4.1 (2026-06-01)
|
||||
- 🎯 **模块化重构**: 将 3700+ 行单文件拆分为多个模块
|
||||
- 📁 **清晰结构**: config、database、utils、routes、templates 分离
|
||||
- 🔧 **易于维护**: 每个模块职责明确,便于扩展和维护
|
||||
- ✅ **功能完整**: 保留所有原有网页功能
|
||||
|
||||
### v3.4.0 (2026-06-01) [已废弃]
|
||||
- 🎯 **模块化重构**: 将 3700+ 行单文件拆分为多个模块
|
||||
- 📁 **清晰结构**: config、database、utils、routes、templates 分离
|
||||
- 🔧 **易于维护**: 每个模块职责明确,便于扩展和维护
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -20,12 +20,13 @@ ALERT_CONFIG_FILE = 'alert_config.json'
|
||||
|
||||
# 应用配置
|
||||
APP_NAME = "项目服务管理面板"
|
||||
APP_VERSION = "v3.4.0"
|
||||
APP_VERSION = "v3.4.1"
|
||||
APP_PORT = 16022
|
||||
|
||||
# 监控配置
|
||||
DEFAULT_THRESHOLDS = {
|
||||
'cpu': 80,
|
||||
'cpu_temp': 80,
|
||||
'memory': 85,
|
||||
'disk': 90,
|
||||
'interval': 60
|
||||
|
||||
14
logs/app.log
14
logs/app.log
@@ -30317,3 +30317,17 @@ Press CTRL+C to quit
|
||||
[2026-06-01 12:30:38] ⚠️ 进程收到 SIGTERM 信号,即将退出!
|
||||
[2026-06-01 12:31:16] 项目服务管理面板 v3.4.0 初始化完成
|
||||
[2026-06-01 12:31:16] 启动 项目服务管理面板,端口: 16022
|
||||
[2026-06-01 15:53:53] 项目服务管理面板 v3.4.0 初始化完成
|
||||
[2026-06-01 15:53:53] 启动 项目服务管理面板,端口: 16022
|
||||
[2026-06-01 15:55:35] 项目服务管理面板 v3.4.0 初始化完成
|
||||
[2026-06-01 15:55:35] 启动 项目服务管理面板,端口: 16022
|
||||
[2026-06-01 15:56:16] 项目服务管理面板 v3.4.0 初始化完成
|
||||
[2026-06-01 15:56:16] 启动 项目服务管理面板,端口: 16022
|
||||
[2026-06-01 15:57:03] 项目服务管理面板 v3.4.1 初始化完成
|
||||
[2026-06-01 15:57:03] 启动 项目服务管理面板,端口: 16022
|
||||
[2026-06-01 16:02:13] 项目服务管理面板 v3.4.1 初始化完成
|
||||
[2026-06-01 16:02:13] 启动 项目服务管理面板,端口: 16022
|
||||
[2026-06-01 16:02:39] 项目服务管理面板 v3.4.1 初始化完成
|
||||
[2026-06-01 16:02:39] 启动 项目服务管理面板,端口: 16022
|
||||
[2026-06-01 16:10:23] 项目服务管理面板 v3.4.1 初始化完成
|
||||
[2026-06-01 16:10:23] 启动 项目服务管理面板,端口: 16022
|
||||
|
||||
@@ -20,10 +20,25 @@ def register_project_routes(app):
|
||||
"""获取项目列表"""
|
||||
projects = load_projects()
|
||||
|
||||
# 添加状态信息
|
||||
# 添加状态信息(结构化对象,前端期望 status.status 和 status.ports)
|
||||
for project in projects:
|
||||
ports = project.get('ports', [])
|
||||
project['status'] = 'running' if all(check_port(p) for p in ports) else 'stopped'
|
||||
port_statuses = {}
|
||||
for p in ports:
|
||||
port_statuses[str(p)] = {'running': check_port(p)}
|
||||
|
||||
running_count = sum(1 for ps in port_statuses.values() if ps['running'])
|
||||
if running_count == len(ports) and len(ports) > 0:
|
||||
overall = 'running'
|
||||
elif running_count > 0:
|
||||
overall = 'partial'
|
||||
else:
|
||||
overall = 'stopped'
|
||||
|
||||
project['status'] = {
|
||||
'status': overall,
|
||||
'ports': port_statuses
|
||||
}
|
||||
|
||||
return jsonify({
|
||||
'projects': projects,
|
||||
@@ -169,10 +184,25 @@ def register_project_routes(app):
|
||||
data = load_projects_full()
|
||||
services = data.get('external_services', [])
|
||||
|
||||
# 添加状态
|
||||
# 添加状态(结构化对象,前端期望 status.status 和 status.ports)
|
||||
for service in services:
|
||||
ports = service.get('ports', [])
|
||||
service['status'] = 'running' if all(check_port(p) for p in ports) else 'stopped'
|
||||
port_statuses = {}
|
||||
for p in ports:
|
||||
port_statuses[str(p)] = {'running': check_port(p)}
|
||||
|
||||
running_count = sum(1 for ps in port_statuses.values() if ps['running'])
|
||||
if running_count == len(ports) and len(ports) > 0:
|
||||
overall = 'running'
|
||||
elif running_count > 0:
|
||||
overall = 'partial'
|
||||
else:
|
||||
overall = 'stopped'
|
||||
|
||||
service['status'] = {
|
||||
'status': overall,
|
||||
'ports': port_statuses
|
||||
}
|
||||
|
||||
return jsonify({'services': services})
|
||||
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
import os
|
||||
import subprocess
|
||||
import time
|
||||
from flask import jsonify
|
||||
from datetime import datetime
|
||||
from flask import jsonify, request
|
||||
from utils import log_message, get_cpu_temperatures, load_alert_config
|
||||
|
||||
try:
|
||||
@@ -46,6 +47,7 @@ def register_system_routes(app):
|
||||
disk = psutil.disk_usage('/')
|
||||
disk_total_gb = round(disk.total / (1024**3), 2)
|
||||
disk_used_gb = round(disk.used / (1024**3), 2)
|
||||
disk_free_gb = round(disk.free / (1024**3), 2)
|
||||
disk_percent = disk.percent
|
||||
|
||||
# 网速(需要计算差值)
|
||||
@@ -82,10 +84,15 @@ def register_system_routes(app):
|
||||
|
||||
return jsonify({
|
||||
'available': True,
|
||||
'system': {
|
||||
'uptime': uptime_str,
|
||||
'boot_time': datetime.fromtimestamp(boot_time).strftime('%Y-%m-%d %H:%M:%S'),
|
||||
'process_count': process_count
|
||||
},
|
||||
'cpu': {
|
||||
'percent': cpu_percent,
|
||||
'count_physical': cpu_count,
|
||||
'count_logical': cpu_count_logical,
|
||||
'count': cpu_count,
|
||||
'logical': cpu_count_logical,
|
||||
'temp_package_0': cpu_temps.get('package_0'),
|
||||
'temp_package_1': cpu_temps.get('package_1')
|
||||
},
|
||||
@@ -98,17 +105,19 @@ def register_system_routes(app):
|
||||
'disk': {
|
||||
'total_gb': disk_total_gb,
|
||||
'used_gb': disk_used_gb,
|
||||
'free_gb': disk_free_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
|
||||
'sent_mb': round(net.bytes_sent / (1024**2), 2),
|
||||
'recv_mb': round(net.bytes_recv / (1024**2), 2),
|
||||
'sent_speed': round(upload_speed, 2),
|
||||
'recv_speed': round(download_speed, 2)
|
||||
}
|
||||
})
|
||||
except Exception as e:
|
||||
return jsonify({'error': str(e)}), 500
|
||||
log_message(f"系统监控API错误: {e}")
|
||||
return jsonify({'error': str(e), 'available': False}), 500
|
||||
|
||||
@app.route('/api/system/processes')
|
||||
def api_system_processes():
|
||||
@@ -117,6 +126,9 @@ def register_system_routes(app):
|
||||
return jsonify({'error': 'psutil未安装'}), 500
|
||||
|
||||
try:
|
||||
limit = int(request.args.get('limit', 10))
|
||||
limit = max(1, min(50, limit))
|
||||
|
||||
processes = []
|
||||
for proc in psutil.process_iter(['pid', 'name', 'cpu_percent', 'memory_percent', 'status']):
|
||||
try:
|
||||
@@ -132,7 +144,7 @@ def register_system_routes(app):
|
||||
|
||||
# 按CPU排序
|
||||
processes.sort(key=lambda x: x['cpu'], reverse=True)
|
||||
processes = processes[:50] # 只返回前50个
|
||||
processes = processes[:limit]
|
||||
|
||||
return jsonify({'processes': processes})
|
||||
except Exception as e:
|
||||
@@ -167,7 +179,6 @@ def register_system_routes(app):
|
||||
@app.route('/api/alerts', methods=['POST'])
|
||||
def api_save_alerts():
|
||||
"""保存预警配置"""
|
||||
from flask import request
|
||||
import json
|
||||
from config import ALERT_CONFIG_FILE
|
||||
|
||||
|
||||
@@ -16,9 +16,12 @@ def get_html_template():
|
||||
with open(template_file, 'r', encoding='utf-8') as f:
|
||||
template_content = f.read()
|
||||
|
||||
# 替换版本号
|
||||
# 替换版本号(支持多种格式)
|
||||
template_content = template_content.replace('v2.1', APP_VERSION)
|
||||
template_content = template_content.replace('v3.3.1', APP_VERSION)
|
||||
template_content = template_content.replace('v3.3.5', APP_VERSION)
|
||||
template_content = template_content.replace('v3.4.0', APP_VERSION)
|
||||
template_content = template_content.replace('v3.4.1', APP_VERSION) # 预留未来版本
|
||||
|
||||
return template_content
|
||||
except:
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
# ==================== HTML 模板 ====================
|
||||
|
||||
HTML_TEMPLATE = '''<!DOCTYPE html>
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
@@ -307,6 +305,16 @@ HTML_TEMPLATE = '''<!DOCTYPE html>
|
||||
<span class="text-gray-500 text-xs">%</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="threshold-item">
|
||||
<div class="threshold-label">
|
||||
<i class="ri-temp-hot-line threshold-icon text-red-400"></i>
|
||||
<span class="text-gray-300 text-sm">CPU 温度</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<input type="number" id="thresholdCpuTemp" class="threshold-input" min="30" max="120" value="80" onchange="saveThresholds()">
|
||||
<span class="text-gray-500 text-xs">°C</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="threshold-item">
|
||||
<div class="threshold-label">
|
||||
<i class="ri-database-2-line threshold-icon text-green-400"></i>
|
||||
@@ -758,7 +766,7 @@ HTML_TEMPLATE = '''<!DOCTYPE html>
|
||||
let realtimeTimer = null;
|
||||
let realtimeInterval = 2; // 秒
|
||||
let lastAlertTime = 0; // 上次警告时间
|
||||
let thresholds = { cpu: 80, memory: 85, disk: 90, interval: 60 };
|
||||
let thresholds = { cpu: 80, cpu_temp: 80, memory: 85, disk: 90, interval: 60 };
|
||||
let desktopNotifyEnabled = false;
|
||||
|
||||
function switchTab(tab) {
|
||||
@@ -801,6 +809,7 @@ HTML_TEMPLATE = '''<!DOCTYPE html>
|
||||
if (config.thresholds) {
|
||||
thresholds = config.thresholds;
|
||||
document.getElementById('thresholdCpu').value = thresholds.cpu || 80;
|
||||
document.getElementById('thresholdCpuTemp').value = thresholds.cpu_temp || 80;
|
||||
document.getElementById('thresholdMemory').value = thresholds.memory || 85;
|
||||
document.getElementById('thresholdDisk').value = thresholds.disk || 90;
|
||||
document.getElementById('thresholdInterval').value = thresholds.interval || 60;
|
||||
@@ -813,6 +822,7 @@ HTML_TEMPLATE = '''<!DOCTYPE html>
|
||||
// 保存阈值设置
|
||||
async function saveThresholds() {
|
||||
thresholds.cpu = parseInt(document.getElementById('thresholdCpu').value) || 80;
|
||||
thresholds.cpu_temp = parseInt(document.getElementById('thresholdCpuTemp').value) || 80;
|
||||
thresholds.memory = parseInt(document.getElementById('thresholdMemory').value) || 85;
|
||||
thresholds.disk = parseInt(document.getElementById('thresholdDisk').value) || 90;
|
||||
thresholds.interval = parseInt(document.getElementById('thresholdInterval').value) || 60;
|
||||
@@ -937,6 +947,12 @@ HTML_TEMPLATE = '''<!DOCTYPE html>
|
||||
if (data.disk.percent >= thresholds.disk) {
|
||||
warnings.push(`磁盘使用率 ${data.disk.percent.toFixed(1)}% 超过阈值 ${thresholds.disk}%`);
|
||||
}
|
||||
if (data.cpu.temp_package_0 !== null && data.cpu.temp_package_0 >= thresholds.cpu_temp) {
|
||||
warnings.push(`CPU0 温度 ${data.cpu.temp_package_0.toFixed(1)}°C 超过阈值 ${thresholds.cpu_temp}°C`);
|
||||
}
|
||||
if (data.cpu.temp_package_1 !== null && data.cpu.temp_package_1 >= thresholds.cpu_temp) {
|
||||
warnings.push(`CPU1 温度 ${data.cpu.temp_package_1.toFixed(1)}°C 超过阈值 ${thresholds.cpu_temp}°C`);
|
||||
}
|
||||
|
||||
if (warnings.length > 0) {
|
||||
lastAlertTime = now;
|
||||
@@ -2353,14 +2369,4 @@ HTML_TEMPLATE = '''<!DOCTYPE html>
|
||||
}, 10000);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
'''
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
os.makedirs(os.path.join(BASE_DIR, 'logs'), exist_ok=True)
|
||||
os.makedirs(CRON_BACKUP_DIR, exist_ok=True)
|
||||
init_db()
|
||||
|
||||
log_message("=" * 50)
|
||||
log_message("项目服务管理面板 v2.0.0 启动")
|
||||
</html>
|
||||
Reference in New Issue
Block a user