# 機能設計書 28-GitHub連携

## 概要

本ドキュメントは、Liam DBシステムにおけるGitHub連携機能の設計を記述するものである。本機能は、GitHub App認証とOAuth認証を使用してGitHubリポジトリへのアクセスを実現し、スキーマファイルの取得やリポジトリ情報の取得を可能にする。

### 本機能の処理概要

GitHub連携機能は、2つの認証方式を提供する：
1. **GitHub App認証**：サーバーサイドでinstallation_idを使用してリポジトリにアクセス
2. **OAuth認証**：ユーザーのアクセストークンを使用してユーザー権限でアクセス

**業務上の目的・背景**：Liam DBはデータベーススキーマをGitHubリポジトリで管理することを前提としている。GitHub連携により、バージョン管理されたスキーマファイルを取得し、AIエージェントによる設計支援を実現する。

**機能の利用シーン**：
- プロジェクト作成時にGitHub Appインストール一覧を取得
- リポジトリからスキーマファイルを取得
- プロジェクト一覧で最終コミット情報を表示
- プルリクエスト詳細を取得してレビュー支援

**主要な処理内容**：
1. GitHub App認証によるOctokit生成（createOctokit）
2. リポジトリ一覧取得（getRepositoriesByInstallationId）
3. ファイル内容取得（getFileContent）
4. ブランチ一覧取得（getRepositoryBranches）
5. 最終コミット取得（getLastCommit）
6. OAuthトークン管理（Cookie暗号化保存、リフレッシュ）
7. ユーザーインストール一覧取得（getInstallations）

**関連システム・外部連携**：GitHub API（REST API）、@octokit/rest、@octokit/auth-app

**権限による制御**：GitHub Appがインストールされたリポジトリのみアクセス可能。OAuthトークンはユーザー権限に依存。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 6 | プロジェクト一覧画面 | 関連画面 | 最終コミット情報表示 |
| 7 | プロジェクト新規作成画面 | 主画面 | リポジトリ・ブランチ選択 |
| 14 | デザインセッション詳細画面 | 関連画面 | スキーマファイル取得 |

## 機能種別

外部連携 / API統合

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| installationId | number | Yes | GitHub App インストール識別子 | - |
| owner | string | Yes | リポジトリオーナー | - |
| repo | string | Yes | リポジトリ名 | - |
| filePath | string | Yes（ファイル取得時） | ファイルパス | - |
| ref | string | Yes（ファイル取得時） | ブランチ名またはコミットSHA | - |
| token | string | Yes（OAuth時） | ユーザーアクセストークン | - |

### 入力データソース

- Cookie（暗号化されたOAuthトークン）
- 環境変数（GITHUB_APP_ID、GITHUB_PRIVATE_KEY、GITHUB_CLIENT_ID、GITHUB_CLIENT_SECRET）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| repositories | Repository[] | リポジトリ一覧 |
| content | string | ファイル内容 |
| sha | string | ファイルまたはコミットのSHA |
| branches | GitHubBranch[] | ブランチ一覧 |
| commit | CommitInfo | コミット情報（sha, date, message, author） |
| installations | Installation[] | GitHub Appインストール一覧 |

### 出力先

- API レスポンス（JSON）
- Cookie（更新されたトークン）

## 処理フロー

### 処理シーケンス

```
1. GitHub App認証
   └─ createOctokit(installationId)
   └─ createAppAuth で認証設定
   └─ Octokit インスタンス生成
2. リポジトリ操作
   └─ getRepositoriesByInstallationId / getFileContent / getRepositoryBranches
3. OAuth認証
   └─ getUserAccessToken() でCookieからトークン取得
   └─ 有効期限切れの場合はnull返却（クライアントでリフレッシュ判断）
4. トークンリフレッシュ
   └─ POST /api/github/token/refresh
   └─ readRefreshToken() でリフレッシュトークン取得
   └─ GitHub OAuth APIでトークンリフレッシュ
   └─ writeAccessToken / writeRefreshToken でCookie更新
```

### フローチャート

