# 機能設計書 40-Giteaバックエンド

## 概要

Gitea APIを使用したGitリポジトリ操作バックエンドについて記述する。セルフホスティング可能なGiteaインスタンスのGitリポジトリをCMSのデータストアとして使用する。

### 本機能の処理概要

GiteaバックエンドはDecap CMSのImplementationインターフェースを実装するクラスで、Gitea（軽量Gitホスティングサービス）のリポジトリをCMSのデータストアとして使用する。GitHubバックエンドと類似した構造を持ち、Gitea REST APIを通じてコンテンツ操作を行う。エディトリアルワークフローは非対応。

**業務上の目的・背景**：Giteaは軽量でセルフホスティング可能なGitサービスであり、プライベート環境やリソースの限られた環境でのGitホスティングに適している。Giteaバックエンドにより、これらの環境でもDecap CMSを利用できるようになる。

**機能の利用シーン**：config.ymlでbackend.name: 'gitea'と設定されている場合に使用。セルフホスティングのGiteaインスタンスとの連携に使用される。

**主要な処理内容**：
1. トークンベース認証
2. エントリのフォルダ/ファイルベース読み込み（カーソルベースページネーション付き）
3. エントリの保存・削除
4. メディアファイルの一覧取得・アップロード・削除
5. ファイルトラバーサル（ページネーション制御）

**関連システム・外部連携**：Gitea REST API（デフォルト: try.gitea.io/api/v1）

**権限による制御**：リポジトリへの書き込み権限チェック（hasWriteAccess）。collaborator permission APIで確認。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 1 | ログイン画面 | 主機能 | Gitea認証によるログイン |

## 機能種別

データ連携 / CRUD操作 / 外部API連携

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| config.backend.repo | string | Yes | Giteaリポジトリ（owner/repo形式） | 未設定時はエラー |
| config.backend.branch | string | No | ブランチ名（デフォルト: 'master'） | なし |
| config.backend.api_root | string | No | API Root（デフォルト: 'https://try.gitea.io/api/v1'） | なし |

### 入力データソース

- config.yml（バックエンド設定）
- Gitea API（リポジトリデータ）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| entries | ImplementationEntry[] | エントリデータ |
| media | MediaFile[] | メディアファイル一覧（id, name, size, displayURL, path, isDirectory） |
| user | User | 認証ユーザー情報（full_name→name、login、avatar_url、token） |

### 出力先

- Reduxストア経由でUI表示
- Gitea API（保存時）

## 処理フロー

### 処理シーケンス

```
1. 初期化
   ├─ 設定値の読み込み
   ├─ Editorial Workflow使用時はエラースロー
   └─ asyncLock初期化
2. 認証
   ├─ トークン設定
   ├─ APIインスタンス生成
   ├─ ユーザー情報取得
   ├─ 書き込み権限チェック（hasWriteAccess）
   └─ ユーザー情報返却（full_name→name変換）
3. エントリ操作
   ├─ entriesByFolder: ファイル一覧 → 拡張子フィルタ → ページネーション → エントリ読み込み
   ├─ allEntriesByFolder: 全エントリ読み込み
   ├─ entriesByFiles: ファイル指定読み込み（originRepoURL or repoURL）
   └─ persistEntry: ロック付き保存
4. メディア操作
   ├─ getMedia: ファイル一覧（isDirectory=tree対応）
   ├─ getMediaFile: Blob取得 → File変換
   └─ persistMedia: SHA計算 → ファイル保存
5. ページネーション
   └─ traverseCursor: first/last/next/prev のカーソル操作
```

### フローチャート

