Files
GitMa/docs/superpowers/specs/2026-03-30-git-repo-manager-design.md
panw f720de6b58 feat: initialize project structure and configuration
- Create backend directory structure (app/models, app/schemas, app/services, app/api, app/tasks, tests)
- Create frontend directory structure (src/router, src/views, src/components, src/api, src/stores)
- Create data directories (ssh_keys, repos)
- Add requirements.txt with FastAPI, SQLAlchemy, Pydantic, and testing dependencies
- Add frontend package.json with Vue 3, Vue Router, Pinia, and Element Plus
- Add .env.example with configuration template
- Add .gitignore for Python, data directories, and frontend
- Add pytest conftest.py with test fixtures for database and environment

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-30 14:57:51 +08:00

379 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Git Repo Manager - 设计文档
## 概述
一个面向小团队3-10人的 Git 仓库本地同步管理工具。通过 Web 界面管理多个 Gitea 服务器,支持将远程仓库批量同步到本地,提供提交历史查看和定时自动同步功能。首版聚焦 Gitea 服务器支持。
## 技术选型
| 层 | 技术 | 理由 |
|----|------|------|
| 后端 | Python FastAPI | 开发快速,异步支持好 |
| 前端 | Vue 3 + Element Plus + Pinia | 中文生态完善,适合管理后台 |
| 数据库 | SQLite + SQLAlchemy ORM | 零部署,适合小团队 |
| 调度 | APScheduler | Python 生态成熟方案 |
| SSH | paramiko | 纯 Python SSH 实现 |
| HTTP 客户端 | Axios | 前端请求拦截器 |
| 部署 | 单进程 uvicorn | 一个命令启动全部功能 |
## 架构设计
### 整体架构
单体 FastAPI 应用,内嵌前端静态资源,单进程运行:
```
┌─────────────────────────────────────────────┐
│ FastAPI 单进程 │
│ │
│ ┌─────────┐ ┌──────────┐ ┌────────────┐ │
│ │ REST API │ │ Scheduler │ │ Git Engine │ │
│ │ /api/v1 │ │ APSched │ │ git+SSH │ │
│ └────┬─────┘ └─────┬────┘ └─────┬──────┘ │
│ │ │ │ │
│ ┌────┴──────────────┴──────────────┴──────┐ │
│ │ Service Layer │ │
│ │ ServerService / RepoService / SyncSvc │ │
│ └────────────────┬────────────────────────┘ │
│ │ │
│ ┌────────────────┴────────────────────────┐ │
│ │ SQLAlchemy + SQLite (data + keys) │ │
│ └─────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────┐ │
│ │ Vue 3 SPA (打包后静态托管) │ │
│ └─────────────────────────────────────────┘ │
└─────────────────────────────────────────────┘
```
### 项目目录结构
```
git-manager/
├── backend/
│ ├── app/
│ │ ├── main.py # FastAPI 入口,挂载路由和静态文件
│ │ ├── config.py # 配置管理,读取环境变量
│ │ ├── database.py # SQLite 连接,会话管理
│ │ ├── security.py # 加解密工具API 认证
│ │ ├── models/ # SQLAlchemy 数据模型
│ │ │ ├── __init__.py
│ │ │ ├── server.py # Server 模型
│ │ │ ├── repo.py # Repo 模型
│ │ │ ├── ssh_key.py # SshKey 模型
│ │ │ └── sync_log.py # SyncLog 模型
│ │ ├── schemas/ # Pydantic 请求/响应模型
│ │ │ ├── __init__.py
│ │ │ ├── server.py
│ │ │ ├── repo.py
│ │ │ ├── ssh_key.py
│ │ │ ├── sync_log.py
│ │ │ └── common.py # 标准响应格式
│ │ ├── services/ # 业务逻辑层
│ │ │ ├── __init__.py
│ │ │ ├── server_service.py
│ │ │ ├── repo_service.py
│ │ │ ├── sync_service.py
│ │ │ └── ssh_key_service.py
│ │ ├── api/ # API 路由
│ │ │ ├── __init__.py
│ │ │ ├── deps.py # 依赖注入DB 会话、认证)
│ │ │ ├── servers.py
│ │ │ ├── repos.py
│ │ │ ├── ssh_keys.py
│ │ │ ├── sync_logs.py
│ │ │ ├── schedules.py
│ │ │ └── status.py
│ │ └── tasks/ # 定时任务
│ │ ├── __init__.py
│ │ └── sync_task.py
│ ├── init_db.py # 数据库初始化脚本
│ └── requirements.txt
├── frontend/
│ ├── src/
│ │ ├── App.vue
│ │ ├── main.js
│ │ ├── router/
│ │ │ └── index.js
│ │ ├── views/ # 页面组件
│ │ │ ├── Dashboard.vue
│ │ │ ├── Servers.vue
│ │ │ ├── Repos.vue
│ │ │ ├── SyncLogs.vue
│ │ │ ├── SshKeys.vue
│ │ │ └── Settings.vue
│ │ ├── components/ # 通用组件
│ │ │ ├── ServerForm.vue
│ │ │ ├── RepoSyncStatus.vue
│ │ │ └── CommitHistory.vue
│ │ ├── api/ # API 调用封装
│ │ │ ├── index.js # Axios 实例,拦截器
│ │ │ ├── servers.js
│ │ │ ├── repos.js
│ │ │ ├── sshKeys.js
│ │ │ └── syncLogs.js
│ │ └── stores/ # Pinia 状态管理
│ │ ├── servers.js
│ │ ├── repos.js
│ │ └── app.js
│ ├── index.html
│ ├── vite.config.js
│ └── package.json
└── data/ # 运行时数据目录
├── git_manager.db # SQLite 数据库
├── ssh_keys/ # SSH 密钥文件(加密存储)
└── repos/ # 本地仓库镜像
└── {server_name}/
└── {repo_full_name}/
```
## API 设计
### 设计原则
- 统一 `/api/v1/` 前缀,版本化管理
- 标准 JSON 响应格式:`{"code": 0, "data": {}, "message": "success"}`
- Bearer Token 认证:`Authorization: Bearer <token>`
- Swagger 文档自动生成(`/docs`
### 接口列表
#### 服务器管理
| 方法 | 路径 | 说明 |
|------|------|------|
| POST | `/api/v1/servers` | 添加 Gitea 服务器 |
| GET | `/api/v1/servers` | 服务器列表 |
| GET | `/api/v1/servers/{id}` | 服务器详情 |
| PUT | `/api/v1/servers/{id}` | 更新服务器配置 |
| DELETE | `/api/v1/servers/{id}` | 删除服务器 |
| POST | `/api/v1/servers/{id}/test` | 测试连接 + SSH 密钥有效性 |
| POST | `/api/v1/servers/{id}/sync` | 触发该服务器所有仓库同步 |
#### 仓库管理
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/api/v1/servers/{id}/repos` | 获取服务器仓库列表(调 Gitea API |
| GET | `/api/v1/repos` | 所有本地已同步的仓库 |
| GET | `/api/v1/repos/{id}` | 仓库详情 |
| POST | `/api/v1/repos/{id}/sync` | 手动同步单个仓库 |
| GET | `/api/v1/repos/{id}/commits` | 仓库提交历史 |
#### SSH 密钥管理
| 方法 | 路径 | 说明 |
|------|------|------|
| POST | `/api/v1/ssh-keys` | 上传私钥 |
| GET | `/api/v1/ssh-keys` | 密钥列表 |
| DELETE | `/api/v1/ssh-keys/{id}` | 删除密钥 |
| POST | `/api/v1/ssh-keys/{id}/test` | 测试密钥有效性 |
#### 同步记录
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/api/v1/sync-logs` | 同步日志列表(支持分页和状态筛选) |
| GET | `/api/v1/sync-logs/{id}` | 同步详情 |
#### 调度配置
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/api/v1/schedules` | 查看调度配置 |
| PUT | `/api/v1/schedules` | 修改调度配置 |
#### 系统状态
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/api/v1/status` | 系统运行状态、磁盘空间、仓库统计 |
## 数据模型
### ER 关系
```
ssh_keys 1 ──── N servers
servers 1 ──── N repos
repos 1 ──── N sync_logs
```
### ssh_keys 表
| 字段 | 类型 | 说明 |
|------|------|------|
| id | INTEGER PK | 自增主键 |
| name | VARCHAR(100) | 密钥名称 |
| private_key | TEXT | AES-256 加密后的私钥内容 |
| fingerprint | VARCHAR(64) | 密钥指纹(用于展示) |
| created_at | DATETIME | 创建时间 |
### servers 表
| 字段 | 类型 | 说明 |
|------|------|------|
| id | INTEGER PK | 自增主键 |
| name | VARCHAR(100) | 服务器名称 |
| url | VARCHAR(500) | Gitea 服务器地址(如 https://gitea.example.com |
| api_token | TEXT | Gitea API TokenAES-256 加密存储) |
| ssh_key_id | INTEGER FK | 关联 ssh_keys.id |
| sync_enabled | BOOLEAN | 是否启用自动同步 |
| schedule_cron | VARCHAR(50) | 定时表达式(如 `0 */2 * * *` |
| local_path | VARCHAR(500) | 本地仓库存储路径 |
| status | VARCHAR(20) | 连接状态connected / disconnected / untested |
| created_at | DATETIME | 创建时间 |
| updated_at | DATETIME | 更新时间 |
### repos 表
| 字段 | 类型 | 说明 |
|------|------|------|
| id | INTEGER PK | 自增主键 |
| server_id | INTEGER FK | 关联 servers.id |
| name | VARCHAR(200) | 仓库名称 |
| full_name | VARCHAR(300) | 完整路径owner/repo |
| clone_url | VARCHAR(500) | SSH 克隆地址 |
| local_path | VARCHAR(500) | 本地镜像路径 |
| last_sync_at | DATETIME | 最后同步时间 |
| status | VARCHAR(20) | 状态pending / syncing / synced / failed |
| created_at | DATETIME | 创建时间 |
### sync_logs 表
| 字段 | 类型 | 说明 |
|------|------|------|
| id | INTEGER PK | 自增主键 |
| repo_id | INTEGER FK | 关联 repos.id |
| status | VARCHAR(20) | synced / failed |
| started_at | DATETIME | 开始时间 |
| finished_at | DATETIME | 结束时间 |
| commits_count | INTEGER | 本次同步新增提交数 |
| error_msg | TEXT | 失败时的错误信息 |
| created_at | DATETIME | 创建时间 |
## 同步流程
### 首次同步clone
```
用户添加服务器 → 调 Gitea API 获取仓库列表
→ 对每个仓库:
1. 生成 local_path: data/repos/{server_name}/{full_name}
2. 解密 SSH 私钥到内存
3. git clone --mirror <clone_url> <local_path>
4. 记录 sync_log状态 synced提交数
5. 更新 repo.status = synced
```
### 增量同步fetch
```
定时任务触发 / 手动触发
→ 遍历该服务器下所有仓库:
1. repo.status = syncing
2. 解密 SSH 私钥到内存
3. git fetch --all
4. 对比 refs 变化,计算新增提交数
5. 记录 sync_log
6. repo.status = synced / failed
```
## 前端页面
### 页面布局
```
┌──────────────────────────────────────────────────┐
│ Git Manager [状态] [≡] │
├────────┬─────────────────────────────────────────┤
│ 仪表盘 │ │
│ 服务器 │ 主内容区 │
│ 仓库 │ │
│ 同步记录 │ │
│ SSH密钥 │ │
│ 系统设置 │ │
├────────┴─────────────────────────────────────────┤
│ 上次同步: 2026-03-30 14:00 | 下次: 16:00 | 共 42 仓库 │
└──────────────────────────────────────────────────┘
```
### 页面功能
**仪表盘:** 服务器数量、仓库总数、同步状态统计synced/failed/syncing、最近 5 条同步记录、磁盘占用。
**服务器管理:** 添加/编辑/删除 Gitea 服务器,配置 URL、API Token、关联 SSH 密钥、同步计划Cron 表达式),"测试连接"按钮验证配置有效性。
**仓库列表:** 按服务器分组展示,显示同步状态(标签颜色区分)、最后同步时间。支持搜索过滤。每行有"手动同步"按钮。
**同步记录:** 时间线形式展示同步日志,支持按状态(成功/失败)筛选,展开查看新增提交数和错误详情。
**SSH 密钥:** 上传私钥文件或粘贴内容,显示密钥指纹和关联服务器数,删除时检查是否有关联服务器。
**系统设置:** API Token 管理、全局仓库存储根路径、调度全局开关(暂停/恢复所有定时任务)。
## 安全设计
| 项 | 方案 |
|----|------|
| 私钥存储 | AES-256 加密后存入 SQLite加密密钥从环境变量 `GM_ENCRYPT_KEY` 读取 |
| Gitea Token | 同样 AES-256 加密存储 |
| API 认证 | Bearer Token配置在环境变量 `GM_API_TOKEN` |
| SSH 连接 | paramiko 使用内存中的解密私钥,不写入磁盘临时文件 |
| 输入校验 | Pydantic 模型自动校验所有 API 入参 |
## 环境变量
| 变量 | 必填 | 默认值 | 说明 |
|------|------|--------|------|
| GM_ENCRYPT_KEY | 是 | - | AES-256 加密密钥32字节 |
| GM_API_TOKEN | 是 | - | API 认证 Token |
| GM_DATA_DIR | 否 | ./data | 数据存储根目录 |
| GM_HOST | 否 | 0.0.0.0 | 监听地址 |
| GM_PORT | 否 | 8000 | 监听端口 |
## 部署方式
```bash
# 安装后端依赖
pip install -r backend/requirements.txt
# 构建前端
cd frontend && npm install && npm run build
# 构建产物自动输出到 backend/app/static/
# 配置环境变量
export GM_ENCRYPT_KEY=<your-encryption-key>
export GM_API_TOKEN=<your-api-token>
# 初始化数据库
python -m backend.init_db
# 启动服务
uvicorn backend.app.main:app --host 0.0.0.0 --port 8000
```
## MVP 交付范围
### 包含
- 多 Gitea 服务器管理(增删改查)
- SSH 密钥上传和管理
- 拉取服务器所有仓库列表
- 仓库 clone/fetch 同步
- 手动触发同步
- 定时自动同步APScheduler + Cron
- 同步历史记录查看
- 提交历史查看
- REST API版本化供外部集成
- API Token 认证
- Vue 3 Web 管理界面
### 不包含(后续迭代)
- 多用户 / 权限系统
- Webhook 实时推送
- GitLab / GitHub 支持
- Docker 容器化部署
- 仓库内容浏览(文件树)