# 機能設計書 46-Decap Server

## 概要

本ドキュメントは、Decap CMS Proxy Server（`decap-server`パッケージ）の機能設計を記述する。Expressベースのローカルプロキシサーバーで、Gitモード（localGit）とファイルシステムモード（localFs）の2つの動作モードをサポートする。

### 本機能の処理概要

**業務上の目的・背景**：Decap CMSをローカル開発環境で使用する際、リモートのGitホスティングサービスへのアクセスなしにコンテンツの編集・プレビューを行いたい。Decap ServerはローカルのファイルシステムやGitリポジトリを直接操作するプロキシサーバーとして動作し、CMS（ProxyBackend, No.41）からのHTTPリクエストを受けてファイル操作を実行する。

**機能の利用シーン**：`npx decap-server`コマンドでローカルサーバーを起動し、CMSのlocal_backend設定と組み合わせて使用する。開発者がコンテンツの編集・プレビュー・テストを行う際に利用される。

**主要な処理内容**：
1. Expressサーバーの起動（デフォルトポート: 8081）
2. CORS、JSON body parsing、ロギングの共通ミドルウェア設定
3. Gitモード: simple-gitによるローカルGitリポジトリ操作（ブランチ管理、コミット、マージ、リベース）
4. FSモード: ファイルシステム直接操作（ファイル読み書き、削除）
5. Joiスキーマによるリクエストバリデーション（パストラバーサル防止）
6. エントリ・メディアファイルのCRUD操作
7. エディトリアルワークフロー対応（Gitモードのみ）

**関連システム・外部連携**：ProxyBackend（No.41）からのHTTP POSTリクエストを受信。ローカルのGitリポジトリまたはファイルシステムを操作。

**権限による制御**：CORSでオリジン制御可能（環境変数`ORIGIN`）。認証機能はない（ローカル開発用）。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | 全画面共通 | サーバーサイド | CMSクライアントからの全API操作のサーバーサイド処理 |

## 機能種別

CRUD操作 / サーバーアプリケーション

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| PORT | number | No | リスニングポート | デフォルト: 8081 |
| BIND_HOST | string | No | バインドホスト | 未指定時は全インターフェース |
| MODE | string | No | 動作モード | 'fs' または 'git'。デフォルト: 'fs' |
| GIT_REPO_DIRECTORY | string | No | Gitリポジトリパス | デフォルト: process.cwd() |
| ORIGIN | string | No | CORS許可オリジン | デフォルト: '*' |
| LOG_LEVEL | string | No | ログレベル | デフォルト: 'info' |
| body.action | string | Yes | API操作名 | Joiスキーマで検証 |
| body.params | object | Yes | 操作パラメータ | パストラバーサルチェック付き |

### 入力データソース

- HTTP POSTリクエスト（ProxyBackend）: JSON body
- ローカルファイルシステム / Gitリポジトリ: ファイルデータ

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| entries | Array | エントリデータ配列（file, data） |
| mediaFiles | Array | メディアファイル配列（id, content, encoding, name, path） |
| info | object | サーバー情報（repo, publish_modes, type） |
| unpublishedEntry | object | 未公開エントリ情報（collection, slug, status, diffs, updatedAt） |

### 出力先

- HTTPレスポンス（JSON）: ProxyBackendへのレスポンス
- ローカルファイルシステム / Gitリポジトリ: ファイル書き込み

## 処理フロー

### 処理シーケンス

```
1. サーバー起動
   └─ Express app作成、ポート/ホスト設定
   └─ 共通ミドルウェア登録（CORS, JSON, Morgan）
2. モード別ミドルウェア登録
   └─ fs: localFsMiddleware
   └─ git: localGitMiddleware（リポジトリバリデーション付き）
3. リクエスト受信
   └─ Joiスキーマバリデーション
   └─ action名でswitch分岐
4. Git操作（gitモード）
   └─ Mutexで排他制御
   └─ ブランチ切り替え → 操作 → ブランチ復帰
5. FS操作（fsモード）
   └─ ファイル読み書き直接実行
```

### フローチャート

