661 lines
9.0 KiB
Markdown
661 lines
9.0 KiB
Markdown
|
|
# API 接口文档
|
|||
|
|
|
|||
|
|
## 概述
|
|||
|
|
|
|||
|
|
PR-Helper 提供 RESTful JSON API 和 SSE 流式端点。所有 API 都需要认证(除登录/注册外)。
|
|||
|
|
|
|||
|
|
## 认证
|
|||
|
|
|
|||
|
|
### 会话认证
|
|||
|
|
|
|||
|
|
- 基于 Cookie 的会话
|
|||
|
|
- Cookie 名称: `pr_session`
|
|||
|
|
- 有效期: 7 天
|
|||
|
|
- HttpOnly: true
|
|||
|
|
|
|||
|
|
### 认证头
|
|||
|
|
|
|||
|
|
所有受保护的 API 需要有效的会话 Cookie。
|
|||
|
|
|
|||
|
|
## 公开端点
|
|||
|
|
|
|||
|
|
### 登录页面
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
GET /login
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
返回登录页面 HTML。
|
|||
|
|
|
|||
|
|
### 注册页面
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
GET /register
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
返回注册页面 HTML。
|
|||
|
|
|
|||
|
|
### 用户登录
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
POST /api/auth/login
|
|||
|
|
Content-Type: application/json
|
|||
|
|
|
|||
|
|
{
|
|||
|
|
"email": "user@example.com",
|
|||
|
|
"password": "password123"
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**响应**:
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"message": "登录成功"
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**错误**:
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"error": "邮箱或密码错误"
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 用户注册
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
POST /api/auth/register
|
|||
|
|
Content-Type: application/json
|
|||
|
|
|
|||
|
|
{
|
|||
|
|
"email": "user@example.com",
|
|||
|
|
"password": "password123"
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**响应**:
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"message": "注册成功"
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 用户登出
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
POST /api/auth/logout
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**响应**:
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"message": "已登出"
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 页面端点
|
|||
|
|
|
|||
|
|
### 首页
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
GET /
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
返回首页 HTML(仓库列表)。
|
|||
|
|
|
|||
|
|
### 仓库详情页
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
GET /repo/:id
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
返回仓库详情页 HTML(Git 图形、差异查看器等)。
|
|||
|
|
|
|||
|
|
### 设置页
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
GET /settings
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
返回设置页面 HTML。
|
|||
|
|
|
|||
|
|
## 仓库管理 API
|
|||
|
|
|
|||
|
|
### 获取仓库列表
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
GET /api/repos
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**响应**:
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
[
|
|||
|
|
{
|
|||
|
|
"id": 1,
|
|||
|
|
"url": "https://github.com/user/repo.git",
|
|||
|
|
"local_path": "data/repos/user_repo.git_1234567890",
|
|||
|
|
"size_bytes": 1048576,
|
|||
|
|
"cloned_at": "2024-01-01T00:00:00Z",
|
|||
|
|
"last_used": "2024-01-01T12:00:00Z",
|
|||
|
|
"auth_type": "none"
|
|||
|
|
}
|
|||
|
|
]
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 克隆仓库
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
POST /api/repos
|
|||
|
|
Content-Type: application/json
|
|||
|
|
|
|||
|
|
{
|
|||
|
|
"url": "https://github.com/user/repo.git",
|
|||
|
|
"auth_type": "none",
|
|||
|
|
"credential": ""
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**认证类型**:
|
|||
|
|
|
|||
|
|
- `none`: 无认证
|
|||
|
|
- `https`: HTTPS 基本认证(credential = "username:password")
|
|||
|
|
|
|||
|
|
**响应 (SSE 流)**:
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
event: progress
|
|||
|
|
data: {"step":"开始克隆","current":0,"total":0}
|
|||
|
|
|
|||
|
|
event: progress
|
|||
|
|
data: {"step":"克隆完成","current":1,"total":1}
|
|||
|
|
|
|||
|
|
event: done
|
|||
|
|
data: {"repo_id":1,"branches":["main","dev"],"tags":["v1.0"],"commit_num":100}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 删除仓库
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
DELETE /api/repos/:id
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**响应**:
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"message": "仓库已删除"
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 清理仓库缓存
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
POST /api/repos/:id/cleanup
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
删除并重新克隆仓库。
|
|||
|
|
|
|||
|
|
**响应 (SSE 流)**:
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
event: progress
|
|||
|
|
data: {"step":"清理完成"}
|
|||
|
|
|
|||
|
|
event: progress
|
|||
|
|
data: {"step":"开始克隆"}
|
|||
|
|
|
|||
|
|
event: done
|
|||
|
|
data: {"repo_id":1}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 拉取更新
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
POST /api/repos/:id/pull
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**响应 (SSE 流)**:
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
event: progress
|
|||
|
|
data: {"step":"拉取完成"}
|
|||
|
|
|
|||
|
|
event: done
|
|||
|
|
data: {"commit_num":105,"branches":["main","dev"]}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 获取 Git 图形
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
GET /api/repos/:id/graph
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**响应**:
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"commits": [
|
|||
|
|
{
|
|||
|
|
"hash": "abc123...",
|
|||
|
|
"short_hash": "abc1234",
|
|||
|
|
"message": "feat: add new feature",
|
|||
|
|
"author": "John Doe",
|
|||
|
|
"email": "john@example.com",
|
|||
|
|
"timestamp": "2024-01-01T12:00:00Z",
|
|||
|
|
"parent_ids": ["def456..."]
|
|||
|
|
}
|
|||
|
|
],
|
|||
|
|
"refs": [
|
|||
|
|
{
|
|||
|
|
"name": "main",
|
|||
|
|
"hash": "abc123...",
|
|||
|
|
"is_head": true,
|
|||
|
|
"is_tag": false
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"name": "v1.0",
|
|||
|
|
"hash": "abc123...",
|
|||
|
|
"is_head": false,
|
|||
|
|
"is_tag": true
|
|||
|
|
}
|
|||
|
|
],
|
|||
|
|
"edges": [
|
|||
|
|
{
|
|||
|
|
"source": "abc123...",
|
|||
|
|
"target": "def456..."
|
|||
|
|
}
|
|||
|
|
]
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 获取引用列表
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
GET /api/repos/:id/refs
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**响应**:
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
[
|
|||
|
|
{
|
|||
|
|
"name": "main",
|
|||
|
|
"hash": "abc123...",
|
|||
|
|
"is_head": true,
|
|||
|
|
"is_tag": false
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"name": "v1.0",
|
|||
|
|
"hash": "abc123...",
|
|||
|
|
"is_head": false,
|
|||
|
|
"is_tag": true
|
|||
|
|
}
|
|||
|
|
]
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 获取提交列表
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
GET /api/repos/:id/commits?ref=main&limit=50
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**参数**:
|
|||
|
|
|
|||
|
|
| 参数 | 类型 | 必需 | 说明 |
|
|||
|
|
|------|------|------|------|
|
|||
|
|
| ref | string | 否 | 分支/标签名(默认 HEAD) |
|
|||
|
|
| limit | int | 否 | 最大提交数(默认 50) |
|
|||
|
|
|
|||
|
|
**响应**:
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
[
|
|||
|
|
{
|
|||
|
|
"hash": "abc123...",
|
|||
|
|
"short_hash": "abc1234",
|
|||
|
|
"message": "feat: add new feature",
|
|||
|
|
"author": "John Doe",
|
|||
|
|
"email": "john@example.com",
|
|||
|
|
"timestamp": "2024-01-01T12:00:00Z",
|
|||
|
|
"parent_ids": ["def456..."]
|
|||
|
|
}
|
|||
|
|
]
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 获取差异
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
GET /api/repos/:id/diff?base=main&head=feature
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**参数**:
|
|||
|
|
|
|||
|
|
| 参数 | 类型 | 必需 | 说明 |
|
|||
|
|
|------|------|------|------|
|
|||
|
|
| base | string | 是 | 基准分支/标签/提交 |
|
|||
|
|
| head | string | 是 | 目标分支/标签/提交 |
|
|||
|
|
| per_file | bool | 否 | 是否按文件返回 |
|
|||
|
|
|
|||
|
|
**响应 (统一差异)**:
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"diff": "diff --git a/main.go b/main.go\n..."
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**响应 (按文件)**:
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
[
|
|||
|
|
{
|
|||
|
|
"filename": "main.go",
|
|||
|
|
"patch": "diff --git a/main.go b/main.go\n..."
|
|||
|
|
}
|
|||
|
|
]
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## PR 生成 API
|
|||
|
|
|
|||
|
|
### 生成 PR 描述
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
POST /api/repos/:id/generate
|
|||
|
|
Content-Type: application/json
|
|||
|
|
|
|||
|
|
{
|
|||
|
|
"base": "main",
|
|||
|
|
"head": "feature"
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**响应 (SSE 流)**:
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
event: content
|
|||
|
|
data: {"content":"# 标题\n\n"}
|
|||
|
|
|
|||
|
|
event: content
|
|||
|
|
data: {"content":"**类型**: feat\n\n"}
|
|||
|
|
|
|||
|
|
event: content
|
|||
|
|
data: {"content":"## 概述\n..."}
|
|||
|
|
|
|||
|
|
event: done
|
|||
|
|
data: {"content":""}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 代码审查 API
|
|||
|
|
|
|||
|
|
### 执行代码审查
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
POST /api/repos/:id/review
|
|||
|
|
Content-Type: application/json
|
|||
|
|
|
|||
|
|
{
|
|||
|
|
"base": "main",
|
|||
|
|
"head": "feature",
|
|||
|
|
"top_n": 20,
|
|||
|
|
"concurrency": 5
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**参数**:
|
|||
|
|
|
|||
|
|
| 参数 | 类型 | 必需 | 说明 |
|
|||
|
|
|------|------|------|------|
|
|||
|
|
| base | string | 是 | 基准分支 |
|
|||
|
|
| head | string | 是 | 目标分支 |
|
|||
|
|
| top_n | int | 否 | 审查文件数上限(默认 20) |
|
|||
|
|
| concurrency | int | 否 | 并发数(默认 5) |
|
|||
|
|
|
|||
|
|
**响应 (SSE 流)**:
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
event: start
|
|||
|
|
data: {"total_files":30,"reviewed_files":20,"top_n":20}
|
|||
|
|
|
|||
|
|
event: file_start
|
|||
|
|
data: {"file":"main.go","index":1,"total":20}
|
|||
|
|
|
|||
|
|
event: content
|
|||
|
|
data: {"content":"..."}
|
|||
|
|
|
|||
|
|
event: suggestion
|
|||
|
|
data: {"file":"main.go","severity":"warning","content":"..."}
|
|||
|
|
|
|||
|
|
event: file_end
|
|||
|
|
data: {"file":"main.go"}
|
|||
|
|
|
|||
|
|
event: summary
|
|||
|
|
data: {"score":7,"overall":"...","findings":"...","recommendations":"..."}
|
|||
|
|
|
|||
|
|
event: analysis_saved
|
|||
|
|
data: {"analysis_id":1}
|
|||
|
|
|
|||
|
|
event: done
|
|||
|
|
data: {"content":""}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 获取审查历史
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
GET /api/repos/:id/review/analyses
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**响应**:
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
[
|
|||
|
|
{
|
|||
|
|
"id": 1,
|
|||
|
|
"base_ref": "main",
|
|||
|
|
"head_ref": "feature",
|
|||
|
|
"created_at": "2024-01-01T12:00:00Z"
|
|||
|
|
}
|
|||
|
|
]
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 获取单个审查结果
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
GET /api/repos/:id/review/analyses/:aid
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**响应**:
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"id": 1,
|
|||
|
|
"base_ref": "main",
|
|||
|
|
"head_ref": "feature",
|
|||
|
|
"created_at": "2024-01-01T12:00:00Z",
|
|||
|
|
"result": {
|
|||
|
|
"file_reviews": [...],
|
|||
|
|
"summary": {...},
|
|||
|
|
"top_n": 20
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 保存审查笔记
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
POST /api/repos/:id/review/notes
|
|||
|
|
Content-Type: application/json
|
|||
|
|
|
|||
|
|
{
|
|||
|
|
"analysis_id": 1,
|
|||
|
|
"scope": "file",
|
|||
|
|
"scope_key": "main.go",
|
|||
|
|
"content": "这个文件需要重构"
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**scope 类型**:
|
|||
|
|
|
|||
|
|
| scope | scope_key | 说明 |
|
|||
|
|
|-------|-----------|------|
|
|||
|
|
| overall | (空) | 整体笔记 |
|
|||
|
|
| file | 文件名 | 文件级笔记 |
|
|||
|
|
| suggestion | 建议 ID | 建议级笔记 |
|
|||
|
|
|
|||
|
|
**响应**:
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"id": 1,
|
|||
|
|
"analysis_id": 1,
|
|||
|
|
"scope": "file",
|
|||
|
|
"scope_key": "main.go",
|
|||
|
|
"content": "这个文件需要重构",
|
|||
|
|
"created_at": "2024-01-01T12:00:00Z",
|
|||
|
|
"updated_at": "2024-01-01T12:00:00Z"
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 获取审查笔记
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
GET /api/repos/:id/review/notes?analysis_id=1&scope=file
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**参数**:
|
|||
|
|
|
|||
|
|
| 参数 | 类型 | 必需 | 说明 |
|
|||
|
|
|------|------|------|------|
|
|||
|
|
| analysis_id | int | 是 | 分析 ID |
|
|||
|
|
| scope | string | 否 | 过滤作用域 |
|
|||
|
|
|
|||
|
|
**响应**:
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
[
|
|||
|
|
{
|
|||
|
|
"id": 1,
|
|||
|
|
"analysis_id": 1,
|
|||
|
|
"scope": "file",
|
|||
|
|
"scope_key": "main.go",
|
|||
|
|
"content": "这个文件需要重构",
|
|||
|
|
"created_at": "2024-01-01T12:00:00Z",
|
|||
|
|
"updated_at": "2024-01-01T12:00:00Z"
|
|||
|
|
}
|
|||
|
|
]
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 设置 API
|
|||
|
|
|
|||
|
|
### 获取设置
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
GET /api/settings
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**响应**:
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"llm.endpoint": "https://api.deepseek.com",
|
|||
|
|
"llm.api_key": "sk-...",
|
|||
|
|
"llm.model": "deepseek-v4-pro",
|
|||
|
|
"review.top_n": "20",
|
|||
|
|
"review.concurrency": "5",
|
|||
|
|
"cache.max_age_days": "7",
|
|||
|
|
"cache.max_size_mb": "5000"
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 更新设置
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
PUT /api/settings
|
|||
|
|
Content-Type: application/json
|
|||
|
|
|
|||
|
|
{
|
|||
|
|
"llm.endpoint": "https://api.openai.com",
|
|||
|
|
"llm.api_key": "sk-...",
|
|||
|
|
"llm.model": "gpt-4"
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**响应**:
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"message": "设置已更新"
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 错误响应
|
|||
|
|
|
|||
|
|
### 格式
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"error": "错误信息"
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 常见状态码
|
|||
|
|
|
|||
|
|
| 状态码 | 说明 |
|
|||
|
|
|--------|------|
|
|||
|
|
| 200 | 成功 |
|
|||
|
|
| 400 | 请求参数错误 |
|
|||
|
|
| 401 | 未认证 |
|
|||
|
|
| 404 | 资源不存在 |
|
|||
|
|
| 500 | 服务器内部错误 |
|
|||
|
|
|
|||
|
|
## SSE 协议
|
|||
|
|
|
|||
|
|
### 事件格式
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
event: {event_name}
|
|||
|
|
data: {json_data}
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
- 每个事件以 `event:` 开头
|
|||
|
|
- 数据以 `data:` 开头
|
|||
|
|
- 事件之间用空行分隔
|
|||
|
|
- 数据必须是有效的 JSON
|
|||
|
|
|
|||
|
|
### 客户端实现
|
|||
|
|
|
|||
|
|
```javascript
|
|||
|
|
SSE.post(url, body, {
|
|||
|
|
eventName: (data) => {
|
|||
|
|
// 处理事件
|
|||
|
|
},
|
|||
|
|
error: (data) => {
|
|||
|
|
// 处理错误
|
|||
|
|
},
|
|||
|
|
done: () => {
|
|||
|
|
// 流结束
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 中断请求
|
|||
|
|
|
|||
|
|
```javascript
|
|||
|
|
const controller = SSE.post(url, body, handlers);
|
|||
|
|
// 中断
|
|||
|
|
controller.abort();
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 限流
|
|||
|
|
|
|||
|
|
当前无速率限制。建议在反向代理层实现限流。
|
|||
|
|
|
|||
|
|
## CORS
|
|||
|
|
|
|||
|
|
当前不支持跨域请求。仅限同源访问。
|