Skip to content

Commit 731ae76

Browse files
committed
添加任务钩子
1 parent 3244a1f commit 731ae76

File tree

7 files changed

+997
-4
lines changed

7 files changed

+997
-4
lines changed

app.py

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010
import jwt
1111
import secrets
1212
import uuid
13+
import logging
14+
15+
logger = logging.getLogger(__name__)
1316

1417
app = Flask(__name__, static_folder='static')
1518
CORS(app) # 启用跨域支持
@@ -933,6 +936,181 @@ def get_all_execution_logs():
933936
return jsonify({'error': str(e)}), 500
934937

935938

939+
# 钩子管理相关API
940+
@app.route('/api/tasks/<int:task_id>/hooks', methods=['PUT'])
941+
@login_required
942+
def update_task_hooks(task_id):
943+
"""更新任务钩子配置"""
944+
try:
945+
import json
946+
947+
data = request.get_json()
948+
hooks_config = data.get('hooks_config', {})
949+
950+
# 验证钩子配置
951+
valid_hook_types = ['before_execute', 'after_success', 'after_failure']
952+
valid_script_types = ['python', 'shell']
953+
954+
for hook_type, config in hooks_config.items():
955+
if hook_type not in valid_hook_types:
956+
return jsonify({'error': f'无效的钩子类型: {hook_type}'}), 400
957+
958+
if not isinstance(config, dict):
959+
continue
960+
961+
script_type = config.get('script_type')
962+
if script_type and script_type not in valid_script_types:
963+
return jsonify({'error': f'无效的脚本类型: {script_type}'}), 400
964+
965+
timeout = config.get('timeout', 30)
966+
if not isinstance(timeout, (int, float)) or timeout < 1 or timeout > 300:
967+
return jsonify({'error': 'timeout 必须在 1-300 秒之间'}), 400
968+
969+
with get_db() as db:
970+
# 验证任务所属
971+
task = db.query(NotifyTask).filter(
972+
NotifyTask.id == task_id,
973+
NotifyTask.user_id == request.current_user.id
974+
).first()
975+
if not task:
976+
return jsonify({'error': '任务不存在'}), 404
977+
978+
# 更新钩子配置
979+
task.hooks_config = json.dumps(hooks_config, ensure_ascii=False)
980+
db.commit()
981+
982+
return jsonify({
983+
'success': True,
984+
'message': '钩子配置已更新'
985+
})
986+
except Exception as e:
987+
logger.error(f"更新钩子配置失败: {str(e)}")
988+
return jsonify({'error': str(e)}), 500
989+
990+
991+
@app.route('/api/tasks/<int:task_id>/hooks/logs', methods=['GET'])
992+
@login_required
993+
def get_task_hook_logs(task_id):
994+
"""获取任务钩子执行日志"""
995+
try:
996+
from models import HookExecutionLog
997+
998+
with get_db() as db:
999+
# 验证任务所属
1000+
task = db.query(NotifyTask).filter(
1001+
NotifyTask.id == task_id,
1002+
NotifyTask.user_id == request.current_user.id
1003+
).first()
1004+
if not task:
1005+
return jsonify({'error': '任务不存在'}), 404
1006+
1007+
# 分页查询
1008+
page = int(request.args.get('page', 1))
1009+
page_size = int(request.args.get('page_size', 50))
1010+
hook_type = request.args.get('hook_type') # 可选过滤
1011+
status = request.args.get('status') # 可选过滤
1012+
1013+
logs_query = db.query(HookExecutionLog).filter(
1014+
HookExecutionLog.task_id == task_id
1015+
)
1016+
1017+
if hook_type:
1018+
logs_query = logs_query.filter(HookExecutionLog.hook_type == hook_type)
1019+
1020+
if status:
1021+
logs_query = logs_query.filter(HookExecutionLog.status == status)
1022+
1023+
logs_query = logs_query.order_by(HookExecutionLog.execution_start.desc())
1024+
1025+
total = logs_query.count()
1026+
logs = logs_query.offset((page - 1) * page_size).limit(page_size).all()
1027+
1028+
return jsonify({
1029+
'total': total,
1030+
'page': page,
1031+
'page_size': page_size,
1032+
'logs': [log.to_dict() for log in logs]
1033+
})
1034+
except Exception as e:
1035+
logger.error(f"获取钩子日志失败: {str(e)}")
1036+
return jsonify({'error': str(e)}), 500
1037+
1038+
1039+
@app.route('/api/tasks/<int:task_id>/hooks/test', methods=['POST'])
1040+
@login_required
1041+
def test_task_hook(task_id):
1042+
"""测试钩子脚本"""
1043+
try:
1044+
import json
1045+
from hooks import execute_hook
1046+
1047+
data = request.get_json()
1048+
hook_type = data.get('hook_type')
1049+
script_type = data.get('script_type', 'python')
1050+
script = data.get('script', '')
1051+
timeout = data.get('timeout', 30)
1052+
1053+
if not hook_type or hook_type not in ['before_execute', 'after_success', 'after_failure']:
1054+
return jsonify({'error': '无效的钩子类型'}), 400
1055+
1056+
if not script:
1057+
return jsonify({'error': '脚本内容不能为空'}), 400
1058+
1059+
with get_db() as db:
1060+
# 验证任务所属
1061+
task = db.query(NotifyTask).filter(
1062+
NotifyTask.id == task_id,
1063+
NotifyTask.user_id == request.current_user.id
1064+
).first()
1065+
if not task:
1066+
return jsonify({'error': '任务不存在'}), 404
1067+
1068+
# 构造测试配置
1069+
test_hooks_config = {
1070+
hook_type: {
1071+
'enabled': True,
1072+
'script_type': script_type,
1073+
'script': script,
1074+
'timeout': timeout
1075+
}
1076+
}
1077+
1078+
# 临时保存原配置
1079+
original_config = task.hooks_config
1080+
task.hooks_config = json.dumps(test_hooks_config, ensure_ascii=False)
1081+
1082+
# 准备测试上下文
1083+
test_context = {}
1084+
if hook_type == 'after_success':
1085+
test_context = {'send_results': {'test': {'status': 'sent', 'message': '测试执行'}}}
1086+
elif hook_type == 'after_failure':
1087+
test_context = {'error': '测试错误', 'traceback': '测试堆栈'}
1088+
1089+
# 执行测试
1090+
result = execute_hook(
1091+
hook_type=hook_type,
1092+
task_id=task_id,
1093+
task=task,
1094+
context=test_context,
1095+
db_session=db,
1096+
task_execution_log_id=None
1097+
)
1098+
1099+
# 恢复原配置
1100+
task.hooks_config = original_config
1101+
db.commit()
1102+
1103+
return jsonify({
1104+
'success': result.get('success', False),
1105+
'output': result.get('output', ''),
1106+
'error': result.get('error'),
1107+
'data': result.get('data')
1108+
})
1109+
except Exception as e:
1110+
logger.error(f"测试钩子失败: {str(e)}")
1111+
return jsonify({'error': str(e)}), 500
1112+
1113+
9361114
# 告警规则相关API
9371115
@app.route('/api/alerts/rules', methods=['GET'])
9381116
@login_required

0 commit comments

Comments
 (0)