```mermaid
flowchart TD
    A[GitHub API呼び出し] --> B{認証方式}
    B -->|App認証| C[createOctokit]
    B -->|OAuth| D[getUserAccessToken]
    C --> E[GITHUB_APP_ID + PRIVATE_KEY]
    E --> F[createAppAuth]
    F --> G[Octokit]
    G --> H[API呼び出し]
    D --> I{トークン有効?}
    I -->|Yes| J[Octokit with token]
    I -->|No| K[リフレッシュ必要]
    K --> L[POST /api/github/token/refresh]
    L --> M[GitHub OAuth API]
    M --> N[新トークン取得]
    N --> O[Cookie更新]
    O --> J
    J --> H
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-28-01 | トークン有効期限 | アクセストークン8時間、リフレッシュトークン6ヶ月 | OAuth認証時 |
| BR-28-02 | 安全マージン | トークン有効期限の3分前からリフレッシュ対象 | トークン検証時 |
| BR-28-03 | 暗号化Cookie | トークンはAES-GCMで暗号化してCookie保存 | トークン保存時 |
| BR-28-04 | ファイルサイズ制限 | ダウンロード時のオプショナルサイズ制限 | downloadFileContent |
| BR-28-05 | タイムアウト | ファイルダウンロードは10秒タイムアウト | downloadFileContent |

### 計算ロジック

- 有効期限切れ判定: `Date.now() + SAFETY_MS (3分) >= expiresAtのtimestamp`
- Cookie有効期間: `maxAge = (expiresAt - now) / 1000` 秒

## データベース操作仕様

### 操作別データベース影響一覧

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| - | - | - | GitHub連携機能自体はDB操作なし |

### テーブル別操作詳細

本機能は外部API（GitHub）との連携のみを行い、直接的なデータベース操作は行わない。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| server_misconfigured | 設定エラー | GITHUB_CLIENT_ID/SECRET未設定 | 500エラー返却 |
| refresh_failed | リフレッシュエラー | GitHub OAuthリフレッシュ失敗 | 401エラー返却 |
| file_not_found | ファイルエラー | ファイルが存在しない | null返却（ログ出力） |
| request_timeout | タイムアウト | ファイルダウンロード10秒超過 | Error返却 |
| file_too_large | サイズエラー | ファイルサイズ制限超過 | Error返却 |

### リトライ仕様

- API呼び出しにリトライなし
- トークンリフレッシュ失敗時は401を返し、再ログインを促す

## トランザクション仕様

本機能は外部API連携のため、トランザクション制御は行わない。

## パフォーマンス要件

- getRepositoriesByInstallationId: per_page=100で取得
- getRepositoryBranches: paginateで全ブランチ取得（per_page=100）
- downloadFileContent: 10秒タイムアウト、オプションでサイズ制限

## セキュリティ考慮事項

- OAuthトークンはAES-GCMで暗号化してHttpOnly Cookieに保存
- トークンのkey_idで暗号化キーをローテーション可能
- 環境変数でGitHub App秘密鍵を管理
- ダウンロードURLはraw.githubusercontent.comのみ許可

## 備考

GitHub App認証とOAuth認証は用途により使い分け：
- App認証：サーバーサイドでのリポジトリアクセス（インストールされたリポジトリ限定）
- OAuth：ユーザー権限での操作（インストール一覧取得等）

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: GitHub App認証を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | config.ts | `frontend/internal-packages/github/src/config.ts` | 環境変数の検証 |
| 1-2 | api.server.ts | `frontend/internal-packages/github/src/api.server.ts` | App認証とAPI呼び出し |

**主要処理フロー**:
- **11-22行目（api.server.ts）**: createOctokitでApp認証Octokitインスタンス生成
- **41-49行目**: getRepositoriesByInstallationId
- **54-106行目**: getFileContent（ファイル取得とBase64デコード）
- **108-131行目**: getRepositoryBranches
- **137-183行目**: getLastCommit

#### Step 2: OAuthトークン管理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | cookie.ts | `frontend/apps/app/libs/github/cookie.ts` | トークンの暗号化・復号化・Cookie操作 |
| 2-2 | token.ts | `frontend/apps/app/libs/github/token.ts` | アクセストークン取得 |

**主要処理フロー**:
- **44-57行目（cookie.ts）**: packCookieValueでAES-GCM暗号化
- **59-78行目**: unpackCookieValueで復号化
- **80-100行目**: readTokenFromでCookieから読み取り
- **102-128行目**: writeTokenToでCookie書き込み
- **143-154行目**: readAccessToken、readRefreshToken
- **156-168行目**: writeAccessToken、writeRefreshToken
- **170-173行目**: clearTokens

#### Step 3: トークンリフレッシュを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | route.ts | `frontend/apps/app/app/api/github/token/refresh/route.ts` | リフレッシュAPIエンドポイント |

**主要処理フロー**:
- **56-108行目**: refreshAccessToken関数（GitHub OAuth APIでリフレッシュ）
- **110-152行目**: handler関数（リフレッシュトークン読み取り → リフレッシュ → Cookie更新）

#### Step 4: OAuth APIを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | api.oauth.ts | `frontend/internal-packages/github/src/api.oauth.ts` | ユーザートークンでのAPI呼び出し |
| 4-2 | types.ts | `frontend/internal-packages/github/src/types.ts` | 型定義 |

**主要処理フロー**:
- **26-57行目（api.oauth.ts）**: getInstallationsでユーザーのインストール一覧取得

### プログラム呼び出し階層図

```
[GitHub App認証]
createOctokit(installationId)
    │
    ├─ createAppAuth
    │      ├─ GITHUB_APP_ID
    │      └─ GITHUB_PRIVATE_KEY
    │
    └─ Octokit (authenticated)
           │
           ├─ getRepositoriesByInstallationId
           │      └─ GET /installation/repositories
           │
           ├─ getFileContent
           │      └─ repos.getContent
           │
           ├─ getRepositoryBranches
           │      ├─ repos.get
           │      └─ repos.listBranches (paginate)
           │
           ├─ getLastCommit
           │      └─ repos.listCommits
           │
           └─ getPullRequestDetails
                  └─ pulls.get

