Skip to content

集成 API

GitHub 集成管理相关的 API 端点。

端点概览

方法端点描述认证
GET/integrations/github/install-url获取安装 URL
GET/integrations/github/callback安装回调
GET/integrations/github/installations安装列表
GET/integrations/github/installations/{id}安装详情
POST/integrations/github/installations/{id}/sync同步安装
GET/integrations/github/installations/{id}/repositories已导入仓库
GET/integrations/github/installations/{id}/remote-repositories远程仓库列表

获取安装 URL

GET /api/v1/integrations/github/install-url

获取 GitHub App 安装链接。

请求

http
GET /api/v1/integrations/github/install-url?state=custom_state
Authorization: Bearer <token>

查询参数:

参数类型必填说明
statestringCSRF 防护状态码

响应

成功 (200 OK):

json
{
  "install_url": "https://github.com/apps/sentinel-app/installations/new"
}

使用流程

  1. 调用此端点获取安装 URL
  2. 重定向用户到该 URL
  3. 用户选择账户和仓库权限
  4. GitHub 重定向回 callback URL
  5. 调用 callback 端点完成安装

示例

typescript
const getInstallUrl = async () => {
  const response = await fetch('/api/v1/integrations/github/install-url', {
    headers: { 'Authorization': `Bearer ${token}` },
  });
  
  const data = await response.json();
  window.location.href = data.install_url;
};

安装回调

GET /api/v1/integrations/github/callback

处理 GitHub App 安装回调。

请求

http
GET /api/v1/integrations/github/callback?installation_id=12345678&setup_action=install
Authorization: Bearer <token>

查询参数:

参数类型必填说明
installation_idintegerGitHub 安装 ID
setup_actionstring操作类型 (install/update)

响应

成功 (200 OK):

json
{
  "installation": {
    "installation_id": 12345678,
    "account_login": "octocat",
    "account_type": "User",
    "target_type": "User",
    "repository_selection": "selected",
    "permissions": {
      "contents": "read",
      "pull_requests": "write",
      "metadata": "read"
    },
    "created_at": "2024-01-01T12:00:00Z",
    "updated_at": "2024-01-01T12:00:00Z"
  }
}

Account/Target 类型:

  • User - 个人账户
  • Organization - 组织账户

Repository 选择:

  • all - 所有仓库
  • selected - 选定的仓库

安装列表

GET /api/v1/integrations/github/installations

获取当前用户的 GitHub App 安装列表。

请求

http
GET /api/v1/integrations/github/installations?limit=20&offset=0
Authorization: Bearer <token>

查询参数:

参数类型默认值说明
limitinteger20每页数量
offsetinteger0偏移量

响应

成功 (200 OK):

json
{
  "installations": [
    {
      "installation_id": 12345678,
      "account_login": "octocat",
      "account_type": "User",
      "target_type": "User",
      "repository_selection": "selected",
      "permissions": {
        "contents": "read",
        "pull_requests": "write"
      },
      "suspended_at": null,
      "last_synced_at": "2024-01-01T12:00:00Z",
      "created_at": "2024-01-01T12:00:00Z",
      "updated_at": "2024-01-01T12:00:00Z"
    }
  ],
  "total": 1
}

示例

typescript
const listInstallations = async () => {
  const response = await fetch('/api/v1/integrations/github/installations', {
    headers: { 'Authorization': `Bearer ${token}` },
  });
  
  return response.json();
};

安装详情

GET /api/v1/integrations/github/installations/{id}

获取特定安装的详细信息。

请求

http
GET /api/v1/integrations/github/installations/12345678
Authorization: Bearer <token>

响应

成功 (200 OK):

返回安装对象,字段同安装列表。

错误响应:

  • 404 Not Found: 安装不存在或无权访问

同步安装

POST /api/v1/integrations/github/installations/{id}/sync

从 GitHub 同步安装信息(权限、仓库列表等)。

请求

http
POST /api/v1/integrations/github/installations/12345678/sync
Authorization: Bearer <token>

响应

成功 (200 OK):

返回更新后的安装对象。

使用场景

  • 用户在 GitHub 上修改了仓库权限
  • 添加或移除了仓库访问权限
  • 需要刷新安装状态

示例

typescript
const syncInstallation = async (installationId: number) => {
  const response = await fetch(
    `/api/v1/integrations/github/installations/${installationId}/sync`,
    {
      method: 'POST',
      headers: { 'Authorization': `Bearer ${token}` },
    }
  );
  
  return response.json();
};

已导入仓库

GET /api/v1/integrations/github/installations/{id}/repositories

获取该安装下已导入到 Sentinel 的仓库列表。

请求

http
GET /api/v1/integrations/github/installations/12345678/repositories?limit=50
Authorization: Bearer <token>

查询参数:

参数类型默认值说明
limitinteger50每页数量
offsetinteger0偏移量

响应

成功 (200 OK):

json
{
  "repositories": [
    {
      "repository_id": "650e8400-e29b-41d4-a716-446655440000",
      "project_id": "550e8400-e29b-41d4-a716-446655440000",
      "external_id": "123456789",
      "owner": "octocat",
      "name": "Hello-World",
      "full_name": "octocat/Hello-World",
      "clone_url": "https://github.com/octocat/Hello-World.git",
      "default_branch": "main",
      "visibility": "public",
      "enabled": true,
      "created_at": "2024-01-01T12:00:00Z"
    }
  ],
  "total": 1
}

