Files
012-kaopeilian/deploy/scripts/webhook_handler.py
111 998211c483 feat: 初始化考培练系统项目
- 从服务器拉取完整代码
- 按框架规范整理项目结构
- 配置 Drone CI 测试环境部署
- 包含后端(FastAPI)、前端(Vue3)、管理端

技术栈: Vue3 + TypeScript + FastAPI + MySQL
2026-01-24 19:33:28 +08:00

165 lines
4.8 KiB
Python
Executable File
Raw Permalink 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
"""
GitHub Webhook处理器
监听GitHub推送事件自动触发部署
"""
import os
import subprocess
import json
import hmac
import hashlib
import logging
from flask import Flask, request, jsonify
import threading
import time
app = Flask(__name__)
# 配置
WEBHOOK_SECRET = "kaopeilian-webhook-secret-2025" # GitHub中配置的密钥
PROJECT_DIR = "/root/aiedu"
UPDATE_SCRIPT = "/root/aiedu/scripts/auto_update.sh"
# 配置日志
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler('/var/log/kaopeilian_webhook.log'),
logging.StreamHandler()
]
)
def verify_signature(payload_body, signature_header):
"""验证GitHub Webhook签名"""
if not signature_header:
return False
hash_object = hmac.new(
WEBHOOK_SECRET.encode('utf-8'),
payload_body,
hashlib.sha256
)
expected_signature = "sha256=" + hash_object.hexdigest()
return hmac.compare_digest(expected_signature, signature_header)
def run_update_async():
"""异步执行更新脚本"""
try:
# 等待5秒再执行避免GitHub推送过程中的竞争条件
time.sleep(5)
result = subprocess.run(
[UPDATE_SCRIPT],
cwd=PROJECT_DIR,
capture_output=True,
text=True,
timeout=600 # 10分钟超时
)
if result.returncode == 0:
logging.info(f"Update completed successfully: {result.stdout}")
else:
logging.error(f"Update failed: {result.stderr}")
except subprocess.TimeoutExpired:
logging.error("Update script timed out")
except Exception as e:
logging.error(f"Error running update script: {e}")
@app.route('/webhook', methods=['POST'])
def github_webhook():
"""处理GitHub Webhook请求"""
# 验证签名
signature = request.headers.get('X-Hub-Signature-256')
if not verify_signature(request.data, signature):
logging.warning("Invalid webhook signature")
return jsonify({"error": "Invalid signature"}), 403
# 解析请求
try:
payload = request.get_json()
except Exception as e:
logging.error(f"Failed to parse JSON: {e}")
return jsonify({"error": "Invalid JSON"}), 400
# 检查事件类型
event_type = request.headers.get('X-GitHub-Event')
if event_type != 'push':
logging.info(f"Ignoring event type: {event_type}")
return jsonify({"message": "Event ignored"}), 200
# 检查分支
ref = payload.get('ref', '')
if ref != 'refs/heads/production':
logging.info(f"Ignoring push to branch: {ref}")
return jsonify({"message": "Branch ignored"}), 200
# 获取提交信息
commit_info = {
'id': payload.get('after', 'unknown'),
'message': payload.get('head_commit', {}).get('message', 'No message'),
'author': payload.get('head_commit', {}).get('author', {}).get('name', 'Unknown'),
'timestamp': payload.get('head_commit', {}).get('timestamp', 'Unknown')
}
logging.info(f"Received push event: {commit_info}")
# 异步触发更新
update_thread = threading.Thread(target=run_update_async)
update_thread.daemon = True
update_thread.start()
return jsonify({
"message": "Update triggered successfully",
"commit": commit_info
}), 200
@app.route('/health', methods=['GET'])
def health_check():
"""健康检查端点"""
return jsonify({
"status": "healthy",
"service": "kaopeilian-webhook",
"timestamp": time.time()
}), 200
@app.route('/status', methods=['GET'])
def status():
"""状态检查端点"""
try:
# 检查项目目录
project_exists = os.path.exists(PROJECT_DIR)
# 检查更新脚本
script_exists = os.path.exists(UPDATE_SCRIPT)
script_executable = os.access(UPDATE_SCRIPT, os.X_OK) if script_exists else False
# 检查Docker服务
docker_result = subprocess.run(['docker', 'compose', 'ps'],
cwd=PROJECT_DIR, capture_output=True)
docker_running = docker_result.returncode == 0
return jsonify({
"status": "ok",
"checks": {
"project_directory": project_exists,
"update_script_exists": script_exists,
"update_script_executable": script_executable,
"docker_compose_running": docker_running
}
}), 200
except Exception as e:
return jsonify({
"status": "error",
"error": str(e)
}), 500
if __name__ == '__main__':
logging.info("Starting GitHub Webhook handler...")
app.run(host='0.0.0.0', port=9000, debug=False)