🎯 重构目标: - 将 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接口
212 lines
7.3 KiB
Python
212 lines
7.3 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
项目管理 API 路由
|
|
"""
|
|
|
|
import json
|
|
from flask import jsonify, request
|
|
from config import PROJECTS_FILE
|
|
from utils import (
|
|
log_message, load_projects, load_projects_full, save_projects,
|
|
check_port, stop_process, run_command
|
|
)
|
|
|
|
|
|
def register_project_routes(app):
|
|
"""注册项目管理相关路由"""
|
|
|
|
@app.route('/api/projects')
|
|
def api_projects():
|
|
"""获取项目列表"""
|
|
projects = load_projects()
|
|
|
|
# 添加状态信息
|
|
for project in projects:
|
|
ports = project.get('ports', [])
|
|
project['status'] = 'running' if all(check_port(p) for p in ports) else 'stopped'
|
|
|
|
return jsonify({
|
|
'projects': projects,
|
|
'server': load_projects_full().get('server', {})
|
|
})
|
|
|
|
@app.route('/api/projects/<project_id>/start', methods=['POST'])
|
|
def api_start(project_id):
|
|
projects = load_projects()
|
|
project = next((p for p in projects if p['id'] == project_id), None)
|
|
if not project:
|
|
return jsonify({'error': '项目不存在'}), 404
|
|
|
|
if project['type'] != 'web':
|
|
return jsonify({'error': '只有Web服务支持启动'}), 400
|
|
|
|
cwd = project.get('directory', '')
|
|
if not cwd:
|
|
return jsonify({'error': '项目目录未配置'}), 400
|
|
|
|
from config import WORKSPACE_DIR
|
|
import os
|
|
cwd = os.path.join(WORKSPACE_DIR, cwd) if not cwd.startswith('/') else cwd
|
|
|
|
if not os.path.exists(cwd):
|
|
return jsonify({'error': f'目录不存在: {cwd}'}), 400
|
|
|
|
if project.get('start_cmds'):
|
|
for name, info in project['start_cmds'].items():
|
|
run_command(info['cmd'], cwd, project_id, f'start-{name}')
|
|
else:
|
|
cmd = project.get('start_cmd', 'python3 app.py')
|
|
run_command(cmd, cwd, project_id, 'start')
|
|
|
|
return jsonify({'message': '服务启动中...'})
|
|
|
|
@app.route('/api/projects/<project_id>/stop', methods=['POST'])
|
|
def api_stop(project_id):
|
|
projects = load_projects()
|
|
project = next((p for p in projects if p['id'] == project_id), None)
|
|
if not project:
|
|
return jsonify({'error': '项目不存在'}), 404
|
|
|
|
if project['type'] != 'web':
|
|
return jsonify({'error': '只有Web服务支持停止'}), 400
|
|
|
|
ports = project.get('ports', [])
|
|
stopped = 0
|
|
for port in ports:
|
|
stopped += stop_process(port)
|
|
|
|
return jsonify({'message': f'已停止 {stopped} 个进程'})
|
|
|
|
@app.route('/api/projects/<project_id>/restart', methods=['POST'])
|
|
def api_restart(project_id):
|
|
"""重启服务"""
|
|
# 先停止
|
|
api_stop(project_id)
|
|
|
|
# 等待1秒
|
|
import time
|
|
time.sleep(1)
|
|
|
|
# 再启动
|
|
return api_start(project_id)
|
|
|
|
@app.route('/api/projects/add', methods=['POST'])
|
|
def api_add_project():
|
|
"""添加新项目"""
|
|
data = request.get_json()
|
|
|
|
if not data.get('id') or not data.get('name') or not data.get('ports'):
|
|
return jsonify({'error': '缺少必要字段'}), 400
|
|
|
|
projects = load_projects()
|
|
|
|
# 检查ID是否已存在
|
|
if any(p['id'] == data['id'] for p in projects):
|
|
return jsonify({'error': '项目ID已存在'}), 400
|
|
|
|
# 添加项目
|
|
project = {
|
|
'id': data['id'],
|
|
'name': data['name'],
|
|
'type': data.get('type', 'web'),
|
|
'ports': data['ports'],
|
|
'directory': data.get('directory', ''),
|
|
'start_cmd': data.get('start_cmd', ''),
|
|
'stop_cmd': data.get('stop_cmd', ''),
|
|
'health_url': data.get('health_url', ''),
|
|
'description': data.get('description', ''),
|
|
'git_repo': data.get('git_repo', ''),
|
|
'version': data.get('version', 'v1.0.0')
|
|
}
|
|
|
|
projects.append(project)
|
|
save_projects(projects)
|
|
|
|
log_message(f"添加项目: {project['name']}")
|
|
|
|
return jsonify({'message': '项目添加成功', 'project': project})
|
|
|
|
@app.route('/api/projects/<project_id>', methods=['PUT'])
|
|
def api_update_project(project_id):
|
|
"""更新项目配置"""
|
|
data = request.get_json()
|
|
projects = load_projects()
|
|
|
|
project = next((p for p in projects if p['id'] == project_id), None)
|
|
if not project:
|
|
return jsonify({'error': '项目不存在'}), 404
|
|
|
|
# 更新字段
|
|
for key in ['name', 'ports', 'directory', 'start_cmd', 'stop_cmd', 'health_url', 'description', 'git_repo', 'version']:
|
|
if key in data:
|
|
project[key] = data[key]
|
|
|
|
save_projects(projects)
|
|
|
|
log_message(f"更新项目: {project['name']}")
|
|
|
|
return jsonify({'message': '项目更新成功'})
|
|
|
|
@app.route('/api/projects/<project_id>', methods=['DELETE'])
|
|
def api_delete_project(project_id):
|
|
"""删除项目"""
|
|
projects = load_projects()
|
|
project = next((p for p in projects if p['id'] == project_id), None)
|
|
|
|
if not project:
|
|
return jsonify({'error': '项目不存在'}), 404
|
|
|
|
projects = [p for p in projects if p['id'] != project_id]
|
|
save_projects(projects)
|
|
|
|
log_message(f"删除项目: {project['name']}")
|
|
|
|
return jsonify({'message': '项目删除成功'})
|
|
|
|
@app.route('/api/external-services')
|
|
def api_external_services():
|
|
"""获取外部服务列表"""
|
|
data = load_projects_full()
|
|
services = data.get('external_services', [])
|
|
|
|
# 添加状态
|
|
for service in services:
|
|
ports = service.get('ports', [])
|
|
service['status'] = 'running' if all(check_port(p) for p in ports) else 'stopped'
|
|
|
|
return jsonify({'services': services})
|
|
|
|
@app.route('/api/external-services/add', methods=['POST'])
|
|
def api_add_external_service():
|
|
"""添加外部服务"""
|
|
data = request.get_json()
|
|
|
|
if not data.get('id') or not data.get('name') or not data.get('ports'):
|
|
return jsonify({'error': '缺少必要字段'}), 400
|
|
|
|
full_data = load_projects_full()
|
|
services = full_data.get('external_services', [])
|
|
|
|
# 检查ID是否已存在
|
|
if any(s['id'] == data['id'] for s in services):
|
|
return jsonify({'error': '服务ID已存在'}), 400
|
|
|
|
# 添加服务
|
|
service = {
|
|
'id': data['id'],
|
|
'name': data['name'],
|
|
'type': 'external',
|
|
'ports': data['ports'],
|
|
'health_url': data.get('health_url', ''),
|
|
'description': data.get('description', '')
|
|
}
|
|
|
|
services.append(service)
|
|
full_data['external_services'] = services
|
|
|
|
with open(PROJECTS_FILE, 'w', encoding='utf-8') as f:
|
|
json.dump(full_data, f, ensure_ascii=False, indent=2)
|
|
|
|
log_message(f"添加外部服务: {service['name']}")
|
|
|
|
return jsonify({'message': '服务添加成功', 'service': service}) |