```mermaid
flowchart TD
    A[HTTP POST /api/v1] --> B[Joiバリデーション]
    B --> C{action?}
    C -->|info| D[サーバー情報返却]
    C -->|entriesByFolder| E[フォルダエントリ取得]
    C -->|persistEntry| F{gitモード?}
    F -->|Yes| G[Mutex取得→ブランチ操作→コミット]
    F -->|No| H[ファイル書き込み]
    C -->|getMedia| I[メディアファイル読み込み]
    C -->|persistMedia| J[メディアファイル書き込み]
    C -->|deleteFiles| K[ファイル削除]
    C -->|unpublishedEntry| L[未公開エントリ取得]
    C -->|publishUnpublishedEntry| M[ブランチマージ→削除]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-46-01 | モード選択 | 環境変数MODEで'fs'または'git'を選択。デフォルトは'fs' | サーバー起動時 |
| BR-46-02 | Git排他制御 | Mutexにより同時に1つのGit操作のみ実行。タイムアウト3秒 | gitモードの全操作 |
| BR-46-03 | ブランチ復帰 | Git操作後は元のブランチに必ず戻る（finally句） | gitモードのrunOnBranch |
| BR-46-04 | パストラバーサル防止 | Joiカスタムバリデーターでリポジトリ外へのパスアクセスを防止 | 全リクエスト |
| BR-46-05 | GPG署名無効化 | コミット/リベース/マージ時にGPG署名を一時的に無効化 | gitモードのcommit/rebase/merge |
| BR-46-06 | ワークフロー対応 | gitモードのみeditorial_workflowをサポート。fsモードはsimpleのみ | info API |
| BR-46-07 | ボディサイズ制限 | JSON bodyの最大サイズは50MB | 共通ミドルウェア |
| BR-46-08 | デフォルトブランチ検証 | 指定ブランチが存在しない場合は422エラーを返す | gitモードの操作時 |

### 計算ロジック

特筆すべき計算ロジックなし。

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

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

データベースは使用しない。ローカルファイルシステムとGitリポジトリが操作対象。

| 操作 | 対象 | 操作種別 | 概要 |
|-----|------|---------|------|
| entriesByFolder | ファイルシステム | READ | フォルダ内のファイル一覧取得と内容読み込み |
| persistEntry | ファイルシステム + Git | WRITE | ファイル書き込み（+Gitコミット） |
| deleteFiles | ファイルシステム + Git | DELETE | ファイル削除（+Gitコミット） |
| publishUnpublishedEntry | Git | MERGE | ブランチマージとブランチ削除 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 422 | バリデーションエラー | Joiスキーマ不適合 | リクエストパラメータを修正 |
| 422 | 不明アクション | 未定義のaction名 | サポートされるactionを使用 |
| 422 | ブランチ不存在 | 指定ブランチがローカルに存在しない | ブランチ名を確認 |
| 404 | 未公開エントリ不存在 | 指定のcmsBranchが存在しない | コレクション/スラッグを確認 |
| 500 | 内部エラー | ファイル操作やGit操作の失敗 | サーバーログを確認 |

### リトライ仕様

Mutex取得のタイムアウトは3秒。タイムアウト時は「Request timed out」エラー。

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

- Gitモード: Mutexで排他制御。操作はブランチ切り替え→処理→元ブランチ復帰の単位で実行
- FSモード: ファイル単位の操作。トランザクション制御なし

## パフォーマンス要件

ローカル開発環境での使用を前提。ファイルI/OとGit操作がボトルネック。Mutexにより同時リクエストはシリアライズされる。

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

- パストラバーサル防止（Joiカスタムバリデーター）
- CORS制御（環境変数ORIGIN）
- Gitコミット時に`--no-verify`と`--no-gpg-sign`を使用（ローカル開発用）
- 認証機能なし（ローカルネットワーク前提）

## 備考

- `npx decap-server`で起動可能
- `.env`ファイルで環境変数を設定可能（dotenvを使用）
- ミドルウェアAPIとしても利用可能（`registerLocalGit`/`registerLocalFs`をインポート）

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | types.ts | `packages/decap-server/src/middlewares/types.ts` | 全APIパラメータ型定義（DefaultParams, EntriesByFolderParams, PersistEntryParams, Asset等） |

**読解のコツ**: types.tsはサーバーが受け付ける全てのリクエストパラメータの型を定義している。DataFile型（57行目）とAsset型（59行目）がエントリ保存の中心的なデータ構造。

#### Step 2: エントリーポイントを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | index.ts | `packages/decap-server/src/index.ts` | サーバー起動処理。Express app作成、モード切り替え、リスニング開始 |

**主要処理フロー**:
- **10行目**: Express app作成
- **11-13行目**: 環境変数からポート、ホスト、ログレベル取得
- **21行目**: 共通ミドルウェア登録
- **24-31行目**: MODE環境変数によるfs/gitモード分岐
- **37-45行目**: ホスト指定の有無でlisten方法を切り替え

#### Step 3: 共通ミドルウェアを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | common/index.ts | `packages/decap-server/src/middlewares/common/index.ts` | Morgan（ロギング）、CORS、JSON bodyパーサーの登録 |

**主要処理フロー**:
- **13-17行目**: Morganストリームをwinstonロガーに接続
- **18行目**: Morganの'combined'フォーマット
- **19-23行目**: CORS設定（ORIGIN環境変数）
- **24行目**: express.json()で50MBのリミット設定

#### Step 4: Gitモードミドルウェアを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | localGit/index.ts | `packages/decap-server/src/middlewares/localGit/index.ts` | Gitモードの全API実装。simple-gitによるGit操作 |

**主要処理フロー**:
- **173-177行目**: simple-git初期化とMutex（タイムアウト3秒）
- **184-191行目**: infoアクション - リポジトリ名と対応publish_modesを返却
- **201-214行目**: entriesByFolderアクション - ブランチ切り替え→ファイル一覧取得→エントリ読み込み
- **305-347行目**: persistEntryアクション - ワークフロー有無で分岐、ブランチ作成/切り替え→リベース→コミット
- **359-367行目**: publishUnpublishedEntryアクション - マージ→ブランチ削除
- **456-463行目**: registerMiddleware - リポジトリパス解決、Joiミドルウェア登録、Gitミドルウェア登録

#### Step 5: FSモードミドルウェアを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | localFs/index.ts | `packages/decap-server/src/middlewares/localFs/index.ts` | FSモードの全API実装。ファイルシステム直接操作 |

**主要処理フロー**:
- **33-41行目**: infoアクション - type='local_fs'、publish_modes=['simple']のみ
- **42-65行目**: エントリ読み込みアクション群
- **66-89行目**: persistEntryアクション - ファイル書き込みとリネーム
- **105-113行目**: persistMediaアクション - メディアファイル書き込み

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

```
index.ts (サーバー起動)
    |
    +-- registerCommonMiddlewares(app)
    |       +-- morgan (ロギング)
    |       +-- cors (CORS)
    |       +-- express.json (JSONパーサー)
    |
    +-- [FSモード] registerLocalFs(app)
    |       +-- joi(getSchema) (バリデーション)
    |       +-- localFsMiddleware
    |           +-- listRepoFiles / entriesFromFiles
    |           +-- writeFile / deleteFile / move
    |           +-- readMediaFile
    |
    +-- [Gitモード] registerLocalGit(app)
            +-- validateRepo (リポジトリ検証)
            +-- joi(getSchema) (バリデーション)
            +-- localGitMiddleware
                +-- mutex.acquire()
                +-- runOnBranch(git, branch, func)
                +-- commitEntry / commit / rebase / merge
                +-- getDiffs / readMediaFile