远程仓库列表

GET /api/v1/integrations/github/installations/{id}/remote-repositories

从 GitHub 获取该安装可访问的仓库列表(未导入的)。

请求

http
GET /api/v1/integrations/github/installations/12345678/remote-repositories?page=1&per_page=30
Authorization: Bearer <token>

查询参数:

参数类型默认值说明
pageinteger1页码
per_pageinteger30每页数量 (最大100)

响应

成功 (200 OK):

json
{
  "repositories": [
    {
      "external_id": "123456789",
      "owner": "octocat",
      "name": "Hello-World",
      "full_name": "octocat/Hello-World",
      "clone_url": "https://github.com/octocat/Hello-World.git",
      "default_branch": "main",
      "visibility": "public",
      "private": false,
      "imported": false,
      "imported_repository_id": null
    },
    {
      "external_id": "987654321",
      "owner": "octocat",
      "name": "Spoon-Knife",
      "full_name": "octocat/Spoon-Knife",
      "clone_url": "https://github.com/octocat/Spoon-Knife.git",
      "default_branch": "main",
      "visibility": "public",
      "private": false,
      "imported": true,
      "imported_repository_id": "650e8400-e29b-41d4-a716-446655440000"
    }
  ],
  "total_count": 2
}

说明:

  • imported: 是否已导入到 Sentinel
  • imported_repository_id: 已导入时的仓库 ID

使用场景

用于仓库选择界面:

  1. 显示所有可访问的仓库
  2. 标记哪些已导入
  3. 允许用户选择导入新仓库

示例

typescript
const listRemoteRepositories = async (
  installationId: number,
  page = 1,
  perPage = 30
) => {
  const response = await fetch(
    `/api/v1/integrations/github/installations/${installationId}/remote-repositories?page=${page}&per_page=${perPage}`,
    {
      headers: { 'Authorization': `Bearer ${token}` },
    }
  );
  
  return response.json();
};

// 过滤未导入的仓库
const getUnimportedRepos = async (installationId: number) => {
  const { repositories } = await listRemoteRepositories(installationId);
  return repositories.filter(repo => !repo.imported);
};

数据模型

Installation 对象

typescript
interface GithubInstallation {
  installation_id: number;                    // GitHub 安装 ID
  account_login: string;                      // 账户名称
  account_type: 'User' | 'Organization';      // 账户类型
  target_type: 'User' | 'Organization';       // 目标类型
  repository_selection: 'all' | 'selected';   // 仓库选择
  permissions: Record<string, string>;        // 权限映射
  suspended_at?: string;                      // 暂停时间
  last_synced_at?: string;                    // 最后同步时间
  created_at: string;                         // 创建时间
  updated_at: string;                         // 更新时间
}

RemoteRepository 对象

typescript
interface RemoteRepository {
  external_id: string;              // GitHub 仓库 ID
  owner: string;                    // 所有者
  name: string;                     // 仓库名
  full_name: string;                // 完整名称
  clone_url: string;                // 克隆 URL
  default_branch: string;           // 默认分支
  visibility: string;               // 可见性
  private: boolean;                 // 是否私有
  imported: boolean;                // 是否已导入
  imported_repository_id?: string;  // 导入后的 ID
}

GitHub App 权限

Sentinel GitHub App 需要以下权限:

权限级别用途
Contentsread读取代码内容
Pull requestswrite评论 PR,发布审查结果
Metadataread读取仓库元数据
Webhooksread/write管理 Webhook

最佳实践

1. 定期同步

typescript
const syncAllInstallations = async () => {
  const { installations } = await listInstallations();
  
  await Promise.all(
    installations.map(inst =>
      syncInstallation(inst.installation_id)
    )
  );
};

// 每小时同步一次
setInterval(syncAllInstallations, 60 * 60 * 1000);

2. 仓库导入向导

typescript
const importWizard = async (installationId: number, projectId: string) => {
  // 1. 获取远程仓库
  const { repositories } = await listRemoteRepositories(installationId);
  
  // 2. 过滤未导入的
  const unimported = repositories.filter(r => !r.imported);
  
  // 3. 用户选择
  const selected = await showSelectionDialog(unimported);
  
  // 4. 批量导入
  for (const repo of selected) {
    await importRepository(projectId, {
      external_id: repo.external_id,
      installation_id: installationId,
      owner: repo.owner,
      name: repo.name,
      clone_url: repo.clone_url,
      default_branch: repo.default_branch,
      visibility: repo.visibility,
    });
  }
};

3. 权限检查

typescript
const hasRequiredPermissions = (installation: GithubInstallation) => {
  const required = ['contents', 'pull_requests', 'metadata'];
  
  return required.every(perm =>
    installation.permissions[perm] === 'read' ||
    installation.permissions[perm] === 'write'
  );
};

安装流程

1. 用户点击"Connect GitHub"

2. 获取安装 URL

3. 重定向到 GitHub

4. 用户授权 App

5. GitHub 回调到 callback URL

6. 前端调用 callback 端点

7. 安装记录创建完成

8. 显示可导入的仓库列表

相关端点


相关文档

Released under the MIT License.