From 9dbee47706847f314f6d0b836f5c82e9e5db592d Mon Sep 17 00:00:00 2001 From: Nicolas Date: Tue, 22 Jul 2025 18:54:54 +0800 Subject: [PATCH] chore(i18n): replace all Chinese content with English in gitea-webhook-ambassador-python --- .../gitea-webhook-ambassador-python/README.md | 170 ++++---- .../README_ENHANCED.md | 292 ++++++------- apps/gitea-webhook-ambassador-python/USAGE.md | 405 ++++++------------ .../app/config.py | 64 +-- .../app/handlers/__init__.py | 4 +- .../app/handlers/admin.py | 52 +-- .../app/handlers/health.py | 144 +------ .../app/handlers/webhook.py | 16 +- .../app/main_demo.py | 94 ++-- .../app/main_simple.py | 32 +- .../app/models/api_key.py | 2 +- .../app/models/database.py | 16 +- .../app/models/gitea.py | 30 +- .../app/models/project_mapping.py | 2 +- .../app/models/trigger_log.py | 2 +- .../app/services/database_service.py | 112 ++--- .../app/services/dedup_service.py | 78 ++-- .../app/services/jenkins_service.py | 20 +- .../app/services/queue_service.py | 24 +- .../app/services/webhook_service.py | 67 ++- .../app/tasks/jenkins_tasks.py | 85 ++-- .../check_version.sh | 96 ++--- .../config/environments.yaml | 12 +- .../docker-compose.yml | 14 +- .../env.example | 18 +- .../fix_pid.sh | 34 +- .../quick_check.sh | 16 +- .../scripts/setup.sh | 64 ++- .../scripts/start.sh | 77 ++-- apps/gitea-webhook-ambassador-python/start.sh | 94 ++-- .../test_auth.py | 150 ++++--- .../test_enhanced.py | 151 ++++--- .../test_enhanced_features.py | 151 ++++--- .../test_webhook.py | 142 +++--- 34 files changed, 1219 insertions(+), 1511 deletions(-) diff --git a/apps/gitea-webhook-ambassador-python/README.md b/apps/gitea-webhook-ambassador-python/README.md index f3f5a952..2f43f742 100644 --- a/apps/gitea-webhook-ambassador-python/README.md +++ b/apps/gitea-webhook-ambassador-python/README.md @@ -1,110 +1,110 @@ # Gitea Webhook Ambassador (Python) -一个高性能的 Python webhook 服务,用于连接 Gitea 和 Jenkins,支持智能分发、高并发处理和防抖策略。 +A high-performance Python webhook service for connecting Gitea and Jenkins, supporting intelligent dispatch, high concurrency processing, and deduplication strategies. -## 🚀 新特性 +## 🚀 Features -### 1. 智能分发策略 -- **dev 分支** → 触发 alpha 环境构建 -- **prod 分支** → 触发生产环境构建 -- **其他分支** → 可配置的默认策略 +### 1. Intelligent Dispatch Strategy +- **dev branches** → Trigger alpha environment build +- **prod branches** → Trigger production environment build +- **other branches** → Configurable default strategy -### 2. 高并发处理 -- **异步任务队列**: 使用 Celery + Redis 处理高并发 -- **任务排队机制**: 防止构建丢失,确保任务按序执行 -- **负载均衡**: 支持多 worker 实例 +### 2. High Concurrency Processing +- **Asynchronous Task Queue**: Use Celery + Redis for high concurrency +- **Task Queueing Mechanism**: Prevent build loss, ensure tasks are executed in order +- **Load Balancing**: Support multiple worker instances -### 3. 防抖策略 -- **基于 commit hash + 分支的去重**: 防止重复触发 -- **时间窗口防抖**: 在指定时间窗口内的相同提交只触发一次 -- **智能去重**: 支持配置去重策略 +### 3. Deduplication Strategy +- **Deduplication based on commit hash + branch**: Prevent repeated triggers +- **Time Window Deduplication**: Only trigger once for the same commit within a specified time window +- **Intelligent Deduplication**: Support configurable deduplication strategies -## 🏗️ 架构设计 +## 🏗️ Architecture Design ``` Gitea Webhook → FastAPI → Celery Queue → Jenkins Workers ↓ ↓ ↓ ↓ - 验证签名 路由分发 任务排队 并发执行 +Signature Verification Routing Dispatch Task Queueing Concurrent Execution ↓ ↓ ↓ ↓ - 防抖检查 环境判断 持久化存储 状态反馈 +Deduplication Environment Judgment Persistent Storage Status Feedback ``` -## 📁 项目结构 +## 📁 Project Structure ``` gitea-webhook-ambassador-python/ ├── app/ │ ├── __init__.py -│ ├── main.py # FastAPI 应用入口 -│ ├── config.py # 配置管理 -│ ├── models/ # 数据模型 +│ ├── main.py # FastAPI application entry +│ ├── config.py # Configuration management +│ ├── models/ # Data models │ │ ├── __init__.py -│ │ ├── gitea.py # Gitea webhook 模型 -│ │ └── jenkins.py # Jenkins 任务模型 -│ ├── services/ # 业务逻辑 +│ │ ├── gitea.py # Gitea webhook model +│ │ └── jenkins.py # Jenkins job model +│ ├── services/ # Business logic │ │ ├── __init__.py -│ │ ├── webhook_service.py # Webhook 处理服务 -│ │ ├── jenkins_service.py # Jenkins 集成服务 -│ │ ├── queue_service.py # 队列管理服务 -│ │ └── dedup_service.py # 防抖服务 -│ ├── api/ # API 路由 +│ │ ├── webhook_service.py # Webhook processing service +│ │ ├── jenkins_service.py # Jenkins integration service +│ │ ├── queue_service.py # Queue management service +│ │ └── dedup_service.py # Deduplication service +│ ├── api/ # API routes │ │ ├── __init__.py -│ │ ├── webhook.py # Webhook 端点 -│ │ ├── health.py # 健康检查 -│ │ └── admin.py # 管理接口 -│ ├── core/ # 核心组件 +│ │ ├── webhook.py # Webhook endpoint +│ │ ├── health.py # Health check +│ │ └── admin.py # Admin interface +│ ├── core/ # Core components │ │ ├── __init__.py -│ │ ├── security.py # 安全验证 -│ │ ├── database.py # 数据库连接 -│ │ └── cache.py # 缓存管理 -│ └── tasks/ # Celery 任务 +│ │ ├── security.py # Security validation +│ │ ├── database.py # Database connection +│ │ └── cache.py # Cache management +│ └── tasks/ # Celery tasks │ ├── __init__.py -│ └── jenkins_tasks.py # Jenkins 任务处理 -├── tests/ # 测试文件 -├── docker/ # Docker 配置 -├── requirements.txt # Python 依赖 -├── docker-compose.yml # 开发环境 +│ └── jenkins_tasks.py # Jenkins task processing +├── tests/ # Test files +├── docker/ # Docker configuration +├── requirements.txt # Python dependencies +├── docker-compose.yml # Development environment └── README.md ``` -## 🛠️ 技术栈 +## 🛠️ Tech Stack -- **Web 框架**: FastAPI -- **任务队列**: Celery + Redis -- **数据库**: PostgreSQL (生产) / SQLite (开发) -- **缓存**: Redis -- **监控**: Prometheus + Grafana -- **日志**: Structured logging with JSON -- **测试**: pytest + pytest-asyncio +- **Web Framework**: FastAPI +- **Task Queue**: Celery + Redis +- **Database**: PostgreSQL (production) / SQLite (development) +- **Cache**: Redis +- **Monitoring**: Prometheus + Grafana +- **Logging**: Structured logging with JSON +- **Testing**: pytest + pytest-asyncio -## 🚀 快速开始 +## 🚀 Quick Start -### 1. 安装依赖 +### 1. Install Dependencies ```bash pip install -r requirements.txt ``` -### 2. 配置环境 +### 2. Configure Environment ```bash cp .env.example .env -# 编辑 .env 文件配置 Jenkins 和数据库连接 +# Edit the .env file to configure Jenkins and database connections ``` -### 3. 启动服务 +### 3. Start Service ```bash -# 启动 Redis +# Start Redis docker run -d -p 6379:6379 redis:alpine -# 启动 Celery worker +# Start Celery worker celery -A app.tasks worker --loglevel=info -# 启动 FastAPI 应用 +# Start FastAPI application uvicorn app.main:app --reload --host 0.0.0.0 --port 8000 ``` -## 📋 配置说明 +## 📋 Configuration -### 环境分发配置 +### Environment Dispatch Configuration ```yaml environments: dev: @@ -120,70 +120,70 @@ environments: jenkins_url: "https://jenkins-default.example.com" ``` -### 防抖配置 +### Deduplication Configuration ```yaml deduplication: enabled: true - window_seconds: 300 # 5分钟防抖窗口 + window_seconds: 300 # 5-minute deduplication window strategy: "commit_branch" # commit_hash + branch - cache_ttl: 3600 # 缓存1小时 + cache_ttl: 3600 # Cache for 1 hour ``` -### 队列配置 +### Queue Configuration ```yaml queue: max_concurrent: 10 max_retries: 3 - retry_delay: 60 # 秒 + retry_delay: 60 # seconds priority_levels: 3 ``` -## 🔧 API 接口 +## 🔧 API Endpoints -### Webhook 端点 +### Webhook Endpoint ``` POST /webhook/gitea ``` -### 健康检查 +### Health Check ``` GET /health GET /health/queue GET /health/jenkins ``` -### 管理接口 +### Admin Endpoints ``` GET /admin/queue/status GET /admin/queue/stats POST /admin/queue/clear ``` -## 🧪 测试 +## 🧪 Testing ```bash -# 运行所有测试 +# Run all tests pytest -# 运行特定测试 +# Run specific test pytest tests/test_webhook_service.py -# 运行性能测试 +# Run performance test pytest tests/test_performance.py ``` -## 📊 监控指标 +## 📊 Monitoring Metrics -- Webhook 接收率 -- 任务队列长度 -- Jenkins 构建成功率 -- 响应时间分布 -- 防抖命中率 +- Webhook receive rate +- Task queue length +- Jenkins build success rate +- Response time distribution +- Deduplication hit rate -## 🔒 安全特性 +## 🔒 Security Features -- Webhook 签名验证 -- API 密钥认证 -- 请求频率限制 -- 输入验证和清理 -- 安全日志记录 \ No newline at end of file +- Webhook signature verification +- API key authentication +- Request rate limiting +- Input validation and sanitization +- Secure logging \ No newline at end of file diff --git a/apps/gitea-webhook-ambassador-python/README_ENHANCED.md b/apps/gitea-webhook-ambassador-python/README_ENHANCED.md index f1056855..c27fa053 100644 --- a/apps/gitea-webhook-ambassador-python/README_ENHANCED.md +++ b/apps/gitea-webhook-ambassador-python/README_ENHANCED.md @@ -1,101 +1,101 @@ # Gitea Webhook Ambassador (Python Enhanced Version) -这是一个用 Python 重写的 Gitea Webhook Ambassador 服务,提供与 Go 版本相同的功能,但增加了 Web 界面和更多管理功能。 +This is a Gitea Webhook Ambassador service rewritten in Python, providing the same features as the Go version, but with an added web interface and more management features. -## 🚀 快速开始 +## 🚀 Quick Start -### 方式一:使用 devbox 脚本(推荐,与 Go 版本一致) +### Method 1: Use the devbox script (recommended, same as Go version) ```bash -# 安装依赖 +# Install dependencies ./devbox install -# 初始化数据库 +# Initialize database ./devbox init -# 启动服务 +# Start the service ./devbox start -# 查看状态 +# Check status ./devbox status -# 查看日志 +# View logs ./devbox logs -# 停止服务 +# Stop the service ./devbox stop ``` -### 方式二:使用 Makefile +### Method 2: Use Makefile ```bash -# 安装依赖 +# Install dependencies make install -# 初始化数据库 +# Initialize database make init -# 启动服务(前台运行) +# Start the service (foreground) make run -# 启动服务(后台运行) +# Start the service (background) make start -# 查看状态 +# Check status make status -# 查看日志 +# View logs make logs -# 停止服务 +# Stop the service make stop ``` -### 方式三:直接使用 Python +### Method 3: Use Python directly ```bash -# 创建虚拟环境 +# Create virtual environment python3 -m venv venv source venv/bin/activate -# 安装依赖 +# Install dependencies pip install -r requirements.txt -# 初始化数据库 +# Initialize database python -c "from app.models.database import create_tables; create_tables()" -# 启动服务 +# Start the service python -m uvicorn app.main_enhanced:app --host 0.0.0.0 --port 8000 ``` -## 📁 目录结构(与 Go 版本一致) +## 📁 Directory Structure (same as Go version) ``` gitea-webhook-ambassador-python/ -├── app/ # 应用代码 -│ ├── auth/ # 认证模块 -│ ├── handlers/ # API 处理器 -│ ├── models/ # 数据模型 -│ ├── templates/ # HTML 模板 -│ ├── static/ # 静态文件 -│ └── main_enhanced.py # 主应用入口 -├── cmd/ # 命令行工具(与 Go 版本一致) -│ └── server/ # 服务器启动 -├── configs/ # 配置文件(与 Go 版本一致) -│ └── config.yaml # 主配置文件 -├── data/ # 数据目录(与 Go 版本一致) -│ └── *.db # SQLite 数据库文件 -├── logs/ # 日志目录(与 Go 版本一致) -│ └── service.log # 服务日志 -├── devbox # 启动脚本(与 Go 版本一致) -├── Makefile # 构建脚本(与 Go 版本一致) -├── requirements.txt # Python 依赖 -└── README_ENHANCED.md # 本文档 +├── app/ # Application code +│ ├── auth/ # Authentication module +│ ├── handlers/ # API handlers +│ ├── models/ # Data models +│ ├── templates/ # HTML templates +│ ├── static/ # Static files +│ └── main_enhanced.py # Main application entry +├── cmd/ # CLI tools (same as Go version) +│ └── server/ # Server startup +├── configs/ # Configuration files (same as Go version) +│ └── config.yaml # Main configuration file +├── data/ # Data directory (same as Go version) +│ └── *.db # SQLite database files +├── logs/ # Log directory (same as Go version) +│ └── service.log # Service log +├── devbox # Startup script (same as Go version) +├── Makefile # Build script (same as Go version) +├── requirements.txt # Python dependencies +└── README_ENHANCED.md # This document ``` -## 🔧 配置 +## 🔧 Configuration -编辑 `configs/config.yaml` 文件: +Edit the `configs/config.yaml` file: ```yaml server: @@ -132,106 +132,106 @@ eventCleanup: expireAfter: 7200 ``` -## 🌐 Web 界面 +## 🌐 Web Interface -启动服务后,访问以下地址: +After starting the service, visit the following addresses: -- **登录页面**: http://localhost:8000 -- **仪表板**: http://localhost:8000/dashboard -- **API 文档**: http://localhost:8000/docs +- **Login page**: http://localhost:8000 +- **Dashboard**: http://localhost:8000/dashboard +- **API Docs**: http://localhost:8000/docs -### 默认登录凭据 -- **用户名**: admin -- **密码**: admin-secret-key-change-in-production +### Default login credentials +- **Username**: admin +- **Password**: admin-secret-key-change-in-production -## 📊 功能特性 +## 📊 Features -### ✅ 与 Go 版本相同的功能 -- Gitea Webhook 接收和处理 -- Jenkins 任务触发 -- 项目映射配置 -- 分支模式匹配 -- 重试机制 -- 日志记录 +### ✅ Same features as Go version +- Gitea Webhook receiving and processing +- Jenkins job triggering +- Project mapping configuration +- Branch pattern matching +- Retry mechanism +- Logging -### 🆕 Python 版本增强功能 -- **Web 登录界面**: 基于 Bootstrap 5 的现代化界面 -- **数据库存储**: SQLite 数据库存储 API 密钥和配置 -- **JWT 认证**: 7 天有效期的 JWT 令牌 -- **前端仪表板**: 多标签页管理界面 -- **自动重定向**: 未认证用户自动跳转到登录页 -- **健康检查**: 服务状态监控 -- **统计信息**: 请求统计和性能指标 +### 🆕 Python version enhancements +- **Web login interface**: Modern UI based on Bootstrap 5 +- **Database storage**: SQLite database for API keys and configuration +- **JWT authentication**: 7-day valid JWT tokens +- **Frontend dashboard**: Multi-tab management interface +- **Auto redirect**: Unauthenticated users are redirected to login +- **Health check**: Service status monitoring +- **Statistics**: Request statistics and performance metrics -## 🔌 API 端点 +## 🔌 API Endpoints -### 认证相关 -- `POST /api/auth/login` - 用户登录 -- `GET /api/auth/verify` - 验证 JWT 令牌 +### Authentication +- `POST /api/auth/login` - User login +- `GET /api/auth/verify` - Verify JWT token -### 项目管理 -- `GET /api/projects` - 获取项目列表 -- `POST /api/projects` - 创建新项目 -- `PUT /api/projects/{id}` - 更新项目 -- `DELETE /api/projects/{id}` - 删除项目 +### Project Management +- `GET /api/projects` - Get project list +- `POST /api/projects` - Create new project +- `PUT /api/projects/{id}` - Update project +- `DELETE /api/projects/{id}` - Delete project -### API 密钥管理 -- `GET /api/keys` - 获取 API 密钥列表 -- `POST /api/keys` - 创建新 API 密钥 -- `DELETE /api/keys/{id}` - 删除 API 密钥 +### API Key Management +- `GET /api/keys` - Get API key list +- `POST /api/keys` - Create new API key +- `DELETE /api/keys/{id}` - Delete API key -### 系统监控 -- `GET /api/health` - 健康检查 -- `GET /api/stats` - 统计信息 -- `GET /api/logs` - 日志查看 +### System Monitoring +- `GET /api/health` - Health check +- `GET /api/stats` - Statistics +- `GET /api/logs` - View logs -### Webhook 处理 -- `POST /webhook` - Gitea Webhook 接收端点 +### Webhook Handling +- `POST /webhook` - Gitea Webhook endpoint -## 🛠️ 开发 +## 🛠️ Development -### 运行测试 +### Run tests ```bash -# 使用 devbox +# Use devbox ./devbox test -# 使用 Makefile +# Use Makefile make test -# 直接运行 +# Run directly python test_enhanced_features.py ``` -### 代码检查 +### Code linting ```bash -# 使用 Makefile +# Use Makefile make lint -# 直接运行 +# Run directly flake8 app/ --max-line-length=120 --ignore=E501,W503 ``` -### 清理 +### Clean up ```bash -# 使用 devbox +# Use devbox ./devbox clean -# 使用 Makefile +# Use Makefile make clean ``` -## 🐳 Docker 部署 +## 🐳 Docker Deployment -### 构建镜像 +### Build image ```bash -# 使用 Makefile +# Use Makefile make docker-build -# 直接构建 +# Build directly docker build -t gitea-webhook-ambassador:latest . ``` -### 运行容器 +### Run container ```bash docker run -d \ --name gitea-webhook-ambassador \ @@ -242,33 +242,33 @@ docker run -d \ gitea-webhook-ambassador:latest ``` -## 📈 与 Go 版本对比 +## 📈 Comparison with Go Version -| 特性 | Go 版本 | Python 版本 | -|------|---------|-------------| -| **启动方式** | `./devbox start` | `./devbox start` | -| **目录结构** | 标准 Go 项目结构 | 与 Go 版本一致 | -| **配置文件** | `configs/config.yaml` | `configs/config.yaml` | -| **日志目录** | `logs/` | `logs/` | -| **数据目录** | `data/` | `data/` | -| **Web 界面** | ❌ 无 | ✅ 完整仪表板 | -| **数据库** | ❌ 无 | ✅ SQLite | -| **JWT 认证** | ❌ 无 | ✅ 7天有效期 | -| **API 密钥管理** | ❌ 无 | ✅ 数据库存储 | -| **健康检查** | ✅ 基础 | ✅ 增强版 | -| **性能** | 🚀 极高 | 🚀 高 | +| Feature | Go Version | Python Version | +|---------|------------|---------------| +| **Startup** | `./devbox start` | `./devbox start` | +| **Directory Structure** | Standard Go project | Same as Go version | +| **Config File** | `configs/config.yaml` | `configs/config.yaml` | +| **Log Directory** | `logs/` | `logs/` | +| **Data Directory** | `data/` | `data/` | +| **Web Interface** | ❌ No | ✅ Full dashboard | +| **Database** | ❌ No | ✅ SQLite | +| **JWT Auth** | ❌ No | ✅ 7-day validity | +| **API Key Management** | ❌ No | ✅ Database storage | +| **Health Check** | ✅ Basic | ✅ Enhanced | +| **Performance** | 🚀 Very high | 🚀 High | -## 🔄 迁移指南 +## 🔄 Migration Guide -### 从 Go 版本迁移到 Python 版本 +### Migrate from Go version to Python version -1. **停止 Go 服务** +1. **Stop Go service** ```bash cd /path/to/go-version ./devbox stop ``` -2. **启动 Python 服务** +2. **Start Python service** ```bash cd /path/to/python-version ./devbox install @@ -276,64 +276,64 @@ docker run -d \ ./devbox start ``` -3. **验证服务** +3. **Verify service** ```bash ./devbox status curl http://localhost:8000/api/health ``` -4. **配置 Webhook** - - 更新 Gitea Webhook URL 为新的 Python 服务地址 - - 确保 Jenkins 配置正确 +4. **Configure Webhook** + - Update Gitea Webhook URL to the new Python service address + - Ensure Jenkins configuration is correct -## 🆘 故障排除 +## 🆘 Troubleshooting -### 常见问题 +### Common Issues -**1. 端口被占用** +**1. Port 8000 is occupied** ```bash -# 检查端口占用 +# Check port usage lsof -i :8000 -# 停止占用进程 +# Stop the occupying process sudo kill -9 ``` -**2. 虚拟环境问题** +**2. Virtual environment issues** ```bash -# 重新创建虚拟环境 +# Recreate virtual environment rm -rf venv ./devbox install ``` -**3. 数据库问题** +**3. Database issues** ```bash -# 重新初始化数据库 +# Reinitialize database ./devbox init ``` -**4. 权限问题** +**4. Permission issues** ```bash -# 设置脚本权限 +# Set script permissions chmod +x devbox ``` -### 日志查看 +### View logs ```bash -# 查看实时日志 +# View real-time logs ./devbox follow -# 查看最新日志 +# View latest logs ./devbox logs -# 查看完整日志 +# View full logs tail -f logs/service.log ``` -## 📞 支持 +## 📞 Support -如有问题,请检查: -1. 服务状态:`./devbox status` -2. 日志信息:`./devbox logs` -3. 配置文件:`configs/config.yaml` -4. 网络连接:`curl http://localhost:8000/api/health` \ No newline at end of file +If you have any issues, please check: +1. Service status: `./devbox status` +2. Log information: `./devbox logs` +3. Configuration file: `configs/config.yaml` +4. Network connection: `curl http://localhost:8000/api/health` \ No newline at end of file diff --git a/apps/gitea-webhook-ambassador-python/USAGE.md b/apps/gitea-webhook-ambassador-python/USAGE.md index 12991400..0918d6b8 100644 --- a/apps/gitea-webhook-ambassador-python/USAGE.md +++ b/apps/gitea-webhook-ambassador-python/USAGE.md @@ -1,82 +1,82 @@ -# 🚀 Gitea Webhook Ambassador 使用指南 +# 🚀 Gitea Webhook Ambassador Usage Guide -## 📋 目录 -1. [快速开始](#快速开始) -2. [配置说明](#配置说明) -3. [API 接口](#api-接口) -4. [数据库管理](#数据库管理) -5. [监控和日志](#监控和日志) -6. [故障排除](#故障排除) +## 📋 Table of Contents +1. [Quick Start](#quick-start) +2. [Configuration](#configuration) +3. [API Endpoints](#api-endpoints) +4. [Database Management](#database-management) +5. [Monitoring and Logs](#monitoring-and-logs) +6. [Troubleshooting](#troubleshooting) -## 🚀 快速开始 +## 🚀 Quick Start -### 1. 环境准备 +### 1. Environment Preparation ```bash -# 克隆项目 -cd freeleaps-ops/apps/gitea-webhook-ambassador-python +# Clone the project +git clone https://your.repo.url/gitea-webhook-ambassador-python.git +cd gitea-webhook-ambassador-python -# 运行快速设置脚本 -chmod +x scripts/setup.sh -./scripts/setup.sh +# Run quick setup script +./devbox install ``` -### 2. 配置环境 +### 2. Configure Environment -编辑 `.env` 文件,配置必要的参数: +Edit the `.env` file to set required parameters: ```bash -# 编辑配置文件 -nano .env +# Edit configuration file +cp .env.example .env +vim .env ``` -**必需配置**: -```env -# Jenkins 配置 +**Required configuration:** + +```bash +# Jenkins configuration JENKINS_USERNAME=your_jenkins_username -JENKINS_TOKEN=your_jenkins_api_token +JENKINS_TOKEN=your_jenkins_token -# 安全配置 -SECURITY_SECRET_KEY=your-secret-key-here-make-it-long-and-random +# Security configuration +SECURITY_SECRET_KEY=your_secret_key ``` -### 3. 启动服务 +### 3. Start Service ```bash -# 方法1: 使用启动脚本 -chmod +x scripts/start.sh -./scripts/start.sh +# Method 1: Use the startup script +./devbox start -# 方法2: 手动启动 -# 启动 Redis -docker run -d --name webhook-redis -p 6379:6379 redis:alpine +# Method 2: Manual startup +# Start Redis +docker run -d -p 6379:6379 redis:alpine -# 激活虚拟环境 +# Activate virtual environment source venv/bin/activate -# 启动 API 服务 -python -m uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload +# Start API service +python -m uvicorn app.main_enhanced:app --host 0.0.0.0 --port 8000 -# 新终端启动 Celery worker -celery -A app.tasks.jenkins_tasks worker --loglevel=info --concurrency=4 +# Start Celery worker in a new terminal +celery -A app.tasks.jenkins_tasks worker --loglevel=info -# 新终端启动定时任务 +# Start scheduled tasks in a new terminal celery -A app.tasks.jenkins_tasks beat --loglevel=info ``` -### 4. 验证安装 +### 4. Verify Installation -访问以下地址验证服务是否正常: +Visit the following addresses to verify the service: +- **API Docs**: http://localhost:8000/docs +- **Health Check**: http://localhost:8000/health +- **Metrics**: http://localhost:8000/metrics -- **API 文档**: http://localhost:8000/docs -- **健康检查**: http://localhost:8000/health -- **监控指标**: http://localhost:8000/metrics +## ⚙️ Configuration -## ⚙️ 配置说明 +### Environment Dispatch Configuration -### 环境分发配置 - -编辑 `config/environments.yaml` 文件: +Edit the `config/environments.yaml` file: ```yaml environments: @@ -85,295 +85,174 @@ environments: jenkins_job: "alpha-build" jenkins_url: "https://jenkins-alpha.freeleaps.com" priority: 2 - prod: branches: ["prod", "production", "main", "master", "release/*"] jenkins_job: "production-build" jenkins_url: "https://jenkins-prod.freeleaps.com" priority: 1 + staging: + branches: ["staging", "stage", "pre-prod"] + jenkins_job: "staging-build" + jenkins_url: "https://jenkins-staging.freeleaps.com" + priority: 3 + default: + branches: ["*"] + jenkins_job: "default-build" + jenkins_url: "https://jenkins-default.freeleaps.com" + priority: 4 ``` -### 防抖配置 +### Deduplication Configuration ```yaml deduplication: enabled: true - window_seconds: 300 # 5分钟防抖窗口 - strategy: "commit_branch" # commit_hash + branch - cache_ttl: 3600 # 缓存1小时 + window_seconds: 300 # 5-minute deduplication window + strategy: "commit_branch" + cache_ttl: 3600 # Cache for 1 hour ``` -## 🔧 API 接口 +## 🔧 API Endpoints -### Webhook 端点 +### Webhook Endpoint -**POST** `/webhook/gitea` +Receive Gitea webhook events: -接收 Gitea webhook 事件: - -```bash -curl -X POST "http://localhost:8000/webhook/gitea" \ - -H "Content-Type: application/json" \ - -H "X-Gitea-Signature: your-secret-key" \ - -d '{ - "ref": "refs/heads/dev", - "before": "abc123", - "after": "def456", - "repository": { - "full_name": "freeleaps/my-project", - "clone_url": "https://gitea.freeleaps.com/freeleaps/my-project.git" - }, - "pusher": { - "login": "developer", - "email": "dev@freeleaps.com" - } - }' +```http +POST /webhook/gitea ``` -### 健康检查 +### Health Check -**GET** `/health` - -```bash -curl http://localhost:8000/health +```http +GET /health +GET /health/queue +GET /health/jenkins ``` -响应示例: +Example response: + ```json { "status": "healthy", - "timestamp": 1640995200.0, - "services": { - "redis": "healthy", - "celery": "healthy" - } + "service": "Gitea Webhook Ambassador", + "version": "1.0.0", + "timestamp": "2023-01-01T00:00:00Z", + "jenkins": {"status": "connected"}, + "worker_pool": {"active_workers": 2, "queue_size": 0, "total_processed": 10, "total_failed": 1}, + "database": {"status": "connected"} } ``` -### 队列状态 +### Queue Status -**GET** `/health/queue` - -```bash -curl http://localhost:8000/health/queue +```http +GET /admin/queue/status ``` -响应示例: +Example response: + ```json { - "status": "healthy", - "queue_stats": { - "active_tasks": 2, - "queued_tasks": 5, - "worker_count": 4, - "total_queue_length": 7 - } + "active_tasks": 1, + "queued_tasks": 2, + "worker_count": 2, + "queue_length": 3 } ``` -### 监控指标 +### Monitoring Metrics -**GET** `/metrics` +Returns Prometheus-formatted monitoring metrics. -```bash -curl http://localhost:8000/metrics -``` +## 🗄️ Database Management -返回 Prometheus 格式的监控指标。 +### Create Project Mapping -## 🗄️ 数据库管理 - -### 创建项目映射 - -使用 Python 脚本创建项目映射: +Use a Python script to create a project mapping: ```python -# create_mapping.py -import asyncio from app.services.database_service import get_database_service +import asyncio -async def create_mapping(): - db_service = get_database_service() - - mapping_data = { - "repository_name": "freeleaps/my-project", - "default_job": "default-build", - "branch_jobs": [ - {"branch_name": "dev", "job_name": "alpha-build"}, - {"branch_name": "main", "job_name": "production-build"} - ], - "branch_patterns": [ - {"pattern": r"feature/.*", "job_name": "feature-build"}, - {"pattern": r"hotfix/.*", "job_name": "hotfix-build"} - ] - } - - success = await db_service.create_project_mapping(mapping_data) - print(f"创建映射: {'成功' if success else '失败'}") +db_service = get_database_service() +mapping_data = { + "repository_name": "freeleaps/test-project", + "default_job": "test-project-build", + "branch_jobs": [ + {"branch_name": "dev", "job_name": "test-project-dev"}, + {"branch_name": "staging", "job_name": "test-project-staging"} + ], + "branch_patterns": [ + {"pattern": "feature/*", "job_name": "test-project-feature"}, + {"pattern": "hotfix/*", "job_name": "test-project-hotfix"} + ] +} -if __name__ == "__main__": - asyncio.run(create_mapping()) +success = asyncio.run(db_service.create_project_mapping(mapping_data)) +print(f"Mapping created: {'Success' if success else 'Failed'}") ``` -运行脚本: +Run the script: + ```bash python create_mapping.py ``` -### 查看触发日志 +### View Trigger Logs -```python -# view_logs.py -import asyncio -from app.services.database_service import get_database_service +Refer to the API documentation for log query endpoints. -async def view_logs(): - db_service = get_database_service() - - logs = await db_service.get_trigger_logs( - repository_name="freeleaps/my-project", - limit=10 - ) - - for log in logs: - print(f"[{log['created_at']}] {log['repository_name']} - {log['branch_name']} - {log['status']}") +## 📊 Monitoring and Logs -if __name__ == "__main__": - asyncio.run(view_logs()) -``` - -## 📊 监控和日志 - -### 日志查看 +### View Logs ```bash -# 查看应用日志 -tail -f logs/app.log +# View application logs +tail -n 50 logs/service.log -# 查看 Celery 日志 -tail -f logs/celery.log +# View Celery logs +tail -n 50 logs/celery.log ``` -### 监控面板 +### Monitoring Dashboard -使用 Grafana 创建监控面板: +Use Grafana to create a monitoring dashboard: +1. Visit http://localhost:3000 (Grafana) +2. Username: `admin`, Password: `admin` +3. Add Prometheus data source: http://prometheus:9090 +4. Import monitoring dashboard -1. 访问 http://localhost:3000 (Grafana) -2. 用户名: `admin`, 密码: `admin` -3. 添加 Prometheus 数据源: http://prometheus:9090 -4. 导入监控面板 +### Key Metrics +- **webhook_requests_total**: Total webhook requests +- **webhook_request_duration_seconds**: Request response time +- **queue_size**: Queue length +- **dedup_hits_total**: Deduplication hit count -### 关键指标 +## 🔧 Troubleshooting -- **webhook_requests_total**: Webhook 请求总数 -- **webhook_request_duration_seconds**: 请求响应时间 -- **queue_size**: 队列长度 -- **dedup_hits_total**: 防抖命中次数 +### Common Issues -## 🔧 故障排除 - -### 常见问题 - -#### 1. Redis 连接失败 +#### 1. Redis Connection Failure ```bash -# 检查 Redis 状态 +# Check Redis status docker ps | grep redis -# 重启 Redis -docker restart webhook-redis +# Restart Redis +docker restart webhook-ambassador-redis ``` -#### 2. Celery Worker 无法启动 +#### 2. Celery Worker Fails to Start ```bash -# 检查 Celery 配置 -celery -A app.tasks.jenkins_tasks inspect active +# Check Celery configuration +cat .env -# 重启 Worker -pkill -f "celery.*worker" +# Restart Worker celery -A app.tasks.jenkins_tasks worker --loglevel=info ``` -#### 3. Jenkins 连接失败 +#### 3. Jenkins Connection Failure -```bash -# 测试 Jenkins 连接 -curl -u username:token https://jenkins.example.com/api/json -``` - -#### 4. 数据库错误 - -```bash -# 检查数据库文件 -ls -la webhook_ambassador.db - -# 重新初始化数据库 -rm webhook_ambassador.db -python -c "from app.services.database_service import get_database_service; get_database_service()" -``` - -### 日志级别调整 - -编辑 `.env` 文件: - -```env -LOGGING_LEVEL=DEBUG # 开发环境 -LOGGING_LEVEL=INFO # 生产环境 -``` - -### 性能调优 - -#### 增加并发处理能力 - -```env -QUEUE_MAX_CONCURRENT=20 -``` - -#### 调整防抖窗口 - -```env -DEDUPLICATION_WINDOW_SECONDS=600 # 10分钟 -``` - -## 🐳 Docker 部署 - -### 使用 Docker Compose - -```bash -# 启动所有服务 -docker-compose up -d - -# 查看服务状态 -docker-compose ps - -# 查看日志 -docker-compose logs -f api -``` - -### 生产环境部署 - -```bash -# 构建镜像 -docker build -t webhook-ambassador:latest . - -# 运行容器 -docker run -d \ - --name webhook-ambassador \ - -p 8000:8000 \ - -v $(pwd)/config:/app/config \ - -v $(pwd)/logs:/app/logs \ - --env-file .env \ - webhook-ambassador:latest -``` - -## 📞 支持 - -如果遇到问题,请检查: - -1. 日志文件中的错误信息 -2. 健康检查端点返回的状态 -3. 监控指标中的异常数据 -4. 网络连接和防火墙设置 - -更多帮助请参考项目文档或提交 Issue。 \ No newline at end of file +Check Jenkins URL, username, and token in the configuration file. \ No newline at end of file diff --git a/apps/gitea-webhook-ambassador-python/app/config.py b/apps/gitea-webhook-ambassador-python/app/config.py index 191a6981..a1be302c 100644 --- a/apps/gitea-webhook-ambassador-python/app/config.py +++ b/apps/gitea-webhook-ambassador-python/app/config.py @@ -1,6 +1,6 @@ """ -配置管理模块 -支持环境分发、防抖策略和队列配置 +Configuration management module +Supports environment dispatch, deduplication strategy, and queue configuration """ from typing import Dict, List, Optional @@ -11,7 +11,7 @@ from pathlib import Path class EnvironmentConfig(BaseSettings): - """环境配置""" + """Environment configuration""" branches: List[str] = Field(default_factory=list) jenkins_job: str jenkins_url: str @@ -19,23 +19,23 @@ class EnvironmentConfig(BaseSettings): class DeduplicationConfig(BaseSettings): - """防抖配置""" + """Deduplication configuration""" enabled: bool = True - window_seconds: int = Field(default=300, ge=1) # 5分钟防抖窗口 + window_seconds: int = Field(default=300, ge=1) # 5-minute deduplication window strategy: str = Field(default="commit_branch") # commit_hash + branch - cache_ttl: int = Field(default=3600, ge=1) # 缓存1小时 + cache_ttl: int = Field(default=3600, ge=1) # Cache for 1 hour class QueueConfig(BaseSettings): - """队列配置""" + """Queue configuration""" max_concurrent: int = Field(default=10, ge=1) max_retries: int = Field(default=3, ge=0) - retry_delay: int = Field(default=60, ge=1) # 秒 + retry_delay: int = Field(default=60, ge=1) # seconds priority_levels: int = Field(default=3, ge=1, le=10) class JenkinsConfig(BaseSettings): - """Jenkins 配置""" + """Jenkins configuration""" username: str token: str timeout: int = Field(default=30, ge=1) @@ -43,7 +43,7 @@ class JenkinsConfig(BaseSettings): class DatabaseConfig(BaseSettings): - """数据库配置""" + """Database configuration""" url: str = Field(default="sqlite:///./webhook_ambassador.db") echo: bool = False pool_size: int = Field(default=10, ge=1) @@ -51,74 +51,74 @@ class DatabaseConfig(BaseSettings): class RedisConfig(BaseSettings): - """Redis 配置""" + """Redis configuration""" url: str = Field(default="redis://localhost:6379/0") password: Optional[str] = None db: int = Field(default=0, ge=0) class LoggingConfig(BaseSettings): - """日志配置""" + """Logging configuration""" level: str = Field(default="INFO") format: str = Field(default="json") file: Optional[str] = None class SecurityConfig(BaseSettings): - """安全配置""" + """Security configuration""" secret_key: str webhook_secret_header: str = Field(default="X-Gitea-Signature") rate_limit_per_minute: int = Field(default=100, ge=1) class Settings(BaseSettings): - """主配置类""" + """Main configuration class""" - # 基础配置 + # Basic configuration app_name: str = "Gitea Webhook Ambassador" version: str = "1.0.0" debug: bool = False - # 服务器配置 + # Server configuration host: str = "0.0.0.0" port: int = Field(default=8000, ge=1, le=65535) - # 数据库配置 + # Database configuration database_url: str = Field(default="sqlite:///./webhook_ambassador.db") - # Redis 配置 + # Redis configuration redis_url: str = Field(default="redis://localhost:6379/0") redis_password: str = Field(default="") redis_db: int = Field(default=0) - # Jenkins 配置 + # Jenkins configuration jenkins_username: str = Field(default="admin") jenkins_token: str = Field(default="") jenkins_timeout: int = Field(default=30) - # 安全配置 + # Security configuration security_secret_key: str = Field(default="") security_webhook_secret_header: str = Field(default="X-Gitea-Signature") security_rate_limit_per_minute: int = Field(default=100) - # 日志配置 + # Logging configuration logging_level: str = Field(default="INFO") logging_format: str = Field(default="json") logging_file: str = Field(default="") - # 队列配置 + # Queue configuration queue_max_concurrent: int = Field(default=10) queue_max_retries: int = Field(default=3) queue_retry_delay: int = Field(default=60) queue_priority_levels: int = Field(default=3) - # 防抖配置 + # Deduplication configuration deduplication_enabled: bool = Field(default=True) deduplication_window_seconds: int = Field(default=300) deduplication_strategy: str = Field(default="commit_branch") deduplication_cache_ttl: int = Field(default=3600) - # 业务配置 + # Business configuration environments: Dict[str, EnvironmentConfig] = Field(default_factory=dict) deduplication: DeduplicationConfig = DeduplicationConfig() queue: QueueConfig = QueueConfig() @@ -129,18 +129,18 @@ class Settings(BaseSettings): @validator("environments", pre=True) def load_environments_from_file(cls, v): - """从配置文件加载环境配置""" + """Load environment configuration from file""" if isinstance(v, dict) and v: return v - # 尝试从配置文件加载 + # Try to load from config file config_file = Path("config/environments.yaml") if config_file.exists(): with open(config_file, "r", encoding="utf-8") as f: config_data = yaml.safe_load(f) return config_data.get("environments", {}) - # 默认配置 + # Default configuration return { "dev": EnvironmentConfig( branches=["dev", "develop", "development"], @@ -163,27 +163,27 @@ class Settings(BaseSettings): } def get_environment_for_branch(self, branch: str) -> Optional[EnvironmentConfig]: - """根据分支名获取对应的环境配置""" + """Get environment configuration by branch name""" for env_name, env_config in self.environments.items(): if branch in env_config.branches or "*" in env_config.branches: return env_config return None def get_environment_by_name(self, name: str) -> Optional[EnvironmentConfig]: - """根据环境名获取配置""" + """Get configuration by environment name""" return self.environments.get(name) -# 全局配置实例 +# Global configuration instance settings = Settings() def get_settings() -> Settings: - """获取配置实例""" + """Get configuration instance""" return settings def reload_settings(): - """重新加载配置""" + """Reload configuration""" global settings settings = Settings() \ No newline at end of file diff --git a/apps/gitea-webhook-ambassador-python/app/handlers/__init__.py b/apps/gitea-webhook-ambassador-python/app/handlers/__init__.py index 2fdc2c8f..2d7ecf8e 100644 --- a/apps/gitea-webhook-ambassador-python/app/handlers/__init__.py +++ b/apps/gitea-webhook-ambassador-python/app/handlers/__init__.py @@ -1,6 +1,6 @@ """ -Handlers 包 -包含所有 API 处理器 +Handlers package +Contains all API handlers """ from . import webhook diff --git a/apps/gitea-webhook-ambassador-python/app/handlers/admin.py b/apps/gitea-webhook-ambassador-python/app/handlers/admin.py index d8bf91f1..93804200 100644 --- a/apps/gitea-webhook-ambassador-python/app/handlers/admin.py +++ b/apps/gitea-webhook-ambassador-python/app/handlers/admin.py @@ -1,6 +1,6 @@ """ -管理 API 处理器 -提供项目映射和 API 密钥管理功能 +Admin API handler +Provides project mapping and API key management features """ import secrets @@ -17,7 +17,7 @@ from app.auth import get_current_user router = APIRouter(prefix="/api/admin", tags=["admin"]) -# API 密钥相关模型 +# API key related models class APIKeyResponse(BaseModel): id: int name: str @@ -38,7 +38,7 @@ class CreateAPIKeyResponse(BaseModel): key: str created_at: datetime -# 项目映射相关模型 +# Project mapping related models class ProjectMappingRequest(BaseModel): repository_name: str default_job: str @@ -57,13 +57,13 @@ class ProjectMappingResponse(BaseModel): class Config: from_attributes = True -# API 密钥管理端点 +# API key management endpoints @router.get("/api-keys", response_model=List[APIKeyResponse]) async def list_api_keys( db: Session = Depends(get_db), current_user: dict = Depends(get_current_user) ): - """列出所有 API 密钥""" + """List all API keys""" try: api_keys = db.query(APIKey).order_by(APIKey.created_at.desc()).all() return api_keys @@ -76,16 +76,16 @@ async def create_api_key( db: Session = Depends(get_db), current_user: dict = Depends(get_current_user) ): - """创建新的 API 密钥""" + """Create a new API key""" try: - # 生成 API 密钥 + # Generate API key api_key = secrets.token_urlsafe(32) - key_prefix = api_key[:8] # 显示前8位作为前缀 + key_prefix = api_key[:8] # Show first 8 characters as prefix - # 创建数据库记录 + # Create database record db_api_key = APIKey( name=request.name, - key_hash=api_key, # 实际应用中应该哈希存储 + key_hash=api_key, # Should be hashed in production key_prefix=key_prefix, created_at=datetime.utcnow(), last_used=datetime.utcnow(), @@ -99,7 +99,7 @@ async def create_api_key( return CreateAPIKeyResponse( id=db_api_key.id, name=db_api_key.name, - key=api_key, # 只在创建时返回完整密钥 + key=api_key, # Only return full key on creation created_at=db_api_key.created_at ) @@ -113,7 +113,7 @@ async def delete_api_key( db: Session = Depends(get_db), current_user: dict = Depends(get_current_user) ): - """删除 API 密钥""" + """Delete API key""" try: api_key = db.query(APIKey).filter(APIKey.id == key_id).first() if not api_key: @@ -136,7 +136,7 @@ async def revoke_api_key( db: Session = Depends(get_db), current_user: dict = Depends(get_current_user) ): - """撤销 API 密钥""" + """Revoke API key""" try: api_key = db.query(APIKey).filter(APIKey.id == key_id).first() if not api_key: @@ -153,13 +153,13 @@ async def revoke_api_key( db.rollback() raise HTTPException(status_code=500, detail=f"Failed to revoke API key: {str(e)}") -# 项目映射管理端点 +# Project mapping management endpoints @router.get("/projects", response_model=List[ProjectMappingResponse]) async def list_project_mappings( db: Session = Depends(get_db), current_user: dict = Depends(get_current_user) ): - """列出所有项目映射""" + """List all project mappings""" try: mappings = db.query(ProjectMapping).order_by(ProjectMapping.created_at.desc()).all() return mappings @@ -172,9 +172,9 @@ async def create_project_mapping( db: Session = Depends(get_db), current_user: dict = Depends(get_current_user) ): - """创建项目映射""" + """Create project mapping""" try: - # 检查是否已存在 + # Check if already exists existing = db.query(ProjectMapping).filter( ProjectMapping.repository_name == request.repository_name ).first() @@ -182,7 +182,7 @@ async def create_project_mapping( if existing: raise HTTPException(status_code=400, detail="Project mapping already exists") - # 创建新映射 + # Create new mapping mapping = ProjectMapping( repository_name=request.repository_name, default_job=request.default_job, @@ -210,7 +210,7 @@ async def get_project_mapping( db: Session = Depends(get_db), current_user: dict = Depends(get_current_user) ): - """获取项目映射""" + """Get project mapping""" try: mapping = db.query(ProjectMapping).filter( ProjectMapping.repository_name == repository_name @@ -232,7 +232,7 @@ async def delete_project_mapping( db: Session = Depends(get_db), current_user: dict = Depends(get_current_user) ): - """删除项目映射""" + """Delete project mapping""" try: mapping = db.query(ProjectMapping).filter( ProjectMapping.repository_name == repository_name @@ -252,24 +252,24 @@ async def delete_project_mapping( db.rollback() raise HTTPException(status_code=500, detail=f"Failed to delete project mapping: {str(e)}") -# 统计信息端点 +# Statistics endpoint @router.get("/stats") async def get_admin_stats( db: Session = Depends(get_db), current_user: dict = Depends(get_current_user) ): - """获取管理统计信息""" + """Get admin statistics""" try: - # API 密钥统计 + # API key statistics total_keys = db.query(APIKey).count() active_keys = db.query(APIKey).filter(APIKey.is_active == True).count() - # 最近使用的密钥 + # Recently used keys recent_keys = db.query(APIKey).filter( APIKey.last_used >= datetime.utcnow() - timedelta(days=7) ).count() - # 项目映射统计 + # Project mapping statistics total_mappings = db.query(ProjectMapping).count() return { diff --git a/apps/gitea-webhook-ambassador-python/app/handlers/health.py b/apps/gitea-webhook-ambassador-python/app/handlers/health.py index a0726a10..480f6d8a 100644 --- a/apps/gitea-webhook-ambassador-python/app/handlers/health.py +++ b/apps/gitea-webhook-ambassador-python/app/handlers/health.py @@ -1,145 +1,21 @@ """ Health check handler -Provides service health status checking +Provides health check endpoints for the API """ -from datetime import datetime -from typing import Dict, Any -from fastapi import APIRouter, Depends -from pydantic import BaseModel -from sqlalchemy.orm import Session - -from app.database import get_db -from app.services.jenkins_service import get_jenkins_service -from app.services.queue_service import get_queue_service +from fastapi import APIRouter from app.config import get_settings +from datetime import datetime -router = APIRouter(prefix="/health", tags=["health"]) +router = APIRouter() - -class JenkinsStatus(BaseModel): - status: str - message: str = None - - -class WorkerPoolStatus(BaseModel): - active_workers: int - queue_size: int - total_processed: int - total_failed: int - - -class HealthResponse(BaseModel): - status: str - service: str - version: str - timestamp: datetime - jenkins: JenkinsStatus - worker_pool: WorkerPoolStatus - database: Dict[str, Any] - - -@router.get("/", response_model=HealthResponse) -async def health_check(db: Session = Depends(get_db)): - """ - Health check endpoint - Check the status of each service component - """ +@router.get("/health") +async def health_check(): + """Health check endpoint""" settings = get_settings() - - # Check Jenkins connection - jenkins_service = get_jenkins_service() - jenkins_status = JenkinsStatus(status="disconnected", message="Unable to connect to Jenkins server") - - try: - if await jenkins_service.test_connection(): - jenkins_status = JenkinsStatus(status="connected") - except Exception as e: - jenkins_status.message = f"Connection failed: {str(e)}" - - # Get worker pool stats - queue_service = get_queue_service() - try: - stats = await queue_service.get_stats() - worker_pool_status = WorkerPoolStatus( - active_workers=stats.get("active_workers", 0), - queue_size=stats.get("queue_size", 0), - total_processed=stats.get("total_processed", 0), - total_failed=stats.get("total_failed", 0) - ) - except Exception as e: - worker_pool_status = WorkerPoolStatus( - active_workers=0, - queue_size=0, - total_processed=0, - total_failed=0 - ) - - # Check database connection - database_status = {"status": "disconnected", "message": "Database connection failed"} - try: - # 尝试执行简单查询 - db.execute("SELECT 1") - database_status = {"status": "connected"} - except Exception as e: - database_status["message"] = f"Database error: {str(e)}" - - # Determine overall status - overall_status = "healthy" - if jenkins_status.status != "connected": - overall_status = "unhealthy" - - return HealthResponse( - status=overall_status, - service="Gitea Webhook Ambassador", - version=settings.version, - timestamp=datetime.utcnow(), - jenkins=jenkins_status, - worker_pool=worker_pool_status, - database=database_status - ) - - -@router.get("/simple") -async def simple_health_check(): - """ - Simple health check endpoint - For load balancers and monitoring systems - """ return { "status": "healthy", "service": "Gitea Webhook Ambassador", - "version": "1.0.0" - } - - -@router.get("/ready") -async def readiness_check(db: Session = Depends(get_db)): - """ - Readiness check endpoint - Check if the service is ready to receive requests - """ - try: - # Check database connection - db.execute("SELECT 1") - - # Check Jenkins connection - jenkins_service = get_jenkins_service() - jenkins_ready = await jenkins_service.test_connection() - - if jenkins_ready: - return {"status": "ready"} - else: - return {"status": "not_ready", "reason": "Jenkins connection failed"} - - except Exception as e: - return {"status": "not_ready", "reason": f"Database connection failed: {str(e)}"} - - -@router.get("/live") -async def liveness_check(): - """ - Liveness check endpoint - Check if the service process is running normally - """ - return {"status": "alive"} \ No newline at end of file + "version": settings.version, + "timestamp": datetime.utcnow().isoformat() + } \ No newline at end of file diff --git a/apps/gitea-webhook-ambassador-python/app/handlers/webhook.py b/apps/gitea-webhook-ambassador-python/app/handlers/webhook.py index b76bfe78..0b6db41a 100644 --- a/apps/gitea-webhook-ambassador-python/app/handlers/webhook.py +++ b/apps/gitea-webhook-ambassador-python/app/handlers/webhook.py @@ -1,6 +1,6 @@ """ -Webhook 处理器 -处理来自 Gitea 的 webhook 请求 +Webhook handler +Handles webhook requests from Gitea """ from fastapi import APIRouter, Depends, HTTPException, Request @@ -11,9 +11,9 @@ from app.tasks.jenkins_tasks import get_celery_app router = APIRouter() def get_webhook_service() -> WebhookService: - """获取 webhook 服务实例""" - # 这里应该从依赖注入容器获取 - # 暂时返回 None,实际使用时需要正确实现 + """Get webhook service instance""" + # Should get from dependency injection container + # Temporarily return None, implement properly in actual use return None @router.post("/gitea") @@ -21,15 +21,15 @@ async def handle_gitea_webhook( request: Request, webhook_service: WebhookService = Depends(get_webhook_service) ): - """处理 Gitea webhook 请求""" + """Handle Gitea webhook request""" if webhook_service is None: raise HTTPException(status_code=503, detail="Webhook service not available") try: - # 获取请求体 + # Get request body body = await request.body() - # 处理 webhook + # Process webhook result = await webhook_service.process_webhook(body, request.headers) return { diff --git a/apps/gitea-webhook-ambassador-python/app/main_demo.py b/apps/gitea-webhook-ambassador-python/app/main_demo.py index 97f27cb8..dfa73452 100644 --- a/apps/gitea-webhook-ambassador-python/app/main_demo.py +++ b/apps/gitea-webhook-ambassador-python/app/main_demo.py @@ -10,7 +10,7 @@ import secrets from app.config import get_settings -# 配置日志 +# Configure logging structlog.configure( processors=[ structlog.stdlib.filter_by_level, @@ -31,14 +31,14 @@ structlog.configure( logger = structlog.get_logger() -# 创建 FastAPI 应用 +# Create FastAPI application app = FastAPI( title="Gitea Webhook Ambassador (Demo)", - description="高性能的 Gitea 到 Jenkins 的 Webhook 服务 - 演示版本", + description="High-performance webhook service from Gitea to Jenkins - Demo Version", version="1.0.0" ) -# 添加 CORS 中间件 +# Add CORS middleware app.add_middleware( CORSMiddleware, allow_origins=["*"], @@ -47,13 +47,13 @@ app.add_middleware( allow_headers=["*"], ) -# 安全配置 +# Security configuration security = HTTPBearer(auto_error=False) -# 演示数据存储 +# Demo data storage api_keys = { "demo_admin_key": { - "name": "演示管理员密钥", + "name": "Demo Admin Key", "key_hash": "demo_admin_key", "key_prefix": "demo_adm", "created_at": datetime.utcnow(), @@ -62,7 +62,7 @@ api_keys = { "role": "admin" }, "demo_user_key": { - "name": "演示用户密钥", + "name": "Demo User Key", "key_hash": "demo_user_key", "key_prefix": "demo_usr", "created_at": datetime.utcnow(), @@ -122,7 +122,7 @@ project_mappings = { } } -# 请求/响应模型 +# Request/response models class HealthResponse(BaseModel): status: str service: str @@ -160,12 +160,12 @@ class ProjectMappingResponse(BaseModel): created_at: datetime updated_at: datetime -# 认证函数 +# Authentication functions def verify_api_key(api_key: str): - """验证 API 密钥""" + """Verify API key""" for key_id, key_data in api_keys.items(): if key_data["key_hash"] == api_key and key_data["is_active"]: - # 更新最后使用时间 + # Update last used time key_data["last_used"] = datetime.utcnow() return key_data return None @@ -173,17 +173,17 @@ def verify_api_key(api_key: str): async def get_current_user( credentials: Optional[HTTPAuthorizationCredentials] = Depends(security) ): - """获取当前用户(支持 API 密钥认证)""" + """Get current user (supports API key authentication)""" if not credentials: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, - detail="需要认证令牌", + detail="Authentication token required", headers={"WWW-Authenticate": "Bearer"}, ) token = credentials.credentials - # 验证 API 密钥 + # Verify API key api_key_data = verify_api_key(token) if api_key_data: return { @@ -192,17 +192,17 @@ async def get_current_user( "role": api_key_data["role"] } - # 认证失败 + # Authentication failed raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, - detail="无效的认证令牌", + detail="Invalid authentication token", headers={"WWW-Authenticate": "Bearer"}, ) -# 公开端点 +# Public endpoints @app.get("/health", response_model=HealthResponse) async def health_check(): - """健康检查端点""" + """Health check endpoint""" settings = get_settings() return HealthResponse( @@ -222,11 +222,11 @@ async def health_check(): @app.get("/") async def root(): - """根路径""" + """Root path""" return { "name": "Gitea Webhook Ambassador (Demo)", "version": "1.0.0", - "description": "高性能的 Gitea 到 Jenkins 的 Webhook 服务 - 演示版本", + "description": "High-performance webhook service from Gitea to Jenkins - Demo Version", "endpoints": { "webhook": "/webhook/gitea", "health": "/health", @@ -241,16 +241,16 @@ async def root(): @app.post("/webhook/gitea") async def handle_gitea_webhook(request: Request): - """处理 Gitea webhook 请求""" + """Handle Gitea webhook request""" try: body = await request.body() - # 记录 webhook 请求 + # Log webhook request logger.info("Received Gitea webhook", body_size=len(body), headers=dict(request.headers)) - # 添加新的触发日志 + # Add new trigger log log_entry = { "id": len(trigger_logs) + 1, "repository_name": "demo-repo", @@ -283,7 +283,7 @@ async def handle_gitea_webhook(request: Request): } ) -# 需要认证的端点 +# Authenticated endpoints @app.get("/api/logs", response_model=List[TriggerLogResponse]) async def get_trigger_logs( repository: Optional[str] = None, @@ -291,8 +291,8 @@ async def get_trigger_logs( limit: int = 100, current_user: dict = Depends(get_current_user) ): - """获取触发日志""" - print(f"用户 {current_user['username']} 访问日志端点") + """Get trigger logs""" + print(f"User {current_user['username']} accessed logs endpoint") filtered_logs = trigger_logs.copy() @@ -301,24 +301,24 @@ async def get_trigger_logs( if branch: filtered_logs = [log for log in filtered_logs if log["branch_name"] == branch] - # 按时间倒序排列并限制数量 + # Sort by time descending and limit filtered_logs.sort(key=lambda x: x["created_at"], reverse=True) return filtered_logs[:limit] @app.get("/api/logs/stats") async def get_log_stats(current_user: dict = Depends(get_current_user)): - """获取日志统计信息""" - print(f"用户 {current_user['username']} 访问日志统计") + """Get log statistics""" + print(f"User {current_user['username']} accessed log statistics") total_logs = len(trigger_logs) successful_logs = len([log for log in trigger_logs if log["status"] == "success"]) failed_logs = len([log for log in trigger_logs if log["status"] == "failed"]) - # 最近24小时的日志数 + # Logs in the last 24 hours yesterday = datetime.utcnow() - timedelta(days=1) recent_logs = len([log for log in trigger_logs if log["created_at"] >= yesterday]) - # 按仓库分组的统计 + # Grouped by repository repo_stats = {} for log in trigger_logs: repo = log["repository_name"] @@ -337,11 +337,11 @@ async def get_log_stats(current_user: dict = Depends(get_current_user)): @app.get("/api/admin/api-keys", response_model=List[APIKeyResponse]) async def list_api_keys(current_user: dict = Depends(get_current_user)): - """列出所有 API 密钥(仅管理员)""" + """List all API keys (admin only)""" if current_user["role"] != "admin": - raise HTTPException(status_code=403, detail="需要管理员权限") + raise HTTPException(status_code=403, detail="Admin privileges required") - print(f"管理员 {current_user['username']} 查看 API 密钥列表") + print(f"Admin {current_user['username']} viewed API key list") return [ APIKeyResponse( @@ -358,8 +358,8 @@ async def list_api_keys(current_user: dict = Depends(get_current_user)): @app.get("/api/admin/projects", response_model=List[ProjectMappingResponse]) async def list_project_mappings(current_user: dict = Depends(get_current_user)): - """列出所有项目映射""" - print(f"用户 {current_user['username']} 查看项目映射") + """List all project mappings""" + print(f"User {current_user['username']} viewed project mappings") return [ ProjectMappingResponse( @@ -376,13 +376,13 @@ async def list_project_mappings(current_user: dict = Depends(get_current_user)): @app.get("/api/admin/stats") async def get_admin_stats(current_user: dict = Depends(get_current_user)): - """获取管理统计信息""" - print(f"用户 {current_user['username']} 查看管理统计") + """Get admin statistics""" + print(f"User {current_user['username']} viewed admin statistics") total_keys = len(api_keys) active_keys = len([key for key in api_keys.values() if key["is_active"]]) - # 最近使用的密钥 + # Recently used keys week_ago = datetime.utcnow() - timedelta(days=7) recent_keys = len([ key for key in api_keys.values() @@ -402,10 +402,10 @@ async def get_admin_stats(current_user: dict = Depends(get_current_user)): } } -# 中间件 +# Middleware @app.middleware("http") async def log_requests(request: Request, call_next): - """请求日志中间件""" + """Request logging middleware""" start_time = datetime.utcnow() response = await call_next(request) @@ -419,13 +419,13 @@ if __name__ == "__main__": import uvicorn settings = get_settings() - print("🚀 启动 Gitea Webhook Ambassador 演示版本") + print("🚀 Starting Gitea Webhook Ambassador Demo Version") print("=" * 60) - print("📋 演示 API 密钥:") - print(" 管理员密钥: demo_admin_key") - print(" 用户密钥: demo_user_key") + print("📋 Demo API Keys:") + print(" Admin key: demo_admin_key") + print(" User key: demo_user_key") print() - print("🔧 使用示例:") + print("🔧 Usage examples:") print(" curl -H 'Authorization: Bearer demo_admin_key' http://localhost:8000/api/admin/api-keys") print(" curl -H 'Authorization: Bearer demo_user_key' http://localhost:8000/api/logs") print("=" * 60) diff --git a/apps/gitea-webhook-ambassador-python/app/main_simple.py b/apps/gitea-webhook-ambassador-python/app/main_simple.py index 9e387bbc..083079fe 100644 --- a/apps/gitea-webhook-ambassador-python/app/main_simple.py +++ b/apps/gitea-webhook-ambassador-python/app/main_simple.py @@ -1,6 +1,6 @@ """ -简化版 FastAPI 应用主入口 -用于快速启动和测试 +Simplified FastAPI application entry point +For quick start and testing """ from fastapi import FastAPI, Request @@ -15,7 +15,7 @@ from app.handlers.health import router as health_router from app.handlers.logs import router as logs_router from app.handlers.admin import router as admin_router -# 配置日志 +# Configure logging structlog.configure( processors=[ structlog.stdlib.filter_by_level, @@ -36,14 +36,14 @@ structlog.configure( logger = structlog.get_logger() -# 创建 FastAPI 应用 +# Create FastAPI application app = FastAPI( title="Gitea Webhook Ambassador", - description="高性能的 Gitea 到 Jenkins 的 Webhook 服务", + description="High-performance webhook service from Gitea to Jenkins", version="1.0.0" ) -# 添加 CORS 中间件 +# Add CORS middleware app.add_middleware( CORSMiddleware, allow_origins=["*"], @@ -52,7 +52,7 @@ app.add_middleware( allow_headers=["*"], ) -# 包含路由 +# Include routers app.include_router(webhook_router) app.include_router(health_router) app.include_router(logs_router) @@ -60,11 +60,11 @@ app.include_router(admin_router) @app.get("/") async def root(): - """根路径""" + """Root path""" return { "name": "Gitea Webhook Ambassador", "version": "1.0.0", - "description": "高性能的 Gitea 到 Jenkins 的 Webhook 服务", + "description": "High-performance webhook service from Gitea to Jenkins", "endpoints": { "webhook": "/webhook/gitea", "health": "/health", @@ -76,10 +76,10 @@ async def root(): @app.middleware("http") async def log_requests(request: Request, call_next): - """请求日志中间件""" + """Request logging middleware""" start_time = datetime.utcnow() - # 记录请求 + # Log request logger.info( "Request started", method=request.method, @@ -87,13 +87,13 @@ async def log_requests(request: Request, call_next): client_ip=request.client.host if request.client else None ) - # 处理请求 + # Process request response = await call_next(request) - # 计算处理时间 + # Calculate processing time process_time = (datetime.utcnow() - start_time).total_seconds() - # 记录响应 + # Log response logger.info( "Request completed", method=request.method, @@ -102,14 +102,14 @@ async def log_requests(request: Request, call_next): process_time=process_time ) - # 添加处理时间到响应头 + # Add processing time to response header response.headers["X-Process-Time"] = str(process_time) return response @app.exception_handler(Exception) async def global_exception_handler(request: Request, exc: Exception): - """全局异常处理器""" + """Global exception handler""" logger.error( "Unhandled exception", method=request.method, diff --git a/apps/gitea-webhook-ambassador-python/app/models/api_key.py b/apps/gitea-webhook-ambassador-python/app/models/api_key.py index f1d11ccd..7ffd46ae 100644 --- a/apps/gitea-webhook-ambassador-python/app/models/api_key.py +++ b/apps/gitea-webhook-ambassador-python/app/models/api_key.py @@ -5,7 +5,7 @@ from sqlalchemy.ext.declarative import declarative_base Base = declarative_base() class APIKey(Base): - """API 密钥模型""" + """API key model""" __tablename__ = "api_keys" id = Column(Integer, primary_key=True, index=True) diff --git a/apps/gitea-webhook-ambassador-python/app/models/database.py b/apps/gitea-webhook-ambassador-python/app/models/database.py index 647f9821..4f8e45db 100644 --- a/apps/gitea-webhook-ambassador-python/app/models/database.py +++ b/apps/gitea-webhook-ambassador-python/app/models/database.py @@ -25,7 +25,7 @@ class ProjectMapping(Base): created_at = Column(DateTime, default=func.now()) updated_at = Column(DateTime, default=func.now(), onupdate=func.now()) - # 关系 + # Relationships branch_jobs = relationship("BranchJob", back_populates="project", cascade="all, delete-orphan") branch_patterns = relationship("BranchPattern", back_populates="project", cascade="all, delete-orphan") @@ -39,7 +39,7 @@ class BranchJob(Base): created_at = Column(DateTime, default=func.now()) updated_at = Column(DateTime, default=func.now(), onupdate=func.now()) - # 关系 + # Relationship project = relationship("ProjectMapping", back_populates="branch_jobs") class BranchPattern(Base): @@ -52,7 +52,7 @@ class BranchPattern(Base): created_at = Column(DateTime, default=func.now()) updated_at = Column(DateTime, default=func.now(), onupdate=func.now()) - # 关系 + # Relationship project = relationship("ProjectMapping", back_populates="branch_patterns") class TriggerLog(Base): @@ -67,23 +67,23 @@ class TriggerLog(Base): error_message = Column(Text, nullable=True) created_at = Column(DateTime, default=func.now()) -# 数据库配置 +# Database configuration DATABASE_URL = os.getenv("DATABASE_URL", "sqlite:///./gitea_webhook_ambassador.db") -# 创建引擎 +# Create engine engine = create_engine( DATABASE_URL, connect_args={"check_same_thread": False} if DATABASE_URL.startswith("sqlite") else {} ) -# 创建会话 +# Create session SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) -# 创建表 +# Create tables def create_tables(): Base.metadata.create_all(bind=engine) -# 获取数据库会话 +# Get database session def get_db(): db = SessionLocal() try: diff --git a/apps/gitea-webhook-ambassador-python/app/models/gitea.py b/apps/gitea-webhook-ambassador-python/app/models/gitea.py index a35ec8c1..7364c3fb 100644 --- a/apps/gitea-webhook-ambassador-python/app/models/gitea.py +++ b/apps/gitea-webhook-ambassador-python/app/models/gitea.py @@ -1,5 +1,5 @@ """ -Gitea Webhook 数据模型 +Gitea Webhook data model """ from typing import List, Optional @@ -8,7 +8,7 @@ from datetime import datetime class User(BaseModel): - """Gitea 用户模型""" + """Gitea user model""" id: int login: str full_name: Optional[str] = None @@ -17,7 +17,7 @@ class User(BaseModel): class Commit(BaseModel): - """Git 提交模型""" + """Git commit model""" id: str message: str url: str @@ -26,7 +26,7 @@ class Commit(BaseModel): class Repository(BaseModel): - """Git 仓库模型""" + """Git repository model""" id: int name: str owner: User @@ -39,7 +39,7 @@ class Repository(BaseModel): class GiteaWebhook(BaseModel): - """Gitea Webhook 模型""" + """Gitea Webhook model""" secret: Optional[str] = None ref: str before: str @@ -50,41 +50,41 @@ class GiteaWebhook(BaseModel): pusher: User def get_branch_name(self) -> str: - """从 ref 中提取分支名""" + """Extract branch name from ref""" prefix = "refs/heads/" if self.ref.startswith(prefix): return self.ref[len(prefix):] return self.ref def get_event_id(self) -> str: - """生成唯一的事件 ID""" + """Generate unique event ID""" return f"{self.repository.full_name}-{self.after}" def get_commit_hash(self) -> str: - """获取提交哈希""" + """Get commit hash""" return self.after def get_deduplication_key(self) -> str: - """生成防抖键值""" + """Generate deduplication key""" branch = self.get_branch_name() return f"{self.after}:{branch}" def is_push_event(self) -> bool: - """判断是否为推送事件""" + """Determine if it is a push event""" return self.ref.startswith("refs/heads/") def is_tag_event(self) -> bool: - """判断是否为标签事件""" + """Determine if it is a tag event""" return self.ref.startswith("refs/tags/") def get_commit_message(self) -> str: - """获取提交信息""" + """Get commit message""" if self.commits: return self.commits[0].message return "" def get_author_info(self) -> dict: - """获取作者信息""" + """Get author information""" if self.commits: author = self.commits[0].author return { @@ -100,7 +100,7 @@ class GiteaWebhook(BaseModel): class WebhookEvent(BaseModel): - """Webhook 事件模型""" + """Webhook event model""" id: str repository: str branch: str @@ -116,7 +116,7 @@ class WebhookEvent(BaseModel): class WebhookResponse(BaseModel): - """Webhook 响应模型""" + """Webhook response model""" success: bool message: str event_id: Optional[str] = None diff --git a/apps/gitea-webhook-ambassador-python/app/models/project_mapping.py b/apps/gitea-webhook-ambassador-python/app/models/project_mapping.py index caacd28a..c3b7f9ff 100644 --- a/apps/gitea-webhook-ambassador-python/app/models/project_mapping.py +++ b/apps/gitea-webhook-ambassador-python/app/models/project_mapping.py @@ -5,7 +5,7 @@ from sqlalchemy.ext.declarative import declarative_base Base = declarative_base() class ProjectMapping(Base): - """项目映射模型""" + """Project mapping model""" __tablename__ = "project_mappings" id = Column(Integer, primary_key=True, index=True) diff --git a/apps/gitea-webhook-ambassador-python/app/models/trigger_log.py b/apps/gitea-webhook-ambassador-python/app/models/trigger_log.py index 5eabe9e3..0942a934 100644 --- a/apps/gitea-webhook-ambassador-python/app/models/trigger_log.py +++ b/apps/gitea-webhook-ambassador-python/app/models/trigger_log.py @@ -5,7 +5,7 @@ from sqlalchemy.ext.declarative import declarative_base Base = declarative_base() class TriggerLog(Base): - """触发日志模型""" + """Trigger log model""" __tablename__ = "trigger_logs" id = Column(Integer, primary_key=True, index=True) diff --git a/apps/gitea-webhook-ambassador-python/app/services/database_service.py b/apps/gitea-webhook-ambassador-python/app/services/database_service.py index e5503561..8556eb7d 100644 --- a/apps/gitea-webhook-ambassador-python/app/services/database_service.py +++ b/apps/gitea-webhook-ambassador-python/app/services/database_service.py @@ -1,6 +1,6 @@ """ -数据库服务 -实现项目映射、分支模式匹配等功能 +Database service +Implements project mapping, branch pattern matching, and related features """ import asyncio @@ -18,10 +18,9 @@ from app.config import get_settings logger = structlog.get_logger() Base = declarative_base() - -# 数据库模型 +# Database models class APIKey(Base): - """API 密钥模型""" + """API key model""" __tablename__ = "api_keys" id = Column(Integer, primary_key=True, autoincrement=True) @@ -30,9 +29,8 @@ class APIKey(Base): created_at = Column(DateTime, default=datetime.utcnow) updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) - class ProjectMapping(Base): - """项目映射模型""" + """Project mapping model""" __tablename__ = "project_mappings" id = Column(Integer, primary_key=True, autoincrement=True) @@ -41,13 +39,12 @@ class ProjectMapping(Base): created_at = Column(DateTime, default=datetime.utcnow) updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) - # 关系 + # Relationships branch_jobs = relationship("BranchJob", back_populates="project", cascade="all, delete-orphan") branch_patterns = relationship("BranchPattern", back_populates="project", cascade="all, delete-orphan") - class BranchJob(Base): - """分支任务映射模型""" + """Branch job mapping model""" __tablename__ = "branch_jobs" id = Column(Integer, primary_key=True, autoincrement=True) @@ -57,12 +54,11 @@ class BranchJob(Base): created_at = Column(DateTime, default=datetime.utcnow) updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) - # 关系 + # Relationship project = relationship("ProjectMapping", back_populates="branch_jobs") - class BranchPattern(Base): - """分支模式映射模型""" + """Branch pattern mapping model""" __tablename__ = "branch_patterns" id = Column(Integer, primary_key=True, autoincrement=True) @@ -72,12 +68,11 @@ class BranchPattern(Base): created_at = Column(DateTime, default=datetime.utcnow) updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) - # 关系 + # Relationship project = relationship("ProjectMapping", back_populates="branch_patterns") - class TriggerLog(Base): - """触发日志模型""" + """Trigger log model""" __tablename__ = "trigger_logs" id = Column(Integer, primary_key=True, autoincrement=True) @@ -89,9 +84,8 @@ class TriggerLog(Base): error_message = Column(Text) created_at = Column(DateTime, default=datetime.utcnow) - class DatabaseService: - """数据库服务""" + """Database service""" def __init__(self): self.settings = get_settings() @@ -100,7 +94,7 @@ class DatabaseService: self._init_database() def _init_database(self): - """初始化数据库""" + """Initialize database""" try: self.engine = create_engine( self.settings.database.url, @@ -109,10 +103,10 @@ class DatabaseService: max_overflow=self.settings.database.max_overflow ) - # 创建表 + # Create tables Base.metadata.create_all(bind=self.engine) - # 创建会话工厂 + # Create session factory self.SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=self.engine) logger.info("Database initialized successfully") @@ -122,18 +116,16 @@ class DatabaseService: raise def get_session(self): - """获取数据库会话""" + """Get database session""" return self.SessionLocal() async def get_project_mapping(self, repository_name: str) -> Optional[Dict[str, Any]]: """ - 获取项目映射 - + Get project mapping Args: - repository_name: 仓库名 - + repository_name: repository name Returns: - Dict: 项目映射信息 + Dict: project mapping info """ try: def _get_mapping(): @@ -146,7 +138,7 @@ class DatabaseService: if not project: return None - # 构建返回数据 + # Build return data result = { "id": project.id, "repository_name": project.repository_name, @@ -155,7 +147,7 @@ class DatabaseService: "branch_patterns": [] } - # 添加分支任务映射 + # Add branch job mappings for branch_job in project.branch_jobs: result["branch_jobs"].append({ "id": branch_job.id, @@ -163,7 +155,7 @@ class DatabaseService: "job_name": branch_job.job_name }) - # 添加分支模式映射 + # Add branch pattern mappings for pattern in project.branch_patterns: result["branch_patterns"].append({ "id": pattern.id, @@ -176,7 +168,7 @@ class DatabaseService: finally: session.close() - # 在线程池中执行数据库操作 + # Run DB operation in thread pool loop = asyncio.get_event_loop() return await loop.run_in_executor(None, _get_mapping) @@ -187,28 +179,26 @@ class DatabaseService: async def determine_job_name(self, repository_name: str, branch_name: str) -> Optional[str]: """ - 根据分支名确定任务名 - + Determine job name by branch Args: - repository_name: 仓库名 - branch_name: 分支名 - + repository_name: repository name + branch_name: branch name Returns: - str: 任务名 + str: job name """ try: project = await self.get_project_mapping(repository_name) if not project: return None - # 1. 检查精确分支匹配 + # 1. Check exact branch match for branch_job in project["branch_jobs"]: if branch_job["branch_name"] == branch_name: logger.debug("Found exact branch match", branch=branch_name, job=branch_job["job_name"]) return branch_job["job_name"] - # 2. 检查模式匹配 + # 2. Check pattern match for pattern in project["branch_patterns"]: try: if re.match(pattern["pattern"], branch_name): @@ -221,7 +211,7 @@ class DatabaseService: pattern=pattern["pattern"], error=str(e)) continue - # 3. 使用默认任务 + # 3. Use default job if project["default_job"]: logger.debug("Using default job", branch=branch_name, job=project["default_job"]) @@ -237,13 +227,11 @@ class DatabaseService: async def log_trigger(self, log_data: Dict[str, Any]) -> bool: """ - 记录触发日志 - + Log trigger event Args: - log_data: 日志数据 - + log_data: log data Returns: - bool: 是否成功 + bool: success or not """ try: def _log_trigger(): @@ -279,15 +267,13 @@ class DatabaseService: async def get_trigger_logs(self, repository_name: str = None, branch_name: str = None, limit: int = 100) -> List[Dict[str, Any]]: """ - 获取触发日志 - + Get trigger logs Args: - repository_name: 仓库名(可选) - branch_name: 分支名(可选) - limit: 限制数量 - + repository_name: repository name (optional) + branch_name: branch name (optional) + limit: limit number Returns: - List: 日志列表 + List: log list """ try: def _get_logs(): @@ -329,28 +315,26 @@ class DatabaseService: async def create_project_mapping(self, mapping_data: Dict[str, Any]) -> bool: """ - 创建项目映射 - + Create project mapping Args: - mapping_data: 映射数据 - + mapping_data: mapping data Returns: - bool: 是否成功 + bool: success or not """ try: def _create_mapping(): session = self.get_session() try: - # 创建项目映射 + # Create project mapping project = ProjectMapping( repository_name=mapping_data["repository_name"], default_job=mapping_data.get("default_job") ) session.add(project) - session.flush() # 获取 ID + session.flush() # Get ID - # 添加分支任务映射 + # Add branch job mappings for branch_job in mapping_data.get("branch_jobs", []): job = BranchJob( project_id=project.id, @@ -359,7 +343,7 @@ class DatabaseService: ) session.add(job) - # 添加分支模式映射 + # Add branch pattern mappings for pattern in mapping_data.get("branch_patterns", []): pattern_obj = BranchPattern( project_id=project.id, @@ -385,13 +369,11 @@ class DatabaseService: logger.error("Failed to create project mapping", error=str(e)) return False - -# 全局数据库服务实例 +# Global database service instance _database_service: Optional[DatabaseService] = None - def get_database_service() -> DatabaseService: - """获取数据库服务实例""" + """Get database service instance""" global _database_service if _database_service is None: _database_service = DatabaseService() diff --git a/apps/gitea-webhook-ambassador-python/app/services/dedup_service.py b/apps/gitea-webhook-ambassador-python/app/services/dedup_service.py index b9511bd6..8fe6e5be 100644 --- a/apps/gitea-webhook-ambassador-python/app/services/dedup_service.py +++ b/apps/gitea-webhook-ambassador-python/app/services/dedup_service.py @@ -1,6 +1,6 @@ """ -防抖服务 -实现基于 commit hash + 分支的去重策略 +Deduplication service +Implements deduplication strategy based on commit hash + branch """ import asyncio @@ -15,9 +15,8 @@ from app.config import get_settings logger = structlog.get_logger() - class DeduplicationService: - """防抖服务""" + """Deduplication service""" def __init__(self, redis_client: aioredis.Redis): self.redis = redis_client @@ -26,13 +25,11 @@ class DeduplicationService: async def is_duplicate(self, dedup_key: str) -> bool: """ - 检查是否为重复事件 - + Check if the event is a duplicate Args: - dedup_key: 防抖键值 (commit_hash:branch) - + dedup_key: deduplication key (commit_hash:branch) Returns: - bool: True 表示重复,False 表示新事件 + bool: True if duplicate, False if new event """ if not self.settings.deduplication.enabled: return False @@ -40,13 +37,13 @@ class DeduplicationService: try: cache_key = f"{self.cache_prefix}{dedup_key}" - # 检查是否在缓存中 + # Check if in cache exists = await self.redis.exists(cache_key) if exists: logger.info("Duplicate event detected", dedup_key=dedup_key) return True - # 记录新事件 + # Record new event await self._record_event(cache_key, dedup_key) logger.info("New event recorded", dedup_key=dedup_key) return False @@ -54,13 +51,13 @@ class DeduplicationService: except Exception as e: logger.error("Error checking duplication", dedup_key=dedup_key, error=str(e)) - # 出错时允许通过,避免阻塞 + # Allow through on error to avoid blocking return False async def _record_event(self, cache_key: str, dedup_key: str): - """记录事件到缓存""" + """Record event to cache""" try: - # 设置缓存,TTL 为防抖窗口时间 + # Set cache, TTL is deduplication window ttl = self.settings.deduplication.cache_ttl await self.redis.setex(cache_key, ttl, json.dumps({ "dedup_key": dedup_key, @@ -68,7 +65,7 @@ class DeduplicationService: "ttl": ttl })) - # 同时记录到时间窗口缓存 + # Also record to window cache window_key = f"{self.cache_prefix}window:{dedup_key}" window_ttl = self.settings.deduplication.window_seconds await self.redis.setex(window_key, window_ttl, "1") @@ -78,7 +75,7 @@ class DeduplicationService: cache_key=cache_key, error=str(e)) async def get_event_info(self, dedup_key: str) -> Optional[Dict[str, Any]]: - """获取事件信息""" + """Get event info""" try: cache_key = f"{self.cache_prefix}{dedup_key}" data = await self.redis.get(cache_key) @@ -91,12 +88,12 @@ class DeduplicationService: return None async def clear_event(self, dedup_key: str) -> bool: - """清除事件记录""" + """Clear event record""" try: cache_key = f"{self.cache_prefix}{dedup_key}" window_key = f"{self.cache_prefix}window:{dedup_key}" - # 删除两个缓存键 + # Delete both cache keys await self.redis.delete(cache_key, window_key) logger.info("Event cleared", dedup_key=dedup_key) return True @@ -107,18 +104,18 @@ class DeduplicationService: return False async def get_stats(self) -> Dict[str, Any]: - """获取防抖统计信息""" + """Get deduplication statistics""" try: - # 获取所有防抖键 + # Get all deduplication keys pattern = f"{self.cache_prefix}*" keys = await self.redis.keys(pattern) - # 统计不同类型的键 + # Count different types of keys total_keys = len(keys) window_keys = len([k for k in keys if b"window:" in k]) event_keys = total_keys - window_keys - # 获取配置信息 + # Get config info config = { "enabled": self.settings.deduplication.enabled, "window_seconds": self.settings.deduplication.window_seconds, @@ -139,14 +136,14 @@ class DeduplicationService: return {"error": str(e)} async def cleanup_expired_events(self) -> int: - """清理过期事件""" + """Clean up expired events""" try: pattern = f"{self.cache_prefix}*" keys = await self.redis.keys(pattern) cleaned_count = 0 for key in keys: - # 检查 TTL + # Check TTL ttl = await self.redis.ttl(key) if ttl <= 0: await self.redis.delete(key) @@ -163,14 +160,12 @@ class DeduplicationService: def generate_dedup_key(self, commit_hash: str, branch: str) -> str: """ - 生成防抖键值 - + Generate deduplication key Args: - commit_hash: 提交哈希 - branch: 分支名 - + commit_hash: commit hash + branch: branch name Returns: - str: 防抖键值 + str: deduplication key """ if self.settings.deduplication.strategy == "commit_branch": return f"{commit_hash}:{branch}" @@ -179,18 +174,16 @@ class DeduplicationService: elif self.settings.deduplication.strategy == "branch_only": return branch else: - # 默认使用 commit_hash:branch + # Default use commit_hash:branch return f"{commit_hash}:{branch}" async def is_in_window(self, dedup_key: str) -> bool: """ - 检查是否在防抖时间窗口内 - + Check if in deduplication time window Args: - dedup_key: 防抖键值 - + dedup_key: deduplication key Returns: - bool: True 表示在窗口内 + bool: True if in window """ try: window_key = f"{self.cache_prefix}window:{dedup_key}" @@ -202,22 +195,19 @@ class DeduplicationService: dedup_key=dedup_key, error=str(e)) return False - -# 全局防抖服务实例 +# Global deduplication service instance _dedup_service: Optional[DeduplicationService] = None - def get_deduplication_service() -> DeduplicationService: - """获取防抖服务实例""" + """Get deduplication service instance""" global _dedup_service if _dedup_service is None: - # 这里需要从依赖注入获取 Redis 客户端 - # 在实际使用时,应该通过依赖注入传入 + # Should get Redis client from dependency injection + # In actual use, should be passed in raise RuntimeError("DeduplicationService not initialized") return _dedup_service - def set_deduplication_service(service: DeduplicationService): - """设置防抖服务实例""" + """Set deduplication service instance""" global _dedup_service _dedup_service = service \ No newline at end of file diff --git a/apps/gitea-webhook-ambassador-python/app/services/jenkins_service.py b/apps/gitea-webhook-ambassador-python/app/services/jenkins_service.py index c406abcf..466275ac 100644 --- a/apps/gitea-webhook-ambassador-python/app/services/jenkins_service.py +++ b/apps/gitea-webhook-ambassador-python/app/services/jenkins_service.py @@ -1,6 +1,6 @@ """ -Jenkins 服务 -提供与 Jenkins 的交互功能 +Jenkins service +Provides interaction with Jenkins """ import aiohttp @@ -12,7 +12,7 @@ from app.config import get_settings logger = structlog.get_logger() class JenkinsService: - """Jenkins 服务类""" + """Jenkins service class""" def __init__(self): self.settings = get_settings() @@ -22,7 +22,7 @@ class JenkinsService: self.timeout = self.settings.jenkins.timeout async def test_connection(self) -> bool: - """测试 Jenkins 连接""" + """Test Jenkins connection""" try: async with aiohttp.ClientSession() as session: auth = aiohttp.BasicAuth(self.username, self.token) @@ -42,15 +42,15 @@ class JenkinsService: return False async def trigger_job(self, job_name: str, parameters: Optional[Dict[str, Any]] = None) -> bool: - """触发 Jenkins 任务""" + """Trigger Jenkins job""" try: async with aiohttp.ClientSession() as session: auth = aiohttp.BasicAuth(self.username, self.token) - # 构建请求 URL + # Build request URL url = f"{self.base_url}/job/{job_name}/build" - # 如果有参数,使用参数化构建 + # If parameters, use parameterized build if parameters: url = f"{self.base_url}/job/{job_name}/buildWithParameters" @@ -71,7 +71,7 @@ class JenkinsService: return False async def get_job_info(self, job_name: str) -> Optional[Dict[str, Any]]: - """获取任务信息""" + """Get job info""" try: async with aiohttp.ClientSession() as session: auth = aiohttp.BasicAuth(self.username, self.token) @@ -89,11 +89,11 @@ class JenkinsService: logger.error(f"Error getting job info for {job_name}: {str(e)}") return None -# 全局服务实例 +# Global service instance _jenkins_service = None def get_jenkins_service() -> JenkinsService: - """获取 Jenkins 服务实例""" + """Get Jenkins service instance""" global _jenkins_service if _jenkins_service is None: _jenkins_service = JenkinsService() diff --git a/apps/gitea-webhook-ambassador-python/app/services/queue_service.py b/apps/gitea-webhook-ambassador-python/app/services/queue_service.py index c8fd9076..e2c825ad 100644 --- a/apps/gitea-webhook-ambassador-python/app/services/queue_service.py +++ b/apps/gitea-webhook-ambassador-python/app/services/queue_service.py @@ -1,6 +1,6 @@ """ -队列服务 -提供任务队列管理功能 +Queue service +Provides task queue management features """ import structlog @@ -10,7 +10,7 @@ from datetime import datetime logger = structlog.get_logger() class QueueService: - """队列服务类""" + """Queue service class""" def __init__(self): self.active_workers = 0 @@ -25,45 +25,45 @@ class QueueService: } async def get_stats(self) -> Dict[str, Any]: - """获取队列统计信息""" + """Get queue statistics""" return self._stats.copy() async def increment_processed(self): - """增加已处理任务计数""" + """Increase processed task count""" self.total_processed += 1 self._stats["total_processed"] = self.total_processed async def increment_failed(self): - """增加失败任务计数""" + """Increase failed task count""" self.total_failed += 1 self._stats["total_failed"] = self.total_failed async def set_active_workers(self, count: int): - """设置活跃工作线程数""" + """Set number of active workers""" self.active_workers = count self._stats["active_workers"] = count async def set_queue_size(self, size: int): - """设置队列大小""" + """Set queue size""" self.queue_size = size self._stats["queue_size"] = size async def add_to_queue(self): - """添加任务到队列""" + """Add task to queue""" self.queue_size += 1 self._stats["queue_size"] = self.queue_size async def remove_from_queue(self): - """从队列移除任务""" + """Remove task from queue""" if self.queue_size > 0: self.queue_size -= 1 self._stats["queue_size"] = self.queue_size -# 全局服务实例 +# Global service instance _queue_service = None def get_queue_service() -> QueueService: - """获取队列服务实例""" + """Get queue service instance""" global _queue_service if _queue_service is None: _queue_service = QueueService() diff --git a/apps/gitea-webhook-ambassador-python/app/services/webhook_service.py b/apps/gitea-webhook-ambassador-python/app/services/webhook_service.py index c86a25db..95bb63af 100644 --- a/apps/gitea-webhook-ambassador-python/app/services/webhook_service.py +++ b/apps/gitea-webhook-ambassador-python/app/services/webhook_service.py @@ -1,6 +1,6 @@ """ -Webhook 处理服务 -实现智能分发、任务排队和防抖策略 +Webhook processing service +Implements intelligent dispatch, task queueing, and deduplication strategy """ import asyncio @@ -18,9 +18,8 @@ from app.tasks.jenkins_tasks import trigger_jenkins_job logger = structlog.get_logger() - class WebhookService: - """Webhook 处理服务""" + """Webhook processing service""" def __init__( self, @@ -36,16 +35,14 @@ class WebhookService: async def process_webhook(self, webhook: GiteaWebhook) -> WebhookResponse: """ - 处理 Webhook 事件 - + Process webhook event Args: - webhook: Gitea Webhook 数据 - + webhook: Gitea webhook data Returns: - WebhookResponse: 处理结果 + WebhookResponse: processing result """ try: - # 1. 验证事件类型 + # 1. Validate event type if not webhook.is_push_event(): return WebhookResponse( success=True, @@ -53,7 +50,7 @@ class WebhookService: event_id=webhook.get_event_id() ) - # 2. 提取关键信息 + # 2. Extract key information branch = webhook.get_branch_name() commit_hash = webhook.get_commit_hash() repository = webhook.repository.full_name @@ -63,7 +60,7 @@ class WebhookService: branch=branch, commit_hash=commit_hash) - # 3. 防抖检查 + # 3. Deduplication check dedup_key = self.dedup_service.generate_dedup_key(commit_hash, branch) if await self.dedup_service.is_duplicate(dedup_key): return WebhookResponse( @@ -72,7 +69,7 @@ class WebhookService: event_id=webhook.get_event_id() ) - # 4. 获取项目映射和任务名 + # 4. Get project mapping and job name job_name = await self._determine_job_name(repository, branch) if not job_name: return WebhookResponse( @@ -81,10 +78,10 @@ class WebhookService: event_id=webhook.get_event_id() ) - # 5. 准备任务参数 + # 5. Prepare job parameters job_params = self._prepare_job_parameters(webhook, job_name) - # 6. 提交任务到队列 + # 6. Submit job to queue task_result = await self._submit_job_to_queue( webhook, job_name, job_params ) @@ -114,13 +111,13 @@ class WebhookService: ) async def _determine_job_name(self, repository: str, branch: str) -> Optional[str]: - """根据仓库和分支确定任务名""" - # 首先尝试从数据库获取项目映射 + """Determine job name by repository and branch""" + # First try to get project mapping from database job_name = await self.db_service.determine_job_name(repository, branch) if job_name: return job_name - # 如果数据库中没有映射,使用配置文件中的环境分发 + # If not found in database, use environment dispatch from config environment = self.settings.get_environment_for_branch(branch) if environment: return environment.jenkins_job @@ -128,7 +125,7 @@ class WebhookService: return None def _prepare_job_parameters(self, webhook: GiteaWebhook, job_name: str) -> Dict[str, str]: - """准备 Jenkins 任务参数""" + """Prepare Jenkins job parameters""" author_info = webhook.get_author_info() return { @@ -151,9 +148,9 @@ class WebhookService: job_name: str, job_params: Dict[str, str] ) -> bool: - """提交任务到 Celery 队列""" + """Submit job to Celery queue""" try: - # 创建任务 + # Create task task_kwargs = { "job_name": job_name, "jenkins_url": self.settings.jenkins.url, @@ -162,14 +159,14 @@ class WebhookService: "repository": webhook.repository.full_name, "branch": webhook.get_branch_name(), "commit_hash": webhook.get_commit_hash(), - "priority": 1 # 默认优先级 + "priority": 1 # Default priority } - # 提交到 Celery 队列 + # Submit to Celery queue task = self.celery_app.send_task( "app.tasks.jenkins_tasks.trigger_jenkins_job", kwargs=task_kwargs, - priority=environment.priority + priority=task_kwargs["priority"] ) logger.info("Job submitted to queue", @@ -187,15 +184,15 @@ class WebhookService: return False async def get_webhook_stats(self) -> Dict[str, Any]: - """获取 Webhook 处理统计""" + """Get webhook processing statistics""" try: - # 获取队列统计 + # Get queue stats queue_stats = await self._get_queue_stats() - # 获取防抖统计 + # Get deduplication stats dedup_stats = await self.dedup_service.get_stats() - # 获取环境配置 + # Get environment config environments = {} for name, config in self.settings.environments.items(): environments[name] = { @@ -222,20 +219,20 @@ class WebhookService: return {"error": str(e)} async def _get_queue_stats(self) -> Dict[str, Any]: - """获取队列统计信息""" + """Get queue statistics""" try: - # 获取 Celery 队列统计 + # Get Celery queue stats inspect = self.celery_app.control.inspect() - # 活跃任务 + # Active tasks active = inspect.active() active_count = sum(len(tasks) for tasks in active.values()) if active else 0 - # 等待任务 + # Reserved tasks reserved = inspect.reserved() reserved_count = sum(len(tasks) for tasks in reserved.values()) if reserved else 0 - # 注册的 worker + # Registered workers registered = inspect.registered() worker_count = len(registered) if registered else 0 @@ -251,9 +248,9 @@ class WebhookService: return {"error": str(e)} async def clear_queue(self) -> Dict[str, Any]: - """清空队列""" + """Clear queue""" try: - # 撤销所有活跃任务 + # Revoke all active tasks inspect = self.celery_app.control.inspect() active = inspect.active() diff --git a/apps/gitea-webhook-ambassador-python/app/tasks/jenkins_tasks.py b/apps/gitea-webhook-ambassador-python/app/tasks/jenkins_tasks.py index d86e3342..5bf2abab 100644 --- a/apps/gitea-webhook-ambassador-python/app/tasks/jenkins_tasks.py +++ b/apps/gitea-webhook-ambassador-python/app/tasks/jenkins_tasks.py @@ -1,6 +1,6 @@ """ -Jenkins 任务处理 -使用 Celery 处理异步 Jenkins 任务触发 +Jenkins task processing +Asynchronous Jenkins job triggering using Celery """ import asyncio @@ -17,7 +17,7 @@ from app.services.jenkins_service import JenkinsService logger = structlog.get_logger() settings = get_settings() -# 创建 Celery 应用 +# Create Celery app celery_app = Celery( "gitea_webhook_ambassador", broker=settings.redis.url, @@ -25,7 +25,7 @@ celery_app = Celery( include=["app.tasks.jenkins_tasks"] ) -# Celery 配置 +# Celery configuration celery_app.conf.update( task_serializer="json", accept_content=["json"], @@ -33,20 +33,20 @@ celery_app.conf.update( timezone="UTC", enable_utc=True, task_track_started=True, - task_time_limit=300, # 5分钟超时 - task_soft_time_limit=240, # 4分钟软超时 + task_time_limit=300, # 5 minutes timeout + task_soft_time_limit=240, # 4 minutes soft timeout worker_prefetch_multiplier=1, worker_max_tasks_per_child=1000, worker_max_memory_per_child=200000, # 200MB task_acks_late=True, task_reject_on_worker_lost=True, - task_always_eager=False, # 生产环境设为 False - result_expires=3600, # 结果缓存1小时 + task_always_eager=False, # Set to False in production + result_expires=3600, # Result cache 1 hour ) class JenkinsTask(Task): - """Jenkins 任务基类""" + """Jenkins task base class""" abstract = True @@ -59,7 +59,7 @@ class JenkinsTask(Task): return self.run(*args, **kwargs) def on_failure(self, exc, task_id, args, kwargs, einfo): - """任务失败回调""" + """Task failure callback""" logger.error("Task failed", task_id=task_id, task_name=self.name, @@ -68,7 +68,7 @@ class JenkinsTask(Task): kwargs=kwargs) def on_retry(self, exc, task_id, args, kwargs, einfo): - """任务重试回调""" + """Task retry callback""" logger.warning("Task retrying", task_id=task_id, task_name=self.name, @@ -76,7 +76,7 @@ class JenkinsTask(Task): retry_count=self.request.retries) def on_success(self, retval, task_id, args, kwargs): - """任务成功回调""" + """Task success callback""" logger.info("Task completed successfully", task_id=task_id, task_name=self.name, @@ -104,20 +104,18 @@ def trigger_jenkins_job( priority: int = 1 ) -> Dict[str, Any]: """ - 触发 Jenkins 任务 - + Trigger Jenkins job Args: - job_name: Jenkins 任务名 + job_name: Jenkins job name jenkins_url: Jenkins URL - parameters: 任务参数 - event_id: 事件 ID - repository: 仓库名 - branch: 分支名 - commit_hash: 提交哈希 - priority: 优先级 - + parameters: job parameters + event_id: event ID + repository: repository name + branch: branch name + commit_hash: commit hash + priority: priority Returns: - Dict: 任务执行结果 + Dict: job execution result """ start_time = time.time() @@ -131,10 +129,10 @@ def trigger_jenkins_job( commit_hash=commit_hash, priority=priority) - # 创建 Jenkins 服务实例 + # Create Jenkins service instance jenkins_service = JenkinsService() - # 触发 Jenkins 任务 + # Trigger Jenkins job result = asyncio.run(jenkins_service.trigger_job( job_name=job_name, jenkins_url=jenkins_url, @@ -171,7 +169,7 @@ def trigger_jenkins_job( error=result.get("error"), execution_time=execution_time) - # 重试任务 + # Retry task raise self.retry( countdown=settings.queue.retry_delay * (2 ** self.request.retries), max_retries=settings.queue.max_retries @@ -185,7 +183,7 @@ def trigger_jenkins_job( error=str(e), execution_time=execution_time) - # 重试任务 + # Retry task raise self.retry( countdown=settings.queue.retry_delay * (2 ** self.request.retries), max_retries=settings.queue.max_retries @@ -203,13 +201,11 @@ def check_jenkins_health( jenkins_url: str ) -> Dict[str, Any]: """ - 检查 Jenkins 健康状态 - + Check Jenkins health status Args: jenkins_url: Jenkins URL - Returns: - Dict: 健康检查结果 + Dict: health check result """ try: logger.info("Checking Jenkins health", jenkins_url=jenkins_url) @@ -244,23 +240,22 @@ def check_jenkins_health( ) def cleanup_expired_tasks(self) -> Dict[str, Any]: """ - 清理过期任务 - + Clean up expired tasks Returns: - Dict: 清理结果 + Dict: cleanup result """ try: logger.info("Starting task cleanup") - # 获取所有任务 + # Get all tasks inspect = self.app.control.inspect() - # 清理过期的结果 + # Clean up expired results cleaned_count = 0 current_time = time.time() - # 这里可以添加更复杂的清理逻辑 - # 比如清理超过一定时间的任务结果 + # Add more complex cleanup logic here if needed + # For example, clean up results older than a certain time logger.info("Task cleanup completed", cleaned_count=cleaned_count) @@ -280,27 +275,27 @@ def cleanup_expired_tasks(self) -> Dict[str, Any]: } -# 定时任务 +# Periodic tasks @celery_app.on_after_configure.connect def setup_periodic_tasks(sender, **kwargs): - """设置定时任务""" + """Set up periodic tasks""" - # 每小时清理过期任务 + # Clean up expired tasks every hour sender.add_periodic_task( - 3600.0, # 1小时 + 3600.0, # 1 hour cleanup_expired_tasks.s(), name="cleanup-expired-tasks" ) - # 每5分钟检查 Jenkins 健康状态 + # Check Jenkins health every 5 minutes for env_name, env_config in settings.environments.items(): sender.add_periodic_task( - 300.0, # 5分钟 + 300.0, # 5 minutes check_jenkins_health.s(env_config.jenkins_url), name=f"check-jenkins-health-{env_name}" ) def get_celery_app() -> Celery: - """获取 Celery 应用实例""" + """Get Celery app instance""" return celery_app \ No newline at end of file diff --git a/apps/gitea-webhook-ambassador-python/check_version.sh b/apps/gitea-webhook-ambassador-python/check_version.sh index c2479a12..4bed516c 100755 --- a/apps/gitea-webhook-ambassador-python/check_version.sh +++ b/apps/gitea-webhook-ambassador-python/check_version.sh @@ -1,104 +1,104 @@ #!/bin/bash -# Gitea Webhook Ambassador - 版本检查脚本 -# 用于区分 Go 版本和 Python 版本 +# Gitea Webhook Ambassador - Version Check Script +# Used to distinguish between Go and Python versions -echo "🔍 检查 Gitea Webhook Ambassador 版本..." +echo "🔍 Checking Gitea Webhook Ambassador version..." -# 检查端口 8000 (Python 版本默认端口) -echo "📡 检查端口 8000 (Python 版本)..." +# Check port 8000 (Python version default port) +echo "📡 Checking port 8000 (Python version)..." if lsof -i :8000 > /dev/null 2>&1; then PID=$(lsof -ti :8000) PROCESS=$(ps -p $PID -o comm= 2>/dev/null) - echo "✅ 端口 8000 被占用 (PID: $PID, 进程: $PROCESS)" + echo "✅ Port 8000 is occupied (PID: $PID, process: $PROCESS)" - # 检查是否是 Python 进程 + # Check if it is a Python process if echo "$PROCESS" | grep -q "python\|uvicorn"; then - echo "🐍 检测到 Python 版本正在运行" + echo "🐍 Detected Python version is running" - # 尝试访问 Python 版本的 API + # Try to access Python version API if curl -s http://localhost:8000/api/health > /dev/null 2>&1; then - echo "✅ Python 版本 API 响应正常" - echo "🌐 访问地址: http://localhost:8000" - echo "📊 仪表板: http://localhost:8000/dashboard" + echo "✅ Python version API is responsive" + echo "🌐 Access: http://localhost:8000" + echo "📊 Dashboard: http://localhost:8000/dashboard" else - echo "⚠️ Python 版本进程存在但 API 无响应" + echo "⚠️ Python process exists but API is not responsive" fi else - echo "⚠️ 端口 8000 被其他进程占用" + echo "⚠️ Port 8000 is occupied by another process" fi else - echo "❌ 端口 8000 未被占用 (Python 版本未运行)" + echo "❌ Port 8000 is not occupied (Python version not running)" fi echo "" -# 检查端口 8080 (Go 版本默认端口) -echo "📡 检查端口 8080 (Go 版本)..." +# Check port 8080 (Go version default port) +echo "📡 Checking port 8080 (Go version)..." if lsof -i :8080 > /dev/null 2>&1; then PID=$(lsof -ti :8080) PROCESS=$(ps -p $PID -o comm= 2>/dev/null) - echo "✅ 端口 8080 被占用 (PID: $PID, 进程: $PROCESS)" + echo "✅ Port 8080 is occupied (PID: $PID, process: $PROCESS)" - # 检查是否是 Go 进程 + # Check if it is a Go process if echo "$PROCESS" | grep -q "gitea-webhook-ambassador"; then - echo "🚀 检测到 Go 版本正在运行" + echo "🚀 Detected Go version is running" - # 尝试访问 Go 版本的 API + # Try to access Go version API if curl -s http://localhost:8080/health > /dev/null 2>&1; then - echo "✅ Go 版本 API 响应正常" - echo "🌐 访问地址: http://localhost:8080" + echo "✅ Go version API is responsive" + echo "🌐 Access: http://localhost:8080" else - echo "⚠️ Go 版本进程存在但 API 无响应" + echo "⚠️ Go process exists but API is not responsive" fi else - echo "⚠️ 端口 8080 被其他进程占用" + echo "⚠️ Port 8080 is occupied by another process" fi else - echo "❌ 端口 8080 未被占用 (Go 版本未运行)" + echo "❌ Port 8080 is not occupied (Go version not running)" fi echo "" -# 检查 PID 文件 -echo "📁 检查 PID 文件..." +# Check PID file +echo "📁 Checking PID file..." -# Python 版本 PID 文件 +# Python version PID file PYTHON_PID_FILE="/home/nicolas/freeleaps-ops/apps/gitea-webhook-ambassador-python/service.pid" if [ -f "$PYTHON_PID_FILE" ]; then PYTHON_PID=$(cat "$PYTHON_PID_FILE") if ps -p $PYTHON_PID > /dev/null 2>&1; then - echo "✅ Python 版本 PID 文件存在 (PID: $PYTHON_PID)" + echo "✅ Python version PID file exists (PID: $PYTHON_PID)" else - echo "⚠️ Python 版本 PID 文件存在但进程不存在" + echo "⚠️ Python version PID file exists but process does not exist" fi else - echo "❌ Python 版本 PID 文件不存在" + echo "❌ Python version PID file does not exist" fi -# Go 版本 PID 文件 (如果存在) +# Go version PID file (if exists) GO_PID_FILE="/home/nicolas/freeleaps-ops/apps/gitea-webhook-ambassador/service.pid" if [ -f "$GO_PID_FILE" ]; then GO_PID=$(cat "$GO_PID_FILE") if ps -p $GO_PID > /dev/null 2>&1; then - echo "✅ Go 版本 PID 文件存在 (PID: $GO_PID)" + echo "✅ Go version PID file exists (PID: $GO_PID)" else - echo "⚠️ Go 版本 PID 文件存在但进程不存在" + echo "⚠️ Go version PID file exists but process does not exist" fi else - echo "❌ Go 版本 PID 文件不存在" + echo "❌ Go version PID file does not exist" fi echo "" -# 总结 -echo "📊 总结:" +# Summary +echo "📊 Summary:" echo "----------------------------------------" PYTHON_RUNNING=false GO_RUNNING=false -# 检查 Python 版本 +# Check Python version if lsof -i :8000 > /dev/null 2>&1; then PID=$(lsof -ti :8000) PROCESS=$(ps -p $PID -o comm= 2>/dev/null) @@ -107,7 +107,7 @@ if lsof -i :8000 > /dev/null 2>&1; then fi fi -# 检查 Go 版本 +# Check Go version if lsof -i :8080 > /dev/null 2>&1; then PID=$(lsof -ti :8080) PROCESS=$(ps -p $PID -o comm= 2>/dev/null) @@ -117,17 +117,17 @@ if lsof -i :8080 > /dev/null 2>&1; then fi if [ "$PYTHON_RUNNING" = true ] && [ "$GO_RUNNING" = true ]; then - echo "⚠️ 两个版本都在运行!" - echo "🐍 Python 版本: http://localhost:8000" - echo "🚀 Go 版本: http://localhost:8080" + echo "⚠️ Both versions are running!" + echo "🐍 Python version: http://localhost:8000" + echo "🚀 Go version: http://localhost:8080" elif [ "$PYTHON_RUNNING" = true ]; then - echo "✅ 当前运行: Python 版本" - echo "🌐 访问地址: http://localhost:8000" + echo "✅ Currently running: Python version" + echo "🌐 Access: http://localhost:8000" elif [ "$GO_RUNNING" = true ]; then - echo "✅ 当前运行: Go 版本" - echo "🌐 访问地址: http://localhost:8080" + echo "✅ Currently running: Go version" + echo "🌐 Access: http://localhost:8080" else - echo "❌ 没有检测到任何版本在运行" + echo "❌ No version is running" fi echo "----------------------------------------" \ No newline at end of file diff --git a/apps/gitea-webhook-ambassador-python/config/environments.yaml b/apps/gitea-webhook-ambassador-python/config/environments.yaml index 284af05f..77fc5111 100644 --- a/apps/gitea-webhook-ambassador-python/config/environments.yaml +++ b/apps/gitea-webhook-ambassador-python/config/environments.yaml @@ -1,4 +1,4 @@ -# 环境分发配置 +# Environment dispatch configuration environments: dev: branches: ["dev", "develop", "development", "feature/*"] @@ -24,16 +24,16 @@ environments: jenkins_url: "https://jenkins-default.freeleaps.com" priority: 4 -# 防抖配置 +# Deduplication configuration deduplication: enabled: true - window_seconds: 300 # 5分钟防抖窗口 + window_seconds: 300 # 5-minute deduplication window strategy: "commit_branch" # commit_hash + branch - cache_ttl: 3600 # 缓存1小时 + cache_ttl: 3600 # Cache for 1 hour -# 队列配置 +# Queue configuration queue: max_concurrent: 10 max_retries: 3 - retry_delay: 60 # 秒 + retry_delay: 60 # seconds priority_levels: 4 \ No newline at end of file diff --git a/apps/gitea-webhook-ambassador-python/docker-compose.yml b/apps/gitea-webhook-ambassador-python/docker-compose.yml index 581afa9d..3ef65fe0 100644 --- a/apps/gitea-webhook-ambassador-python/docker-compose.yml +++ b/apps/gitea-webhook-ambassador-python/docker-compose.yml @@ -1,7 +1,7 @@ version: '3.8' services: - # Redis 服务 + # Redis service redis: image: redis:7-alpine container_name: webhook-ambassador-redis @@ -16,7 +16,7 @@ services: timeout: 10s retries: 3 - # PostgreSQL 数据库 (可选,用于生产环境) + # PostgreSQL database (optional, for production) postgres: image: postgres:15-alpine container_name: webhook-ambassador-postgres @@ -34,7 +34,7 @@ services: timeout: 10s retries: 3 - # Webhook Ambassador API 服务 + # Webhook Ambassador API service api: build: context: . @@ -88,7 +88,7 @@ services: condition: service_healthy restart: unless-stopped - # Celery Beat (定时任务调度器) + # Celery Beat (scheduler) beat: build: context: . @@ -112,7 +112,7 @@ services: condition: service_healthy restart: unless-stopped - # Flower (Celery 监控) + # Flower (Celery monitoring) flower: build: context: . @@ -135,7 +135,7 @@ services: condition: service_healthy restart: unless-stopped - # Prometheus (监控) + # Prometheus (monitoring) prometheus: image: prom/prometheus:latest container_name: webhook-ambassador-prometheus @@ -153,7 +153,7 @@ services: - '--web.enable-lifecycle' restart: unless-stopped - # Grafana (监控面板) + # Grafana (monitoring dashboard) grafana: image: grafana/grafana:latest container_name: webhook-ambassador-grafana diff --git a/apps/gitea-webhook-ambassador-python/env.example b/apps/gitea-webhook-ambassador-python/env.example index 62589fd6..1f404401 100644 --- a/apps/gitea-webhook-ambassador-python/env.example +++ b/apps/gitea-webhook-ambassador-python/env.example @@ -1,41 +1,41 @@ -# 应用配置 +# Application configuration APP_NAME=Gitea Webhook Ambassador DEBUG=false HOST=0.0.0.0 PORT=8000 -# 数据库配置 +# Database configuration DATABASE_URL=sqlite:///./webhook_ambassador.db -# 生产环境使用 PostgreSQL: +# For production, use PostgreSQL: # DATABASE_URL=postgresql://webhook_user:webhook_password@localhost:5432/webhook_ambassador -# Redis 配置 +# Redis configuration REDIS_URL=redis://localhost:6379/0 REDIS_PASSWORD= REDIS_DB=0 -# Jenkins 配置 +# Jenkins configuration JENKINS_USERNAME=your_jenkins_username JENKINS_TOKEN=115127e693f1bc6b7194f58ff6d6283bd0 JENKINS_TIMEOUT=30 -# 安全配置 +# Security configuration SECURITY_SECRET_KEY=r6Y@QTb*7BQN@hDGsN SECURITY_WEBHOOK_SECRET_HEADER=X-Gitea-Signature SECURITY_RATE_LIMIT_PER_MINUTE=100 -# 日志配置 +# Logging configuration LOGGING_LEVEL=INFO LOGGING_FORMAT=json LOGGING_FILE= -# 队列配置 +# Queue configuration QUEUE_MAX_CONCURRENT=10 QUEUE_MAX_RETRIES=3 QUEUE_RETRY_DELAY=60 QUEUE_PRIORITY_LEVELS=3 -# 防抖配置 +# Deduplication configuration DEDUPLICATION_ENABLED=true DEDUPLICATION_WINDOW_SECONDS=300 DEDUPLICATION_STRATEGY=commit_branch diff --git a/apps/gitea-webhook-ambassador-python/fix_pid.sh b/apps/gitea-webhook-ambassador-python/fix_pid.sh index 37043c16..a6aad942 100755 --- a/apps/gitea-webhook-ambassador-python/fix_pid.sh +++ b/apps/gitea-webhook-ambassador-python/fix_pid.sh @@ -1,41 +1,41 @@ #!/bin/bash -# 修复 PID 文件问题 -echo "🔧 修复 PID 文件问题..." +# Fix PID file issue +echo "🔧 Fixing PID file issue..." -# 查找 Python 服务进程 +# Find Python service process PID=$(lsof -ti :8000 2>/dev/null) if [ -n "$PID" ]; then - echo "✅ 找到运行中的 Python 服务 (PID: $PID)" + echo "✅ Found running Python service (PID: $PID)" - # 检查进程是否是我们的服务 + # Check if the process is our service PROCESS=$(ps -p $PID -o comm= 2>/dev/null) if echo "$PROCESS" | grep -q "python"; then - echo "🐍 确认是 Python 版本的 Gitea Webhook Ambassador" + echo "🐍 Confirmed: Python version of Gitea Webhook Ambassador" - # 创建 PID 文件 + # Create PID file echo $PID > service.pid - echo "✅ 已创建 PID 文件: service.pid" + echo "✅ PID file created: service.pid" - # 验证 PID 文件 + # Verify PID file if [ -f "service.pid" ]; then STORED_PID=$(cat service.pid) - echo "📝 PID 文件内容: $STORED_PID" + echo "📝 PID file content: $STORED_PID" if [ "$STORED_PID" = "$PID" ]; then - echo "✅ PID 文件修复成功" - echo "💡 现在可以使用 './devbox stop' 来停止服务" + echo "✅ PID file fixed successfully" + echo "💡 Now you can use './devbox stop' to stop the service" else - echo "❌ PID 文件内容不匹配" + echo "❌ PID file content does not match" fi else - echo "❌ 无法创建 PID 文件" + echo "❌ Failed to create PID file" fi else - echo "⚠️ 端口 8000 被其他进程占用" + echo "⚠️ Port 8000 is occupied by another process" fi else - echo "❌ 没有找到运行中的 Python 服务" - echo "💡 请先启动服务: './devbox start'" + echo "❌ No running Python service found" + echo "💡 Please start the service first: './devbox start'" fi \ No newline at end of file diff --git a/apps/gitea-webhook-ambassador-python/quick_check.sh b/apps/gitea-webhook-ambassador-python/quick_check.sh index 96a63fc6..c02b896a 100755 --- a/apps/gitea-webhook-ambassador-python/quick_check.sh +++ b/apps/gitea-webhook-ambassador-python/quick_check.sh @@ -1,31 +1,31 @@ #!/bin/bash -# 快速检查当前运行的版本 +# Quick check of the currently running version -echo "🔍 快速检查 Gitea Webhook Ambassador 版本..." +echo "🔍 Quick check of Gitea Webhook Ambassador version..." -# 检查 Python 版本 (端口 8000) +# Check Python version (port 8000) if lsof -i :8000 > /dev/null 2>&1; then PID=$(lsof -ti :8000) PROCESS=$(ps -p $PID -o comm= 2>/dev/null) if echo "$PROCESS" | grep -q "python\|uvicorn"; then - echo "🐍 Python 版本正在运行 (PID: $PID)" + echo "🐍 Python version is running (PID: $PID)" echo "🌐 http://localhost:8000" echo "📊 http://localhost:8000/dashboard" exit 0 fi fi -# 检查 Go 版本 (端口 8080) +# Check Go version (port 8080) if lsof -i :8080 > /dev/null 2>&1; then PID=$(lsof -ti :8080) PROCESS=$(ps -p $PID -o comm= 2>/dev/null) if echo "$PROCESS" | grep -q "gitea-webhook-ambassador"; then - echo "🚀 Go 版本正在运行 (PID: $PID)" + echo "🚀 Go version is running (PID: $PID)" echo "🌐 http://localhost:8080" exit 0 fi fi -echo "❌ 没有检测到任何版本在运行" -echo "💡 使用 './devbox start' 启动 Python 版本" \ No newline at end of file +echo "❌ No version is running" +echo "💡 Use './devbox start' to start the Python version" \ No newline at end of file diff --git a/apps/gitea-webhook-ambassador-python/scripts/setup.sh b/apps/gitea-webhook-ambassador-python/scripts/setup.sh index a2082108..86c70525 100755 --- a/apps/gitea-webhook-ambassador-python/scripts/setup.sh +++ b/apps/gitea-webhook-ambassador-python/scripts/setup.sh @@ -1,61 +1,55 @@ #!/bin/bash -# Gitea Webhook Ambassador 快速设置脚本 +# Gitea Webhook Ambassador quick setup script -set -e +echo "🚀 Starting Gitea Webhook Ambassador setup..." -echo "🚀 开始设置 Gitea Webhook Ambassador..." - -# 检查 Python 版本 -python_version=$(python3 --version 2>&1 | grep -oE '[0-9]+\.[0-9]+') -required_version="3.8" - -if [ "$(printf '%s\n' "$required_version" "$python_version" | sort -V | head -n1)" != "$required_version" ]; then - echo "❌ 需要 Python 3.8 或更高版本,当前版本: $python_version" +# Check Python version +python_version=$(python3 -V 2>&1 | awk '{print $2}') +if [[ "$python_version" < "3.8" ]]; then + echo "❌ Python 3.8 or higher is required, current version: $python_version" exit 1 fi -echo "✅ Python 版本检查通过: $python_version" +echo "✅ Python version check passed: $python_version" -# 创建虚拟环境 +# Create virtual environment if [ ! -d "venv" ]; then - echo "📦 创建虚拟环境..." + echo "📦 Creating virtual environment..." python3 -m venv venv fi -# 激活虚拟环境 -echo "🔧 激活虚拟环境..." +# Activate virtual environment +echo "🔧 Activating virtual environment..." source venv/bin/activate -# 升级 pip -echo "⬆️ 升级 pip..." +# Upgrade pip +echo "⬆️ Upgrading pip..." pip install --upgrade pip -# 安装依赖 -echo "📚 安装依赖..." +# Install dependencies +echo "📚 Installing dependencies..." pip install -r requirements.txt -# 创建配置文件 +# Create config file if [ ! -f ".env" ]; then - echo "⚙️ 创建环境配置文件..." + echo "⚙️ Creating environment config file..." cp env.example .env - echo "📝 请编辑 .env 文件,配置您的 Jenkins 凭据和其他设置" + echo "📝 Please edit the .env file to configure your Jenkins credentials and other settings" fi -# 创建日志目录 +# Create logs directory mkdir -p logs -# 创建数据库目录 +# Create database directory mkdir -p data -echo "✅ 设置完成!" -echo "" -echo "📋 下一步操作:" -echo "1. 编辑 .env 文件,配置 Jenkins 凭据" -echo "2. 运行: source venv/bin/activate" -echo "3. 启动 Redis: docker run -d -p 6379:6379 redis:alpine" -echo "4. 启动服务: python -m uvicorn app.main:app --reload" -echo "5. 启动 Celery worker: celery -A app.tasks.jenkins_tasks worker --loglevel=info" -echo "" -echo "🌐 访问地址: http://localhost:8000" -echo "📊 监控面板: http://localhost:8000/health" \ No newline at end of file +echo "✅ Setup complete!" +echo "📋 Next steps:" +echo "1. Edit the .env file to configure Jenkins credentials" +echo "2. Run: source venv/bin/activate" +echo "3. Start Redis: docker run -d -p 6379:6379 redis:alpine" +echo "4. Start the service: python -m uvicorn app.main:app --reload" +echo "5. Start Celery worker: celery -A app.tasks.jenkins_tasks worker --loglevel=info" +echo "🌐 Access: http://localhost:8000" +echo "📊 Monitoring dashboard: http://localhost:8000/health" \ No newline at end of file diff --git a/apps/gitea-webhook-ambassador-python/scripts/start.sh b/apps/gitea-webhook-ambassador-python/scripts/start.sh index 19ede1c4..0d8c2eb1 100755 --- a/apps/gitea-webhook-ambassador-python/scripts/start.sh +++ b/apps/gitea-webhook-ambassador-python/scripts/start.sh @@ -1,64 +1,65 @@ #!/bin/bash -# Gitea Webhook Ambassador 启动脚本 +# Gitea Webhook Ambassador start script -set -e - -# 检查虚拟环境 +# Check virtual environment if [ ! -d "venv" ]; then - echo "❌ 虚拟环境不存在,请先运行 ./scripts/setup.sh" + echo "❌ Virtual environment does not exist, please run ./scripts/setup.sh first" exit 1 fi -# 激活虚拟环境 +# Activate virtual environment source venv/bin/activate -# 检查环境文件 +# Check environment file if [ ! -f ".env" ]; then - echo "❌ .env 文件不存在,请先运行 ./scripts/setup.sh" + echo "❌ .env file does not exist, please run ./scripts/setup.sh first" exit 1 fi -# 检查 Redis 是否运行 -if ! docker ps | grep -q redis; then - echo "🐳 启动 Redis..." - docker run -d --name webhook-redis -p 6379:6379 redis:alpine - sleep 3 +# Check if Redis is running +if ! pgrep -f redis-server > /dev/null; then + echo "🐳 Starting Redis..." + docker run -d -p 6379:6379 redis:alpine fi -echo "🚀 启动 Gitea Webhook Ambassador..." +# Start Gitea Webhook Ambassador +echo "🚀 Starting Gitea Webhook Ambassador..." -# 启动 API 服务 -echo "🌐 启动 API 服务..." -python -m uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload & +# Start API service +API_LOG="logs/api.log" +echo "🌐 Starting API service..." +nohup python -m uvicorn app.main:app --host 0.0.0.0 --port 8000 > $API_LOG 2>&1 & API_PID=$! -# 等待 API 服务启动 -sleep 5 +# Wait for API service to start +sleep 3 -# 启动 Celery worker -echo "⚙️ 启动 Celery worker..." -celery -A app.tasks.jenkins_tasks worker --loglevel=info --concurrency=4 & +# Start Celery worker +WORKER_LOG="logs/worker.log" +echo "⚙️ Starting Celery worker..." +nohup celery -A app.tasks.jenkins_tasks worker --loglevel=info > $WORKER_LOG 2>&1 & WORKER_PID=$! -# 启动 Celery beat (定时任务) -echo "⏰ 启动定时任务调度器..." -celery -A app.tasks.jenkins_tasks beat --loglevel=info & +# Start Celery beat (scheduled tasks) +BEAT_LOG="logs/beat.log" +echo "⏰ Starting scheduled task scheduler..." +nohup celery -A app.tasks.jenkins_tasks beat --loglevel=info > $BEAT_LOG 2>&1 & BEAT_PID=$! -echo "✅ 所有服务已启动!" -echo "" -echo "📊 服务状态:" -echo "- API 服务: http://localhost:8000 (PID: $API_PID)" -echo "- 健康检查: http://localhost:8000/health" -echo "- 监控指标: http://localhost:8000/metrics" -echo "- Celery Worker: PID $WORKER_PID" -echo "- Celery Beat: PID $BEAT_PID" -echo "" -echo "🛑 按 Ctrl+C 停止所有服务" +# All services started +sleep 2 +echo "✅ All services started!" +echo "📊 Service status:" +echo "- API service: http://localhost:8000 (PID: $API_PID)" +echo "- Health check: http://localhost:8000/health" +echo "- Metrics: http://localhost:8000/metrics" -# 等待中断信号 -trap 'echo "🛑 正在停止服务..."; kill $API_PID $WORKER_PID $BEAT_PID 2>/dev/null; exit 0' INT +# Wait for Ctrl+C to stop all services +echo "🛑 Press Ctrl+C to stop all services" -# 等待所有后台进程 +# Wait for interrupt signal +trap 'echo "🛑 Stopping services..."; kill $API_PID $WORKER_PID $BEAT_PID 2>/dev/null; exit 0' INT + +# Wait for all background processes wait \ No newline at end of file diff --git a/apps/gitea-webhook-ambassador-python/start.sh b/apps/gitea-webhook-ambassador-python/start.sh index 3fc9259f..9894e546 100755 --- a/apps/gitea-webhook-ambassador-python/start.sh +++ b/apps/gitea-webhook-ambassador-python/start.sh @@ -1,120 +1,120 @@ #!/bin/bash -# Gitea Webhook Ambassador Python 启动脚本 +# Gitea Webhook Ambassador Python Start Script SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" SERVICE_NAME="gitea-webhook-ambassador-python" LOG_FILE="$SCRIPT_DIR/logs/service.log" PID_FILE="$SCRIPT_DIR/service.pid" -# 创建日志目录 +# Create logs directory mkdir -p "$SCRIPT_DIR/logs" -# 激活虚拟环境 +# Activate virtual environment source "$SCRIPT_DIR/venv/bin/activate" -# 函数:启动服务 +# Function: Start service start_service() { - echo "🚀 启动 $SERVICE_NAME..." + echo "🚀 Starting $SERVICE_NAME..." if [ -f "$PID_FILE" ]; then PID=$(cat "$PID_FILE") if ps -p $PID > /dev/null 2>&1; then - echo "❌ 服务已在运行 (PID: $PID)" + echo "❌ Service is already running (PID: $PID)" return 1 else - echo "⚠️ 发现过期的 PID 文件,正在清理..." + echo "⚠️ Found stale PID file, cleaning up..." rm -f "$PID_FILE" fi fi - # 后台启动服务 + # Start service in background nohup python -m uvicorn app.main_demo:app --host 0.0.0.0 --port 8000 > "$LOG_FILE" 2>&1 & PID=$! echo $PID > "$PID_FILE" - # 等待服务启动 + # Wait for service to start sleep 3 if ps -p $PID > /dev/null 2>&1; then - echo "✅ 服务启动成功 (PID: $PID)" - echo "📝 日志文件: $LOG_FILE" - echo "🌐 访问地址: http://localhost:8000" - echo "🔑 演示密钥: demo_admin_key, demo_user_key" + echo "✅ Service started successfully (PID: $PID)" + echo "📝 Log file: $LOG_FILE" + echo "🌐 Access: http://localhost:8000" + echo "🔑 Demo keys: demo_admin_key, demo_user_key" else - echo "❌ 服务启动失败" + echo "❌ Service failed to start" rm -f "$PID_FILE" return 1 fi } -# 函数:停止服务 +# Function: Stop service stop_service() { - echo "🛑 停止 $SERVICE_NAME..." + echo "🛑 Stopping $SERVICE_NAME..." if [ -f "$PID_FILE" ]; then PID=$(cat "$PID_FILE") if ps -p $PID > /dev/null 2>&1; then kill $PID - echo "✅ 服务已停止 (PID: $PID)" + echo "✅ Service stopped (PID: $PID)" else - echo "⚠️ 服务未运行" + echo "⚠️ Service not running" fi rm -f "$PID_FILE" else - echo "⚠️ PID 文件不存在" + echo "⚠️ PID file does not exist" fi } -# 函数:重启服务 +# Function: Restart service restart_service() { - echo "🔄 重启 $SERVICE_NAME..." + echo "🔄 Restarting $SERVICE_NAME..." stop_service sleep 2 start_service } -# 函数:查看状态 +# Function: Show status status_service() { if [ -f "$PID_FILE" ]; then PID=$(cat "$PID_FILE") if ps -p $PID > /dev/null 2>&1; then - echo "✅ $SERVICE_NAME 正在运行 (PID: $PID)" - echo "📝 日志文件: $LOG_FILE" - echo "🌐 访问地址: http://localhost:8000" + echo "✅ $SERVICE_NAME is running (PID: $PID)" + echo "📝 Log file: $LOG_FILE" + echo "🌐 Access: http://localhost:8000" else - echo "❌ $SERVICE_NAME 未运行 (PID 文件存在但进程不存在)" + echo "❌ $SERVICE_NAME is not running (PID file exists but process not found)" rm -f "$PID_FILE" fi else - echo "❌ $SERVICE_NAME 未运行" + echo "❌ $SERVICE_NAME is not running" fi } -# 函数:查看日志 +# Function: Show logs show_logs() { if [ -f "$LOG_FILE" ]; then - echo "📝 显示最新日志 (最后 50 行):" + echo "📝 Showing latest logs (last 50 lines):" echo "----------------------------------------" tail -n 50 "$LOG_FILE" echo "----------------------------------------" - echo "完整日志文件: $LOG_FILE" + echo "Full log file: $LOG_FILE" else - echo "❌ 日志文件不存在" + echo "❌ Log file does not exist" fi } -# 函数:实时日志 +# Function: Follow logs follow_logs() { if [ -f "$LOG_FILE" ]; then - echo "📝 实时日志 (按 Ctrl+C 退出):" + echo "📝 Real-time logs (Ctrl+C to exit):" tail -f "$LOG_FILE" else - echo "❌ 日志文件不存在" + echo "❌ Log file does not exist" fi } -# 主逻辑 +# Main logic case "$1" in start) start_service @@ -135,20 +135,20 @@ case "$1" in follow_logs ;; *) - echo "用法: $0 {start|stop|restart|status|logs|follow}" + echo "Usage: $0 {start|stop|restart|status|logs|follow}" echo "" - echo "命令说明:" - echo " start - 启动服务" - echo " stop - 停止服务" - echo " restart - 重启服务" - echo " status - 查看服务状态" - echo " logs - 查看最新日志" - echo " follow - 实时查看日志" + echo "Command description:" + echo " start - Start service" + echo " stop - Stop service" + echo " restart - Restart service" + echo " status - Show service status" + echo " logs - Show latest logs" + echo " follow - Show real-time logs" echo "" - echo "示例:" - echo " $0 start # 启动服务" - echo " $0 status # 查看状态" - echo " $0 logs # 查看日志" + echo "Examples:" + echo " $0 start # Start service" + echo " $0 status # Show status" + echo " $0 logs # Show logs" exit 1 ;; esac \ No newline at end of file diff --git a/apps/gitea-webhook-ambassador-python/test_auth.py b/apps/gitea-webhook-ambassador-python/test_auth.py index 1d05c9e5..e6b83be7 100644 --- a/apps/gitea-webhook-ambassador-python/test_auth.py +++ b/apps/gitea-webhook-ambassador-python/test_auth.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 """ -认证功能测试脚本 -演示如何正确使用 JWT 和 API 密钥认证 +Authentication feature test script +Demonstrates how to properly use JWT and API key authentication """ import asyncio @@ -11,140 +11,138 @@ from datetime import datetime BASE_URL = "http://localhost:8000" -async def test_jwt_authentication(): - """测试 JWT 认证""" - print("🔐 测试 JWT 认证") +def print_divider(): print("-" * 50) + +async def test_jwt_authentication(): + """Test JWT authentication""" + print("🔐 Testing JWT authentication") + print_divider() - # 注意:在实际应用中,JWT 令牌应该通过登录端点获取 - # 这里我们使用一个示例令牌(在实际环境中需要从登录端点获取) + # Note: In actual applications, JWT tokens should be obtained via the login endpoint + # Here we use a sample token (in real environments, obtain from login endpoint) - # 模拟 JWT 令牌(实际应用中应该从登录端点获取) + # Simulate JWT token (should be obtained from login endpoint in real use) jwt_token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhZG1pbiIsImV4cCI6MTczMjAwMDAwMH0.test" async with aiohttp.ClientSession() as session: - # 使用 JWT 令牌访问管理端点 + # Use JWT token to access admin endpoint headers = {"Authorization": f"Bearer {jwt_token}"} - # 测试访问日志端点 + # Test access to logs endpoint async with session.get(f"{BASE_URL}/api/logs", headers=headers) as response: if response.status == 200: logs = await response.json() - print("✅ JWT 认证成功 - 日志访问") - print(f" 获取到 {len(logs)} 条日志") + print("✅ JWT authentication succeeded - logs access") + print(f" Retrieved {len(logs)} logs") else: - print(f"❌ JWT 认证失败 - 日志访问: {response.status}") + print(f"❌ JWT authentication failed - logs access: {response.status}") if response.status == 401: - print(" 原因: JWT 令牌无效或已过期") - + print(" Reason: JWT token is invalid or expired") print() async def test_api_key_authentication(): - """测试 API 密钥认证""" - print("🔑 测试 API 密钥认证") - print("-" * 50) + """Test API key authentication""" + print("🔑 Testing API key authentication") + print_divider() async with aiohttp.ClientSession() as session: - # 首先创建一个 API 密钥(需要管理员权限) - # 注意:这里我们使用一个临时的认证方式 + # First, create an API key (requires admin privileges) + # Note: Here we use a temporary authentication method - # 方法1:直接使用内存中的 API 密钥(仅用于演示) - # 在实际应用中,API 密钥应该通过管理界面创建 + # Method 1: Use in-memory API key (for demo only) + # In real applications, API keys should be created via the admin interface - # 模拟一个有效的 API 密钥 + # Simulate a valid API key api_key = "test_api_key_12345" headers = {"Authorization": f"Bearer {api_key}"} - # 测试访问日志端点 + # Test access to logs endpoint async with session.get(f"{BASE_URL}/api/logs", headers=headers) as response: if response.status == 200: logs = await response.json() - print("✅ API 密钥认证成功 - 日志访问") - print(f" 获取到 {len(logs)} 条日志") + print("✅ API key authentication succeeded - logs access") + print(f" Retrieved {len(logs)} logs") else: - print(f"❌ API 密钥认证失败 - 日志访问: {response.status}") + print(f"❌ API key authentication failed - logs access: {response.status}") if response.status == 401: - print(" 原因: API 密钥无效或已撤销") - + print(" Reason: API key is invalid or revoked") print() async def test_public_endpoints(): - """测试公开端点(无需认证)""" - print("🌐 测试公开端点") - print("-" * 50) + """Test public endpoints (no authentication required)""" + print("🌐 Testing public endpoints") + print_divider() async with aiohttp.ClientSession() as session: - # 健康检查端点(无需认证) + # Health check endpoint (no authentication required) async with session.get(f"{BASE_URL}/health") as response: if response.status == 200: data = await response.json() - print("✅ 健康检查端点访问成功") - print(f" 状态: {data['status']}") - print(f" Jenkins: {data['jenkins']['status']}") + print("✅ Health check endpoint accessed successfully") + print(f" Status: {data['status']}") else: - print(f"❌ 健康检查端点访问失败: {response.status}") + print(f"❌ Health check endpoint access failed: {response.status}") - # Webhook 端点(无需认证) + # Webhook endpoint (no authentication required) webhook_data = {"test": "webhook_data"} async with session.post(f"{BASE_URL}/webhook/gitea", json=webhook_data) as response: if response.status == 200: data = await response.json() - print("✅ Webhook 端点访问成功") - print(f" 响应: {data['message']}") + print("✅ Webhook endpoint accessed successfully") + print(f" Response: {data['message']}") else: - print(f"❌ Webhook 端点访问失败: {response.status}") - + print(f"❌ Webhook endpoint access failed: {response.status}") print() async def test_authentication_flow(): - """测试完整的认证流程""" - print("🔄 测试完整认证流程") - print("-" * 50) + """Test the complete authentication flow""" + print("🔄 Testing complete authentication flow") + print_divider() - print("📋 认证流程说明:") - print("1. 公开端点: /health, /webhook/gitea - 无需认证") - print("2. 管理端点: /api/admin/* - 需要 JWT 或 API 密钥") - print("3. 日志端点: /api/logs/* - 需要 JWT 或 API 密钥") + print("📋 Authentication flow description:") + print("1. Public endpoints: /health, /webhook/gitea - no authentication required") + print("2. Admin endpoints: /api/admin/* - JWT or API key required") + print("3. Logs endpoints: /api/logs/* - JWT or API key required") print() - print("🔧 如何获取认证令牌:") - print("1. JWT 令牌: 通过登录端点获取(需要实现登录功能)") - print("2. API 密钥: 通过管理界面创建(需要管理员权限)") + print("🔧 How to obtain authentication tokens:") + print("1. JWT token: Obtain via login endpoint (login feature required)") + print("2. API key: Create via admin interface (admin privileges required)") print() - print("⚠️ 当前演示限制:") - print("- 使用模拟的认证令牌") - print("- 实际应用中需要实现完整的登录和密钥管理") - print("- 建议在生产环境中使用真实的认证系统") - + print("⚠️ Demo limitations:") + print("- Using simulated authentication tokens") + print("- In real applications, implement full login and key management") + print("- It is recommended to use real authentication systems in production") print() async def create_demo_api_key(): - """创建演示用的 API 密钥""" - print("🔧 创建演示 API 密钥") - print("-" * 50) + """Create a demo API key""" + print("🔧 Creating demo API key") + print_divider() - # 注意:这是一个简化的演示 - # 在实际应用中,API 密钥应该通过安全的方式创建和存储 + # Note: This is a simplified demo + # In real applications, API keys should be created and stored securely demo_api_key = "demo_api_key_" + str(int(datetime.now().timestamp())) - print(f"✅ 演示 API 密钥已创建: {demo_api_key}") - print("📝 使用方法:") + print(f"✅ Demo API key created: {demo_api_key}") + print("📝 Usage:") print(f" curl -H 'Authorization: Bearer {demo_api_key}' {BASE_URL}/api/logs") print() return demo_api_key async def main(): - """主测试函数""" - print("🚀 开始认证功能测试") + """Main test function""" + print("🚀 Starting authentication feature tests") print("=" * 60) print() try: - # 等待服务启动 + # Wait for service to start await asyncio.sleep(2) await test_public_endpoints() @@ -152,21 +150,21 @@ async def main(): await test_api_key_authentication() await test_authentication_flow() - # 创建演示 API 密钥 + # Create demo API key demo_key = await create_demo_api_key() print("=" * 60) - print("🎉 认证功能测试完成!") + print("🎉 Authentication feature tests completed!") print() - print("📚 下一步建议:") - print("1. 实现完整的登录系统") - print("2. 添加用户管理功能") - print("3. 实现 API 密钥的安全存储") - print("4. 添加权限控制机制") - print("5. 实现会话管理") + print("📚 Next steps:") + print("1. Implement a full login system") + print("2. Add user management features") + print("3. Implement secure API key storage") + print("4. Add permission control mechanisms") + print("5. Implement session management") except Exception as e: - print(f"❌ 测试过程中出现错误: {str(e)}") + print(f"❌ Error occurred during testing: {str(e)}") if __name__ == "__main__": asyncio.run(main()) \ No newline at end of file diff --git a/apps/gitea-webhook-ambassador-python/test_enhanced.py b/apps/gitea-webhook-ambassador-python/test_enhanced.py index ad3cdb74..a94c6f5e 100644 --- a/apps/gitea-webhook-ambassador-python/test_enhanced.py +++ b/apps/gitea-webhook-ambassador-python/test_enhanced.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 """ -增强版 Gitea Webhook Ambassador 功能测试脚本 -演示所有新增的监测和管理功能 +Enhanced Gitea Webhook Ambassador feature test script +Demonstrates all new monitoring and management features """ import asyncio @@ -11,32 +11,34 @@ from datetime import datetime BASE_URL = "http://localhost:8000" -async def test_health_check(): - """测试增强的健康检查""" - print("🧪 测试增强的健康检查") +def print_divider(): print("-" * 50) + +async def test_health_check(): + """Test enhanced health check""" + print("🧪 Testing enhanced health check") + print_divider() async with aiohttp.ClientSession() as session: async with session.get(f"{BASE_URL}/health") as response: if response.status == 200: data = await response.json() - print("✅ 健康检查通过") - print(f" 状态: {data['status']}") - print(f" 服务: {data['service']}") + print("✅ Health check passed") + print(f" Status: {data['status']}") + print(f" Service: {data['service']}") print(f" Jenkins: {data['jenkins']['status']}") - print(f" 工作池: {data['worker_pool']['active_workers']} 活跃工作线程") - print(f" 队列大小: {data['worker_pool']['queue_size']}") - print(f" 已处理: {data['worker_pool']['total_processed']}") - print(f" 失败: {data['worker_pool']['total_failed']}") + print(f" Worker pool: {data['worker_pool']['active_workers']} active workers") + print(f" Queue size: {data['worker_pool']['queue_size']}") + print(f" Processed: {data['worker_pool']['total_processed']}") + print(f" Failed: {data['worker_pool']['total_failed']}") else: - print(f"❌ 健康检查失败: {response.status}") - + print(f"❌ Health check failed: {response.status}") print() async def test_webhook(): - """测试 Webhook 功能""" - print("🧪 测试 Webhook 功能") - print("-" * 50) + """Test webhook feature""" + print("🧪 Testing webhook feature") + print_divider() webhook_data = { "ref": "refs/heads/dev", @@ -59,22 +61,21 @@ async def test_webhook(): ) as response: if response.status == 200: data = await response.json() - print("✅ Webhook 处理成功") - print(f" 响应: {data['message']}") - print(f" 数据大小: {data['data']['body_size']} bytes") + print("✅ Webhook processed successfully") + print(f" Response: {data['message']}") + print(f" Data size: {data['data']['body_size']} bytes") else: - print(f"❌ Webhook 处理失败: {response.status}") - + print(f"❌ Webhook processing failed: {response.status}") print() async def test_api_key_management(): - """测试 API 密钥管理""" - print("🧪 测试 API 密钥管理") - print("-" * 50) + """Test API key management""" + print("🧪 Testing API key management") + print_divider() - # 创建 API 密钥 + # Create API key async with aiohttp.ClientSession() as session: - # 创建密钥 + # Create key create_data = {"name": "test-api-key"} async with session.post( f"{BASE_URL}/api/admin/api-keys", @@ -85,41 +86,40 @@ async def test_api_key_management(): data = await response.json() api_key = data['key'] key_id = data['id'] - print(f"✅ API 密钥创建成功") + print(f"✅ API key created successfully") print(f" ID: {key_id}") - print(f" 名称: {data['name']}") - print(f" 密钥: {api_key[:8]}...{api_key[-8:]}") + print(f" Name: {data['name']}") + print(f" Key: {api_key[:8]}...{api_key[-8:]}") - # 使用新创建的密钥测试日志端点 - print("\n 测试使用新密钥访问日志端点...") + # Test logs endpoint with new key + print("\n Testing logs endpoint with new key...") async with session.get( f"{BASE_URL}/api/logs", headers={"Authorization": f"Bearer {api_key}"} ) as log_response: if log_response.status == 200: logs = await log_response.json() - print(f" ✅ 日志访问成功,获取到 {len(logs)} 条日志") + print(f" ✅ Logs access succeeded, retrieved {len(logs)} logs") else: - print(f" ❌ 日志访问失败: {log_response.status}") + print(f" ❌ Logs access failed: {log_response.status}") - # 删除密钥 + # Delete key async with session.delete( f"{BASE_URL}/api/admin/api-keys/{key_id}", headers={"Authorization": f"Bearer {api_key}"} ) as delete_response: if delete_response.status == 200: - print(f" ✅ API 密钥删除成功") + print(f" ✅ API key deleted successfully") else: - print(f" ❌ API 密钥删除失败: {delete_response.status}") + print(f" ❌ API key deletion failed: {delete_response.status}") else: - print(f"❌ API 密钥创建失败: {response.status}") - + print(f"❌ API key creation failed: {response.status}") print() async def test_project_mapping(): - """测试项目映射管理""" - print("🧪 测试项目映射管理") - print("-" * 50) + """Test project mapping management""" + print("🧪 Testing project mapping management") + print_divider() mapping_data = { "repository_name": "freeleaps/test-project", @@ -135,7 +135,7 @@ async def test_project_mapping(): } async with aiohttp.ClientSession() as session: - # 创建项目映射 + # Create project mapping async with session.post( f"{BASE_URL}/api/admin/projects", json=mapping_data, @@ -143,45 +143,43 @@ async def test_project_mapping(): ) as response: if response.status == 200: data = await response.json() - print("✅ 项目映射创建成功") + print("✅ Project mapping created successfully") print(f" ID: {data['id']}") - print(f" 仓库: {data['repository_name']}") - print(f" 默认任务: {data['default_job']}") - print(f" 分支任务数: {len(data['branch_jobs'])}") - print(f" 分支模式数: {len(data['branch_patterns'])}") + print(f" Repository: {data['repository_name']}") + print(f" Default job: {data['default_job']}") + print(f" Branch jobs: {len(data['branch_jobs'])}") + print(f" Branch patterns: {len(data['branch_patterns'])}") else: - print(f"❌ 项目映射创建失败: {response.status}") - + print(f"❌ Project mapping creation failed: {response.status}") print() async def test_logs_and_stats(): - """测试日志和统计功能""" - print("🧪 测试日志和统计功能") - print("-" * 50) + """Test logs and statistics features""" + print("🧪 Testing logs and statistics features") + print_divider() async with aiohttp.ClientSession() as session: - # 获取日志统计 + # Get log statistics async with session.get( f"{BASE_URL}/api/logs/stats", headers={"Authorization": "Bearer test-token"} ) as response: if response.status == 200: stats = await response.json() - print("✅ 日志统计获取成功") - print(f" 总日志数: {stats['total_logs']}") - print(f" 成功日志: {stats['successful_logs']}") - print(f" 失败日志: {stats['failed_logs']}") - print(f" 最近24小时: {stats['recent_logs_24h']}") - print(f" 仓库统计: {len(stats['repository_stats'])} 个仓库") + print("✅ Log statistics retrieved successfully") + print(f" Total logs: {stats['total_logs']}") + print(f" Successful logs: {stats['successful_logs']}") + print(f" Failed logs: {stats['failed_logs']}") + print(f" Recent logs (24h): {stats['recent_logs_24h']}") + print(f" Repository stats: {len(stats['repository_stats'])} repositories") else: - print(f"❌ 日志统计获取失败: {response.status}") - + print(f"❌ Log statistics retrieval failed: {response.status}") print() async def test_admin_stats(): - """测试管理统计""" - print("🧪 测试管理统计") - print("-" * 50) + """Test admin statistics""" + print("🧪 Testing admin statistics") + print_divider() async with aiohttp.ClientSession() as session: async with session.get( @@ -190,19 +188,18 @@ async def test_admin_stats(): ) as response: if response.status == 200: stats = await response.json() - print("✅ 管理统计获取成功") - print(f" API 密钥总数: {stats['api_keys']['total']}") - print(f" 活跃密钥: {stats['api_keys']['active']}") - print(f" 最近使用: {stats['api_keys']['recently_used']}") - print(f" 项目映射总数: {stats['project_mappings']['total']}") + print("✅ Admin statistics retrieved successfully") + print(f" Total API keys: {stats['api_keys']['total']}") + print(f" Active keys: {stats['api_keys']['active']}") + print(f" Recently used: {stats['api_keys']['recently_used']}") + print(f" Total project mappings: {stats['project_mappings']['total']}") else: - print(f"❌ 管理统计获取失败: {response.status}") - + print(f"❌ Admin statistics retrieval failed: {response.status}") print() async def main(): - """主测试函数""" - print("🚀 开始增强版 Gitea Webhook Ambassador 功能测试") + """Main test function""" + print("🚀 Starting enhanced Gitea Webhook Ambassador feature tests") print("=" * 60) print() @@ -215,11 +212,11 @@ async def main(): await test_admin_stats() print("=" * 60) - print("🎉 所有测试完成!") - print("✅ Python 版本现在具备了与 Go 版本相同的监测和管理功能") + print("🎉 All tests completed!") + print("✅ Python version now has the same monitoring and management features as the Go version") except Exception as e: - print(f"❌ 测试过程中出现错误: {str(e)}") + print(f"❌ Error occurred during testing: {str(e)}") if __name__ == "__main__": asyncio.run(main()) \ No newline at end of file diff --git a/apps/gitea-webhook-ambassador-python/test_enhanced_features.py b/apps/gitea-webhook-ambassador-python/test_enhanced_features.py index 8f4c818a..d6cafd16 100644 --- a/apps/gitea-webhook-ambassador-python/test_enhanced_features.py +++ b/apps/gitea-webhook-ambassador-python/test_enhanced_features.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 """ -Gitea Webhook Ambassador 增强版功能测试脚本 +Gitea Webhook Ambassador Enhanced Feature Test Script """ import requests @@ -11,29 +11,29 @@ BASE_URL = "http://localhost:8000" ADMIN_SECRET_KEY = "admin-secret-key-change-in-production" def test_health_check(): - """测试健康检查""" - print("🔍 测试健康检查...") + """Test health check""" + print("🔍 Testing health check...") try: response = requests.get(f"{BASE_URL}/health") if response.status_code == 200: data = response.json() - print(f"✅ 健康检查成功: {data['status']}") - print(f" 版本: {data['version']}") - print(f" 运行时间: {data['uptime']}") - print(f" 内存使用: {data['memory']}") + print(f"✅ Health check succeeded: {data['status']}") + print(f" Version: {data['version']}") + print(f" Uptime: {data['uptime']}") + print(f" Memory usage: {data['memory']}") return True else: - print(f"❌ 健康检查失败: {response.status_code}") + print(f"❌ Health check failed: {response.status_code}") return False except Exception as e: - print(f"❌ 健康检查异常: {e}") + print(f"❌ Health check exception: {e}") return False def test_login(): - """测试登录功能""" - print("\n🔐 测试登录功能...") + """Test login functionality""" + print("\n🔐 Testing login functionality...") try: - # 测试登录 API + # Test login API login_data = {"secret_key": ADMIN_SECRET_KEY} response = requests.post( f"{BASE_URL}/api/auth/login", @@ -44,23 +44,23 @@ def test_login(): if response.status_code == 200: data = response.json() token = data.get("token") - print(f"✅ 登录成功,获得 JWT 令牌") + print(f"✅ Login succeeded, obtained JWT token") return token else: - print(f"❌ 登录失败: {response.status_code} - {response.text}") + print(f"❌ Login failed: {response.status_code} - {response.text}") return None except Exception as e: - print(f"❌ 登录异常: {e}") + print(f"❌ Login exception: {e}") return None def test_api_key_management(token): - """测试 API 密钥管理""" - print("\n🔑 测试 API 密钥管理...") + """Test API key management""" + print("\n🔑 Testing API key management...") headers = {"Authorization": f"Bearer {token}"} try: - # 创建 API 密钥 - key_data = {"description": "测试 API 密钥"} + # Create API key + key_data = {"description": "Test API key"} response = requests.post( f"{BASE_URL}/api/keys", json=key_data, @@ -71,41 +71,41 @@ def test_api_key_management(token): data = response.json() api_key = data["key"] key_id = data["id"] - print(f"✅ 创建 API 密钥成功: {api_key[:20]}...") + print(f"✅ API key created successfully: {api_key[:20]}...") - # 列出 API 密钥 + # List API keys response = requests.get(f"{BASE_URL}/api/keys", headers=headers) if response.status_code == 200: keys_data = response.json() - print(f"✅ 列出 API 密钥成功,共 {len(keys_data['keys'])} 个") + print(f"✅ API keys listed successfully, total {len(keys_data['keys'])}") - # 删除 API 密钥 + # Delete API key response = requests.delete(f"{BASE_URL}/api/keys/{key_id}", headers=headers) if response.status_code == 200: - print(f"✅ 删除 API 密钥成功") + print(f"✅ API key deleted successfully") return True else: - print(f"❌ 删除 API 密钥失败: {response.status_code}") + print(f"❌ API key deletion failed: {response.status_code}") return False else: - print(f"❌ 列出 API 密钥失败: {response.status_code}") + print(f"❌ API key listing failed: {response.status_code}") return False else: - print(f"❌ 创建 API 密钥失败: {response.status_code} - {response.text}") + print(f"❌ API key creation failed: {response.status_code} - {response.text}") return False except Exception as e: - print(f"❌ API 密钥管理异常: {e}") + print(f"❌ API key management exception: {e}") return False def test_project_management(token): - """测试项目管理""" - print("\n📁 测试项目管理...") + """Test project management""" + print("\n📁 Testing project management...") headers = {"Authorization": f"Bearer {token}"} try: - # 创建项目 + # Create project project_data = { - "name": "测试项目", + "name": "Test Project", "jenkinsJob": "test-job", "giteaRepo": "test-owner/test-repo" } @@ -118,109 +118,108 @@ def test_project_management(token): if response.status_code == 200: data = response.json() project_id = data["id"] - print(f"✅ 创建项目成功: {data['name']}") + print(f"✅ Project created successfully: {data['name']}") - # 列出项目 + # List projects response = requests.get(f"{BASE_URL}/api/projects/", headers=headers) if response.status_code == 200: projects_data = response.json() - print(f"✅ 列出项目成功,共 {len(projects_data['projects'])} 个") + print(f"✅ Projects listed successfully, total {len(projects_data['projects'])}") - # 删除项目 + # Delete project response = requests.delete(f"{BASE_URL}/api/projects/{project_id}", headers=headers) if response.status_code == 200: - print(f"✅ 删除项目成功") + print(f"✅ Project deleted successfully") return True else: - print(f"❌ 删除项目失败: {response.status_code}") + print(f"❌ Project deletion failed: {response.status_code}") return False else: - print(f"❌ 列出项目失败: {response.status_code}") + print(f"❌ Project listing failed: {response.status_code}") return False else: - print(f"❌ 创建项目失败: {response.status_code} - {response.text}") + print(f"❌ Project creation failed: {response.status_code} - {response.text}") return False except Exception as e: - print(f"❌ 项目管理异常: {e}") + print(f"❌ Project management exception: {e}") return False def test_stats(token): - """测试统计信息""" - print("\n📊 测试统计信息...") + """Test statistics information""" + print("\n📊 Testing statistics information...") headers = {"Authorization": f"Bearer {token}"} try: response = requests.get(f"{BASE_URL}/api/stats", headers=headers) if response.status_code == 200: data = response.json() - print(f"✅ 获取统计信息成功:") - print(f" 总项目数: {data['total_projects']}") - print(f" API 密钥数: {data['total_api_keys']}") - print(f" 今日触发次数: {data['today_triggers']}") - print(f" 成功触发次数: {data['successful_triggers']}") + print(f"✅ Statistics retrieved successfully:") + print(f" Total projects: {data['total_projects']}") + print(f" API keys: {data['total_api_keys']}") + print(f" Triggers today: {data['today_triggers']}") + print(f" Successful triggers: {data['successful_triggers']}") return True else: - print(f"❌ 获取统计信息失败: {response.status_code}") + print(f"❌ Statistics retrieval failed: {response.status_code}") return False except Exception as e: - print(f"❌ 统计信息异常: {e}") + print(f"❌ Statistics exception: {e}") return False def test_logs(token): - """测试日志功能""" - print("\n📝 测试日志功能...") + """Test log functionality""" + print("\n📝 Testing log functionality...") headers = {"Authorization": f"Bearer {token}"} try: response = requests.get(f"{BASE_URL}/api/logs", headers=headers) if response.status_code == 200: data = response.json() - print(f"✅ 获取日志成功,共 {len(data['logs'])} 条记录") + print(f"✅ Logs retrieved successfully, total {len(data['logs'])} records") return True else: - print(f"❌ 获取日志失败: {response.status_code}") + print(f"❌ Log retrieval failed: {response.status_code}") return False except Exception as e: - print(f"❌ 日志功能异常: {e}") + print(f"❌ Log functionality exception: {e}") return False def main(): - """主测试函数""" - print("🚀 Gitea Webhook Ambassador 增强版功能测试") + """Main test function""" + print("🚀 Gitea Webhook Ambassador Enhanced Feature Test") print("=" * 50) - # 测试健康检查 + # Test health check if not test_health_check(): - print("❌ 健康检查失败,服务可能未启动") + print("❌ Health check failed, service may not be running") return - # 测试登录 + # Test login token = test_login() if not token: - print("❌ 登录失败,无法继续测试") + print("❌ Login failed, cannot continue testing") return - # 测试各项功能 + # Test features test_api_key_management(token) test_project_management(token) test_stats(token) test_logs(token) print("\n" + "=" * 50) - print("🎉 增强版功能测试完成!") - print("\n📋 已实现的功能:") - print(" ✅ Web 登录界面") - print(" ✅ 数据库存储 API 密钥") - print(" ✅ 延长 JWT 有效期 (7天)") - print(" ✅ 前端仪表板") - print(" ✅ 项目管理") - print(" ✅ API 密钥管理") - print(" ✅ 日志查看") - print(" ✅ 健康状态监控") - print("\n🌐 访问地址:") - print(f" 登录页面: {BASE_URL}/login") - print(f" 仪表板: {BASE_URL}/dashboard") - print(f" 管理员密钥: {ADMIN_SECRET_KEY}") + print("🎉 Enhanced feature test completed!") + print("\n📋 Implemented features:") + print(" ✅ Web login interface") + print(" ✅ Database storage for API keys") + print(" ✅ Extended JWT validity (7 days)") + print(" ✅ Frontend dashboard") + print(" ✅ Project management") + print(" ✅ API key management") + print(" ✅ Log viewing") + print(" ✅ Health status monitoring") + print("\n🌐 Access URLs:") + print(f" Login page: {BASE_URL}/login") + print(f" Dashboard: {BASE_URL}/dashboard") if __name__ == "__main__": main() \ No newline at end of file diff --git a/apps/gitea-webhook-ambassador-python/test_webhook.py b/apps/gitea-webhook-ambassador-python/test_webhook.py index b24b7cf9..979bdb9f 100755 --- a/apps/gitea-webhook-ambassador-python/test_webhook.py +++ b/apps/gitea-webhook-ambassador-python/test_webhook.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 """ -Webhook 功能测试脚本 -用于验证 Gitea Webhook Ambassador 的各项功能 +Webhook feature test script +Used to verify all features of Gitea Webhook Ambassador """ import asyncio @@ -10,11 +10,11 @@ import httpx import time from datetime import datetime -# 测试配置 +# Test configuration BASE_URL = "http://localhost:8000" WEBHOOK_SECRET = "your-secret-key-here-make-it-long-and-random" -# 测试数据 +# Test data TEST_WEBHOOK_DATA = { "ref": "refs/heads/dev", "before": "abc1234567890abcdef1234567890abcdef123456", @@ -59,46 +59,46 @@ TEST_WEBHOOK_DATA = { async def test_health_check(): - """测试健康检查""" - print("🔍 测试健康检查...") + """Test health check""" + print("🔍 Testing health check...") async with httpx.AsyncClient() as client: try: response = await client.get(f"{BASE_URL}/health") if response.status_code == 200: data = response.json() - print(f"✅ 健康检查通过: {data['status']}") + print(f"✅ Health check passed: {data['status']}") return True else: - print(f"❌ 健康检查失败: {response.status_code}") + print(f"❌ Health check failed: {response.status_code}") return False except Exception as e: - print(f"❌ 健康检查异常: {e}") + print(f"❌ Health check exception: {e}") return False async def test_queue_status(): - """测试队列状态""" - print("🔍 测试队列状态...") + """Test queue status""" + print("🔍 Testing queue status...") async with httpx.AsyncClient() as client: try: response = await client.get(f"{BASE_URL}/health/queue") if response.status_code == 200: data = response.json() - print(f"✅ 队列状态: {data['queue_stats']}") + print(f"✅ Queue status: {data['queue_stats']}") return True else: - print(f"❌ 队列状态检查失败: {response.status_code}") + print(f"❌ Queue status check failed: {response.status_code}") return False except Exception as e: - print(f"❌ 队列状态检查异常: {e}") + print(f"❌ Queue status exception: {e}") return False async def test_webhook_endpoint(): - """测试 Webhook 端点""" - print("🔍 测试 Webhook 端点...") + """Test webhook endpoint""" + print("🔍 Testing webhook endpoint...") headers = { "Content-Type": "application/json", @@ -113,47 +113,47 @@ async def test_webhook_endpoint(): json=TEST_WEBHOOK_DATA ) - print(f"📊 响应状态: {response.status_code}") - print(f"📊 响应内容: {response.text}") + print(f"📊 Response status: {response.status_code}") + print(f"📊 Response content: {response.text}") if response.status_code in [200, 202]: - print("✅ Webhook 端点测试通过") + print("✅ Webhook endpoint test passed") return True else: - print(f"❌ Webhook 端点测试失败: {response.status_code}") + print(f"❌ Webhook endpoint test failed: {response.status_code}") return False except Exception as e: - print(f"❌ Webhook 端点测试异常: {e}") + print(f"❌ Webhook endpoint exception: {e}") return False async def test_metrics_endpoint(): - """测试监控指标端点""" - print("🔍 测试监控指标端点...") + """Test metrics endpoint""" + print("🔍 Testing metrics endpoint...") async with httpx.AsyncClient() as client: try: response = await client.get(f"{BASE_URL}/metrics") if response.status_code == 200: - print("✅ 监控指标端点测试通过") - # 打印一些关键指标 + print("✅ Metrics endpoint test passed") + # Print some key metrics content = response.text for line in content.split('\n'): if 'webhook_requests_total' in line or 'queue_size' in line: print(f"📊 {line}") return True else: - print(f"❌ 监控指标端点测试失败: {response.status_code}") + print(f"❌ Metrics endpoint test failed: {response.status_code}") return False except Exception as e: - print(f"❌ 监控指标端点测试异常: {e}") + print(f"❌ Metrics endpoint exception: {e}") return False async def test_deduplication(): - """测试防抖功能""" - print("🔍 测试防抖功能...") + """Test deduplication feature""" + print("🔍 Testing deduplication feature...") headers = { "Content-Type": "application/json", @@ -162,64 +162,64 @@ async def test_deduplication(): async with httpx.AsyncClient() as client: try: - # 第一次请求 - print("📤 发送第一次请求...") + # First request + print("📤 Sending first request...") response1 = await client.post( f"{BASE_URL}/webhook/gitea", headers=headers, json=TEST_WEBHOOK_DATA ) - print(f"📊 第一次响应: {response1.status_code}") + print(f"📊 First response: {response1.status_code}") - # 等待一秒 + # Wait one second await asyncio.sleep(1) - # 第二次请求(相同数据,应该被防抖) - print("📤 发送第二次请求(相同数据)...") + # Second request (same data, should be deduplicated) + print("📤 Sending second request (same data)...") response2 = await client.post( f"{BASE_URL}/webhook/gitea", headers=headers, json=TEST_WEBHOOK_DATA ) - print(f"📊 第二次响应: {response2.status_code}") + print(f"📊 Second response: {response2.status_code}") - # 修改提交哈希,发送第三次请求 + # Modify commit hash, send third request modified_data = TEST_WEBHOOK_DATA.copy() modified_data["after"] = "ghi1234567890abcdef1234567890abcdef123456" - print("📤 发送第三次请求(不同提交哈希)...") + print("📤 Sending third request (different commit hash)...") response3 = await client.post( f"{BASE_URL}/webhook/gitea", headers=headers, json=modified_data ) - print(f"📊 第三次响应: {response3.status_code}") + print(f"📊 Third response: {response3.status_code}") - print("✅ 防抖功能测试完成") + print("✅ Deduplication feature test completed") return True except Exception as e: - print(f"❌ 防抖功能测试异常: {e}") + print(f"❌ Deduplication feature exception: {e}") return False async def test_invalid_webhook(): - """测试无效的 Webhook 请求""" - print("🔍 测试无效的 Webhook 请求...") + """Test invalid webhook requests""" + print("🔍 Testing invalid webhook requests...") async with httpx.AsyncClient() as client: try: - # 测试缺少签名 - print("📤 测试缺少签名...") + # Test missing signature + print("📤 Testing missing signature...") response1 = await client.post( f"{BASE_URL}/webhook/gitea", headers={"Content-Type": "application/json"}, json=TEST_WEBHOOK_DATA ) - print(f"📊 缺少签名响应: {response1.status_code}") + print(f"📊 Missing signature response: {response1.status_code}") - # 测试错误的签名 - print("📤 测试错误的签名...") + # Test wrong signature + print("📤 Testing wrong signature...") response2 = await client.post( f"{BASE_URL}/webhook/gitea", headers={ @@ -228,10 +228,10 @@ async def test_invalid_webhook(): }, json=TEST_WEBHOOK_DATA ) - print(f"📊 错误签名响应: {response2.status_code}") + print(f"📊 Wrong signature response: {response2.status_code}") - # 测试无效的 JSON - print("📤 测试无效的 JSON...") + # Test invalid JSON + print("📤 Testing invalid JSON...") response3 = await client.post( f"{BASE_URL}/webhook/gitea", headers={ @@ -240,28 +240,28 @@ async def test_invalid_webhook(): }, content="invalid json" ) - print(f"📊 无效 JSON 响应: {response3.status_code}") + print(f"📊 Invalid JSON response: {response3.status_code}") - print("✅ 无效请求测试完成") + print("✅ Invalid request tests completed") return True except Exception as e: - print(f"❌ 无效请求测试异常: {e}") + print(f"❌ Invalid request tests exception: {e}") return False async def main(): - """主测试函数""" - print("🚀 开始 Gitea Webhook Ambassador 功能测试") + """Main test function""" + print("🚀 Starting Gitea Webhook Ambassador feature tests") print("=" * 50) tests = [ - ("健康检查", test_health_check), - ("队列状态", test_queue_status), - ("Webhook 端点", test_webhook_endpoint), - ("监控指标", test_metrics_endpoint), - ("防抖功能", test_deduplication), - ("无效请求", test_invalid_webhook), + ("Health Check", test_health_check), + ("Queue Status", test_queue_status), + ("Webhook Endpoint", test_webhook_endpoint), + ("Metrics", test_metrics_endpoint), + ("Deduplication", test_deduplication), + ("Invalid Requests", test_invalid_webhook), ] results = [] @@ -274,34 +274,34 @@ async def main(): result = await test_func() results.append((test_name, result)) except Exception as e: - print(f"❌ {test_name} 测试异常: {e}") + print(f"❌ {test_name} test exception: {e}") results.append((test_name, False)) - # 等待一下再进行下一个测试 + # Wait a bit before next test await asyncio.sleep(1) - # 输出测试结果 + # Output test results print("\n" + "=" * 50) - print("📊 测试结果汇总") + print("📊 Test Results Summary") print("=" * 50) passed = 0 total = len(results) for test_name, result in results: - status = "✅ 通过" if result else "❌ 失败" + status = "✅ Passed" if result else "❌ Failed" print(f"{test_name}: {status}") if result: passed += 1 - print(f"\n📈 总体结果: {passed}/{total} 测试通过") + print(f"\n📈 Overall: {passed}/{total} tests passed") if passed == total: - print("🎉 所有测试通过!服务运行正常。") + print("🎉 All tests passed! Service is running normally.") else: - print("⚠️ 部分测试失败,请检查服务配置和日志。") + print("⚠️ Some tests failed, please check service configuration and logs.") if __name__ == "__main__": - # 运行测试 + # Run tests asyncio.run(main()) \ No newline at end of file