```

### データフロー図

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

ProxyBackend       ──▶  POST /api/v1               ──▶  Express Server
(HTTPリクエスト)          Joiバリデーション                 (ミドルウェア)

Express Server     ──▶  localGit/localFs            ──▶  ファイルシステム
(ミドルウェア)            ファイル読み書き + Git操作         /Gitリポジトリ

ファイルデータ     ──▶  Base64エンコード             ──▶  HTTPレスポンス
(ファイルシステム)        readMediaFile                     (JSON)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| index.ts | `packages/decap-server/src/index.ts` | ソース | サーバーエントリポイント |
| middlewares.ts | `packages/decap-server/src/middlewares.ts` | ソース | ミドルウェアAPI公開（ライブラリ利用時） |
| common/index.ts | `packages/decap-server/src/middlewares/common/index.ts` | ソース | 共通ミドルウェア（CORS, JSON, Morgan） |
| localGit/index.ts | `packages/decap-server/src/middlewares/localGit/index.ts` | ソース | Gitモードミドルウェア |
| localFs/index.ts | `packages/decap-server/src/middlewares/localFs/index.ts` | ソース | FSモードミドルウェア |
| types.ts | `packages/decap-server/src/middlewares/types.ts` | ソース | APIパラメータ型定義 |
| joi/index.ts | `packages/decap-server/src/middlewares/joi/index.ts` | ソース | Joiバリデーションミドルウェア |
| joi/customValidators.ts | `packages/decap-server/src/middlewares/joi/customValidators.ts` | ソース | パストラバーサル防止カスタムバリデーター |
| utils/fs.ts | `packages/decap-server/src/middlewares/utils/fs.ts` | ソース | ファイルシステムユーティリティ |
| utils/entries.ts | `packages/decap-server/src/middlewares/utils/entries.ts` | ソース | エントリ読み込みユーティリティ |
| logger.ts | `packages/decap-server/src/logger.ts` | ソース | Winstonロガー設定 |