```mermaid
flowchart TD
    A[Gitea Backend初期化] --> B{useWorkflow?}
    B -->|Yes| C[エラー: Editorial Workflow非対応]
    B -->|No| D[authenticate]
    D --> E[API生成]
    E --> F[api.user]
    F --> G[api.hasWriteAccess]
    G --> H{権限あり?}
    H -->|Yes| I[User返却]
    H -->|No| J[エラー: アクセス権なし]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-40-01 | Editorial Workflow非対応 | Giteaバックエンドはエディトリアルワークフローをサポートしない | useWorkflow=true時にエラー |
| BR-40-02 | ページサイズ | ファイル一覧のページサイズは20 | getCursorAndFiles時 |
| BR-40-03 | ユーザー名変換 | full_nameをname、loginをloginとしてマッピング | authenticate返却時 |
| BR-40-04 | ディレクトリ表示 | getMediaでtype='tree'のファイルをisDirectory=trueとして返却 | getMedia時 |
| BR-40-05 | ロック付き操作 | persistEntryはrunWithLockで排他制御 | 書き込み操作時 |
| BR-40-06 | Origin Maintainer判定 | collaborator permission APIでadmin/writeパーミッションを確認 | userIsOriginMaintainer時 |

### 計算ロジック

特になし

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

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

| 操作 | 対象 | 操作種別 | 概要 |
|-----|------|---------|------|
| persistEntry | Giteaリポジトリ | CREATE/UPDATE | APIでファイルをコミット |
| deleteFiles | Giteaリポジトリ | DELETE | コミットメッセージ付きファイル削除 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | 設定エラー | repoが未設定 | "The Gitea backend needs a 'repo'" エラー |
| - | 設定エラー | useWorkflow=true | "The Gitea backend does not support editorial workflow" エラー |
| - | 認証エラー | 書き込み権限なし | "Your Gitea user account does not have access to this repo." |
| - | リポジトリエラー | リポジトリが見つからない | 詳細なエラーメッセージ |

### リトライ仕様

特になし

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

asyncLock（runWithLock）によるCMSレベルの排他制御

## パフォーマンス要件

- MAX_CONCURRENT_DOWNLOADS = 10
- ページサイズ20のカーソルベースページネーション

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

- Authorization: token {token} ヘッダーによるトークン認証
- セルフホスティング環境ではHTTPSの設定が必要

## 備考

- unpublishedEntries/unpublishedEntry等のワークフローメソッドは空の実装（空オブジェクト返却）
- GitHubバックエンドと類似した構造（getCursorAndFiles、traverseCursor等が同一パターン）
- getMediaでfolderSupport引数によりディレクトリ対応

---

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

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

### 推奨読解順序

#### Step 1: データ構造を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | types.ts | `packages/decap-cms-backend-gitea/src/types.ts` | GiteaUser型（1-22行目）: Giteaユーザーの完全な型定義。GiteaRepository型（116-167行目）: リポジトリの型定義。GitGetBlobResponse/GitGetTreeResponse: Git操作の応答型 |
| 1-2 | implementation.tsx | `packages/decap-cms-backend-gitea/src/implementation.tsx` | ApiFile型（40行目）、Giteaクラスプロパティ（44-62行目） |

**読解のコツ**: GiteaバックエンドはGitHubバックエンドと非常に似た構造を持つ。getCursorAndFilesやtraverseCursorなど同一パターンのメソッドが多い。ただしEditorial Workflowは非対応で、関連メソッドは空の実装となっている点に注意。

#### Step 2: エントリーポイント

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | implementation.tsx | `packages/decap-cms-backend-gitea/src/implementation.tsx` | コンストラクタ（64-90行目）: 設定読み込み、useWorkflowチェック。authenticate（152-189行目）: API生成、ユーザー取得、権限チェック |

**主要処理フロー**:
1. **64-90行目**: コンストラクタ。repo, branch, apiRoot設定。useWorkflow時はエラー
2. **79-81行目**: useWorkflowチェック。エラー: "The Gitea backend does not support editorial workflow"
3. **117-126行目**: currentUser。API /user エンドポイントでユーザー取得（プロミスキャッシュ付き）
4. **152-189行目**: authenticate。API生成、user取得、hasWriteAccess確認
5. **183-188行目**: ユーザー情報返却。full_name→name変換
6. **202-224行目**: getCursorAndFiles。ページサイズ20のカーソル生成

#### Step 3: メディア操作

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | implementation.tsx | `packages/decap-cms-backend-gitea/src/implementation.tsx` | getMedia（299-308行目）: ファイル一覧取得。type='tree'でisDirectory判定。folderSupport引数対応 |

#### Step 4: ワークフロースタブ（空実装）

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | implementation.tsx | `packages/decap-cms-backend-gitea/src/implementation.tsx` | unpublishedEntries（415-418行目）等: {} as any を返す空実装 |

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

```
Gitea (implementation.tsx)
    │
    ├─ authenticate(state)
    │      ├─ new API({ token, branch, repo, originRepo, apiRoot })
    │      ├─ api.user()
    │      └─ api.hasWriteAccess()
    │
    ├─ entriesByFolder(folder, extension, depth)
    │      ├─ api.listFiles(folder, { repoURL, depth })
    │      ├─ filterByExtension(file, extension)
    │      ├─ getCursorAndFiles(filtered, 1)
    │      └─ entriesByFolder(listFiles, readFile, ...)
    │
    ├─ getMedia(mediaFolder, folderSupport)
    │      └─ api.listFiles(mediaFolder, undefined, folderSupport)
    │            └─ { id, name, size, displayURL: { id, path }, path, isDirectory }
    │
    ├─ persistEntry(entry, options)
    │      └─ runWithLock(() => api.persistFiles(...))
    │
    └─ traverseCursor(cursor, action)
           ├─ getCursorAndFiles(files, targetPage)
           └─ entriesByFiles(result.files, readFile, ...)
```

### データフロー図

```
[入力]                    [処理]                         [出力]

config.yml            ─▶ Gitea constructor            ─▶ Gitea instance
  backend:                ├─ useWorkflow検証               ├─ api
    name: gitea           └─ asyncLock初期化                └─ lock
    repo: owner/repo
    api_root: https://gitea.example.com/api/v1

Token                 ─▶ authenticate()              ─▶ User { name: full_name,
                          ├─ API生成                       login, avatar_url,
                          ├─ api.user()                    token }
                          └─ api.hasWriteAccess()

folder, extension     ─▶ entriesByFolder()           ─▶ entries[] + cursor
                          ├─ api.listFiles()
                          ├─ filterByExtension()
                          ├─ getCursorAndFiles()
                          └─ api.readFile()
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| implementation.tsx | `packages/decap-cms-backend-gitea/src/implementation.tsx` | ソース | GiteaバックエンドのImplementation実装 |
| API.ts | `packages/decap-cms-backend-gitea/src/API.ts` | ソース | Gitea REST API操作 |
| types.ts | `packages/decap-cms-backend-gitea/src/types.ts` | ソース | Gitea API型定義（GiteaUser、GiteaRepository等） |
| AuthenticationPage.js | `packages/decap-cms-backend-gitea/src/AuthenticationPage.js` | ソース | Gitea認証画面コンポーネント |
| index.ts | `packages/decap-cms-backend-gitea/src/index.ts` | ソース | パッケージエントリーポイント |