[OAuth認証]
getUserAccessToken
    │
    └─ readAccessToken
           │
           └─ readTokenFrom(ACCESS_COOKIE)
                  │
                  ├─ cookies().get
                  │
                  └─ unpackCookieValue
                         └─ decryptAesGcm

[トークンリフレッシュ]
POST /api/github/token/refresh
    │
    ├─ readRefreshToken
    │
    ├─ refreshAccessToken
    │      └─ fetch GITHUB_TOKEN_URL
    │
    ├─ writeAccessToken
    │      └─ packCookieValue → encryptAesGcm
    │
    └─ writeRefreshToken
```

### データフロー図

```
[GitHub App認証フロー]
installationId ──────────▶ createOctokit
       │                        │
       │                        ├─ GITHUB_APP_ID
       │                        ├─ GITHUB_PRIVATE_KEY
       │                        │
       │                        └─ Octokit (authenticated)
       │                               │
       └───────────────────────────────▶ GitHub API

[OAuth トークンフロー]
Cookie (liam_at) ────────▶ readAccessToken
       │                        │
       │                        ├─ unpackCookieValue
       │                        │      └─ decryptAesGcm
       │                        │
       │                        └─ TokenPayload
       │                               │
       │                               ▼
       │                        isExpiringSoon?
       │                               │
       │                        ┌──────┴──────┐
       │                        ▼             ▼
       │                   有効         期限切れ
       │                        │             │
       │                        ▼             ▼
       └─────────────────▶ token         null (要リフレッシュ)

[リフレッシュフロー]
POST /api/github/token/refresh ──────▶ handler
       │                                   │
       │                                   ├─ readRefreshToken
       │                                   │
       │                                   ├─ refreshAccessToken
       │                                   │      └─ GitHub OAuth API
       │                                   │
       │                                   ├─ writeAccessToken
       │                                   │
       │                                   └─ writeRefreshToken
       │                                          │
       └──────────────────────────────────────────▶ 更新Cookie
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| api.server.ts | `frontend/internal-packages/github/src/api.server.ts` | ソース | GitHub App認証API |
| api.oauth.ts | `frontend/internal-packages/github/src/api.oauth.ts` | ソース | OAuth API |
| config.ts | `frontend/internal-packages/github/src/config.ts` | ソース | 環境変数検証 |
| types.ts | `frontend/internal-packages/github/src/types.ts` | 型定義 | GitHub型定義 |
| cookie.ts | `frontend/apps/app/libs/github/cookie.ts` | ソース | トークンCookie管理 |
| token.ts | `frontend/apps/app/libs/github/token.ts` | ソース | アクセストークン取得 |
| route.ts | `frontend/apps/app/app/api/github/token/refresh/route.ts` | ソース | リフレッシュAPI |
