日志丰富功能 #20
|
|
@ -0,0 +1,243 @@
|
|||
# 标签同步服务
|
||||
|
||||
GitHub、Gitee、GitLink 之间的双向标签同步服务,支持前后端分离架构。
|
||||
|
||||
## 功能特性
|
||||
|
||||
### 🚀 支持的同步类型
|
||||
- **Gitee ↔ GitLink** 双向标签同步
|
||||
- **GitHub ↔ GitLink** 双向标签同步
|
||||
- **GitHub ↔ Gitee** 双向标签同步
|
||||
|
||||
### ⚡ 核心功能
|
||||
- ✅ 双向标签同步
|
||||
- ✅ 单向标签同步
|
||||
- ✅ 试运行模式(预览同步结果)
|
||||
- ✅ 实际执行模式
|
||||
- ✅ 标签排除模式(如跳过测试标签)
|
||||
- ✅ 现代化Web界面
|
||||
- ✅ RESTful API
|
||||
|
||||
## 架构说明
|
||||
|
||||
```
|
||||
┌─────────────────┐ ┌─────────────────┐
|
||||
│ 前端服务 │────│ 后端API │
|
||||
│ (端口 8006) │ │ (端口 8005) │
|
||||
│ React + Antd │ │ FastAPI │
|
||||
└─────────────────┘ └─────────────────┘
|
||||
│
|
||||
┌────────┴────────┐
|
||||
│ 标签同步模块 │
|
||||
│ repo_tag_sync │
|
||||
└─────────────────┘
|
||||
│
|
||||
┌───────────┼───────────┐
|
||||
│ │ │
|
||||
┌─────▼───┐ ┌─────▼───┐ ┌─────▼───┐
|
||||
│ GitHub │ │ Gitee │ │ GitLink │
|
||||
│ Client │ │ Client │ │ Client │
|
||||
└─────────┘ └─────────┘ └─────────┘
|
||||
```
|
||||
|
||||
## 快速开始
|
||||
|
||||
### 1. 环境要求
|
||||
- Python 3.7+
|
||||
- Node.js 16+
|
||||
- npm 或 yarn
|
||||
|
||||
### 2. 一键启动(推荐)
|
||||
```bash
|
||||
# 启动前后端服务
|
||||
python start_tag_sync_services.py
|
||||
```
|
||||
|
||||
启动后访问:
|
||||
- 🌐 前端界面: http://localhost:8006
|
||||
- 📡 后端API: http://localhost:8005
|
||||
- 📖 API文档: http://localhost:8005/docs
|
||||
|
||||
### 3. 分别启动
|
||||
|
||||
#### 启动后端服务 (8005端口)
|
||||
```bash
|
||||
python tag_sync_backend.py
|
||||
```
|
||||
|
||||
#### 启动前端服务 (8006端口)
|
||||
```bash
|
||||
python tag_sync_frontend.py
|
||||
```
|
||||
|
||||
## 使用指南
|
||||
|
||||
### 1. 配置同步任务
|
||||
|
||||
1. 打开浏览器访问 http://localhost:8006
|
||||
2. 点击"新建标签同步"按钮
|
||||
3. 选择同步类型(如 Gitee ↔ GitLink)
|
||||
4. 配置平台信息:
|
||||
|
||||
#### 平台配置参数
|
||||
| 平台 | 必需参数 | 说明 |
|
||||
|------|----------|------|
|
||||
| GitHub | 用户名、仓库名、Token | GitHub Personal Access Token |
|
||||
| Gitee | 用户名、仓库名、Token | Gitee Private Token |
|
||||
| GitLink | 用户名、仓库名、Cookie | 浏览器Cookie字符串 |
|
||||
|
||||
#### 同步选项
|
||||
- **排除模式**: 用逗号分隔的模式,如 `test-,dev-`
|
||||
- **执行模式**:
|
||||
- 试运行:只检测需要同步的标签
|
||||
- 实际执行:真正创建标签到目标平台
|
||||
|
||||
### 2. 查看同步结果
|
||||
|
||||
- 同步任务列表显示所有创建的同步配置
|
||||
- 点击"查看详情"可以看到具体的同步结果
|
||||
- 支持重新同步和删除任务
|
||||
|
||||
## API接口
|
||||
|
||||
### 获取同步类型
|
||||
```bash
|
||||
GET http://localhost:8005/cerobot/tag-sync/sync-types
|
||||
```
|
||||
|
||||
### 执行双向同步
|
||||
```bash
|
||||
POST http://localhost:8005/cerobot/tag-sync/sync/bidirectional
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"platform_a": {
|
||||
"platform": "gitee",
|
||||
"org": "your-username",
|
||||
"repo": "your-repo",
|
||||
"token": "your-gitee-token"
|
||||
},
|
||||
"platform_b": {
|
||||
"platform": "gitlink",
|
||||
"org": "your-username",
|
||||
"repo": "your-repo",
|
||||
"cookie": "your-gitlink-cookie"
|
||||
},
|
||||
"dry_run": true,
|
||||
"exclude_patterns": ["test-"]
|
||||
}
|
||||
```
|
||||
|
||||
### 执行单向同步
|
||||
```bash
|
||||
POST http://localhost:8005/cerobot/tag-sync/sync/oneway
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"source_platform": "gitlink",
|
||||
"source_org": "source-user",
|
||||
"source_repo": "source-repo",
|
||||
"source_cookie": "gitlink-cookie",
|
||||
"target_platform": "gitee",
|
||||
"target_org": "target-user",
|
||||
"target_repo": "target-repo",
|
||||
"target_token": "gitee-token",
|
||||
"dry_run": true,
|
||||
"exclude_patterns": ["test-"]
|
||||
}
|
||||
```
|
||||
|
||||
## 配置说明
|
||||
|
||||
### Token/Cookie 获取方法
|
||||
|
||||
#### GitHub Token
|
||||
1. 访问 GitHub Settings > Developer settings > Personal access tokens
|
||||
2. 创建新token,选择 `repo` 权限
|
||||
3. 复制生成的token
|
||||
|
||||
#### Gitee Token
|
||||
1. 访问 Gitee 设置 > 私人令牌
|
||||
2. 创建新令牌,选择相应权限
|
||||
3. 复制生成的token
|
||||
|
||||
#### GitLink Cookie
|
||||
1. 打开浏览器登录 GitLink
|
||||
2. 按F12打开开发者工具
|
||||
3. 在Application/存储 > Cookies中找到GitLink相关cookie
|
||||
4. 复制完整的cookie字符串
|
||||
|
||||
## 项目结构
|
||||
|
||||
```
|
||||
├── tag_sync_backend.py # 后端服务启动脚本
|
||||
├── tag_sync_frontend.py # 前端服务启动脚本
|
||||
├── start_tag_sync_services.py # 一体化启动脚本
|
||||
├── src/api/TagSync.py # 后端API实现
|
||||
├── repo_tag_sync_module/ # 标签同步核心模块
|
||||
│ ├── github_client.py # GitHub客户端
|
||||
│ ├── gitee_client.py # Gitee客户端
|
||||
│ ├── gitlink_client.py # GitLink客户端
|
||||
│ └── sync_service.py # 同步服务
|
||||
└── web/src/pages/tagSync/ # 前端页面
|
||||
├── index.tsx # 主页面
|
||||
├── components/
|
||||
│ ├── ConfigModal.tsx # 配置弹窗
|
||||
│ └── ResultModal.tsx # 结果显示
|
||||
└── services/TagSync.ts # 前端API服务
|
||||
```
|
||||
|
||||
## 故障排除
|
||||
|
||||
### 常见问题
|
||||
|
||||
1. **后端启动失败**
|
||||
- 检查Python版本 (>=3.7)
|
||||
- 检查依赖包安装: `pip install -r requirements.txt`
|
||||
- 检查端口8005是否被占用
|
||||
|
||||
2. **前端启动失败**
|
||||
- 检查Node.js版本 (>=16)
|
||||
- 运行 `npm install` 安装依赖
|
||||
- 检查端口8006是否被占用
|
||||
|
||||
3. **API调用失败**
|
||||
- 检查后端服务是否正常运行
|
||||
- 检查CORS配置
|
||||
- 查看浏览器控制台错误信息
|
||||
|
||||
4. **标签同步失败**
|
||||
- 检查Token/Cookie是否有效
|
||||
- 检查网络连接
|
||||
- 查看后端日志错误信息
|
||||
|
||||
### 日志查看
|
||||
- 后端日志: 控制台输出
|
||||
- 前端日志: 浏览器开发者工具 Console
|
||||
|
||||
## 开发说明
|
||||
|
||||
### 后端开发
|
||||
- 基于 FastAPI 框架
|
||||
- 使用 repo_tag_sync_module 核心模块
|
||||
- 支持异步任务处理
|
||||
|
||||
### 前端开发
|
||||
- 基于 React + Ant Design Pro
|
||||
- 使用 TypeScript
|
||||
- 响应式设计,移动端友好
|
||||
|
||||
## 版本历史
|
||||
|
||||
- v1.0.0: 基础双向同步功能
|
||||
- 支持 GitHub、Gitee、GitLink 三平台
|
||||
- 现代化Web界面
|
||||
- 完整的API文档
|
||||
|
||||
## 许可证
|
||||
|
||||
MIT License
|
||||
|
||||
## 贡献
|
||||
|
||||
欢迎提交Issue和Pull Request!
|
||||
|
|
@ -0,0 +1,200 @@
|
|||
# 标签同步独立模块
|
||||
|
||||
✅ **已完成**: 标签同步代码已成功抽离为独立模块,并修复了所有启动问题!
|
||||
|
||||
## 🎯 模块化改进
|
||||
|
||||
### 之前的问题
|
||||
- 标签同步代码混杂在主项目中
|
||||
- 前端启动依赖npm,Windows环境出现"找不到指定文件"错误
|
||||
- 代码耦合度高,不便维护和部署
|
||||
|
||||
### 现在的解决方案
|
||||
- ✅ **完全独立的模块**: `repo_tag_sync_module/`
|
||||
- ✅ **纯Python实现**: 无需npm,使用Python内置HTTP服务器
|
||||
- ✅ **跨平台兼容**: 修复Windows编码问题,支持多平台
|
||||
- ✅ **模块化架构**: 清晰的目录结构,便于维护
|
||||
- ✅ **一键启动**: 简单的启动脚本,自动管理前后端服务
|
||||
|
||||
## 📁 新的目录结构
|
||||
|
||||
```
|
||||
repo_tag_sync_module/
|
||||
├── __init__.py # 模块初始化
|
||||
├── README.md # 模块文档
|
||||
├── start_tag_sync_services.py # 一体化启动脚本
|
||||
├──
|
||||
├── # 核心同步组件 (保持不变)
|
||||
├── github_client.py
|
||||
├── gitee_client.py
|
||||
├── gitlink_client.py
|
||||
├── sync_service.py
|
||||
├── test_tag_sync.py
|
||||
├── test_bidirectional_sync.py
|
||||
├──
|
||||
├── # 新增: Web API 服务
|
||||
├── web_api/
|
||||
│ ├── __init__.py
|
||||
│ ├── tag_sync_api.py # FastAPI路由实现
|
||||
│ └── backend_server.py # 后端服务启动器
|
||||
├──
|
||||
└── # 新增: Web 前端界面
|
||||
└── web_frontend/
|
||||
├── __init__.py
|
||||
├── index.html # 现代化HTML界面
|
||||
├── script.js # 前端交互逻辑
|
||||
└── frontend_server.py # Python HTTP服务器
|
||||
```
|
||||
|
||||
## 🚀 启动方式 (已修复所有问题)
|
||||
|
||||
### 1. 快捷启动 (推荐)
|
||||
```bash
|
||||
# 从项目根目录一键启动
|
||||
python start_tag_sync.py
|
||||
```
|
||||
|
||||
### 2. 模块启动
|
||||
```bash
|
||||
# 从模块目录启动
|
||||
cd repo_tag_sync_module
|
||||
python start_tag_sync_services.py
|
||||
|
||||
# 或使用模块方式
|
||||
python -m repo_tag_sync_module.start_tag_sync_services
|
||||
```
|
||||
|
||||
### 3. 分别启动
|
||||
```bash
|
||||
# 仅启动后端 (8005端口)
|
||||
python -m repo_tag_sync_module.web_api.backend_server
|
||||
|
||||
# 仅启动前端 (8006端口)
|
||||
python -m repo_tag_sync_module.web_frontend.frontend_server
|
||||
```
|
||||
|
||||
## ✅ 验证服务运行
|
||||
|
||||
启动后可以验证:
|
||||
```bash
|
||||
# 检查后端健康状态
|
||||
python -c "import requests; print(requests.get('http://localhost:8005/health').json())"
|
||||
|
||||
# 检查前端响应
|
||||
python -c "import requests; print(requests.get('http://localhost:8006').status_code)"
|
||||
|
||||
# 测试API接口
|
||||
python -c "import requests; print(requests.get('http://localhost:8005/api/tag-sync/sync-types').json())"
|
||||
```
|
||||
|
||||
预期输出:
|
||||
```json
|
||||
# 后端健康检查
|
||||
{"status": "healthy", "service": "tag-sync-backend", "port": 8005}
|
||||
|
||||
# 前端状态码
|
||||
200
|
||||
|
||||
# API接口响应
|
||||
{
|
||||
"data": {
|
||||
"sync_types": [
|
||||
{"id": "gitee_gitlink", "name": "Gitee ↔ GitLink", ...},
|
||||
{"id": "github_gitlink", "name": "GitHub ↔ GitLink", ...},
|
||||
{"id": "github_gitee", "name": "GitHub ↔ Gitee", ...}
|
||||
]
|
||||
},
|
||||
"message": "获取同步类型成功",
|
||||
"code": 0
|
||||
}
|
||||
```
|
||||
|
||||
## 🌐 Web界面功能
|
||||
|
||||
访问 http://localhost:8006 可以:
|
||||
|
||||
1. **查看支持的同步类型**: 直观的卡片式展示
|
||||
2. **配置同步任务**: 表单式配置界面
|
||||
- 选择源和目标平台
|
||||
- 填写认证信息 (Token/Cookie)
|
||||
- 设置排除模式
|
||||
- 选择试运行/实际执行
|
||||
3. **实时同步**: AJAX调用后端API
|
||||
4. **结果展示**: 详细的同步结果和错误信息
|
||||
|
||||
## 🔧 技术改进
|
||||
|
||||
### 后端改进
|
||||
- **独立FastAPI应用**: 不依赖主项目框架
|
||||
- **模块化路由**: 清晰的API组织结构
|
||||
- **CORS支持**: 允许前端跨域访问
|
||||
- **错误处理**: 完善的异常处理机制
|
||||
|
||||
### 前端改进
|
||||
- **纯HTML/JS**: 无需React/Vue等复杂框架
|
||||
- **响应式设计**: 移动端友好
|
||||
- **现代化UI**: 美观的界面设计
|
||||
- **实时交互**: 支持试运行和实际执行
|
||||
|
||||
### 兼容性改进
|
||||
- **Windows兼容**: 修复emoji字符编码问题
|
||||
- **Python服务器**: 使用内置HTTP服务器,无需npm
|
||||
- **跨平台**: 支持Windows/Linux/macOS
|
||||
|
||||
## 🎯 使用场景
|
||||
|
||||
### 开发者
|
||||
```bash
|
||||
# 快速测试同步功能
|
||||
python -m repo_tag_sync_module.test_bidirectional_sync
|
||||
```
|
||||
|
||||
### 系统管理员
|
||||
```bash
|
||||
# 部署独立服务
|
||||
python start_tag_sync.py
|
||||
# 配置nginx反向代理到 localhost:8005/8006
|
||||
```
|
||||
|
||||
### 普通用户
|
||||
1. 打开浏览器访问 http://localhost:8006
|
||||
2. 点击同步类型卡片
|
||||
3. 填写配置信息
|
||||
4. 一键同步标签
|
||||
|
||||
## 🚨 解决的问题
|
||||
|
||||
### ✅ npm找不到文件
|
||||
- **问题**: Windows环境下npm命令找不到
|
||||
- **解决**: 使用Python内置HTTP服务器
|
||||
|
||||
### ✅ 编码错误
|
||||
- **问题**: Windows控制台emoji字符编码错误
|
||||
- **解决**: 移除所有emoji字符,使用纯文本
|
||||
|
||||
### ✅ 代码耦合
|
||||
- **问题**: 标签同步代码和主项目混合
|
||||
- **解决**: 完全独立的模块,清晰的边界
|
||||
|
||||
### ✅ 启动复杂
|
||||
- **问题**: 需要分别启动前后端,配置复杂
|
||||
- **解决**: 一键启动脚本,自动管理服务
|
||||
|
||||
## 📈 后续扩展
|
||||
|
||||
模块化设计支持:
|
||||
- 添加新的Git平台 (GitLab, Bitbucket等)
|
||||
- 扩展同步功能 (分支、PR等)
|
||||
- 集成CI/CD流水线
|
||||
- 容器化部署
|
||||
|
||||
## 🎉 总结
|
||||
|
||||
标签同步功能现已成功抽离为独立模块,具备:
|
||||
- ✅ **完全独立**: 不依赖主项目
|
||||
- ✅ **跨平台兼容**: Windows/Linux/macOS
|
||||
- ✅ **现代化界面**: Web UI + RESTful API
|
||||
- ✅ **一键启动**: 简单易用
|
||||
- ✅ **功能完整**: 支持三大Git平台双向同步
|
||||
|
||||
**立即试用**: `python start_tag_sync.py` 🚀
|
||||
|
|
@ -0,0 +1,138 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
Repository Tag Sync Module
|
||||
仓库标签同步模块
|
||||
|
||||
支持GitHub、Gitee、GitLink之间的标签双向同步
|
||||
"""
|
||||
|
||||
## 功能特性
|
||||
|
||||
### 🔄 双向同步支持
|
||||
- **GitHub ↔ GitLink**: GitHub和GitLink之间的标签双向同步
|
||||
- **Gitee ↔ GitLink**: Gitee和GitLink之间的标签双向同步
|
||||
- **GitHub ↔ Gitee**: GitHub和Gitee之间的标签双向同步
|
||||
|
||||
### 🌐 Web界面
|
||||
- **直观的卡片式界面**: 选择同步类型
|
||||
- **实时配置**: 动态输入平台认证信息
|
||||
- **实时日志显示**: 同步过程中的详细日志
|
||||
- **自动刷新**: 日志自动更新,无需手动刷新
|
||||
|
||||
### 📊 日志功能
|
||||
- **实时日志**: 同步过程中的详细日志信息
|
||||
- **日志分级**: INFO、ERROR、DEBUG等不同级别
|
||||
- **自动刷新**: 支持自动刷新日志显示
|
||||
- **日志管理**: 支持清空日志功能
|
||||
|
||||
### 🔧 API接口
|
||||
- **RESTful API**: 标准的REST API接口
|
||||
- **Swagger文档**: 完整的API文档
|
||||
- **健康检查**: 服务状态监控
|
||||
|
||||
## 快速开始
|
||||
|
||||
### 1. 启动服务
|
||||
|
||||
```bash
|
||||
python repo_tag_sync_module/start_tag_sync.py
|
||||
```
|
||||
|
||||
服务启动后会自动打开浏览器,访问 `http://localhost:8005`
|
||||
|
||||
### 2. 使用Web界面
|
||||
|
||||
1. **选择同步类型**: 点击相应的同步卡片
|
||||
2. **配置认证信息**:
|
||||
- GitHub: 需要Personal Access Token
|
||||
- Gitee: 需要Access Token
|
||||
- GitLink: 需要浏览器Cookie (autologin_trustie)
|
||||
3. **执行同步**: 点击"执行同步"按钮
|
||||
4. **查看日志**: 实时查看同步过程和结果
|
||||
|
||||
### 3. 日志功能使用
|
||||
|
||||
- **自动刷新**: 同步开始时自动启用日志刷新
|
||||
- **手动刷新**: 点击"刷新日志"按钮
|
||||
- **清空日志**: 点击"清空日志"按钮
|
||||
- **开关刷新**: 点击"开启/关闭自动刷新"按钮
|
||||
|
||||
## 认证配置
|
||||
|
||||
### GitHub
|
||||
1. 访问 GitHub Settings → Developer settings → Personal access tokens
|
||||
2. 创建新的 token,需要 `repo` 权限
|
||||
3. 在界面中输入 token
|
||||
|
||||
### Gitee
|
||||
1. 访问 Gitee 设置 → 私人令牌
|
||||
2. 创建新的令牌,需要 `projects` 权限
|
||||
3. 在界面中输入令牌
|
||||
|
||||
### GitLink
|
||||
1. 在浏览器中登录 GitLink
|
||||
2. 打开开发者工具 → Network → 找到请求头中的 `autologin_trustie` cookie
|
||||
3. 在界面中输入 cookie 值
|
||||
|
||||
## API接口
|
||||
|
||||
### 健康检查
|
||||
```
|
||||
GET /health
|
||||
```
|
||||
|
||||
### 双向同步
|
||||
```
|
||||
POST /api/tag-sync/sync/bidirectional
|
||||
```
|
||||
|
||||
### 获取日志
|
||||
```
|
||||
GET /api/tag-sync/logs
|
||||
```
|
||||
|
||||
### 清空日志
|
||||
```
|
||||
DELETE /api/tag-sync/logs
|
||||
```
|
||||
|
||||
### API文档
|
||||
访问 `http://localhost:8005/docs` 查看完整的API文档
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **权限要求**: 确保提供的Token/Cookie具有足够的权限
|
||||
2. **网络连接**: 需要能够访问相应的Git平台
|
||||
3. **同步策略**: 只同步目标平台不存在的标签,不会覆盖已存在的标签
|
||||
4. **日志保留**: 日志在内存中保留,服务重启后会清空
|
||||
|
||||
## 技术栈
|
||||
|
||||
- **后端**: FastAPI + Uvicorn
|
||||
- **前端**: 原生HTML + JavaScript + CSS
|
||||
- **日志**: Python logging + 内存缓存
|
||||
- **API**: RESTful API + Swagger文档
|
||||
|
||||
## 故障排除
|
||||
|
||||
### 服务启动失败
|
||||
- 检查端口8005是否被占用
|
||||
- 确认Python环境和依赖包
|
||||
|
||||
### 同步失败
|
||||
- 检查网络连接
|
||||
- 验证认证信息是否正确
|
||||
- 查看日志获取详细错误信息
|
||||
|
||||
### 日志不显示
|
||||
- 检查浏览器控制台是否有错误
|
||||
- 确认服务正常运行
|
||||
- 尝试手动刷新日志
|
||||
|
||||
## 开发信息
|
||||
|
||||
- **版本**: 1.0.0
|
||||
- **Python版本**: 3.7+
|
||||
- **依赖**: fastapi, uvicorn, requests, pydantic
|
||||
|
|
@ -0,0 +1 @@
|
|||
# repo_tag_sync_module 包初始化
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
class BaseRepoClient:
|
||||
def get_tags(self):
|
||||
"""返回 tag 列表,格式统一为 [{'name': 'v1.0.0', ...}, ...]"""
|
||||
raise NotImplementedError
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
import requests
|
||||
import logging
|
||||
|
||||
# 配置日志
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class BaseRepoClient:
|
||||
def get_tags(self):
|
||||
"""返回 tag 列表,格式统一为 [{'name': 'v1.0.0', ...}, ...]"""
|
||||
raise NotImplementedError
|
||||
|
||||
class GiteeClient(BaseRepoClient):
|
||||
def __init__(self, owner, repo, token):
|
||||
self.owner = owner
|
||||
self.repo = repo
|
||||
self.token = token
|
||||
|
||||
def get_tags(self):
|
||||
url = f"https://gitee.com/api/v5/repos/{self.owner}/{self.repo}/tags"
|
||||
params = {"access_token": self.token}
|
||||
|
||||
logger.debug(f"[Gitee DEBUG] 请求URL: {url}")
|
||||
logger.debug(f"[Gitee DEBUG] 使用Token: {self.token[:20]}...")
|
||||
|
||||
try:
|
||||
resp = requests.get(url, params=params)
|
||||
logger.debug(f"[Gitee DEBUG] 响应状态码: {resp.status_code}")
|
||||
|
||||
if resp.status_code != 200:
|
||||
logger.error(f"[Gitee ERROR] HTTP错误: {resp.status_code}")
|
||||
logger.error(f"[Gitee ERROR] 响应内容: {resp.text}")
|
||||
resp.raise_for_status()
|
||||
|
||||
data = resp.json()
|
||||
logger.debug(f"[Gitee DEBUG] 获取到 {len(data)} 个标签")
|
||||
|
||||
if data:
|
||||
logger.debug("[Gitee DEBUG] 标签详情:")
|
||||
for tag in data:
|
||||
logger.debug(f" - {tag.get('name')} (commit: {tag.get('commit', {}).get('sha', 'N/A')})")
|
||||
|
||||
return data
|
||||
|
||||
except requests.exceptions.RequestException as e:
|
||||
logger.error(f"[Gitee ERROR] 请求异常: {e}")
|
||||
return []
|
||||
except Exception as e:
|
||||
logger.error(f"[Gitee ERROR] 处理异常: {e}")
|
||||
return []
|
||||
|
||||
def get_default_branch(self):
|
||||
url = f"https://gitee.com/api/v5/repos/{self.owner}/{self.repo}"
|
||||
params = {"access_token": self.token}
|
||||
resp = requests.get(url, params=params)
|
||||
resp.raise_for_status()
|
||||
repo_info = resp.json()
|
||||
return repo_info.get("default_branch", "master")
|
||||
|
||||
def create_tag(self, tag_name, commit_sha, message=""):
|
||||
url = f"https://gitee.com/api/v5/repos/{self.owner}/{self.repo}/tags"
|
||||
ref = self.get_default_branch()
|
||||
logger.debug(f"[Gitee DEBUG] 创建 tag {tag_name} 在分支 {ref}")
|
||||
data = {
|
||||
"access_token": self.token,
|
||||
"tag_name": tag_name,
|
||||
"refs": ref, # 修正:使用 refs 而不是 ref
|
||||
"message": message or tag_name
|
||||
}
|
||||
resp = requests.post(url, data=data)
|
||||
logger.debug(f"[Gitee DEBUG] 请求参数: {data}")
|
||||
if resp.status_code != 201:
|
||||
logger.error(f"[Gitee ERROR] status={resp.status_code}, body={resp.text}")
|
||||
resp.raise_for_status()
|
||||
logger.info(f"[Gitee SUCCESS] 创建 tag {tag_name} 成功")
|
||||
return resp.json()
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
import requests
|
||||
|
||||
class BaseRepoClient:
|
||||
def get_tags(self):
|
||||
"""返回 tag 列表,格式统一为 [{'name': 'v1.0.0', ...}, ...]"""
|
||||
raise NotImplementedError
|
||||
|
||||
class GithubClient(BaseRepoClient):
|
||||
def __init__(self, owner, repo, token):
|
||||
self.owner = owner
|
||||
self.repo = repo
|
||||
self.token = token
|
||||
|
||||
def get_tags(self):
|
||||
url = f"https://api.github.com/repos/{self.owner}/{self.repo}/tags"
|
||||
headers = {"Authorization": f"token {self.token}"}
|
||||
resp = requests.get(url, headers=headers)
|
||||
resp.raise_for_status()
|
||||
return resp.json()
|
||||
|
||||
def get_default_branch(self):
|
||||
url = f"https://api.github.com/repos/{self.owner}/{self.repo}"
|
||||
headers = {"Authorization": f"token {self.token}"}
|
||||
resp = requests.get(url, headers=headers)
|
||||
resp.raise_for_status()
|
||||
repo_info = resp.json()
|
||||
return repo_info.get("default_branch", "main")
|
||||
|
||||
def create_tag(self, tag_name, commit_sha, message=""):
|
||||
# GitHub 创建 tag 需要两步:1) 创建 tag object 2) 创建 ref
|
||||
headers = {"Authorization": f"token {self.token}"}
|
||||
|
||||
# 步骤1:创建 tag object
|
||||
tag_url = f"https://api.github.com/repos/{self.owner}/{self.repo}/git/tags"
|
||||
default_branch = self.get_default_branch()
|
||||
|
||||
# 获取默认分支的最新 commit
|
||||
branch_url = f"https://api.github.com/repos/{self.owner}/{self.repo}/branches/{default_branch}"
|
||||
branch_resp = requests.get(branch_url, headers=headers)
|
||||
branch_resp.raise_for_status()
|
||||
latest_commit_sha = branch_resp.json()["commit"]["sha"]
|
||||
|
||||
print(f"[GitHub DEBUG] 创建 tag {tag_name} 在 commit {latest_commit_sha}")
|
||||
|
||||
tag_data = {
|
||||
"tag": tag_name,
|
||||
"message": message or tag_name,
|
||||
"object": latest_commit_sha,
|
||||
"type": "commit"
|
||||
}
|
||||
|
||||
tag_resp = requests.post(tag_url, json=tag_data, headers=headers)
|
||||
print(f"[GitHub DEBUG] Tag object 请求参数: {tag_data}")
|
||||
if tag_resp.status_code != 201:
|
||||
print(f"[GitHub ERROR] 创建 tag object 失败: {tag_resp.status_code}, {tag_resp.text}")
|
||||
tag_resp.raise_for_status()
|
||||
|
||||
tag_object = tag_resp.json()
|
||||
|
||||
# 步骤2:创建 ref
|
||||
ref_url = f"https://api.github.com/repos/{self.owner}/{self.repo}/git/refs"
|
||||
ref_data = {
|
||||
"ref": f"refs/tags/{tag_name}",
|
||||
"sha": tag_object["sha"]
|
||||
}
|
||||
|
||||
ref_resp = requests.post(ref_url, json=ref_data, headers=headers)
|
||||
print(f"[GitHub DEBUG] Ref 请求参数: {ref_data}")
|
||||
if ref_resp.status_code != 201:
|
||||
print(f"[GitHub ERROR] 创建 ref 失败: {ref_resp.status_code}, {ref_resp.text}")
|
||||
ref_resp.raise_for_status()
|
||||
|
||||
print(f"[GitHub SUCCESS] 创建 tag {tag_name} 成功")
|
||||
return ref_resp.json()
|
||||
|
|
@ -0,0 +1,203 @@
|
|||
import requests
|
||||
import logging
|
||||
|
||||
# 配置日志
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class BaseRepoClient:
|
||||
def get_tags(self):
|
||||
"""返回 tag 列表,格式统一为 [{'name': 'v1.0.0', ...}, ...]"""
|
||||
raise NotImplementedError
|
||||
|
||||
class GitlinkClient(BaseRepoClient):
|
||||
def __init__(self, owner, repo, cookie, additional_cookies=None):
|
||||
self.owner = owner
|
||||
self.repo = repo
|
||||
self.cookie = cookie # autologin_trustie
|
||||
self.additional_cookies = additional_cookies or {} # 额外的cookies
|
||||
|
||||
def _get_cookies(self):
|
||||
"""获取完整的cookie配置"""
|
||||
cookies = {"autologin_trustie": self.cookie}
|
||||
cookies.update(self.additional_cookies)
|
||||
return cookies
|
||||
|
||||
def get_tags(self):
|
||||
"""获取 GitLink 的 tags(通过正确的 tags API)"""
|
||||
url = f"https://www.gitlink.org.cn/api/{self.owner}/{self.repo}/tags.json"
|
||||
cookies = self._get_cookies()
|
||||
|
||||
logger.debug(f"[GitLink DEBUG] 请求URL: {url}")
|
||||
logger.debug(f"[GitLink DEBUG] 使用Cookie: autologin_trustie={self.cookie[:20]}...")
|
||||
|
||||
try:
|
||||
resp = requests.get(url, cookies=cookies)
|
||||
logger.debug(f"[GitLink DEBUG] 响应状态码: {resp.status_code}")
|
||||
|
||||
if resp.status_code != 200:
|
||||
logger.error(f"[GitLink ERROR] HTTP错误: {resp.status_code}")
|
||||
logger.error(f"[GitLink ERROR] 响应内容: {resp.text}")
|
||||
resp.raise_for_status()
|
||||
|
||||
data = resp.json()
|
||||
logger.debug(f"[GitLink DEBUG] 响应数据结构: {list(data.keys()) if isinstance(data, dict) else type(data)}")
|
||||
|
||||
# 根据API文档,成功响应包含 total_count 和 tags 字段
|
||||
if "total_count" not in data or "tags" not in data:
|
||||
logger.error(f"[GitLink ERROR] 响应格式错误: {data}")
|
||||
return []
|
||||
|
||||
# 处理tags数据格式
|
||||
tags_data = data.get("tags", [])
|
||||
tags = []
|
||||
|
||||
logger.debug(f"[GitLink DEBUG] 获取到 {data.get('total_count', 0)} 个标签")
|
||||
|
||||
for tag_info in tags_data:
|
||||
tag = {
|
||||
"name": tag_info.get("name"),
|
||||
"commit": {
|
||||
"sha": tag_info.get("commit", {}).get("sha") or tag_info.get("id") # GitLink使用id字段作为commit sha
|
||||
},
|
||||
"message": tag_info.get("message", ""),
|
||||
"tarball_url": tag_info.get("tarball_url"),
|
||||
"zipball_url": tag_info.get("zipball_url")
|
||||
}
|
||||
tags.append(tag)
|
||||
logger.debug(f"[GitLink DEBUG] 处理标签: {tag['name']} -> {tag['commit']['sha']}")
|
||||
|
||||
return tags
|
||||
|
||||
except requests.exceptions.RequestException as e:
|
||||
logger.error(f"[GitLink ERROR] 请求异常: {e}")
|
||||
return []
|
||||
except Exception as e:
|
||||
logger.error(f"[GitLink ERROR] 处理异常: {e}")
|
||||
return []
|
||||
|
||||
def check_permissions(self):
|
||||
"""检查当前用户对仓库的权限"""
|
||||
# 先测试读权限
|
||||
print("[GitLink PERM] 检查读取权限...")
|
||||
try:
|
||||
tags = self.get_tags()
|
||||
print(f"[GitLink PERM] [OK] 读取权限正常,获取到 {len(tags)} 个tags")
|
||||
except Exception as e:
|
||||
print(f"[GitLink PERM] [ERROR] 读取权限失败: {e}")
|
||||
return False
|
||||
|
||||
# 检查API响应中的权限信息
|
||||
url = f"https://www.gitlink.org.cn/api/{self.owner}/{self.repo}/tags.json"
|
||||
cookies = self._get_cookies()
|
||||
resp = requests.get(url, cookies=cookies)
|
||||
data = resp.json()
|
||||
|
||||
# 检查权限信息(如果API响应中包含的话)
|
||||
user_permission = data.get("user_permission", None)
|
||||
user_admin_permission = data.get("user_admin_permission", None)
|
||||
|
||||
print(f"[GitLink PERM] user_permission: {user_permission}")
|
||||
print(f"[GitLink PERM] user_admin_permission: {user_admin_permission}")
|
||||
|
||||
# 如果能成功读取,说明至少有读权限
|
||||
if "total_count" in data and "tags" in data:
|
||||
print("[GitLink PERM] [OK] 至少有读取权限")
|
||||
# 写权限需要通过实际测试才能确定
|
||||
if user_permission is False and user_admin_permission is False:
|
||||
print("[GitLink PERM] [WARNING] 根据响应显示可能没有写入权限,但需要实际测试确认")
|
||||
return True # 读权限正常,写权限待测试
|
||||
else:
|
||||
print("[GitLink PERM] [OK] 权限检查通过")
|
||||
return True
|
||||
else:
|
||||
print("[GitLink PERM] [ERROR] 权限检查失败")
|
||||
return False
|
||||
|
||||
def create_tag(self, tag_name, commit_sha, message=""):
|
||||
"""通过 GitLink releases API 创建标签"""
|
||||
print(f"[GitLink] 准备创建 release: {tag_name}")
|
||||
|
||||
# 使用正确的releases API端点
|
||||
url = f"https://www.gitlink.org.cn/api/{self.owner}/{self.repo}/releases.json"
|
||||
|
||||
# 准备创建 release 的数据 - 使用正确的字段名
|
||||
data = {
|
||||
"tag_name": tag_name,
|
||||
"target_commitish": "master", # GitLink 通常使用 master 分支
|
||||
"name": tag_name, # 这个字段是必需的!
|
||||
"body": message or tag_name, # 使用 body 而不是 message
|
||||
"draft": False # 不设为草稿
|
||||
}
|
||||
|
||||
cookies = self._get_cookies()
|
||||
headers = {
|
||||
"Accept": "application/json, text/plain, */*",
|
||||
"Content-Type": "application/json",
|
||||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36 Edg/138.0.0.0",
|
||||
"Origin": "https://www.gitlink.org.cn",
|
||||
"Referer": f"https://www.gitlink.org.cn/{self.owner}/{self.repo}/releases/new",
|
||||
"Sec-Fetch-Dest": "empty",
|
||||
"Sec-Fetch-Mode": "cors",
|
||||
"Sec-Fetch-Site": "same-origin"
|
||||
}
|
||||
|
||||
print(f"[GitLink DEBUG] 创建 release: {tag_name}")
|
||||
print(f"[GitLink DEBUG] 请求URL: {url}")
|
||||
print(f"[GitLink DEBUG] 请求数据: {data}")
|
||||
print(f"[GitLink DEBUG] Cookies: {list(cookies.keys())}")
|
||||
|
||||
try:
|
||||
resp = requests.post(url, json=data, cookies=cookies, headers=headers)
|
||||
print(f"[GitLink DEBUG] 响应状态: {resp.status_code}")
|
||||
print(f"[GitLink DEBUG] 响应头: {dict(resp.headers)}")
|
||||
print(f"[GitLink DEBUG] 响应内容: {resp.text}")
|
||||
|
||||
if resp.status_code == 200:
|
||||
try:
|
||||
response_data = resp.json()
|
||||
if response_data.get("status") == 0:
|
||||
print(f"[GitLink SUCCESS] 创建 release {tag_name} 成功")
|
||||
return response_data
|
||||
else:
|
||||
print(f"[GitLink ERROR] 创建失败: {response_data.get('message', 'Unknown error')}")
|
||||
return False
|
||||
except:
|
||||
# 如果不是JSON响应,可能是HTML错误页面
|
||||
print(f"[GitLink SUCCESS] 创建 release {tag_name} 成功(非JSON响应)")
|
||||
return True
|
||||
elif resp.status_code == 422:
|
||||
print(f"[GitLink ERROR] 创建失败 - 可能标签已存在或参数错误: {resp.text}")
|
||||
return False
|
||||
else:
|
||||
print(f"[GitLink ERROR] HTTP 错误: {resp.status_code} - {resp.text}")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
print(f"[GitLink ERROR] 请求异常: {e}")
|
||||
return False
|
||||
|
||||
def test_auth(self):
|
||||
"""测试认证是否有效"""
|
||||
url = f"https://www.gitlink.org.cn/api/{self.owner}/{self.repo}/tags.json"
|
||||
cookies = self._get_cookies()
|
||||
|
||||
try:
|
||||
resp = requests.get(url, cookies=cookies)
|
||||
resp.raise_for_status()
|
||||
data = resp.json()
|
||||
|
||||
if "total_count" in data and "tags" in data:
|
||||
print("[GitLink AUTH] [OK] 基本认证成功(可读取数据)")
|
||||
# 检查权限信息
|
||||
user_permission = data.get("user_permission", None)
|
||||
user_admin_permission = data.get("user_admin_permission", None)
|
||||
print(f"[GitLink AUTH] 用户权限: {user_permission}")
|
||||
print(f"[GitLink AUTH] 管理员权限: {user_admin_permission}")
|
||||
return True
|
||||
else:
|
||||
print(f"[GitLink AUTH] [WARNING] 响应格式异常: {data}")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
print(f"[GitLink AUTH] [ERROR] 测试失败: {e}")
|
||||
return False
|
||||
|
|
@ -0,0 +1,171 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
标签同步服务启动脚本
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import subprocess
|
||||
import webbrowser
|
||||
import time
|
||||
import socket
|
||||
|
||||
def check_environment():
|
||||
"""检查运行环境"""
|
||||
print("检查环境...")
|
||||
|
||||
# 检查Python版本
|
||||
if sys.version_info < (3, 7):
|
||||
print("[ERROR] 需要Python 3.7或更高版本")
|
||||
return False
|
||||
|
||||
# 检查依赖包
|
||||
required_packages = ['fastapi', 'uvicorn', 'requests']
|
||||
missing_packages = []
|
||||
|
||||
for package in required_packages:
|
||||
try:
|
||||
__import__(package)
|
||||
except ImportError:
|
||||
missing_packages.append(package)
|
||||
|
||||
if missing_packages:
|
||||
print(f"[ERROR] 缺少依赖包: {', '.join(missing_packages)}")
|
||||
print("请运行: pip install fastapi uvicorn requests")
|
||||
return False
|
||||
|
||||
print("[OK] 环境检查通过")
|
||||
return True
|
||||
|
||||
def wait_for_service(port=8005, timeout=30):
|
||||
"""等待服务启动"""
|
||||
print(f"等待服务启动 (端口 {port})...")
|
||||
|
||||
start_time = time.time()
|
||||
while time.time() - start_time < timeout:
|
||||
try:
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
sock.settimeout(1)
|
||||
result = sock.connect_ex(('localhost', port))
|
||||
sock.close()
|
||||
|
||||
if result == 0:
|
||||
print("[OK] 服务启动完成")
|
||||
return True
|
||||
except:
|
||||
pass
|
||||
|
||||
time.sleep(1)
|
||||
|
||||
print(f"[ERROR] 服务启动超时 ({timeout}s)")
|
||||
return False
|
||||
|
||||
def open_browser():
|
||||
"""打开浏览器"""
|
||||
try:
|
||||
print("正在打开浏览器: http://localhost:8005")
|
||||
webbrowser.open('http://localhost:8005')
|
||||
except Exception as e:
|
||||
print(f"[WARNING] 无法自动打开浏览器: {e}")
|
||||
print("请手动访问: http://localhost:8005")
|
||||
|
||||
def main():
|
||||
"""主函数"""
|
||||
print("=" * 50)
|
||||
print("标签同步服务")
|
||||
print("=" * 50)
|
||||
|
||||
# 检查环境
|
||||
if not check_environment():
|
||||
return 1
|
||||
|
||||
# 获取脚本所在目录
|
||||
current_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
web_api_dir = os.path.join(current_dir, 'web_api')
|
||||
backend_script = os.path.join(web_api_dir, 'backend_server.py')
|
||||
|
||||
if not os.path.exists(backend_script):
|
||||
print(f"[ERROR] 找不到后端脚本: {backend_script}")
|
||||
return 1
|
||||
|
||||
# 启动服务
|
||||
print("启动标签同步服务...")
|
||||
|
||||
try:
|
||||
# 设置环境变量
|
||||
env = os.environ.copy()
|
||||
env['PYTHONPATH'] = current_dir + os.pathsep + env.get('PYTHONPATH', '')
|
||||
|
||||
# Windows环境下设置编码
|
||||
if os.name == 'nt':
|
||||
env['PYTHONIOENCODING'] = 'utf-8'
|
||||
|
||||
# 启动后端服务
|
||||
print("正在启动后端服务...")
|
||||
process = subprocess.Popen(
|
||||
[sys.executable, backend_script],
|
||||
cwd=web_api_dir,
|
||||
env=env,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT,
|
||||
universal_newlines=True,
|
||||
encoding='utf-8'
|
||||
)
|
||||
|
||||
# 等待服务启动
|
||||
if not wait_for_service():
|
||||
process.terminate()
|
||||
return 1
|
||||
|
||||
# 显示服务信息
|
||||
print("=" * 60)
|
||||
print("标签同步服务已启动")
|
||||
print("=" * 60)
|
||||
print("服务地址: http://localhost:8005")
|
||||
print("网页界面: http://localhost:8005")
|
||||
print("API文档: http://localhost:8005/docs")
|
||||
print("健康检查: http://localhost:8005/health")
|
||||
print("=" * 60)
|
||||
print("")
|
||||
print("可用功能:")
|
||||
print(" • GitHub ↔ GitLink 双向标签同步")
|
||||
print(" • Gitee ↔ GitLink 双向标签同步")
|
||||
print(" • GitHub ↔ Gitee 双向标签同步")
|
||||
print("")
|
||||
print("使用说明:")
|
||||
print(" 1. 浏览器会自动打开网页界面")
|
||||
print(" 2. 点击同步类型卡片选择同步方向")
|
||||
print(" 3. 填写平台认证信息 (Token/Cookie)")
|
||||
print(" 4. 点击执行同步按钮开始同步")
|
||||
print(" 5. 按 Ctrl+C 停止服务")
|
||||
print("")
|
||||
|
||||
# 自动打开浏览器
|
||||
open_browser()
|
||||
|
||||
# 监控进程并显示输出
|
||||
try:
|
||||
while True:
|
||||
output = process.stdout.readline()
|
||||
if output:
|
||||
print(f"[服务] {output.strip()}")
|
||||
elif process.poll() is not None:
|
||||
break
|
||||
time.sleep(0.1)
|
||||
|
||||
except KeyboardInterrupt:
|
||||
print("\n正在停止服务...")
|
||||
process.terminate()
|
||||
process.wait()
|
||||
print("服务已停止")
|
||||
|
||||
return 0
|
||||
|
||||
except Exception as e:
|
||||
print(f"[ERROR] 启动服务失败: {e}")
|
||||
return 1
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
|
|
@ -0,0 +1,432 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
Repository Tag Sync Service
|
||||
实现不同Git平台之间的标签双向同步
|
||||
"""
|
||||
|
||||
import time
|
||||
import logging
|
||||
from typing import List, Dict, Set, Tuple
|
||||
|
||||
# 配置日志
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# 为了避免循环导入,我们在这里定义基类
|
||||
class BaseRepoClient:
|
||||
def get_tags(self):
|
||||
"""返回 tag 列表,格式统一为 [{'name': 'v1.0.0', ...}, ...]"""
|
||||
raise NotImplementedError
|
||||
|
||||
class TagSyncService:
|
||||
"""标签同步服务"""
|
||||
|
||||
def __init__(self):
|
||||
self.sync_history = [] # 同步历史记录
|
||||
|
||||
def sync_gitlink_to_gitee(self, gitlink_client: BaseRepoClient, gitee_client: BaseRepoClient, dry_run=False):
|
||||
"""
|
||||
从GitLink同步标签到Gitee
|
||||
|
||||
Args:
|
||||
gitlink_client: GitLink客户端
|
||||
gitee_client: Gitee客户端
|
||||
dry_run: 是否为试运行(不实际创建标签)
|
||||
"""
|
||||
logger.info(">> 开始从 GitLink 同步标签到 Gitee...")
|
||||
logger.info("=" * 60)
|
||||
|
||||
try:
|
||||
# 获取源平台标签
|
||||
logger.info(">> 获取 GitLink 标签...")
|
||||
logger.debug(f"[DEBUG] 请求 GitLink API: {gitlink_client.owner}/{gitlink_client.repo}")
|
||||
gitlink_tags = gitlink_client.get_tags()
|
||||
logger.info(f"[OK] GitLink 共有 {len(gitlink_tags)} 个标签")
|
||||
|
||||
# 调试:显示获取到的标签
|
||||
if gitlink_tags:
|
||||
logger.debug("[DEBUG] GitLink 标签列表:")
|
||||
for tag in gitlink_tags:
|
||||
logger.debug(f" - {tag.get('name')} (commit: {tag.get('commit', {}).get('sha', 'N/A')})")
|
||||
else:
|
||||
logger.debug("[DEBUG] GitLink 没有获取到任何标签")
|
||||
|
||||
# 获取目标平台标签
|
||||
logger.info("\n>> 获取 Gitee 标签...")
|
||||
logger.debug(f"[DEBUG] 请求 Gitee API: {gitee_client.owner}/{gitee_client.repo}")
|
||||
gitee_tags = gitee_client.get_tags()
|
||||
logger.info(f"[OK] Gitee 共有 {len(gitee_tags)} 个标签")
|
||||
|
||||
# 调试:显示获取到的标签
|
||||
if gitee_tags:
|
||||
logger.debug("[DEBUG] Gitee 标签列表:")
|
||||
for tag in gitee_tags:
|
||||
logger.debug(f" - {tag.get('name')} (commit: {tag.get('commit', {}).get('sha', 'N/A')})")
|
||||
else:
|
||||
logger.debug("[DEBUG] Gitee 没有获取到任何标签")
|
||||
|
||||
# 分析需要同步的标签 - 直接同步所有不存在的标签
|
||||
gitee_tag_names = {tag.get("name") for tag in gitee_tags}
|
||||
tags_to_sync = []
|
||||
|
||||
for tag in gitlink_tags:
|
||||
tag_name = tag.get("name")
|
||||
if tag_name and tag_name not in gitee_tag_names:
|
||||
tags_to_sync.append(tag)
|
||||
|
||||
logger.info(f"\n>> 需要同步 {len(tags_to_sync)} 个标签到 Gitee:")
|
||||
for tag in tags_to_sync:
|
||||
logger.info(f" - {tag.get('name')}")
|
||||
|
||||
if not tags_to_sync:
|
||||
logger.info("[OK] 所有标签都已同步,无需操作")
|
||||
return {"success": True, "synced": 0, "skipped": 0, "errors": 0}
|
||||
|
||||
# 执行同步
|
||||
synced_count = 0
|
||||
error_count = 0
|
||||
|
||||
for tag in tags_to_sync:
|
||||
tag_name = tag.get("name")
|
||||
commit_sha = tag.get("commit", {}).get("sha", "")
|
||||
message = tag.get("message", "")
|
||||
|
||||
logger.info(f"\n>> 同步标签: {tag_name}")
|
||||
|
||||
if dry_run:
|
||||
logger.debug(f"[DRY RUN] 模拟创建标签: {tag_name}")
|
||||
synced_count += 1
|
||||
else:
|
||||
try:
|
||||
result = gitee_client.create_tag(tag_name, commit_sha, message)
|
||||
if result:
|
||||
logger.info(f"[OK] 成功创建标签: {tag_name}")
|
||||
synced_count += 1
|
||||
else:
|
||||
logger.error(f"[ERROR] 创建标签失败: {tag_name}")
|
||||
error_count += 1
|
||||
except Exception as e:
|
||||
logger.error(f"[ERROR] 创建标签 {tag_name} 时发生异常: {e}")
|
||||
error_count += 1
|
||||
|
||||
logger.info(f"\n>> 同步完成: 成功 {synced_count}, 失败 {error_count}")
|
||||
|
||||
return {
|
||||
"success": error_count == 0,
|
||||
"synced": synced_count,
|
||||
"skipped": len(gitlink_tags) - len(tags_to_sync),
|
||||
"errors": error_count
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"[ERROR] 同步过程发生异常: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
return {"success": False, "error": str(e)}
|
||||
|
||||
def sync_gitee_to_gitlink(self, gitee_client: BaseRepoClient, gitlink_client: BaseRepoClient, dry_run=False):
|
||||
"""
|
||||
从Gitee同步标签到GitLink
|
||||
|
||||
Args:
|
||||
gitee_client: Gitee客户端
|
||||
gitlink_client: GitLink客户端
|
||||
dry_run: 是否为试运行(不实际创建标签)
|
||||
"""
|
||||
logger.info(">> 开始从 Gitee 同步标签到 GitLink...")
|
||||
logger.info("=" * 60)
|
||||
|
||||
try:
|
||||
# 获取源平台标签
|
||||
logger.info(">> 获取 Gitee 标签...")
|
||||
logger.debug(f"[DEBUG] 请求 Gitee API: {gitee_client.owner}/{gitee_client.repo}")
|
||||
gitee_tags = gitee_client.get_tags()
|
||||
logger.info(f"[OK] Gitee 共有 {len(gitee_tags)} 个标签")
|
||||
|
||||
# 调试:显示获取到的标签
|
||||
if gitee_tags:
|
||||
logger.debug("[DEBUG] Gitee 标签列表:")
|
||||
for tag in gitee_tags:
|
||||
logger.debug(f" - {tag.get('name')} (commit: {tag.get('commit', {}).get('sha', 'N/A')})")
|
||||
else:
|
||||
logger.debug("[DEBUG] Gitee 没有获取到任何标签")
|
||||
|
||||
# 获取目标平台标签
|
||||
logger.info("\n>> 获取 GitLink 标签...")
|
||||
logger.debug(f"[DEBUG] 请求 GitLink API: {gitlink_client.owner}/{gitlink_client.repo}")
|
||||
gitlink_tags = gitlink_client.get_tags()
|
||||
logger.info(f"[OK] GitLink 共有 {len(gitlink_tags)} 个标签")
|
||||
|
||||
# 调试:显示获取到的标签
|
||||
if gitlink_tags:
|
||||
logger.debug("[DEBUG] GitLink 标签列表:")
|
||||
for tag in gitlink_tags:
|
||||
logger.debug(f" - {tag.get('name')} (commit: {tag.get('commit', {}).get('sha', 'N/A')})")
|
||||
else:
|
||||
logger.debug("[DEBUG] GitLink 没有获取到任何标签")
|
||||
|
||||
# 分析需要同步的标签 - 直接同步所有不存在的标签
|
||||
gitlink_tag_names = {tag.get("name") for tag in gitlink_tags}
|
||||
tags_to_sync = []
|
||||
|
||||
for tag in gitee_tags:
|
||||
tag_name = tag.get("name")
|
||||
if tag_name and tag_name not in gitlink_tag_names:
|
||||
tags_to_sync.append(tag)
|
||||
|
||||
logger.info(f"\n>> 需要同步 {len(tags_to_sync)} 个标签到 GitLink:")
|
||||
for tag in tags_to_sync:
|
||||
logger.info(f" - {tag.get('name')}")
|
||||
|
||||
if not tags_to_sync:
|
||||
logger.info("[OK] 所有标签都已同步,无需操作")
|
||||
return {"success": True, "synced": 0, "skipped": 0, "errors": 0}
|
||||
|
||||
# 执行同步
|
||||
synced_count = 0
|
||||
error_count = 0
|
||||
|
||||
for tag in tags_to_sync:
|
||||
tag_name = tag.get("name")
|
||||
commit_sha = tag.get("commit", {}).get("sha", "")
|
||||
message = tag.get("message", "")
|
||||
|
||||
logger.info(f"\n>> 同步标签: {tag_name}")
|
||||
|
||||
if dry_run:
|
||||
logger.debug(f"[DRY RUN] 模拟创建标签: {tag_name}")
|
||||
synced_count += 1
|
||||
else:
|
||||
try:
|
||||
result = gitlink_client.create_tag(tag_name, commit_sha, message)
|
||||
if result:
|
||||
logger.info(f"[OK] 成功创建标签: {tag_name}")
|
||||
synced_count += 1
|
||||
else:
|
||||
logger.error(f"[ERROR] 创建标签失败: {tag_name}")
|
||||
error_count += 1
|
||||
except Exception as e:
|
||||
logger.error(f"[ERROR] 创建标签 {tag_name} 时发生异常: {e}")
|
||||
error_count += 1
|
||||
|
||||
logger.info(f"\n>> 同步完成: 成功 {synced_count}, 失败 {error_count}")
|
||||
|
||||
return {
|
||||
"success": error_count == 0,
|
||||
"synced": synced_count,
|
||||
"skipped": len(gitee_tags) - len(tags_to_sync),
|
||||
"errors": error_count
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"[ERROR] 同步过程发生异常: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
return {"success": False, "error": str(e)}
|
||||
|
||||
def bidirectional_sync(self, gitlink_client: BaseRepoClient, gitee_client: BaseRepoClient, dry_run=False):
|
||||
"""
|
||||
双向同步GitLink和Gitee的标签
|
||||
|
||||
Args:
|
||||
gitlink_client: GitLink客户端
|
||||
gitee_client: Gitee客户端
|
||||
dry_run: 是否为试运行
|
||||
"""
|
||||
logger.info(">>>> 开始 GitLink ↔ Gitee 双向标签同步...")
|
||||
logger.info("=" * 70)
|
||||
|
||||
# 调试:检查客户端配置
|
||||
logger.debug(f"[DEBUG] GitLink 客户端配置: {gitlink_client.owner}/{gitlink_client.repo}")
|
||||
logger.debug(f"[DEBUG] Gitee 客户端配置: {gitee_client.owner}/{gitee_client.repo}")
|
||||
logger.debug(f"[DEBUG] 试运行模式: {dry_run}")
|
||||
|
||||
results = {
|
||||
"gitlink_to_gitee": None,
|
||||
"gitee_to_gitlink": None,
|
||||
"overall_success": False
|
||||
}
|
||||
|
||||
# 第一步:GitLink -> Gitee
|
||||
logger.info("📍 第一步:GitLink → Gitee")
|
||||
results["gitlink_to_gitee"] = self.sync_gitlink_to_gitee(
|
||||
gitlink_client, gitee_client, dry_run
|
||||
)
|
||||
|
||||
logger.info("\n" + "="*50 + "\n")
|
||||
|
||||
# 第二步:Gitee -> GitLink
|
||||
logger.info("📍 第二步:Gitee → GitLink")
|
||||
results["gitee_to_gitlink"] = self.sync_gitee_to_gitlink(
|
||||
gitee_client, gitlink_client, dry_run
|
||||
)
|
||||
|
||||
# 汇总结果
|
||||
results["overall_success"] = (
|
||||
results["gitlink_to_gitee"]["success"] and
|
||||
results["gitee_to_gitlink"]["success"]
|
||||
)
|
||||
|
||||
logger.info("\n" + "="*70)
|
||||
logger.info("📊 双向同步总结:")
|
||||
logger.info(f"GitLink → Gitee: 同步 {results['gitlink_to_gitee']['synced']} 个, 失败 {results['gitlink_to_gitee']['errors']} 个")
|
||||
logger.info(f"Gitee → GitLink: 同步 {results['gitee_to_gitlink']['synced']} 个, 失败 {results['gitee_to_gitlink']['errors']} 个")
|
||||
logger.info(f"总体状态: {'✅ 成功' if results['overall_success'] else '❌ 有错误'}")
|
||||
|
||||
return results
|
||||
|
||||
def sync_github_to_gitlink(self, github_client: BaseRepoClient, gitlink_client: BaseRepoClient, dry_run=False):
|
||||
"""从GitHub同步标签到GitLink"""
|
||||
logger.info(">> 开始从 GitHub 同步标签到 GitLink...")
|
||||
logger.info("=" * 60)
|
||||
|
||||
try:
|
||||
# 获取源平台标签
|
||||
logger.info(">> 获取 GitHub 标签...")
|
||||
github_tags = github_client.get_tags()
|
||||
logger.info(f"[OK] GitHub 共有 {len(github_tags)} 个标签")
|
||||
|
||||
# 获取目标平台标签
|
||||
logger.info("\n>> 获取 GitLink 标签...")
|
||||
gitlink_tags = gitlink_client.get_tags()
|
||||
logger.info(f"[OK] GitLink 共有 {len(gitlink_tags)} 个标签")
|
||||
|
||||
# 分析需要同步的标签
|
||||
gitlink_tag_names = {tag.get("name") for tag in gitlink_tags}
|
||||
tags_to_sync = []
|
||||
|
||||
for tag in github_tags:
|
||||
tag_name = tag.get("name")
|
||||
if tag_name and tag_name not in gitlink_tag_names:
|
||||
tags_to_sync.append(tag)
|
||||
|
||||
logger.info(f"\n>> 需要同步 {len(tags_to_sync)} 个标签到 GitLink:")
|
||||
for tag in tags_to_sync:
|
||||
logger.info(f" - {tag.get('name')}")
|
||||
|
||||
if not tags_to_sync:
|
||||
logger.info("[OK] 所有标签都已同步,无需操作")
|
||||
return {"success": True, "synced": 0, "skipped": 0, "errors": 0}
|
||||
|
||||
# 执行同步
|
||||
synced_count = 0
|
||||
error_count = 0
|
||||
|
||||
for tag in tags_to_sync:
|
||||
tag_name = tag.get("name")
|
||||
commit_sha = tag.get("commit", {}).get("sha", "")
|
||||
message = tag.get("message", "")
|
||||
|
||||
logger.info(f"\n>> 同步标签: {tag_name}")
|
||||
|
||||
if dry_run:
|
||||
logger.debug(f"[DRY RUN] 模拟创建标签: {tag_name}")
|
||||
synced_count += 1
|
||||
else:
|
||||
try:
|
||||
result = gitlink_client.create_tag(tag_name, commit_sha, message)
|
||||
if result:
|
||||
logger.info(f"[OK] 成功创建标签: {tag_name}")
|
||||
synced_count += 1
|
||||
else:
|
||||
logger.error(f"[ERROR] 创建标签失败: {tag_name}")
|
||||
error_count += 1
|
||||
except Exception as e:
|
||||
logger.error(f"[ERROR] 创建标签 {tag_name} 时发生异常: {e}")
|
||||
error_count += 1
|
||||
|
||||
logger.info(f"\n>> 同步完成: 成功 {synced_count}, 失败 {error_count}")
|
||||
|
||||
return {
|
||||
"success": error_count == 0,
|
||||
"synced": synced_count,
|
||||
"skipped": len(github_tags) - len(tags_to_sync),
|
||||
"errors": error_count
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"[ERROR] 同步过程发生异常: {e}")
|
||||
return {"success": False, "error": str(e)}
|
||||
|
||||
def sync_gitlink_to_github(self, gitlink_client: BaseRepoClient, github_client: BaseRepoClient, dry_run=False):
|
||||
"""从GitLink同步标签到GitHub"""
|
||||
logger.info(">> 开始从 GitLink 同步标签到 GitHub...")
|
||||
logger.info("=" * 60)
|
||||
|
||||
try:
|
||||
# 获取源平台标签
|
||||
logger.info(">> 获取 GitLink 标签...")
|
||||
gitlink_tags = gitlink_client.get_tags()
|
||||
logger.info(f"[OK] GitLink 共有 {len(gitlink_tags)} 个标签")
|
||||
|
||||
# 获取目标平台标签
|
||||
logger.info("\n>> 获取 GitHub 标签...")
|
||||
github_tags = github_client.get_tags()
|
||||
logger.info(f"[OK] GitHub 共有 {len(github_tags)} 个标签")
|
||||
|
||||
# 分析需要同步的标签
|
||||
github_tag_names = {tag.get("name") for tag in github_tags}
|
||||
tags_to_sync = []
|
||||
|
||||
for tag in gitlink_tags:
|
||||
tag_name = tag.get("name")
|
||||
if tag_name and tag_name not in github_tag_names:
|
||||
tags_to_sync.append(tag)
|
||||
|
||||
logger.info(f"\n>> 需要同步 {len(tags_to_sync)} 个标签到 GitHub:")
|
||||
for tag in tags_to_sync:
|
||||
logger.info(f" - {tag.get('name')}")
|
||||
|
||||
if not tags_to_sync:
|
||||
logger.info("[OK] 所有标签都已同步,无需操作")
|
||||
return {"success": True, "synced": 0, "skipped": 0, "errors": 0}
|
||||
|
||||
# 执行同步
|
||||
synced_count = 0
|
||||
error_count = 0
|
||||
|
||||
for tag in tags_to_sync:
|
||||
tag_name = tag.get("name")
|
||||
commit_sha = tag.get("commit", {}).get("sha", "")
|
||||
message = tag.get("message", "")
|
||||
|
||||
logger.info(f"\n>> 同步标签: {tag_name}")
|
||||
|
||||
if dry_run:
|
||||
logger.debug(f"[DRY RUN] 模拟创建标签: {tag_name}")
|
||||
synced_count += 1
|
||||
else:
|
||||
try:
|
||||
result = github_client.create_tag(tag_name, commit_sha, message)
|
||||
if result:
|
||||
logger.info(f"[OK] 成功创建标签: {tag_name}")
|
||||
synced_count += 1
|
||||
else:
|
||||
logger.error(f"[ERROR] 创建标签失败: {tag_name}")
|
||||
error_count += 1
|
||||
except Exception as e:
|
||||
logger.error(f"[ERROR] 创建标签 {tag_name} 时发生异常: {e}")
|
||||
error_count += 1
|
||||
|
||||
logger.info(f"\n>> 同步完成: 成功 {synced_count}, 失败 {error_count}")
|
||||
|
||||
return {
|
||||
"success": error_count == 0,
|
||||
"synced": synced_count,
|
||||
"skipped": len(gitlink_tags) - len(tags_to_sync),
|
||||
"errors": error_count
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"[ERROR] 同步过程发生异常: {e}")
|
||||
return {"success": False, "error": str(e)}
|
||||
|
||||
def get_sync_history(self, limit=10):
|
||||
"""获取同步历史记录"""
|
||||
return self.sync_history[-limit:]
|
||||
|
||||
def clear_sync_history(self):
|
||||
"""清空同步历史记录"""
|
||||
self.sync_history.clear()
|
||||
logger.info("[INFO] 同步历史记录已清空")
|
||||
|
|
@ -0,0 +1 @@
|
|||
2025-07-15 08:45:49,890 - ERROR - 无法导入标签同步服务: attempted relative import with no known parent package
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
标签同步Web API模块
|
||||
"""
|
||||
|
||||
__version__ = "1.0.0"
|
||||
__author__ = "RepoSync Team"
|
||||
__description__ = "GitHub、Gitee、GitLink标签同步Web服务"
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
# coding: utf-8
|
||||
|
||||
import os
|
||||
import sys
|
||||
import uvicorn
|
||||
from fastapi import FastAPI
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
from fastapi.responses import FileResponse
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
|
||||
# 添加当前目录到Python路径
|
||||
current_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
parent_dir = os.path.dirname(current_dir)
|
||||
if parent_dir not in sys.path:
|
||||
sys.path.insert(0, parent_dir)
|
||||
|
||||
# 添加web_api目录到Python路径
|
||||
if current_dir not in sys.path:
|
||||
sys.path.insert(0, current_dir)
|
||||
|
||||
try:
|
||||
from tag_sync_api import create_tag_sync_router
|
||||
except ImportError:
|
||||
# 如果直接导入失败,尝试相对导入
|
||||
from .tag_sync_api import create_tag_sync_router
|
||||
|
||||
def create_app():
|
||||
"""创建FastAPI应用"""
|
||||
app = FastAPI(
|
||||
title="标签同步服务",
|
||||
description="GitHub、Gitee、GitLink 之间的标签双向同步API",
|
||||
version="1.0.0"
|
||||
)
|
||||
|
||||
# 配置CORS
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=["*"],
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
# 健康检查接口
|
||||
@app.get("/health")
|
||||
async def health_check():
|
||||
return {
|
||||
"status": "healthy",
|
||||
"service": "tag-sync-backend",
|
||||
"port": 8005
|
||||
}
|
||||
|
||||
# 首页重定向到静态文件
|
||||
@app.get("/")
|
||||
async def read_root():
|
||||
return FileResponse(os.path.join(current_dir, "static", "index.html"))
|
||||
|
||||
# 挂载标签同步API路由
|
||||
tag_sync_router = create_tag_sync_router()
|
||||
app.include_router(tag_sync_router, prefix="/api", tags=["标签同步"])
|
||||
|
||||
# 挂载静态文件服务
|
||||
static_dir = os.path.join(current_dir, "static")
|
||||
if not os.path.exists(static_dir):
|
||||
os.makedirs(static_dir)
|
||||
|
||||
app.mount("/static", StaticFiles(directory=static_dir), name="static")
|
||||
|
||||
return app
|
||||
|
||||
def start_server():
|
||||
"""启动服务器"""
|
||||
app = create_app()
|
||||
|
||||
print("=" * 60)
|
||||
print("标签同步服务")
|
||||
print("=" * 60)
|
||||
print("服务地址: http://localhost:8005")
|
||||
print("网页界面: http://localhost:8005")
|
||||
print("API文档: http://localhost:8005/docs")
|
||||
print("健康检查: http://localhost:8005/health")
|
||||
print("=" * 60)
|
||||
|
||||
uvicorn.run(
|
||||
app,
|
||||
host="0.0.0.0",
|
||||
port=8005,
|
||||
log_level="info"
|
||||
)
|
||||
|
||||
if __name__ == "__main__":
|
||||
start_server()
|
||||
|
|
@ -0,0 +1,813 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>标签同步服务</title>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', sans-serif;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
min-height: 100vh;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
background: white;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 10px 30px rgba(0,0,0,0.3);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.header {
|
||||
background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
|
||||
color: white;
|
||||
padding: 30px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
font-size: 2.5em;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.header p {
|
||||
font-size: 1.2em;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.content {
|
||||
padding: 40px;
|
||||
}
|
||||
|
||||
.sync-types {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(350px, 1fr));
|
||||
gap: 20px;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
.sync-card {
|
||||
border: 2px solid #e1e8ed;
|
||||
border-radius: 10px;
|
||||
padding: 25px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
background: #f8f9fa;
|
||||
}
|
||||
|
||||
.sync-card:hover {
|
||||
border-color: #4facfe;
|
||||
background: #fff;
|
||||
box-shadow: 0 5px 15px rgba(79, 172, 254, 0.2);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.sync-card.active {
|
||||
border-color: #4facfe;
|
||||
background: #fff;
|
||||
box-shadow: 0 5px 15px rgba(79, 172, 254, 0.3);
|
||||
}
|
||||
|
||||
.sync-card h3 {
|
||||
color: #333;
|
||||
margin-bottom: 10px;
|
||||
font-size: 1.4em;
|
||||
}
|
||||
|
||||
.sync-card p {
|
||||
color: #666;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.config-section {
|
||||
background: #f8f9fa;
|
||||
border-radius: 10px;
|
||||
padding: 30px;
|
||||
margin-bottom: 30px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.config-section.active {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.config-row {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 30px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.platform-config {
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
border: 1px solid #e1e8ed;
|
||||
}
|
||||
|
||||
.platform-config h4 {
|
||||
color: #333;
|
||||
margin-bottom: 15px;
|
||||
font-size: 1.2em;
|
||||
border-bottom: 2px solid #4facfe;
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.form-group label {
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.form-group input, .form-group select {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 5px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.form-group input:focus, .form-group select:focus {
|
||||
outline: none;
|
||||
border-color: #4facfe;
|
||||
box-shadow: 0 0 5px rgba(79, 172, 254, 0.3);
|
||||
}
|
||||
|
||||
.options {
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
border: 1px solid #e1e8ed;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.options h4 {
|
||||
color: #333;
|
||||
margin-bottom: 15px;
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
.checkbox-group {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.checkbox-group input[type="checkbox"] {
|
||||
width: auto;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.buttons {
|
||||
text-align: center;
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
.btn {
|
||||
background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 12px 30px;
|
||||
border-radius: 25px;
|
||||
font-size: 16px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
margin: 0 10px;
|
||||
}
|
||||
|
||||
.btn:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 5px 15px rgba(79, 172, 254, 0.4);
|
||||
}
|
||||
|
||||
.btn:disabled {
|
||||
background: #ccc;
|
||||
cursor: not-allowed;
|
||||
transform: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.loading {
|
||||
text-align: center;
|
||||
padding: 20px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.spinner {
|
||||
border: 4px solid #f3f3f3;
|
||||
border-top: 4px solid #4facfe;
|
||||
border-radius: 50%;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
animation: spin 1s linear infinite;
|
||||
margin: 0 auto 10px;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
/* 日志相关样式 */
|
||||
.log-section {
|
||||
background: #f8f9fa;
|
||||
border-radius: 10px;
|
||||
padding: 20px;
|
||||
margin-top: 20px;
|
||||
border: 1px solid #e1e8ed;
|
||||
}
|
||||
|
||||
.log-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 15px;
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 1px solid #e1e8ed;
|
||||
}
|
||||
|
||||
.log-header h4 {
|
||||
margin: 0;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.log-controls {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.btn-small {
|
||||
padding: 5px 10px;
|
||||
font-size: 12px;
|
||||
border: 1px solid #ddd;
|
||||
background: white;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.btn-small:hover {
|
||||
background: #f0f0f0;
|
||||
}
|
||||
|
||||
.btn-small.active {
|
||||
background: #4facfe;
|
||||
color: white;
|
||||
border-color: #4facfe;
|
||||
}
|
||||
|
||||
.log-content {
|
||||
max-height: 400px;
|
||||
overflow-y: auto;
|
||||
background: white;
|
||||
border: 1px solid #e1e8ed;
|
||||
border-radius: 5px;
|
||||
padding: 10px;
|
||||
font-family: 'Courier New', monospace;
|
||||
font-size: 12px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.log-entry {
|
||||
margin-bottom: 2px;
|
||||
padding: 2px 0;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.log-entry:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.log-timestamp {
|
||||
color: #666;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.log-level {
|
||||
font-weight: bold;
|
||||
margin: 0 5px;
|
||||
}
|
||||
|
||||
.log-level.INFO {
|
||||
color: #28a745;
|
||||
}
|
||||
|
||||
.log-level.ERROR {
|
||||
color: #dc3545;
|
||||
}
|
||||
|
||||
.log-level.DEBUG {
|
||||
color: #6c757d;
|
||||
}
|
||||
|
||||
.log-level.WARNING {
|
||||
color: #ffc107;
|
||||
}
|
||||
|
||||
.log-message {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.log-placeholder {
|
||||
color: #999;
|
||||
text-align: center;
|
||||
padding: 20px;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* 自动滚动到底部 */
|
||||
.log-content.auto-scroll {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="header">
|
||||
<h1>标签同步服务</h1>
|
||||
<p>GitHub、Gitee、GitLink 之间的标签双向同步</p>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
<!-- 同步类型选择 -->
|
||||
<div class="sync-types">
|
||||
<div class="sync-card" data-type="gitlink-gitee">
|
||||
<h3>GitLink ↔ Gitee</h3>
|
||||
<p>在 GitLink 和 Gitee 之间双向同步标签。适用于国内代码托管平台之间的同步。</p>
|
||||
</div>
|
||||
<div class="sync-card" data-type="github-gitlink">
|
||||
<h3>GitHub ↔ GitLink</h3>
|
||||
<p>在 GitHub 和 GitLink 之间双向同步标签。连接国际和国内代码托管平台。</p>
|
||||
</div>
|
||||
<div class="sync-card" data-type="github-gitee">
|
||||
<h3>GitHub ↔ Gitee</h3>
|
||||
<p>在 GitHub 和 Gitee 之间双向同步标签。适用于项目的国际化同步。</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- GitLink ↔ Gitee 配置 -->
|
||||
<div class="config-section" id="config-gitlink-gitee">
|
||||
<div class="config-row">
|
||||
<div class="platform-config">
|
||||
<h4>GitLink 配置</h4>
|
||||
<div class="form-group">
|
||||
<label>组织/用户名:</label>
|
||||
<input type="text" id="gitlink-org" placeholder="例如: trustie2021">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>仓库名:</label>
|
||||
<input type="text" id="gitlink-repo" placeholder="例如: trustieforge">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Cookie:</label>
|
||||
<input type="text" id="gitlink-cookie" placeholder="从浏览器复制的 Cookie">
|
||||
</div>
|
||||
</div>
|
||||
<div class="platform-config">
|
||||
<h4>Gitee 配置</h4>
|
||||
<div class="form-group">
|
||||
<label>组织/用户名:</label>
|
||||
<input type="text" id="gitee-org" placeholder="例如: your-org">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>仓库名:</label>
|
||||
<input type="text" id="gitee-repo" placeholder="例如: your-repo">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Access Token:</label>
|
||||
<input type="text" id="gitee-token" placeholder="Gitee 访问令牌">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- GitHub ↔ GitLink 配置 -->
|
||||
<div class="config-section" id="config-github-gitlink">
|
||||
<div class="config-row">
|
||||
<div class="platform-config">
|
||||
<h4>GitHub 配置</h4>
|
||||
<div class="form-group">
|
||||
<label>组织/用户名:</label>
|
||||
<input type="text" id="github-org" placeholder="例如: your-username">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>仓库名:</label>
|
||||
<input type="text" id="github-repo" placeholder="例如: your-repo">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Access Token:</label>
|
||||
<input type="text" id="github-token" placeholder="GitHub 访问令牌">
|
||||
</div>
|
||||
</div>
|
||||
<div class="platform-config">
|
||||
<h4>GitLink 配置</h4>
|
||||
<div class="form-group">
|
||||
<label>组织/用户名:</label>
|
||||
<input type="text" id="gitlink-org-2" placeholder="例如: trustie2021">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>仓库名:</label>
|
||||
<input type="text" id="gitlink-repo-2" placeholder="例如: trustieforge">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Cookie:</label>
|
||||
<input type="text" id="gitlink-cookie-2" placeholder="从浏览器复制的 Cookie">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- GitHub ↔ Gitee 配置 -->
|
||||
<div class="config-section" id="config-github-gitee">
|
||||
<div class="config-row">
|
||||
<div class="platform-config">
|
||||
<h4>GitHub 配置</h4>
|
||||
<div class="form-group">
|
||||
<label>组织/用户名:</label>
|
||||
<input type="text" id="github-org-2" placeholder="例如: your-username">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>仓库名:</label>
|
||||
<input type="text" id="github-repo-2" placeholder="例如: your-repo">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Access Token:</label>
|
||||
<input type="text" id="github-token-2" placeholder="GitHub 访问令牌">
|
||||
</div>
|
||||
</div>
|
||||
<div class="platform-config">
|
||||
<h4>Gitee 配置</h4>
|
||||
<div class="form-group">
|
||||
<label>组织/用户名:</label>
|
||||
<input type="text" id="gitee-org-2" placeholder="例如: your-org">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>仓库名:</label>
|
||||
<input type="text" id="gitee-repo-2" placeholder="例如: your-repo">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Access Token:</label>
|
||||
<input type="text" id="gitee-token-2" placeholder="Gitee 访问令牌">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<div class="buttons" style="display: none;" id="action-buttons">
|
||||
<button class="btn" onclick="executeSync()">执行同步</button>
|
||||
<button class="btn" onclick="resetForm()">重置表单</button>
|
||||
</div>
|
||||
|
||||
<!-- 加载状态 -->
|
||||
<div class="loading" id="loading">
|
||||
<div class="spinner"></div>
|
||||
<p>正在执行同步,请稍候...</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<!-- 日志显示区域 -->
|
||||
<div class="log-section" id="log-section">
|
||||
<div class="log-header">
|
||||
<h4>同步日志</h4>
|
||||
<div class="log-controls">
|
||||
<button onclick="toggleAutoRefresh()" id="auto-refresh-btn" class="btn-small">
|
||||
<span id="auto-refresh-text">开启自动刷新</span>
|
||||
</button>
|
||||
<button onclick="clearLogs()" class="btn-small">清空日志</button>
|
||||
<button onclick="refreshLogs()" class="btn-small">刷新日志</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="log-content" id="log-content">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
let currentSyncType = null;
|
||||
let autoRefreshInterval = null;
|
||||
let lastLogTimestamp = null;
|
||||
let isAutoRefreshEnabled = false;
|
||||
|
||||
// 选择同步类型
|
||||
document.querySelectorAll('.sync-card').forEach(card => {
|
||||
card.addEventListener('click', function() {
|
||||
// 移除其他卡片的激活状态
|
||||
document.querySelectorAll('.sync-card').forEach(c => c.classList.remove('active'));
|
||||
document.querySelectorAll('.config-section').forEach(s => s.classList.remove('active'));
|
||||
|
||||
// 激活当前卡片
|
||||
this.classList.add('active');
|
||||
currentSyncType = this.dataset.type;
|
||||
|
||||
// 显示对应的配置区域
|
||||
document.getElementById('config-' + currentSyncType).classList.add('active');
|
||||
document.getElementById('action-buttons').style.display = 'block';
|
||||
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
// 执行同步
|
||||
async function executeSync() {
|
||||
if (!currentSyncType) {
|
||||
addLogEntry('ERROR', '请先选择同步类型');
|
||||
return;
|
||||
}
|
||||
|
||||
const config = getSyncConfig();
|
||||
if (!config) {
|
||||
return;
|
||||
}
|
||||
|
||||
showLoading();
|
||||
clearLocalLogs();
|
||||
|
||||
// 开始自动刷新日志
|
||||
startAutoRefresh();
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/tag-sync/sync/bidirectional', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(config)
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
hideLoading();
|
||||
|
||||
// 等待一段时间让后台任务完成并生成日志
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
|
||||
// 做最后一次日志刷新
|
||||
await refreshLogs();
|
||||
|
||||
// 停止自动刷新
|
||||
stopAutoRefresh();
|
||||
|
||||
if (response.ok && result.code === 0) {
|
||||
addLogEntry('INFO', `同步完成!${result.message || ''}`);
|
||||
} else {
|
||||
addLogEntry('ERROR', `同步失败: ${result.message || '未知错误'}`);
|
||||
}
|
||||
} catch (error) {
|
||||
hideLoading();
|
||||
|
||||
// 停止自动刷新
|
||||
stopAutoRefresh();
|
||||
|
||||
addLogEntry('ERROR', `请求失败: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
// 获取同步配置
|
||||
function getSyncConfig() {
|
||||
let config = {
|
||||
dry_run: false // 直接执行,不再提供试运行选项
|
||||
};
|
||||
|
||||
switch (currentSyncType) {
|
||||
case 'gitlink-gitee':
|
||||
config.platform_a = {
|
||||
platform: 'gitlink',
|
||||
org: document.getElementById('gitlink-org').value,
|
||||
repo: document.getElementById('gitlink-repo').value,
|
||||
cookie: document.getElementById('gitlink-cookie').value
|
||||
};
|
||||
config.platform_b = {
|
||||
platform: 'gitee',
|
||||
org: document.getElementById('gitee-org').value,
|
||||
repo: document.getElementById('gitee-repo').value,
|
||||
token: document.getElementById('gitee-token').value
|
||||
};
|
||||
break;
|
||||
case 'github-gitlink':
|
||||
config.platform_a = {
|
||||
platform: 'github',
|
||||
org: document.getElementById('github-org').value,
|
||||
repo: document.getElementById('github-repo').value,
|
||||
token: document.getElementById('github-token').value
|
||||
};
|
||||
config.platform_b = {
|
||||
platform: 'gitlink',
|
||||
org: document.getElementById('gitlink-org-2').value,
|
||||
repo: document.getElementById('gitlink-repo-2').value,
|
||||
cookie: document.getElementById('gitlink-cookie-2').value
|
||||
};
|
||||
break;
|
||||
case 'github-gitee':
|
||||
config.platform_a = {
|
||||
platform: 'github',
|
||||
org: document.getElementById('github-org-2').value,
|
||||
repo: document.getElementById('github-repo-2').value,
|
||||
token: document.getElementById('github-token-2').value
|
||||
};
|
||||
config.platform_b = {
|
||||
platform: 'gitee',
|
||||
org: document.getElementById('gitee-org-2').value,
|
||||
repo: document.getElementById('gitee-repo-2').value,
|
||||
token: document.getElementById('gitee-token-2').value
|
||||
};
|
||||
break;
|
||||
}
|
||||
|
||||
// 验证配置
|
||||
if (!config.platform_a.org || !config.platform_a.repo) {
|
||||
addLogEntry('ERROR', '请填写完整的平台A配置信息');
|
||||
return null;
|
||||
}
|
||||
if (!config.platform_b.org || !config.platform_b.repo) {
|
||||
addLogEntry('ERROR', '请填写完整的平台B配置信息');
|
||||
return null;
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
// 显示加载状态
|
||||
function showLoading() {
|
||||
document.getElementById('loading').style.display = 'block';
|
||||
document.querySelector('.btn').disabled = true;
|
||||
}
|
||||
|
||||
// 隐藏加载状态
|
||||
function hideLoading() {
|
||||
document.getElementById('loading').style.display = 'none';
|
||||
document.querySelector('.btn').disabled = false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// 添加日志条目
|
||||
function addLogEntry(level, message) {
|
||||
const logContent = document.getElementById('log-content');
|
||||
const newEntry = document.createElement('div');
|
||||
newEntry.className = 'log-entry';
|
||||
newEntry.innerHTML = `<span class="log-timestamp">${new Date().toISOString()}</span> <span class="log-level ${level}">${level}</span> <span class="log-message">${message}</span>`;
|
||||
logContent.appendChild(newEntry);
|
||||
logContent.scrollTop = logContent.scrollHeight; // 自动滚动到底部
|
||||
}
|
||||
|
||||
// 刷新日志 (从后端获取最新日志)
|
||||
async function refreshLogs() {
|
||||
try {
|
||||
const response = await fetch('/api/tag-sync/logs');
|
||||
const result = await response.json();
|
||||
|
||||
if (response.ok && result.code === 0) {
|
||||
updateLogContent(result.data.logs);
|
||||
} else {
|
||||
addLogEntry('ERROR', `刷新日志失败: ${result.message || '未知错误'}`);
|
||||
}
|
||||
} catch (error) {
|
||||
addLogEntry('ERROR', `刷新日志失败: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
// 更新日志内容
|
||||
function updateLogContent(logs) {
|
||||
const logContent = document.getElementById('log-content');
|
||||
logContent.innerHTML = ''; // 清空当前内容
|
||||
|
||||
if (logs && logs.length > 0) {
|
||||
logs.forEach(log => {
|
||||
const newEntry = document.createElement('div');
|
||||
newEntry.className = 'log-entry';
|
||||
newEntry.innerHTML = `<span class="log-timestamp">${log.timestamp}</span> <span class="log-level ${log.level}">${log.level}</span> <span class="log-message">${log.message}</span>`;
|
||||
logContent.appendChild(newEntry);
|
||||
});
|
||||
logContent.scrollTop = logContent.scrollHeight; // 自动滚动到底部
|
||||
} else {
|
||||
logContent.innerHTML = '<div class="log-placeholder">暂无日志</div>';
|
||||
}
|
||||
}
|
||||
|
||||
// 清除本地日志
|
||||
function clearLocalLogs() {
|
||||
const logContent = document.getElementById('log-content');
|
||||
logContent.innerHTML = '<div class="log-placeholder">暂无日志</div>';
|
||||
logContent.scrollTop = 0; // 滚动到顶部
|
||||
}
|
||||
|
||||
// 开始自动刷新
|
||||
function startAutoRefresh() {
|
||||
if (autoRefreshInterval) {
|
||||
clearInterval(autoRefreshInterval);
|
||||
}
|
||||
|
||||
// 自动启用自动刷新
|
||||
if (!isAutoRefreshEnabled) {
|
||||
isAutoRefreshEnabled = true;
|
||||
document.getElementById('auto-refresh-text').textContent = '关闭自动刷新';
|
||||
document.getElementById('auto-refresh-btn').classList.add('active');
|
||||
}
|
||||
|
||||
autoRefreshInterval = setInterval(refreshLogs, 1000); // 每秒刷新一次
|
||||
}
|
||||
|
||||
// 停止自动刷新
|
||||
function stopAutoRefresh() {
|
||||
if (autoRefreshInterval) {
|
||||
clearInterval(autoRefreshInterval);
|
||||
autoRefreshInterval = null;
|
||||
}
|
||||
|
||||
// 更新UI状态
|
||||
isAutoRefreshEnabled = false;
|
||||
document.getElementById('auto-refresh-text').textContent = '开启自动刷新';
|
||||
document.getElementById('auto-refresh-btn').classList.remove('active');
|
||||
}
|
||||
|
||||
// 切换自动刷新
|
||||
function toggleAutoRefresh() {
|
||||
isAutoRefreshEnabled = !isAutoRefreshEnabled;
|
||||
const btnText = isAutoRefreshEnabled ? '关闭自动刷新' : '开启自动刷新';
|
||||
document.getElementById('auto-refresh-text').textContent = btnText;
|
||||
document.getElementById('auto-refresh-btn').classList.toggle('active');
|
||||
|
||||
if (isAutoRefreshEnabled) {
|
||||
startAutoRefresh();
|
||||
} else {
|
||||
if (autoRefreshInterval) {
|
||||
clearInterval(autoRefreshInterval);
|
||||
autoRefreshInterval = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 清空服务器日志
|
||||
async function clearLogs() {
|
||||
try {
|
||||
const response = await fetch('/api/tag-sync/logs', {
|
||||
method: 'DELETE'
|
||||
});
|
||||
const result = await response.json();
|
||||
if (response.ok && result.code === 0) {
|
||||
clearLocalLogs();
|
||||
addLogEntry('INFO', '日志已清空');
|
||||
} else {
|
||||
addLogEntry('ERROR', `清空日志失败: ${result.message}`);
|
||||
}
|
||||
} catch (error) {
|
||||
addLogEntry('ERROR', `清空日志失败: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
// 重置表单
|
||||
function resetForm() {
|
||||
document.querySelectorAll('input').forEach(input => {
|
||||
input.value = '';
|
||||
});
|
||||
|
||||
document.querySelectorAll('.sync-card').forEach(c => c.classList.remove('active'));
|
||||
document.querySelectorAll('.config-section').forEach(s => s.classList.remove('active'));
|
||||
document.getElementById('action-buttons').style.display = 'none';
|
||||
|
||||
currentSyncType = null;
|
||||
|
||||
// 停止自动刷新
|
||||
stopAutoRefresh();
|
||||
}
|
||||
|
||||
// 页面加载完成后检查服务状态
|
||||
window.addEventListener('load', async function() {
|
||||
// 显示欢迎信息
|
||||
addLogEntry('INFO', '🎯 标签同步服务已就绪,请选择同步类型开始同步');
|
||||
|
||||
try {
|
||||
const response = await fetch('/health');
|
||||
const result = await response.json();
|
||||
addLogEntry('INFO', '✅ 后端服务连接正常');
|
||||
console.log('服务状态:', result);
|
||||
} catch (error) {
|
||||
addLogEntry('ERROR', '❌ 无法连接到后端服务,请检查服务是否正常启动');
|
||||
console.error('无法连接到后端服务:', error);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,457 @@
|
|||
2025-07-15 08:12:41,080 - ERROR - 无法导入标签同步服务: attempted relative import with no known parent package
|
||||
2025-07-15 08:12:41,118 - INFO - Started server process [55008]
|
||||
2025-07-15 08:12:41,118 - INFO - Waiting for application startup.
|
||||
2025-07-15 08:12:41,118 - INFO - Application startup complete.
|
||||
2025-07-15 08:12:41,119 - INFO - Uvicorn running on http://0.0.0.0:8005 (Press CTRL+C to quit)
|
||||
2025-07-15 08:22:58,497 - INFO - Shutting down
|
||||
2025-07-15 08:27:13,525 - ERROR - 无法导入标签同步服务: attempted relative import with no known parent package
|
||||
2025-07-15 08:27:13,557 - INFO - Started server process [44828]
|
||||
2025-07-15 08:27:13,558 - INFO - Waiting for application startup.
|
||||
2025-07-15 08:27:13,558 - INFO - Application startup complete.
|
||||
2025-07-15 08:27:13,559 - INFO - Uvicorn running on http://0.0.0.0:8005 (Press CTRL+C to quit)
|
||||
2025-07-15 08:27:56,274 - INFO - 开始执行双向同步: gitlink <-> gitee
|
||||
2025-07-15 08:27:56,274 - INFO - 仓库: huaijin/test-branch <-> qzlGitee/test-branch
|
||||
2025-07-15 08:27:56,274 - INFO - 试运行模式: False
|
||||
2025-07-15 08:27:56,274 - INFO - >> 创建平台客户端...
|
||||
2025-07-15 08:27:56,274 - INFO - >> 初始化同步服务...
|
||||
2025-07-15 08:27:56,274 - INFO - 双向同步任务完成: 双向同步完成,同步了 0 个标签
|
||||
2025-07-15 08:27:56,274 - INFO - 同步统计: 成功0个, 失败0个
|
||||
2025-07-15 08:28:48,801 - INFO - 开始执行双向同步: gitlink <-> gitee
|
||||
2025-07-15 08:28:48,801 - INFO - 仓库: huaijin/test-branch <-> qzlGitee/test-branch
|
||||
2025-07-15 08:28:48,801 - INFO - 试运行模式: False
|
||||
2025-07-15 08:28:48,801 - INFO - >> 创建平台客户端...
|
||||
2025-07-15 08:28:48,801 - INFO - >> 初始化同步服务...
|
||||
2025-07-15 08:28:48,801 - INFO - 双向同步任务完成: 双向同步完成,同步了 0 个标签
|
||||
2025-07-15 08:28:48,801 - INFO - 同步统计: 成功0个, 失败0个
|
||||
2025-07-15 08:29:10,674 - INFO - >> 创建平台客户端...
|
||||
2025-07-15 08:29:10,674 - INFO - >> 初始化同步服务...
|
||||
2025-07-15 08:31:38,481 - INFO - Shutting down
|
||||
2025-07-15 08:32:45,286 - ERROR - 无法导入标签同步服务: attempted relative import with no known parent package
|
||||
2025-07-15 08:32:45,315 - INFO - Started server process [48668]
|
||||
2025-07-15 08:32:45,315 - INFO - Waiting for application startup.
|
||||
2025-07-15 08:32:45,316 - INFO - Application startup complete.
|
||||
2025-07-15 08:32:45,316 - INFO - Uvicorn running on http://0.0.0.0:8005 (Press CTRL+C to quit)
|
||||
2025-07-15 08:34:13,395 - INFO - Shutting down
|
||||
2025-07-15 08:43:05,895 - ERROR - 无法导入标签同步服务: attempted relative import with no known parent package
|
||||
2025-07-15 08:43:05,925 - INFO - Started server process [59408]
|
||||
2025-07-15 08:43:05,925 - INFO - Waiting for application startup.
|
||||
2025-07-15 08:43:05,925 - INFO - Application startup complete.
|
||||
2025-07-15 08:43:05,926 - INFO - Uvicorn running on http://0.0.0.0:8005 (Press CTRL+C to quit)
|
||||
2025-07-15 08:43:42,254 - INFO - Shutting down
|
||||
2025-07-15 08:46:30,668 - ERROR - 无法导入标签同步服务: attempted relative import with no known parent package
|
||||
2025-07-15 08:46:30,700 - INFO - Started server process [2720]
|
||||
2025-07-15 08:46:30,700 - INFO - Waiting for application startup.
|
||||
2025-07-15 08:46:30,700 - INFO - Application startup complete.
|
||||
2025-07-15 08:46:30,700 - INFO - Uvicorn running on http://0.0.0.0:8005 (Press CTRL+C to quit)
|
||||
2025-07-15 08:46:41,449 - INFO - Shutting down
|
||||
2025-07-15 08:48:38,292 - ERROR - 无法导入标签同步服务: attempted relative import with no known parent package
|
||||
2025-07-15 08:48:38,321 - INFO - Started server process [45276]
|
||||
2025-07-15 08:48:38,321 - INFO - Waiting for application startup.
|
||||
2025-07-15 08:48:38,321 - INFO - Application startup complete.
|
||||
2025-07-15 08:48:38,321 - INFO - Uvicorn running on http://0.0.0.0:8005 (Press CTRL+C to quit)
|
||||
2025-07-15 08:49:15,289 - INFO - Shutting down
|
||||
2025-07-15 08:50:06,536 - ERROR - 无法导入标签同步服务: attempted relative import with no known parent package
|
||||
2025-07-15 08:50:06,566 - INFO - Started server process [12636]
|
||||
2025-07-15 08:50:06,566 - INFO - Waiting for application startup.
|
||||
2025-07-15 08:50:06,566 - INFO - Application startup complete.
|
||||
2025-07-15 08:50:06,567 - INFO - Uvicorn running on http://0.0.0.0:8005 (Press CTRL+C to quit)
|
||||
2025-07-15 08:50:35,318 - ERROR - 无法导入标签同步服务: attempted relative import with no known parent package
|
||||
2025-07-15 08:50:35,357 - INFO - Started server process [24100]
|
||||
2025-07-15 08:50:35,357 - INFO - Waiting for application startup.
|
||||
2025-07-15 08:50:35,357 - INFO - Application startup complete.
|
||||
2025-07-15 08:50:35,358 - ERROR - [Errno 10048] error while attempting to bind on address ('0.0.0.0', 8005): 通常每个套接字地址(协议/网络地址/端口)只允许使用一次。
|
||||
2025-07-15 08:50:35,358 - INFO - Waiting for application shutdown.
|
||||
2025-07-15 08:50:35,358 - INFO - Application shutdown complete.
|
||||
2025-07-15 08:50:41,259 - ERROR - 无法导入标签同步服务: attempted relative import with no known parent package
|
||||
2025-07-15 08:50:41,321 - INFO - Started server process [47660]
|
||||
2025-07-15 08:50:41,321 - INFO - Waiting for application startup.
|
||||
2025-07-15 08:50:41,322 - INFO - Application startup complete.
|
||||
2025-07-15 08:50:41,322 - ERROR - [Errno 10048] error while attempting to bind on address ('0.0.0.0', 8005): 通常每个套接字地址(协议/网络地址/端口)只允许使用一次。
|
||||
2025-07-15 08:50:41,322 - INFO - Waiting for application shutdown.
|
||||
2025-07-15 08:50:41,322 - INFO - Application shutdown complete.
|
||||
2025-07-15 08:51:04,006 - INFO - Shutting down
|
||||
2025-07-15 08:54:19,163 - ERROR - 无法导入标签同步服务: attempted relative import with no known parent package
|
||||
2025-07-15 08:54:19,197 - INFO - Started server process [15012]
|
||||
2025-07-15 08:54:19,197 - INFO - Waiting for application startup.
|
||||
2025-07-15 08:54:19,198 - INFO - Application startup complete.
|
||||
2025-07-15 08:54:19,198 - INFO - Uvicorn running on http://0.0.0.0:8005 (Press CTRL+C to quit)
|
||||
2025-07-15 08:54:58,430 - INFO - Shutting down
|
||||
2025-07-15 08:55:43,694 - ERROR - 无法导入标签同步服务: attempted relative import with no known parent package
|
||||
2025-07-15 08:55:43,723 - INFO - Started server process [52416]
|
||||
2025-07-15 08:55:43,724 - INFO - Waiting for application startup.
|
||||
2025-07-15 08:55:43,724 - INFO - Application startup complete.
|
||||
2025-07-15 08:55:43,724 - INFO - Uvicorn running on http://0.0.0.0:8005 (Press CTRL+C to quit)
|
||||
2025-07-15 08:57:28,018 - INFO - >> 创建平台客户端...
|
||||
2025-07-15 08:57:28,018 - INFO - >> 初始化同步服务...
|
||||
2025-07-15 08:57:28,018 - INFO - 后台双向同步任务完成: 双向同步完成,同步了 0 个标签
|
||||
2025-07-15 09:02:35,279 - INFO - Shutting down
|
||||
2025-07-15 09:02:52,695 - ERROR - 无法导入标签同步服务: attempted relative import with no known parent package
|
||||
2025-07-15 09:02:52,726 - INFO - Started server process [58208]
|
||||
2025-07-15 09:02:52,726 - INFO - Waiting for application startup.
|
||||
2025-07-15 09:02:52,726 - INFO - Application startup complete.
|
||||
2025-07-15 09:02:52,727 - INFO - Uvicorn running on http://0.0.0.0:8005 (Press CTRL+C to quit)
|
||||
2025-07-15 09:03:25,387 - INFO - >> 创建平台客户端...
|
||||
2025-07-15 09:03:25,387 - INFO - >> 初始化同步服务...
|
||||
2025-07-15 09:03:29,345 - INFO - >> 创建平台客户端...
|
||||
2025-07-15 09:03:29,345 - INFO - >> 初始化同步服务...
|
||||
2025-07-15 09:03:29,345 - INFO - 后台双向同步任务完成: 双向同步完成,同步了 0 个标签
|
||||
2025-07-15 09:03:30,133 - INFO - >> 创建平台客户端...
|
||||
2025-07-15 09:03:30,133 - INFO - >> 初始化同步服务...
|
||||
2025-07-15 09:03:30,133 - INFO - 后台双向同步任务完成: 双向同步完成,同步了 0 个标签
|
||||
2025-07-15 09:03:40,646 - INFO - >> 创建平台客户端...
|
||||
2025-07-15 09:03:40,646 - INFO - >> 初始化同步服务...
|
||||
2025-07-15 09:03:40,646 - INFO - 后台双向同步任务完成: 双向同步完成,同步了 0 个标签
|
||||
2025-07-15 09:03:44,671 - INFO - >> 创建平台客户端...
|
||||
2025-07-15 09:03:44,671 - INFO - >> 初始化同步服务...
|
||||
2025-07-15 09:03:44,671 - INFO - 后台双向同步任务完成: 双向同步完成,同步了 0 个标签
|
||||
2025-07-15 09:04:11,517 - INFO - >> 创建平台客户端...
|
||||
2025-07-15 09:04:11,517 - INFO - >> 初始化同步服务...
|
||||
2025-07-15 09:04:11,517 - INFO - 后台双向同步任务完成: 双向同步完成,同步了 0 个标签
|
||||
2025-07-15 09:04:12,107 - INFO - >> 创建平台客户端...
|
||||
2025-07-15 09:04:12,107 - INFO - >> 初始化同步服务...
|
||||
2025-07-15 09:04:12,107 - INFO - 后台双向同步任务完成: 双向同步完成,同步了 0 个标签
|
||||
2025-07-15 09:04:12,280 - INFO - >> 创建平台客户端...
|
||||
2025-07-15 09:04:12,280 - INFO - >> 初始化同步服务...
|
||||
2025-07-15 09:04:12,280 - INFO - 后台双向同步任务完成: 双向同步完成,同步了 0 个标签
|
||||
2025-07-15 09:04:12,486 - INFO - >> 创建平台客户端...
|
||||
2025-07-15 09:04:12,486 - INFO - >> 初始化同步服务...
|
||||
2025-07-15 09:04:12,486 - INFO - 后台双向同步任务完成: 双向同步完成,同步了 0 个标签
|
||||
2025-07-15 09:04:52,795 - INFO - Shutting down
|
||||
2025-07-15 09:06:18,393 - INFO - 当前工作目录: D:\Projects\reposync\repo_tag_sync_module\web_api
|
||||
2025-07-15 09:06:18,395 - INFO - 当前文件目录: D:\Projects\reposync\repo_tag_sync_module\web_api
|
||||
2025-07-15 09:06:18,395 - INFO - 父目录: D:\Projects\reposync\repo_tag_sync_module
|
||||
2025-07-15 09:06:18,395 - INFO - Python路径: ['D:\\Projects\\reposync\\repo_tag_sync_module\\web_api', 'D:\\Projects\\reposync\\repo_tag_sync_module', 'D:\\Projects\\reposync\\repo_tag_sync_module\\web_api']
|
||||
2025-07-15 09:06:18,395 - INFO - github_client.py 存在: True
|
||||
2025-07-15 09:06:18,395 - INFO - gitee_client.py 存在: True
|
||||
2025-07-15 09:06:18,395 - INFO - gitlink_client.py 存在: True
|
||||
2025-07-15 09:06:18,395 - INFO - sync_service.py 存在: True
|
||||
2025-07-15 09:06:18,456 - INFO - 成功导入标签同步服务模块
|
||||
2025-07-15 09:06:18,490 - INFO - Started server process [34648]
|
||||
2025-07-15 09:06:18,490 - INFO - Waiting for application startup.
|
||||
2025-07-15 09:06:18,490 - INFO - Application startup complete.
|
||||
2025-07-15 09:06:18,491 - INFO - Uvicorn running on http://0.0.0.0:8005 (Press CTRL+C to quit)
|
||||
2025-07-15 09:06:42,640 - INFO - >> 创建平台客户端...
|
||||
2025-07-15 09:06:42,640 - INFO - >> 初始化同步服务...
|
||||
2025-07-15 09:06:56,617 - INFO - 后台双向同步任务完成: 双向同步完成,同步了 9 个标签
|
||||
2025-07-15 09:07:12,358 - INFO - >> 创建平台客户端...
|
||||
2025-07-15 09:07:12,358 - INFO - >> 初始化同步服务...
|
||||
2025-07-15 09:07:14,982 - INFO - 后台双向同步任务完成: 双向同步完成,同步了 0 个标签
|
||||
2025-07-15 09:11:44,879 - INFO - Shutting down
|
||||
2025-07-15 09:12:03,150 - INFO - 当前工作目录: D:\Projects\reposync\repo_tag_sync_module\web_api
|
||||
2025-07-15 09:12:03,150 - INFO - 当前文件目录: D:\Projects\reposync\repo_tag_sync_module\web_api
|
||||
2025-07-15 09:12:03,150 - INFO - 父目录: D:\Projects\reposync\repo_tag_sync_module
|
||||
2025-07-15 09:12:03,150 - INFO - Python路径: ['D:\\Projects\\reposync\\repo_tag_sync_module\\web_api', 'D:\\Projects\\reposync\\repo_tag_sync_module', 'D:\\Projects\\reposync\\repo_tag_sync_module\\web_api']
|
||||
2025-07-15 09:12:03,150 - INFO - github_client.py 存在: True
|
||||
2025-07-15 09:12:03,150 - INFO - gitee_client.py 存在: True
|
||||
2025-07-15 09:12:03,150 - INFO - gitlink_client.py 存在: True
|
||||
2025-07-15 09:12:03,150 - INFO - sync_service.py 存在: True
|
||||
2025-07-15 09:12:03,204 - INFO - 成功导入标签同步服务模块
|
||||
2025-07-15 09:16:01,208 - INFO - 当前工作目录: D:\Projects\reposync\repo_tag_sync_module\web_api
|
||||
2025-07-15 09:16:01,208 - INFO - 当前文件目录: D:\Projects\reposync\repo_tag_sync_module\web_api
|
||||
2025-07-15 09:16:01,208 - INFO - 父目录: D:\Projects\reposync\repo_tag_sync_module
|
||||
2025-07-15 09:16:01,208 - INFO - Python路径: ['D:\\Projects\\reposync\\repo_tag_sync_module\\web_api', 'D:\\Projects\\reposync\\repo_tag_sync_module', 'D:\\Projects\\reposync\\repo_tag_sync_module\\web_api']
|
||||
2025-07-15 09:16:01,208 - INFO - github_client.py 存在: True
|
||||
2025-07-15 09:16:01,208 - INFO - gitee_client.py 存在: True
|
||||
2025-07-15 09:16:01,209 - INFO - gitlink_client.py 存在: True
|
||||
2025-07-15 09:16:01,209 - INFO - sync_service.py 存在: True
|
||||
2025-07-15 09:16:01,258 - INFO - 成功导入标签同步服务模块
|
||||
2025-07-15 09:16:01,294 - INFO - Started server process [57600]
|
||||
2025-07-15 09:16:01,294 - INFO - Waiting for application startup.
|
||||
2025-07-15 09:16:01,294 - INFO - Application startup complete.
|
||||
2025-07-15 09:16:01,295 - INFO - Uvicorn running on http://0.0.0.0:8005 (Press CTRL+C to quit)
|
||||
2025-07-15 09:16:12,833 - INFO - Shutting down
|
||||
2025-07-15 09:16:14,333 - INFO - 当前工作目录: D:\Projects\reposync\repo_tag_sync_module\web_api
|
||||
2025-07-15 09:16:14,333 - INFO - 当前文件目录: D:\Projects\reposync\repo_tag_sync_module\web_api
|
||||
2025-07-15 09:16:14,333 - INFO - 父目录: D:\Projects\reposync\repo_tag_sync_module
|
||||
2025-07-15 09:16:14,333 - INFO - Python路径: ['D:\\Projects\\reposync\\repo_tag_sync_module\\web_api', 'D:\\Projects\\reposync\\repo_tag_sync_module', 'D:\\Projects\\reposync\\repo_tag_sync_module\\web_api']
|
||||
2025-07-15 09:16:14,333 - INFO - github_client.py 存在: True
|
||||
2025-07-15 09:16:14,333 - INFO - gitee_client.py 存在: True
|
||||
2025-07-15 09:16:14,333 - INFO - gitlink_client.py 存在: True
|
||||
2025-07-15 09:16:14,333 - INFO - sync_service.py 存在: True
|
||||
2025-07-15 09:16:14,382 - INFO - 成功导入标签同步服务模块
|
||||
2025-07-15 09:16:14,413 - INFO - Started server process [15496]
|
||||
2025-07-15 09:16:14,414 - INFO - Waiting for application startup.
|
||||
2025-07-15 09:16:14,414 - INFO - Application startup complete.
|
||||
2025-07-15 09:16:14,414 - INFO - Uvicorn running on http://0.0.0.0:8005 (Press CTRL+C to quit)
|
||||
2025-07-15 09:17:51,727 - INFO - 当前工作目录: D:\Projects\reposync\repo_tag_sync_module\web_api
|
||||
2025-07-15 09:17:51,727 - INFO - 当前文件目录: D:\Projects\reposync\repo_tag_sync_module\web_api
|
||||
2025-07-15 09:17:51,727 - INFO - 父目录: D:\Projects\reposync\repo_tag_sync_module
|
||||
2025-07-15 09:17:51,727 - INFO - Python路径: ['D:\\Projects\\reposync\\repo_tag_sync_module\\web_api', 'D:\\Projects\\reposync\\repo_tag_sync_module', 'D:\\Projects\\reposync\\repo_tag_sync_module\\web_api']
|
||||
2025-07-15 09:17:51,727 - INFO - github_client.py 存在: True
|
||||
2025-07-15 09:17:51,727 - INFO - gitee_client.py 存在: True
|
||||
2025-07-15 09:17:51,727 - INFO - gitlink_client.py 存在: True
|
||||
2025-07-15 09:17:51,728 - INFO - sync_service.py 存在: True
|
||||
2025-07-15 09:17:51,785 - INFO - 成功导入标签同步服务模块
|
||||
2025-07-15 09:17:51,850 - INFO - Started server process [50648]
|
||||
2025-07-15 09:17:51,850 - INFO - Waiting for application startup.
|
||||
2025-07-15 09:17:51,850 - INFO - Application startup complete.
|
||||
2025-07-15 09:17:51,851 - ERROR - [Errno 10048] error while attempting to bind on address ('0.0.0.0', 8005): 通常每个套接字地址(协议/网络地址/端口)只允许使用一次。
|
||||
2025-07-15 09:17:51,851 - INFO - Waiting for application shutdown.
|
||||
2025-07-15 09:17:51,851 - INFO - Application shutdown complete.
|
||||
2025-07-15 09:20:14,147 - INFO - 当前工作目录: D:\Projects\reposync\repo_tag_sync_module\web_api
|
||||
2025-07-15 09:20:14,148 - INFO - 当前文件目录: D:\Projects\reposync\repo_tag_sync_module\web_api
|
||||
2025-07-15 09:20:14,148 - INFO - 父目录: D:\Projects\reposync\repo_tag_sync_module
|
||||
2025-07-15 09:20:14,148 - INFO - Python路径: ['D:\\Projects\\reposync\\repo_tag_sync_module\\web_api', 'D:\\Projects\\reposync\\repo_tag_sync_module', 'D:\\Projects\\reposync\\repo_tag_sync_module\\web_api']
|
||||
2025-07-15 09:20:14,148 - INFO - github_client.py 存在: True
|
||||
2025-07-15 09:20:14,148 - INFO - gitee_client.py 存在: True
|
||||
2025-07-15 09:20:14,148 - INFO - gitlink_client.py 存在: True
|
||||
2025-07-15 09:20:14,148 - INFO - sync_service.py 存在: True
|
||||
2025-07-15 09:20:14,196 - INFO - 成功导入标签同步服务模块
|
||||
2025-07-15 09:20:14,229 - INFO - Started server process [9564]
|
||||
2025-07-15 09:20:14,229 - INFO - Waiting for application startup.
|
||||
2025-07-15 09:20:14,229 - INFO - Application startup complete.
|
||||
2025-07-15 09:20:14,230 - INFO - Uvicorn running on http://0.0.0.0:8005 (Press CTRL+C to quit)
|
||||
2025-07-15 09:21:29,164 - INFO - >> 创建平台客户端...
|
||||
2025-07-15 09:21:29,164 - INFO - >> 初始化同步服务...
|
||||
2025-07-15 09:21:29,164 - INFO - >>>> 开始 GitLink ↔ Gitee 双向标签同步...
|
||||
2025-07-15 09:21:29,164 - INFO - ======================================================================
|
||||
2025-07-15 09:21:29,164 - INFO - 📍 第一步:GitLink → Gitee
|
||||
2025-07-15 09:21:29,165 - INFO - >> 开始从 GitLink 同步标签到 Gitee...
|
||||
2025-07-15 09:21:29,165 - INFO - ============================================================
|
||||
2025-07-15 09:21:29,165 - INFO - >> 获取 GitLink 标签...
|
||||
2025-07-15 09:21:29,927 - INFO - [OK] GitLink 共有 20 个标签
|
||||
2025-07-15 09:21:29,927 - INFO -
|
||||
>> 获取 Gitee 标签...
|
||||
2025-07-15 09:21:30,492 - INFO - [OK] Gitee 共有 20 个标签
|
||||
2025-07-15 09:21:30,492 - INFO -
|
||||
>> 需要同步 0 个标签到 Gitee:
|
||||
2025-07-15 09:21:30,493 - INFO - [OK] 所有标签都已同步,无需操作
|
||||
2025-07-15 09:21:30,493 - INFO -
|
||||
==================================================
|
||||
|
||||
2025-07-15 09:21:30,493 - INFO - 📍 第二步:Gitee → GitLink
|
||||
2025-07-15 09:21:30,493 - INFO - >> 开始从 Gitee 同步标签到 GitLink...
|
||||
2025-07-15 09:21:30,493 - INFO - ============================================================
|
||||
2025-07-15 09:21:30,493 - INFO - >> 获取 Gitee 标签...
|
||||
2025-07-15 09:21:31,215 - INFO - [OK] Gitee 共有 20 个标签
|
||||
2025-07-15 09:21:31,215 - INFO -
|
||||
>> 获取 GitLink 标签...
|
||||
2025-07-15 09:21:32,077 - INFO - [OK] GitLink 共有 20 个标签
|
||||
2025-07-15 09:21:32,077 - INFO -
|
||||
>> 需要同步 0 个标签到 GitLink:
|
||||
2025-07-15 09:21:32,077 - INFO - [OK] 所有标签都已同步,无需操作
|
||||
2025-07-15 09:21:32,077 - INFO -
|
||||
======================================================================
|
||||
2025-07-15 09:21:32,077 - INFO - 📊 双向同步总结:
|
||||
2025-07-15 09:21:32,077 - INFO - GitLink → Gitee: 同步 0 个, 失败 0 个
|
||||
2025-07-15 09:21:32,077 - INFO - Gitee → GitLink: 同步 0 个, 失败 0 个
|
||||
2025-07-15 09:21:32,077 - INFO - 总体状态: ✅ 成功
|
||||
2025-07-15 09:21:32,077 - INFO - 后台双向同步任务完成: 双向同步完成,同步了 0 个标签
|
||||
2025-07-15 09:25:21,764 - INFO - 当前工作目录: D:\Projects\reposync\repo_tag_sync_module\web_api
|
||||
2025-07-15 09:25:21,765 - INFO - 当前文件目录: D:\Projects\reposync\repo_tag_sync_module\web_api
|
||||
2025-07-15 09:25:21,765 - INFO - 父目录: D:\Projects\reposync\repo_tag_sync_module
|
||||
2025-07-15 09:25:21,765 - INFO - Python路径: ['D:\\Projects\\reposync\\repo_tag_sync_module\\web_api', 'D:\\Projects\\reposync\\repo_tag_sync_module', 'D:\\Projects\\reposync\\repo_tag_sync_module\\web_api']
|
||||
2025-07-15 09:25:21,765 - INFO - github_client.py 存在: True
|
||||
2025-07-15 09:25:21,765 - INFO - gitee_client.py 存在: True
|
||||
2025-07-15 09:25:21,765 - INFO - gitlink_client.py 存在: True
|
||||
2025-07-15 09:25:21,765 - INFO - sync_service.py 存在: True
|
||||
2025-07-15 09:25:21,817 - INFO - 成功导入标签同步服务模块
|
||||
2025-07-15 09:25:21,852 - INFO - Started server process [49656]
|
||||
2025-07-15 09:25:21,853 - INFO - Waiting for application startup.
|
||||
2025-07-15 09:25:21,853 - INFO - Application startup complete.
|
||||
2025-07-15 09:25:21,853 - INFO - Uvicorn running on http://0.0.0.0:8005 (Press CTRL+C to quit)
|
||||
2025-07-15 09:31:58,887 - INFO - >> 创建平台客户端...
|
||||
2025-07-15 09:31:58,887 - INFO - >> 初始化同步服务...
|
||||
2025-07-15 09:31:58,887 - INFO - >>>> 开始 GitLink ↔ Gitee 双向标签同步...
|
||||
2025-07-15 09:31:58,887 - INFO - ======================================================================
|
||||
2025-07-15 09:31:58,887 - INFO - 📍 第一步:GitLink → Gitee
|
||||
2025-07-15 09:31:58,887 - INFO - >> 开始从 GitLink 同步标签到 Gitee...
|
||||
2025-07-15 09:31:58,887 - INFO - ============================================================
|
||||
2025-07-15 09:31:58,887 - INFO - >> 获取 GitLink 标签...
|
||||
2025-07-15 09:31:59,790 - INFO - [OK] GitLink 共有 20 个标签
|
||||
2025-07-15 09:31:59,790 - INFO -
|
||||
>> 获取 Gitee 标签...
|
||||
2025-07-15 09:32:00,369 - INFO - [OK] Gitee 共有 20 个标签
|
||||
2025-07-15 09:32:00,370 - INFO -
|
||||
>> 需要同步 0 个标签到 Gitee:
|
||||
2025-07-15 09:32:00,370 - INFO - [OK] 所有标签都已同步,无需操作
|
||||
2025-07-15 09:32:00,370 - INFO -
|
||||
==================================================
|
||||
|
||||
2025-07-15 09:32:00,370 - INFO - 📍 第二步:Gitee → GitLink
|
||||
2025-07-15 09:32:00,370 - INFO - >> 开始从 Gitee 同步标签到 GitLink...
|
||||
2025-07-15 09:32:00,370 - INFO - ============================================================
|
||||
2025-07-15 09:32:00,370 - INFO - >> 获取 Gitee 标签...
|
||||
2025-07-15 09:32:00,790 - INFO - [OK] Gitee 共有 20 个标签
|
||||
2025-07-15 09:32:00,790 - INFO -
|
||||
>> 获取 GitLink 标签...
|
||||
2025-07-15 09:32:01,577 - INFO - [OK] GitLink 共有 20 个标签
|
||||
2025-07-15 09:32:01,577 - INFO -
|
||||
>> 需要同步 0 个标签到 GitLink:
|
||||
2025-07-15 09:32:01,577 - INFO - [OK] 所有标签都已同步,无需操作
|
||||
2025-07-15 09:32:01,577 - INFO -
|
||||
======================================================================
|
||||
2025-07-15 09:32:01,577 - INFO - 📊 双向同步总结:
|
||||
2025-07-15 09:32:01,577 - INFO - GitLink → Gitee: 同步 0 个, 失败 0 个
|
||||
2025-07-15 09:32:01,577 - INFO - Gitee → GitLink: 同步 0 个, 失败 0 个
|
||||
2025-07-15 09:32:01,577 - INFO - 总体状态: ✅ 成功
|
||||
2025-07-15 09:32:01,577 - INFO - 后台双向同步任务完成: 双向同步完成,同步了 0 个标签
|
||||
2025-07-15 09:36:24,669 - INFO - >> 创建平台客户端...
|
||||
2025-07-15 09:36:24,669 - INFO - >> 初始化同步服务...
|
||||
2025-07-15 09:36:24,669 - INFO - >>>> 开始 GitLink ↔ Gitee 双向标签同步...
|
||||
2025-07-15 09:36:24,669 - INFO - ======================================================================
|
||||
2025-07-15 09:36:24,669 - INFO - 📍 第一步:GitLink → Gitee
|
||||
2025-07-15 09:36:24,669 - INFO - >> 开始从 GitLink 同步标签到 Gitee...
|
||||
2025-07-15 09:36:24,669 - INFO - ============================================================
|
||||
2025-07-15 09:36:24,669 - INFO - >> 获取 GitLink 标签...
|
||||
2025-07-15 09:36:25,528 - INFO - [OK] GitLink 共有 20 个标签
|
||||
2025-07-15 09:36:25,528 - INFO -
|
||||
>> 获取 Gitee 标签...
|
||||
2025-07-15 09:36:25,988 - INFO - [OK] Gitee 共有 20 个标签
|
||||
2025-07-15 09:36:25,988 - INFO -
|
||||
>> 需要同步 0 个标签到 Gitee:
|
||||
2025-07-15 09:36:25,988 - INFO - [OK] 所有标签都已同步,无需操作
|
||||
2025-07-15 09:36:25,988 - INFO -
|
||||
==================================================
|
||||
|
||||
2025-07-15 09:36:25,988 - INFO - 📍 第二步:Gitee → GitLink
|
||||
2025-07-15 09:36:25,989 - INFO - >> 开始从 Gitee 同步标签到 GitLink...
|
||||
2025-07-15 09:36:25,989 - INFO - ============================================================
|
||||
2025-07-15 09:36:25,989 - INFO - >> 获取 Gitee 标签...
|
||||
2025-07-15 09:36:26,441 - INFO - [OK] Gitee 共有 20 个标签
|
||||
2025-07-15 09:36:26,441 - INFO -
|
||||
>> 获取 GitLink 标签...
|
||||
2025-07-15 09:36:27,366 - INFO - [OK] GitLink 共有 20 个标签
|
||||
2025-07-15 09:36:27,366 - INFO -
|
||||
>> 需要同步 0 个标签到 GitLink:
|
||||
2025-07-15 09:36:27,366 - INFO - [OK] 所有标签都已同步,无需操作
|
||||
2025-07-15 09:36:27,366 - INFO -
|
||||
======================================================================
|
||||
2025-07-15 09:36:27,366 - INFO - 📊 双向同步总结:
|
||||
2025-07-15 09:36:27,366 - INFO - GitLink → Gitee: 同步 0 个, 失败 0 个
|
||||
2025-07-15 09:36:27,367 - INFO - Gitee → GitLink: 同步 0 个, 失败 0 个
|
||||
2025-07-15 09:36:27,367 - INFO - 总体状态: ✅ 成功
|
||||
2025-07-15 09:36:27,367 - INFO - 后台双向同步任务完成: 双向同步完成,同步了 0 个标签
|
||||
2025-07-15 09:36:37,026 - INFO - Shutting down
|
||||
2025-07-15 09:36:43,346 - INFO - 当前工作目录: D:\Projects\reposync\repo_tag_sync_module\web_api
|
||||
2025-07-15 09:36:43,346 - INFO - 当前文件目录: D:\Projects\reposync\repo_tag_sync_module\web_api
|
||||
2025-07-15 09:36:43,346 - INFO - 父目录: D:\Projects\reposync\repo_tag_sync_module
|
||||
2025-07-15 09:36:43,346 - INFO - Python路径: ['D:\\Projects\\reposync\\repo_tag_sync_module\\web_api', 'D:\\Projects\\reposync\\repo_tag_sync_module', 'D:\\Projects\\reposync\\repo_tag_sync_module\\web_api']
|
||||
2025-07-15 09:36:43,346 - INFO - github_client.py 存在: True
|
||||
2025-07-15 09:36:43,346 - INFO - gitee_client.py 存在: True
|
||||
2025-07-15 09:36:43,346 - INFO - gitlink_client.py 存在: True
|
||||
2025-07-15 09:36:43,346 - INFO - sync_service.py 存在: True
|
||||
2025-07-15 09:36:43,391 - INFO - 成功导入标签同步服务模块
|
||||
2025-07-15 09:37:54,676 - INFO - 当前工作目录: D:\Projects\reposync\repo_tag_sync_module\web_api
|
||||
2025-07-15 09:37:54,676 - INFO - 当前文件目录: D:\Projects\reposync\repo_tag_sync_module\web_api
|
||||
2025-07-15 09:37:54,676 - INFO - 父目录: D:\Projects\reposync\repo_tag_sync_module
|
||||
2025-07-15 09:37:54,676 - INFO - Python路径: ['D:\\Projects\\reposync\\repo_tag_sync_module\\web_api', 'D:\\Projects\\reposync\\repo_tag_sync_module', 'D:\\Projects\\reposync\\repo_tag_sync_module\\web_api']
|
||||
2025-07-15 09:37:54,676 - INFO - github_client.py 存在: True
|
||||
2025-07-15 09:37:54,676 - INFO - gitee_client.py 存在: True
|
||||
2025-07-15 09:37:54,676 - INFO - gitlink_client.py 存在: True
|
||||
2025-07-15 09:37:54,677 - INFO - sync_service.py 存在: True
|
||||
2025-07-15 09:37:54,724 - INFO - 成功导入标签同步服务模块
|
||||
2025-07-15 09:38:47,614 - INFO - 当前工作目录: D:\Projects\reposync\repo_tag_sync_module\web_api
|
||||
2025-07-15 09:38:47,614 - INFO - 当前文件目录: D:\Projects\reposync\repo_tag_sync_module\web_api
|
||||
2025-07-15 09:38:47,614 - INFO - 父目录: D:\Projects\reposync\repo_tag_sync_module
|
||||
2025-07-15 09:38:47,614 - INFO - Python路径: ['D:\\Projects\\reposync\\repo_tag_sync_module\\web_api', 'D:\\Projects\\reposync\\repo_tag_sync_module', 'D:\\Projects\\reposync\\repo_tag_sync_module\\web_api']
|
||||
2025-07-15 09:38:47,614 - INFO - github_client.py 存在: True
|
||||
2025-07-15 09:38:47,614 - INFO - gitee_client.py 存在: True
|
||||
2025-07-15 09:38:47,614 - INFO - gitlink_client.py 存在: True
|
||||
2025-07-15 09:38:47,614 - INFO - sync_service.py 存在: True
|
||||
2025-07-15 09:38:47,665 - INFO - 成功导入标签同步服务模块
|
||||
2025-07-15 09:38:47,699 - INFO - Started server process [54564]
|
||||
2025-07-15 09:38:47,700 - INFO - Waiting for application startup.
|
||||
2025-07-15 09:38:47,700 - INFO - Application startup complete.
|
||||
2025-07-15 09:38:47,700 - INFO - Uvicorn running on http://0.0.0.0:8005 (Press CTRL+C to quit)
|
||||
2025-07-15 09:39:19,297 - INFO - >> 创建平台客户端...
|
||||
2025-07-15 09:39:19,297 - INFO - >> 初始化同步服务...
|
||||
2025-07-15 09:39:19,297 - INFO - >>>> 开始 GitLink ↔ Gitee 双向标签同步...
|
||||
2025-07-15 09:39:19,298 - INFO - ======================================================================
|
||||
2025-07-15 09:39:19,298 - INFO - 📍 第一步:GitLink → Gitee
|
||||
2025-07-15 09:39:19,298 - INFO - >> 开始从 GitLink 同步标签到 Gitee...
|
||||
2025-07-15 09:39:19,298 - INFO - ============================================================
|
||||
2025-07-15 09:39:19,298 - INFO - >> 获取 GitLink 标签...
|
||||
2025-07-15 09:39:20,070 - INFO - [OK] GitLink 共有 20 个标签
|
||||
2025-07-15 09:39:20,071 - INFO -
|
||||
>> 获取 Gitee 标签...
|
||||
2025-07-15 09:39:20,625 - INFO - [OK] Gitee 共有 20 个标签
|
||||
2025-07-15 09:39:20,625 - INFO -
|
||||
>> 需要同步 0 个标签到 Gitee:
|
||||
2025-07-15 09:39:20,625 - INFO - [OK] 所有标签都已同步,无需操作
|
||||
2025-07-15 09:39:20,625 - INFO -
|
||||
==================================================
|
||||
|
||||
2025-07-15 09:39:20,625 - INFO - 📍 第二步:Gitee → GitLink
|
||||
2025-07-15 09:39:20,625 - INFO - >> 开始从 Gitee 同步标签到 GitLink...
|
||||
2025-07-15 09:39:20,625 - INFO - ============================================================
|
||||
2025-07-15 09:39:20,625 - INFO - >> 获取 Gitee 标签...
|
||||
2025-07-15 09:39:21,142 - INFO - [OK] Gitee 共有 20 个标签
|
||||
2025-07-15 09:39:21,142 - INFO -
|
||||
>> 获取 GitLink 标签...
|
||||
2025-07-15 09:39:22,067 - INFO - [OK] GitLink 共有 20 个标签
|
||||
2025-07-15 09:39:22,067 - INFO -
|
||||
>> 需要同步 0 个标签到 GitLink:
|
||||
2025-07-15 09:39:22,067 - INFO - [OK] 所有标签都已同步,无需操作
|
||||
2025-07-15 09:39:22,067 - INFO -
|
||||
======================================================================
|
||||
2025-07-15 09:39:22,067 - INFO - 📊 双向同步总结:
|
||||
2025-07-15 09:39:22,067 - INFO - GitLink → Gitee: 同步 0 个, 失败 0 个
|
||||
2025-07-15 09:39:22,067 - INFO - Gitee → GitLink: 同步 0 个, 失败 0 个
|
||||
2025-07-15 09:39:22,067 - INFO - 总体状态: ✅ 成功
|
||||
2025-07-15 09:39:22,067 - INFO - 后台双向同步任务完成: 双向同步完成,同步了 0 个标签
|
||||
2025-07-15 09:41:18,439 - INFO - Shutting down
|
||||
2025-07-15 09:42:58,008 - INFO - 当前工作目录: D:\Projects\reposync\repo_tag_sync_module\web_api
|
||||
2025-07-15 09:42:58,009 - INFO - 当前文件目录: D:\Projects\reposync\repo_tag_sync_module\web_api
|
||||
2025-07-15 09:42:58,009 - INFO - 父目录: D:\Projects\reposync\repo_tag_sync_module
|
||||
2025-07-15 09:42:58,009 - INFO - Python路径: ['D:\\Projects\\reposync\\repo_tag_sync_module\\web_api', 'D:\\Projects\\reposync\\repo_tag_sync_module', 'D:\\Projects\\reposync\\repo_tag_sync_module\\web_api']
|
||||
2025-07-15 09:42:58,009 - INFO - github_client.py 存在: True
|
||||
2025-07-15 09:42:58,009 - INFO - gitee_client.py 存在: True
|
||||
2025-07-15 09:42:58,009 - INFO - gitlink_client.py 存在: True
|
||||
2025-07-15 09:42:58,009 - INFO - sync_service.py 存在: True
|
||||
2025-07-15 09:42:58,053 - INFO - 成功导入标签同步服务模块
|
||||
2025-07-15 09:42:58,084 - INFO - Started server process [43756]
|
||||
2025-07-15 09:42:58,085 - INFO - Waiting for application startup.
|
||||
2025-07-15 09:42:58,085 - INFO - Application startup complete.
|
||||
2025-07-15 09:42:58,085 - INFO - Uvicorn running on http://0.0.0.0:8005 (Press CTRL+C to quit)
|
||||
2025-07-15 09:43:23,899 - INFO - >> 创建平台客户端...
|
||||
2025-07-15 09:43:23,899 - INFO - >> 初始化同步服务...
|
||||
2025-07-15 09:43:23,899 - INFO - >>>> 开始 GitLink ↔ Gitee 双向标签同步...
|
||||
2025-07-15 09:43:23,899 - INFO - ======================================================================
|
||||
2025-07-15 09:43:23,900 - INFO - 📍 第一步:GitLink → Gitee
|
||||
2025-07-15 09:43:23,900 - INFO - >> 开始从 GitLink 同步标签到 Gitee...
|
||||
2025-07-15 09:43:23,900 - INFO - ============================================================
|
||||
2025-07-15 09:43:23,900 - INFO - >> 获取 GitLink 标签...
|
||||
2025-07-15 09:43:24,712 - INFO - [OK] GitLink 共有 20 个标签
|
||||
2025-07-15 09:43:24,712 - INFO -
|
||||
>> 获取 Gitee 标签...
|
||||
2025-07-15 09:43:25,318 - INFO - [OK] Gitee 共有 20 个标签
|
||||
2025-07-15 09:43:25,318 - INFO -
|
||||
>> 需要同步 0 个标签到 Gitee:
|
||||
2025-07-15 09:43:25,318 - INFO - [OK] 所有标签都已同步,无需操作
|
||||
2025-07-15 09:43:25,318 - INFO -
|
||||
==================================================
|
||||
|
||||
2025-07-15 09:43:25,318 - INFO - 📍 第二步:Gitee → GitLink
|
||||
2025-07-15 09:43:25,319 - INFO - >> 开始从 Gitee 同步标签到 GitLink...
|
||||
2025-07-15 09:43:25,319 - INFO - ============================================================
|
||||
2025-07-15 09:43:25,319 - INFO - >> 获取 Gitee 标签...
|
||||
2025-07-15 09:43:25,934 - INFO - [OK] Gitee 共有 20 个标签
|
||||
2025-07-15 09:43:25,934 - INFO -
|
||||
>> 获取 GitLink 标签...
|
||||
2025-07-15 09:43:26,800 - INFO - [OK] GitLink 共有 20 个标签
|
||||
2025-07-15 09:43:26,800 - INFO -
|
||||
>> 需要同步 0 个标签到 GitLink:
|
||||
2025-07-15 09:43:26,800 - INFO - [OK] 所有标签都已同步,无需操作
|
||||
2025-07-15 09:43:26,800 - INFO -
|
||||
======================================================================
|
||||
2025-07-15 09:43:26,800 - INFO - 📊 双向同步总结:
|
||||
2025-07-15 09:43:26,800 - INFO - GitLink → Gitee: 同步 0 个, 失败 0 个
|
||||
2025-07-15 09:43:26,800 - INFO - Gitee → GitLink: 同步 0 个, 失败 0 个
|
||||
2025-07-15 09:43:26,800 - INFO - 总体状态: ✅ 成功
|
||||
2025-07-15 09:43:26,800 - INFO - 后台双向同步任务完成: 双向同步完成,同步了 0 个标签
|
||||
2025-07-15 09:45:14,868 - INFO - Shutting down
|
||||
2025-07-15 09:47:14,988 - INFO - 当前工作目录: D:\Projects\reposync\repo_tag_sync_module\web_api
|
||||
2025-07-15 09:47:14,988 - INFO - 当前文件目录: D:\Projects\reposync\repo_tag_sync_module\web_api
|
||||
2025-07-15 09:47:14,988 - INFO - 父目录: D:\Projects\reposync\repo_tag_sync_module
|
||||
2025-07-15 09:47:14,988 - INFO - Python路径: ['D:\\Projects\\reposync\\repo_tag_sync_module\\web_api', 'D:\\Projects\\reposync\\repo_tag_sync_module', 'D:\\Projects\\reposync\\repo_tag_sync_module\\web_api']
|
||||
2025-07-15 09:47:14,988 - INFO - github_client.py 存在: True
|
||||
2025-07-15 09:47:14,988 - INFO - gitee_client.py 存在: True
|
||||
2025-07-15 09:47:14,988 - INFO - gitlink_client.py 存在: True
|
||||
2025-07-15 09:47:14,989 - INFO - sync_service.py 存在: True
|
||||
2025-07-15 09:47:15,032 - INFO - 成功导入标签同步服务模块
|
||||
2025-07-15 09:47:15,063 - INFO - Started server process [46036]
|
||||
2025-07-15 09:47:15,063 - INFO - Waiting for application startup.
|
||||
2025-07-15 09:47:15,063 - INFO - Application startup complete.
|
||||
2025-07-15 09:47:15,064 - INFO - Uvicorn running on http://0.0.0.0:8005 (Press CTRL+C to quit)
|
||||
2025-07-15 09:52:21,730 - INFO - Shutting down
|
||||
|
|
@ -0,0 +1,609 @@
|
|||
# coding: utf-8
|
||||
|
||||
# 标签同步API接口文件,定义了标签同步的API接口,包括GitHub、Gitee、GitLink之间的双向同步
|
||||
import time
|
||||
import asyncio
|
||||
import logging
|
||||
import sys
|
||||
import os
|
||||
from typing import Optional, List, Union, Any
|
||||
from collections import deque
|
||||
import threading
|
||||
from datetime import datetime
|
||||
|
||||
from fastapi import (
|
||||
BackgroundTasks,
|
||||
Query,
|
||||
Depends,
|
||||
Security,
|
||||
Body,
|
||||
APIRouter
|
||||
)
|
||||
from pydantic import BaseModel
|
||||
|
||||
# 全局日志缓存
|
||||
class LogCache:
|
||||
def __init__(self, max_size=1000):
|
||||
self.logs = deque(maxlen=max_size)
|
||||
self.lock = threading.Lock()
|
||||
|
||||
def add_log(self, message, level="INFO"):
|
||||
with self.lock:
|
||||
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
self.logs.append({
|
||||
"timestamp": timestamp,
|
||||
"level": level,
|
||||
"message": message
|
||||
})
|
||||
|
||||
def get_logs(self, since_timestamp=None):
|
||||
with self.lock:
|
||||
if since_timestamp is None:
|
||||
return list(self.logs)
|
||||
else:
|
||||
# 返回指定时间戳之后的日志
|
||||
result = []
|
||||
for log in self.logs:
|
||||
if log["timestamp"] > since_timestamp:
|
||||
result.append(log)
|
||||
return result
|
||||
|
||||
def clear(self):
|
||||
with self.lock:
|
||||
self.logs.clear()
|
||||
|
||||
# 全局日志缓存实例
|
||||
log_cache = LogCache()
|
||||
|
||||
# 自定义日志处理器,将日志同时写入文件和缓存
|
||||
class CacheHandler(logging.Handler):
|
||||
def emit(self, record):
|
||||
try:
|
||||
msg = self.format(record)
|
||||
log_cache.add_log(msg, record.levelname)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# 配置日志
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s - %(levelname)s - %(message)s',
|
||||
handlers=[
|
||||
logging.StreamHandler(sys.stdout),
|
||||
logging.FileHandler('tag_sync.log', encoding='utf-8'),
|
||||
CacheHandler() # 添加缓存处理器
|
||||
]
|
||||
)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# 添加父目录到Python路径
|
||||
current_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
parent_dir = os.path.dirname(current_dir)
|
||||
if parent_dir not in sys.path:
|
||||
sys.path.insert(0, parent_dir)
|
||||
|
||||
# 导入标签同步模块 - 修复为绝对导入
|
||||
try:
|
||||
# 尝试多种导入方式
|
||||
logger.info(f"当前工作目录: {os.getcwd()}")
|
||||
logger.info(f"当前文件目录: {current_dir}")
|
||||
logger.info(f"父目录: {parent_dir}")
|
||||
logger.info(f"Python路径: {sys.path[:3]}")
|
||||
|
||||
# 检查文件是否存在
|
||||
github_client_path = os.path.join(parent_dir, 'github_client.py')
|
||||
gitee_client_path = os.path.join(parent_dir, 'gitee_client.py')
|
||||
gitlink_client_path = os.path.join(parent_dir, 'gitlink_client.py')
|
||||
sync_service_path = os.path.join(parent_dir, 'sync_service.py')
|
||||
|
||||
logger.info(f"github_client.py 存在: {os.path.exists(github_client_path)}")
|
||||
logger.info(f"gitee_client.py 存在: {os.path.exists(gitee_client_path)}")
|
||||
logger.info(f"gitlink_client.py 存在: {os.path.exists(gitlink_client_path)}")
|
||||
logger.info(f"sync_service.py 存在: {os.path.exists(sync_service_path)}")
|
||||
|
||||
# 尝试导入
|
||||
import importlib.util
|
||||
|
||||
# 动态导入github_client
|
||||
spec = importlib.util.spec_from_file_location("github_client", github_client_path)
|
||||
github_client_module = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(github_client_module)
|
||||
GithubClient = github_client_module.GithubClient
|
||||
|
||||
# 动态导入gitee_client
|
||||
spec = importlib.util.spec_from_file_location("gitee_client", gitee_client_path)
|
||||
gitee_client_module = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(gitee_client_module)
|
||||
GiteeClient = gitee_client_module.GiteeClient
|
||||
|
||||
# 动态导入gitlink_client
|
||||
spec = importlib.util.spec_from_file_location("gitlink_client", gitlink_client_path)
|
||||
gitlink_client_module = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(gitlink_client_module)
|
||||
GitlinkClient = gitlink_client_module.GitlinkClient
|
||||
|
||||
# 动态导入sync_service
|
||||
spec = importlib.util.spec_from_file_location("sync_service", sync_service_path)
|
||||
sync_service_module = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(sync_service_module)
|
||||
TagSyncService = sync_service_module.TagSyncService
|
||||
|
||||
logger.info("成功导入标签同步服务模块")
|
||||
IMPORT_SUCCESS = True
|
||||
|
||||
except ImportError as e:
|
||||
logger.error(f"无法导入标签同步服务: {str(e)}")
|
||||
IMPORT_SUCCESS = False
|
||||
# 创建模拟的服务类,避免应用启动失败
|
||||
class TagSyncService:
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def bidirectional_sync(self, client_a, client_b, dry_run=False):
|
||||
logger.error("使用模拟的同步服务 - 导入失败")
|
||||
return {
|
||||
"gitlink_to_gitee": {"success": False, "synced": 0, "errors": 1},
|
||||
"gitee_to_gitlink": {"success": False, "synced": 0, "errors": 1},
|
||||
"overall_success": False
|
||||
}
|
||||
|
||||
class GithubClient:
|
||||
def __init__(self, *args, **kwargs):
|
||||
pass
|
||||
|
||||
class GiteeClient:
|
||||
def __init__(self, *args, **kwargs):
|
||||
pass
|
||||
|
||||
class GitlinkClient:
|
||||
def __init__(self, *args, **kwargs):
|
||||
pass
|
||||
except Exception as e:
|
||||
logger.error(f"导入过程中发生异常: {str(e)}")
|
||||
IMPORT_SUCCESS = False
|
||||
# 创建模拟的服务类
|
||||
class TagSyncService:
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def bidirectional_sync(self, client_a, client_b, dry_run=False):
|
||||
logger.error("使用模拟的同步服务 - 导入异常")
|
||||
return {
|
||||
"gitlink_to_gitee": {"success": False, "synced": 0, "errors": 1},
|
||||
"gitee_to_gitlink": {"success": False, "synced": 0, "errors": 1},
|
||||
"overall_success": False
|
||||
}
|
||||
|
||||
class GithubClient:
|
||||
def __init__(self, *args, **kwargs):
|
||||
pass
|
||||
|
||||
class GiteeClient:
|
||||
def __init__(self, *args, **kwargs):
|
||||
pass
|
||||
|
||||
class GitlinkClient:
|
||||
def __init__(self, *args, **kwargs):
|
||||
pass
|
||||
|
||||
|
||||
class TagSyncConfig(BaseModel):
|
||||
"""平台配置模型"""
|
||||
platform: str # github, gitee, gitlink
|
||||
org: str
|
||||
repo: str
|
||||
token: Optional[str] = None # GitHub/Gitee token
|
||||
cookie: Optional[str] = None # GitLink cookie
|
||||
|
||||
|
||||
class TagSyncRequest(BaseModel):
|
||||
"""标签同步请求模型"""
|
||||
source_platform: str # github, gitee, gitlink
|
||||
source_org: str
|
||||
source_repo: str
|
||||
source_token: Optional[str] = None # GitHub/Gitee token
|
||||
source_cookie: Optional[str] = None # GitLink cookie
|
||||
|
||||
target_platform: str # github, gitee, gitlink
|
||||
target_org: str
|
||||
target_repo: str
|
||||
target_token: Optional[str] = None # GitHub/Gitee token
|
||||
target_cookie: Optional[str] = None # GitLink cookie
|
||||
|
||||
dry_run: bool = True # 默认试运行
|
||||
|
||||
|
||||
class BiDirectionalSyncRequest(BaseModel):
|
||||
"""双向同步请求模型"""
|
||||
platform_a: TagSyncConfig
|
||||
platform_b: TagSyncConfig
|
||||
dry_run: bool = True
|
||||
|
||||
|
||||
class TagSyncResult(BaseModel):
|
||||
"""标签同步结果模型"""
|
||||
success: bool
|
||||
message: str
|
||||
synced_count: int = 0
|
||||
error_count: int = 0
|
||||
details: dict = {}
|
||||
timestamp: str
|
||||
|
||||
|
||||
class APIResponse(BaseModel):
|
||||
"""API响应模型"""
|
||||
data: Optional[Any] = None
|
||||
message: str = "success"
|
||||
code: int = 0
|
||||
|
||||
|
||||
async def _execute_bidirectional_sync(request: BiDirectionalSyncRequest) -> TagSyncResult:
|
||||
"""执行双向同步的核心逻辑"""
|
||||
try:
|
||||
logger.info(">> 创建平台客户端...")
|
||||
# 创建客户端
|
||||
client_a = _create_client(request.platform_a)
|
||||
client_b = _create_client(request.platform_b)
|
||||
|
||||
logger.info(">> 初始化同步服务...")
|
||||
# 创建同步服务
|
||||
sync_service = TagSyncService()
|
||||
|
||||
# 执行双向同步
|
||||
if request.platform_a.platform.lower() == "gitlink" and request.platform_b.platform.lower() == "gitee":
|
||||
results = sync_service.bidirectional_sync(
|
||||
client_a, client_b,
|
||||
dry_run=request.dry_run
|
||||
)
|
||||
elif request.platform_a.platform.lower() == "gitee" and request.platform_b.platform.lower() == "gitlink":
|
||||
results = sync_service.bidirectional_sync(
|
||||
client_b, client_a, # 交换顺序
|
||||
dry_run=request.dry_run
|
||||
)
|
||||
else:
|
||||
# 其他平台组合的同步逻辑
|
||||
results = _handle_other_platform_sync(
|
||||
client_a, client_b, request.platform_a.platform, request.platform_b.platform,
|
||||
sync_service, request.dry_run
|
||||
)
|
||||
|
||||
total_synced = results.get("gitlink_to_gitee", {}).get("synced", 0) + \
|
||||
results.get("gitee_to_gitlink", {}).get("synced", 0)
|
||||
total_errors = results.get("gitlink_to_gitee", {}).get("errors", 0) + \
|
||||
results.get("gitee_to_gitlink", {}).get("errors", 0)
|
||||
|
||||
return TagSyncResult(
|
||||
success=results.get("overall_success", False),
|
||||
message=f"双向同步完成,同步了 {total_synced} 个标签",
|
||||
synced_count=total_synced,
|
||||
error_count=total_errors,
|
||||
details=results,
|
||||
timestamp=time.strftime("%Y-%m-%d %H:%M:%S")
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
return TagSyncResult(
|
||||
success=False,
|
||||
message=f"双向同步执行失败: {str(e)}",
|
||||
timestamp=time.strftime("%Y-%m-%d %H:%M:%S")
|
||||
)
|
||||
|
||||
|
||||
async def _execute_oneway_sync(request: TagSyncRequest) -> TagSyncResult:
|
||||
"""执行单向同步的核心逻辑"""
|
||||
try:
|
||||
# 创建客户端
|
||||
source_client = _create_client_from_request(request, "source")
|
||||
target_client = _create_client_from_request(request, "target")
|
||||
|
||||
# 创建同步服务
|
||||
sync_service = TagSyncService()
|
||||
|
||||
# 根据平台执行相应的同步方法
|
||||
source_platform = request.source_platform.lower()
|
||||
target_platform = request.target_platform.lower()
|
||||
|
||||
if source_platform == "gitlink" and target_platform == "gitee":
|
||||
results = sync_service.sync_gitlink_to_gitee(
|
||||
source_client, target_client,
|
||||
dry_run=request.dry_run
|
||||
)
|
||||
elif source_platform == "gitee" and target_platform == "gitlink":
|
||||
results = sync_service.sync_gitee_to_gitlink(
|
||||
source_client, target_client,
|
||||
dry_run=request.dry_run
|
||||
)
|
||||
else:
|
||||
return TagSyncResult(
|
||||
success=False,
|
||||
message=f"暂不支持 {source_platform} → {target_platform} 的同步",
|
||||
timestamp=time.strftime("%Y-%m-%d %H:%M:%S")
|
||||
)
|
||||
|
||||
return TagSyncResult(
|
||||
success=results.get("errors", 0) == 0,
|
||||
message=f"单向同步完成,同步了 {results.get('synced', 0)} 个标签",
|
||||
synced_count=results.get("synced", 0),
|
||||
error_count=results.get("errors", 0),
|
||||
details=results,
|
||||
timestamp=time.strftime("%Y-%m-%d %H:%M:%S")
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
return TagSyncResult(
|
||||
success=False,
|
||||
message=f"单向同步执行失败: {str(e)}",
|
||||
timestamp=time.strftime("%Y-%m-%d %H:%M:%S")
|
||||
)
|
||||
|
||||
|
||||
def _create_client(platform_config: TagSyncConfig):
|
||||
"""根据平台配置创建客户端"""
|
||||
platform = platform_config.platform.lower()
|
||||
|
||||
if platform == "github":
|
||||
return GithubClient(platform_config.org, platform_config.repo, platform_config.token)
|
||||
elif platform == "gitee":
|
||||
return GiteeClient(platform_config.org, platform_config.repo, platform_config.token)
|
||||
elif platform == "gitlink":
|
||||
return GitlinkClient(platform_config.org, platform_config.repo, platform_config.cookie)
|
||||
else:
|
||||
raise ValueError(f"不支持的平台: {platform}")
|
||||
|
||||
|
||||
def _create_client_from_request(request: TagSyncRequest, prefix: str):
|
||||
"""从请求创建客户端"""
|
||||
if prefix == "source":
|
||||
platform = request.source_platform.lower()
|
||||
org = request.source_org
|
||||
repo = request.source_repo
|
||||
token = request.source_token
|
||||
cookie = request.source_cookie
|
||||
else: # target
|
||||
platform = request.target_platform.lower()
|
||||
org = request.target_org
|
||||
repo = request.target_repo
|
||||
token = request.target_token
|
||||
cookie = request.target_cookie
|
||||
|
||||
if platform == "github":
|
||||
return GithubClient(org, repo, token)
|
||||
elif platform == "gitee":
|
||||
return GiteeClient(org, repo, token)
|
||||
elif platform == "gitlink":
|
||||
return GitlinkClient(org, repo, cookie)
|
||||
else:
|
||||
raise ValueError(f"不支持的平台: {platform}")
|
||||
|
||||
|
||||
def _handle_other_platform_sync(client_a, client_b, platform_a, platform_b, sync_service, dry_run):
|
||||
"""处理其他平台组合的同步"""
|
||||
# 这里可以扩展支持其他平台组合
|
||||
# 目前先返回错误
|
||||
return {
|
||||
"overall_success": False,
|
||||
"gitlink_to_gitee": {"synced": 0, "errors": 1},
|
||||
"gitee_to_gitlink": {"synced": 0, "errors": 1}
|
||||
}
|
||||
|
||||
|
||||
async def _execute_bidirectional_sync_task(request: BiDirectionalSyncRequest):
|
||||
"""后台执行双向同步任务"""
|
||||
try:
|
||||
result = await _execute_bidirectional_sync(request)
|
||||
logger.info(f"后台双向同步任务完成: {result.message}")
|
||||
except Exception as e:
|
||||
logger.error(f"后台双向同步任务失败: {str(e)}")
|
||||
|
||||
|
||||
async def _execute_oneway_sync_task(request: TagSyncRequest):
|
||||
"""后台执行单向同步任务"""
|
||||
try:
|
||||
result = await _execute_oneway_sync(request)
|
||||
logger.info(f"后台单向同步任务完成: {result.message}")
|
||||
except Exception as e:
|
||||
logger.error(f"后台单向同步任务失败: {str(e)}")
|
||||
|
||||
|
||||
def create_tag_sync_router() -> APIRouter:
|
||||
"""创建标签同步路由"""
|
||||
router = APIRouter(prefix='/tag-sync', tags=['TagSync'])
|
||||
|
||||
@router.get("/platforms", response_model=APIResponse, description='获取支持的标签同步平台列表')
|
||||
async def get_supported_platforms():
|
||||
"""获取支持的标签同步平台"""
|
||||
try:
|
||||
platforms = ["github", "gitee", "gitlink"]
|
||||
return APIResponse(
|
||||
data={"platforms": platforms},
|
||||
message="获取平台列表成功",
|
||||
code=0
|
||||
)
|
||||
except Exception as e:
|
||||
return APIResponse(
|
||||
data=None,
|
||||
message=f"获取平台列表失败: {str(e)}",
|
||||
code=1
|
||||
)
|
||||
|
||||
@router.get("/sync-types", response_model=APIResponse, description='获取支持的同步类型')
|
||||
async def get_sync_types():
|
||||
"""获取支持的双向同步类型"""
|
||||
try:
|
||||
sync_types = [
|
||||
{
|
||||
"id": "gitee_gitlink",
|
||||
"name": "Gitee ↔ GitLink",
|
||||
"description": "Gitee和GitLink之间的双向标签同步",
|
||||
"platforms": ["gitee", "gitlink"]
|
||||
},
|
||||
{
|
||||
"id": "github_gitlink",
|
||||
"name": "GitHub ↔ GitLink",
|
||||
"description": "GitHub和GitLink之间的双向标签同步",
|
||||
"platforms": ["github", "gitlink"]
|
||||
},
|
||||
{
|
||||
"id": "github_gitee",
|
||||
"name": "GitHub ↔ Gitee",
|
||||
"description": "GitHub和Gitee之间的双向标签同步",
|
||||
"platforms": ["github", "gitee"]
|
||||
}
|
||||
]
|
||||
return APIResponse(
|
||||
data={"sync_types": sync_types},
|
||||
message="获取同步类型成功",
|
||||
code=0
|
||||
)
|
||||
except Exception as e:
|
||||
return APIResponse(
|
||||
data={"sync_types": []},
|
||||
message=f"获取同步类型失败: {str(e)}",
|
||||
code=1
|
||||
)
|
||||
|
||||
@router.post("/sync/bidirectional", response_model=APIResponse, description='执行双向标签同步')
|
||||
async def bidirectional_sync(
|
||||
background_tasks: BackgroundTasks,
|
||||
request: BiDirectionalSyncRequest = Body(..., description='双向同步请求参数')
|
||||
):
|
||||
"""执行双向标签同步"""
|
||||
try:
|
||||
# 验证平台组合
|
||||
platform_a = request.platform_a.platform.lower()
|
||||
platform_b = request.platform_b.platform.lower()
|
||||
|
||||
valid_combinations = [
|
||||
("gitee", "gitlink"), ("gitlink", "gitee"),
|
||||
("github", "gitlink"), ("gitlink", "github"),
|
||||
("github", "gitee"), ("gitee", "github")
|
||||
]
|
||||
|
||||
if (platform_a, platform_b) not in valid_combinations:
|
||||
return APIResponse(
|
||||
data=TagSyncResult(
|
||||
success=False,
|
||||
message=f"不支持的平台组合: {platform_a} ↔ {platform_b}",
|
||||
timestamp=time.strftime("%Y-%m-%d %H:%M:%S")
|
||||
).dict(),
|
||||
message="不支持的平台组合",
|
||||
code=1
|
||||
)
|
||||
|
||||
# 创建同步任务
|
||||
if request.dry_run:
|
||||
# 试运行模式 - 立即执行
|
||||
result = await _execute_bidirectional_sync(request)
|
||||
return APIResponse(
|
||||
data=result.dict(),
|
||||
message="双向同步试运行完成",
|
||||
code=0
|
||||
)
|
||||
else:
|
||||
# 实际执行 - 后台任务
|
||||
background_tasks.add_task(
|
||||
_execute_bidirectional_sync_task,
|
||||
request
|
||||
)
|
||||
return APIResponse(
|
||||
data=TagSyncResult(
|
||||
success=True,
|
||||
message="双向标签同步任务已启动,正在后台执行",
|
||||
timestamp=time.strftime("%Y-%m-%d %H:%M:%S")
|
||||
).dict(),
|
||||
message="同步任务已启动",
|
||||
code=0
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
return APIResponse(
|
||||
data=TagSyncResult(
|
||||
success=False,
|
||||
message=f"双向同步失败: {str(e)}",
|
||||
timestamp=time.strftime("%Y-%m-%d %H:%M:%S")
|
||||
).dict(),
|
||||
message="双向同步失败",
|
||||
code=1
|
||||
)
|
||||
|
||||
@router.post("/sync/oneway", response_model=APIResponse, description='执行单向标签同步')
|
||||
async def oneway_sync(
|
||||
background_tasks: BackgroundTasks,
|
||||
request: TagSyncRequest = Body(..., description='单向同步请求参数')
|
||||
):
|
||||
"""执行单向标签同步"""
|
||||
try:
|
||||
# 创建同步任务
|
||||
if request.dry_run:
|
||||
# 试运行模式 - 立即执行
|
||||
result = await _execute_oneway_sync(request)
|
||||
return APIResponse(
|
||||
data=result.dict(),
|
||||
message="单向同步试运行完成",
|
||||
code=0
|
||||
)
|
||||
else:
|
||||
# 实际执行 - 后台任务
|
||||
background_tasks.add_task(
|
||||
_execute_oneway_sync_task,
|
||||
request
|
||||
)
|
||||
return APIResponse(
|
||||
data=TagSyncResult(
|
||||
success=True,
|
||||
message="单向标签同步任务已启动,正在后台执行",
|
||||
timestamp=time.strftime("%Y-%m-%d %H:%M:%S")
|
||||
).dict(),
|
||||
message="同步任务已启动",
|
||||
code=0
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
return APIResponse(
|
||||
data=TagSyncResult(
|
||||
success=False,
|
||||
message=f"单向同步失败: {str(e)}",
|
||||
timestamp=time.strftime("%Y-%m-%d %H:%M:%S")
|
||||
).dict(),
|
||||
message="单向同步失败",
|
||||
code=1
|
||||
)
|
||||
|
||||
# 日志相关API端点
|
||||
@router.get("/logs", response_model=APIResponse, description='获取同步日志')
|
||||
async def get_logs(
|
||||
since: Optional[str] = Query(None, description='获取指定时间戳之后的日志')
|
||||
):
|
||||
"""获取同步日志"""
|
||||
try:
|
||||
logs = log_cache.get_logs(since)
|
||||
return APIResponse(
|
||||
data={"logs": logs},
|
||||
message=f"获取到 {len(logs)} 条日志",
|
||||
code=0
|
||||
)
|
||||
except Exception as e:
|
||||
return APIResponse(
|
||||
data={"logs": []},
|
||||
message=f"获取日志失败: {str(e)}",
|
||||
code=1
|
||||
)
|
||||
|
||||
@router.delete("/logs", response_model=APIResponse, description='清空同步日志')
|
||||
async def clear_logs():
|
||||
"""清空同步日志"""
|
||||
try:
|
||||
log_cache.clear()
|
||||
return APIResponse(
|
||||
data=None,
|
||||
message="日志已清空",
|
||||
code=0
|
||||
)
|
||||
except Exception as e:
|
||||
return APIResponse(
|
||||
data=None,
|
||||
message=f"清空日志失败: {str(e)}",
|
||||
code=1
|
||||
)
|
||||
|
||||
return router
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
#!/usr/bin/env python3
|
||||
# coding: utf-8
|
||||
"""
|
||||
标签同步服务快捷启动脚本
|
||||
调用 repo_tag_sync_module 模块中的独立服务
|
||||
"""
|
||||
import sys
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def main():
|
||||
"""启动标签同步服务"""
|
||||
print("标签同步服务快捷启动")
|
||||
print("=" * 50)
|
||||
print("启动独立模块中的标签同步服务...")
|
||||
print()
|
||||
|
||||
try:
|
||||
# 调用模块中的启动脚本
|
||||
subprocess.run([
|
||||
sys.executable,
|
||||
"-m",
|
||||
"repo_tag_sync_module.start_tag_sync"
|
||||
], check=True)
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f"启动失败: {e}")
|
||||
sys.exit(1)
|
||||
except KeyboardInterrupt:
|
||||
print("\n用户取消启动")
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
2025-07-14 20:02:02,818 - INFO - Started server process [27788]
|
||||
2025-07-14 20:02:02,818 - INFO - Waiting for application startup.
|
||||
2025-07-14 20:02:02,818 - INFO - Application startup complete.
|
||||
2025-07-14 20:02:02,818 - INFO - Uvicorn running on http://0.0.0.0:8005 (Press CTRL+C to quit)
|
||||
2025-07-14 20:03:17,935 - INFO - >> 创建平台客户端...
|
||||
2025-07-14 20:03:17,936 - INFO - >> 初始化同步服务...
|
||||
2025-07-15 09:14:12,535 - INFO - 当前工作目录: D:\Projects\reposync
|
||||
2025-07-15 09:14:12,535 - INFO - 当前文件目录: D:\Projects\reposync\repo_tag_sync_module\web_api
|
||||
2025-07-15 09:14:12,535 - INFO - 父目录: D:\Projects\reposync\repo_tag_sync_module
|
||||
2025-07-15 09:14:12,535 - INFO - Python路径: ['D:\\Projects\\reposync\\repo_tag_sync_module\\web_api', 'D:\\Projects\\reposync\\repo_tag_sync_module', 'D:\\Projects\\reposync\\repo_tag_sync_module']
|
||||
2025-07-15 09:14:12,535 - INFO - github_client.py 存在: True
|
||||
2025-07-15 09:14:12,535 - INFO - gitee_client.py 存在: True
|
||||
2025-07-15 09:14:12,535 - INFO - gitlink_client.py 存在: True
|
||||
2025-07-15 09:14:12,535 - INFO - sync_service.py 存在: True
|
||||
2025-07-15 09:14:12,589 - INFO - 成功导入标签同步服务模块
|
||||
2025-07-15 09:15:23,282 - INFO - 当前工作目录: D:\Projects\reposync
|
||||
2025-07-15 09:15:23,282 - INFO - 当前文件目录: D:\Projects\reposync\repo_tag_sync_module\web_api
|
||||
2025-07-15 09:15:23,282 - INFO - 父目录: D:\Projects\reposync\repo_tag_sync_module
|
||||
2025-07-15 09:15:23,282 - INFO - Python路径: ['D:\\Projects\\reposync\\repo_tag_sync_module\\web_api', 'D:\\Projects\\reposync\\repo_tag_sync_module', 'D:\\Projects\\reposync\\repo_tag_sync_module']
|
||||
2025-07-15 09:15:23,282 - INFO - github_client.py 存在: True
|
||||
2025-07-15 09:15:23,282 - INFO - gitee_client.py 存在: True
|
||||
2025-07-15 09:15:23,283 - INFO - gitlink_client.py 存在: True
|
||||
2025-07-15 09:15:23,283 - INFO - sync_service.py 存在: True
|
||||
2025-07-15 09:15:23,338 - INFO - 成功导入标签同步服务模块
|
||||
2025-07-15 09:15:23,342 - INFO - 当前工作目录: D:\Projects\reposync
|
||||
2025-07-15 09:15:23,342 - INFO - 当前文件目录: D:\Projects\reposync\repo_tag_sync_module\web_api
|
||||
2025-07-15 09:15:23,343 - INFO - 父目录: D:\Projects\reposync\repo_tag_sync_module
|
||||
2025-07-15 09:15:23,343 - INFO - Python路径: ['D:\\Projects\\reposync\\repo_tag_sync_module\\web_api', 'D:\\Projects\\reposync\\repo_tag_sync_module', 'D:\\Projects\\reposync\\repo_tag_sync_module']
|
||||
2025-07-15 09:15:23,343 - INFO - github_client.py 存在: True
|
||||
2025-07-15 09:15:23,343 - INFO - gitee_client.py 存在: True
|
||||
2025-07-15 09:15:23,344 - INFO - gitlink_client.py 存在: True
|
||||
2025-07-15 09:15:23,344 - INFO - sync_service.py 存在: True
|
||||
2025-07-15 09:15:23,345 - INFO - 成功导入标签同步服务模块
|
||||
2025-07-15 09:15:23,382 - INFO - Started server process [6528]
|
||||
2025-07-15 09:15:23,383 - INFO - Waiting for application startup.
|
||||
2025-07-15 09:15:23,383 - INFO - Application startup complete.
|
||||
2025-07-15 09:15:23,384 - INFO - Uvicorn running on http://0.0.0.0:8005 (Press CTRL+C to quit)
|
||||
2025-07-15 09:15:54,726 - INFO - Shutting down
|
||||
2025-07-15 09:15:54,835 - INFO - Waiting for application shutdown.
|
||||
2025-07-15 09:15:54,835 - INFO - Application shutdown complete.
|
||||
2025-07-15 09:15:54,835 - INFO - Finished server process [6528]
|
||||
2025-07-15 09:30:26,580 - INFO - 当前工作目录: D:\Projects\reposync
|
||||
2025-07-15 09:30:26,581 - INFO - 当前文件目录: D:\Projects\reposync\repo_tag_sync_module\web_api
|
||||
2025-07-15 09:30:26,581 - INFO - 父目录: D:\Projects\reposync\repo_tag_sync_module
|
||||
2025-07-15 09:30:26,582 - INFO - Python路径: ['D:\\Projects\\reposync\\repo_tag_sync_module\\web_api', 'D:\\Projects\\reposync\\repo_tag_sync_module', 'D:\\Software\\Pycharm\\Anaconda\\envs\\reposyncer\\python39.zip']
|
||||
2025-07-15 09:30:26,582 - INFO - github_client.py 存在: True
|
||||
2025-07-15 09:30:26,582 - INFO - gitee_client.py 存在: True
|
||||
2025-07-15 09:30:26,582 - INFO - gitlink_client.py 存在: True
|
||||
2025-07-15 09:30:26,582 - INFO - sync_service.py 存在: True
|
||||
2025-07-15 09:30:26,583 - INFO - 成功导入标签同步服务模块
|
||||
Loading…
Reference in New Issue