Files
project-panel/routes_cron.py
hz4th_coder 69edd1c63e refactor: 模块化重构 v3.4.0
🎯 重构目标:
- 将 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接口
2026-06-01 12:30:24 +08:00

254 lines
11 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""
Cron 管理 API 路由
"""
from flask import jsonify, request
from database import get_db
from utils import (
log_message, sync_crontab_from_db, get_system_crons,
cron_expression_to_human, get_next_run_time, guess_cron_name, guess_cron_category
)
def register_cron_routes(app):
"""注册 Cron 相关路由"""
@app.route('/api/cron/tasks')
def api_cron_tasks():
"""获取数据库中的 Cron 任务列表"""
db = get_db()
c = db.cursor()
c.execute("SELECT * FROM cron_tasks ORDER BY created_at DESC")
tasks = [dict(row) for row in c.fetchall()]
# 添加人类可读描述和下次执行时间
for task in tasks:
task['human_time'] = cron_expression_to_human(task['expression'])
task['next_run'] = get_next_run_time(task['expression'])
return jsonify({'tasks': tasks})
@app.route('/api/cron/tasks', methods=['POST'])
def api_cron_add():
"""添加新的 Cron 任务"""
data = request.json
if not data.get('name') or not data.get('expression') or not data.get('command'):
return jsonify({'error': '缺少必要字段'}), 400
db = get_db()
c = db.cursor()
# 插入任务
c.execute('''INSERT INTO cron_tasks (name, expression, command, description, category, enabled)
VALUES (?, ?, ?, ?, ?, ?)''',
(data['name'], data['expression'], data['command'], data.get('description', ''), data.get('category', 'other'), 1))
task_id = c.lastrowid
db.commit()
# 记录历史版本1
c.execute('''INSERT INTO cron_history (task_id, version, name, expression, command, description, category, enabled, operation)
VALUES (?, 1, ?, ?, ?, ?, ?, ?, 'create')''',
(task_id, data['name'], data['expression'], data['command'], data.get('description', ''), data.get('category', 'other'), 1))
db.commit()
# 同步到系统 crontab
sync_crontab_from_db(db)
log_message(f"添加 Cron 任务: {data['name']} ({data['expression']})")
return jsonify({'message': '任务添加成功', 'task_id': task_id})
@app.route('/api/cron/tasks/<int:task_id>', methods=['PUT'])
def api_cron_update(task_id):
"""更新 Cron 任务"""
data = request.json
db = get_db()
c = db.cursor()
# 获取当前任务
c.execute("SELECT * FROM cron_tasks WHERE id = ?", (task_id,))
old_task = c.fetchone()
if not old_task:
return jsonify({'error': '任务不存在'}), 404
# 获取当前版本号
c.execute("SELECT MAX(version) FROM cron_history WHERE task_id = ?", (task_id,))
max_version = c.fetchone()[0] or 0
new_version = max_version + 1
# 更新任务
update_fields = []
update_values = []
for field in ['name', 'expression', 'command', 'description', 'category', 'enabled']:
if field in data:
update_fields.append(f"{field} = ?")
update_values.append(data[field])
if update_fields:
update_fields.append("updated_at = CURRENT_TIMESTAMP")
c.execute(f"UPDATE cron_tasks SET {','.join(update_fields)} WHERE id = ?", update_values + [task_id])
db.commit()
# 记录历史
c.execute('''INSERT INTO cron_history (task_id, version, name, expression, command, description, category, enabled, operation)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, 'update')''',
(task_id, new_version, data.get('name', old_task['name']), data.get('expression', old_task['expression']),
data.get('command', old_task['command']), data.get('description', old_task['description']),
data.get('category', old_task['category']), data.get('enabled', old_task['enabled'])))
db.commit()
# 同步到系统 crontab
sync_crontab_from_db(db)
log_message(f"更新 Cron 任务 ID={task_id}: 版本 {new_version}")
return jsonify({'message': '任务更新成功', 'version': new_version})
@app.route('/api/cron/tasks/<int:task_id>', methods=['DELETE'])
def api_cron_delete(task_id):
"""删除 Cron 任务"""
db = get_db()
c = db.cursor()
c.execute("SELECT * FROM cron_tasks WHERE id = ?", (task_id,))
task = c.fetchone()
if not task:
return jsonify({'error': '任务不存在'}), 404
# 记录删除历史
c.execute("SELECT MAX(version) FROM cron_history WHERE task_id = ?", (task_id,))
max_version = c.fetchone()[0] or 0
c.execute('''INSERT INTO cron_history (task_id, version, name, expression, command, description, category, enabled, operation)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, 'delete')''',
(task_id, max_version + 1, task['name'], task['expression'], task['command'], task['description'], task['category'], task['enabled']))
db.commit()
# 删除任务
c.execute("DELETE FROM cron_tasks WHERE id = ?", (task_id,))
db.commit()
# 同步到系统 crontab
sync_crontab_from_db(db)
log_message(f"删除 Cron 任务 ID={task_id}: {task['name']}")
return jsonify({'message': '任务删除成功'})
@app.route('/api/cron/tasks/<int:task_id>/toggle', methods=['POST'])
def api_cron_toggle(task_id):
"""启用/禁用 Cron 任务"""
db = get_db()
c = db.cursor()
c.execute("SELECT enabled FROM cron_tasks WHERE id = ?", (task_id,))
task = c.fetchone()
if not task:
return jsonify({'error': '任务不存在'}), 404
new_enabled = 0 if task['enabled'] == 1 else 1
c.execute("UPDATE cron_tasks SET enabled = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?", (new_enabled, task_id))
db.commit()
# 同步到系统 crontab
sync_crontab_from_db(db)
log_message(f"切换 Cron 任务 ID={task_id} 状态: {new_enabled}")
return jsonify({'message': '状态已切换', 'enabled': new_enabled})
@app.route('/api/cron/tasks/<int:task_id>/history')
def api_cron_history(task_id):
"""获取任务历史版本"""
db = get_db()
c = db.cursor()
c.execute("SELECT * FROM cron_history WHERE task_id = ? ORDER BY version DESC", (task_id,))
history = [dict(row) for row in c.fetchall()]
for h in history:
if h['expression']:
h['human_time'] = cron_expression_to_human(h['expression'])
return jsonify({'history': history})
@app.route('/api/cron/tasks/<int:task_id>/rollback/<int:version>', methods=['POST'])
def api_cron_rollback(task_id, version):
"""回退到指定版本"""
db = get_db()
c = db.cursor()
c.execute("SELECT * FROM cron_history WHERE task_id = ? AND version = ?", (task_id, version))
history = c.fetchone()
if not history:
return jsonify({'error': '版本不存在'}), 404
# 更新任务到历史版本
c.execute('''UPDATE cron_tasks SET name = ?, expression = ?, command = ?, description = ?, category = ?, enabled = ?, updated_at = CURRENT_TIMESTAMP
WHERE id = ?''',
(history['name'], history['expression'], history['command'], history['description'], history['category'], history['enabled'], task_id))
db.commit()
# 同步到系统 crontab
sync_crontab_from_db(db)
log_message(f"回退 Cron 任务 ID={task_id} 到版本 {version}")
return jsonify({'message': f'已回退到版本 {version}'})
@app.route('/api/cron/tasks/<int:task_id>/logs')
def api_cron_logs(task_id):
"""获取任务执行日志"""
db = get_db()
c = db.cursor()
c.execute("SELECT * FROM cron_logs WHERE task_id = ? ORDER BY execution_time DESC LIMIT 100", (task_id,))
logs = [dict(row) for row in c.fetchall()]
return jsonify({'logs': logs})
@app.route('/api/cron/categories')
def api_cron_categories():
"""获取任务分类列表"""
categories = [
{'id': 'monitor', 'name': '系统监控', 'icon': 'ri-heart-pulse-line', 'color': 'blue'},
{'id': 'backup', 'name': '数据备份', 'icon': 'ri-archive-line', 'color': 'green'},
{'id': 'cleanup', 'name': '清理任务', 'icon': 'ri-delete-bin-line', 'color': 'yellow'},
{'id': 'report', 'name': '报表生成', 'icon': 'ri-file-chart-line', 'color': 'purple'},
{'id': 'sync', 'name': '数据同步', 'icon': 'ri-refresh-line', 'color': 'cyan'},
{'id': 'notification', 'name': '通知提醒', 'icon': 'ri-notification-line', 'color': 'orange'},
{'id': 'other', 'name': '其他任务', 'icon': 'ri-more-line', 'color': 'gray'},
]
return jsonify({'categories': categories})
@app.route('/api/cron/sync', methods=['POST'])
def api_cron_sync():
"""从系统 crontab 同步到数据库(清空旧的,只保留当前系统实际存在的)"""
crons = get_system_crons()
user_crons = [c for c in crons if c['source'] == 'user']
db = get_db()
c = db.cursor()
# 清空数据库中的旧任务
c.execute("DELETE FROM cron_history WHERE task_id IN (SELECT id FROM cron_tasks)")
c.execute("DELETE FROM cron_logs WHERE task_id IN (SELECT id FROM cron_tasks)")
c.execute("DELETE FROM cron_tasks")
synced_count = 0
for cron in user_crons:
# 尝试从命令推断分类和名称
name = guess_cron_name(cron['command'])
category = guess_cron_category(cron['command'])
c.execute('''INSERT INTO cron_tasks (name, expression, command, description, category, enabled)
VALUES (?, ?, ?, ?, ?, 1)''',
(name, cron['expression'], cron['command'], '', category))
synced_count += 1
db.commit()
log_message(f"从系统 crontab 同步了 {synced_count} 个任务(已清空旧任务)")
return jsonify({'message': f'同步完成,共 {synced_count} 个任务', 'count': synced_count})