# 機能設計書 34-ルート設定

## 概要

本ドキュメントは、Ghostのカスタムルーティング設定機能に関する設計仕様を定義する。この機能により、サイト管理者は`routes.yaml`ファイルを使用してサイトのURL構造、コレクション、タクソノミーをカスタマイズできる。

### 本機能の処理概要

ルート設定は、`routes.yaml`ファイルを使用してGhostのフロントエンドルーティングを制御する機能である。静的ルート、動的コレクション、タクソノミー（タグ・著者）のURL構造を自由に定義できる。

**業務上の目的・背景**：デフォルトのURL構造（`/tag/`, `/author/`等）から離れ、サイト独自のURL設計を実現する。複数のコレクションを設定して、ブログとポッドキャスト、ニュースと特集記事など、異なるコンテンツタイプを分離して管理できる。

**機能の利用シーン**：
- ブログとポートフォリオを別々のセクションに分けたい場合
- カスタムのパーマリンク構造（`/blog/:slug/`など）を設定したい場合
- タグページのURLを`/topics/`に変更したい場合
- 静的ページに独自のテンプレートを割り当てたい場合
- RSSフィードのURLをカスタマイズしたい場合

**主要な処理内容**：
1. `routes.yaml`ファイルの読み込みと解析
2. YAML構文のバリデーション
3. ルーティング設定の適用（routes, collections, taxonomies）
4. URLジェネレーターのリセットと再構築
5. フロントエンドルーターの再読み込み
6. Admin APIを通じた設定ファイルのアップロード・ダウンロード

**関連システム・外部連携**：
- URLサービス：URLジェネレーターの管理
- フロントエンドルーター：ルーティング処理
- テーマエンジン：テンプレートの選択

**権限による制御**：管理者（Administrator）以上のロールを持つスタッフユーザーのみが設定をアップロード・ダウンロードできる。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 61 | Labs設定 | 参照画面 | routes.yamlファイルのアップロード・ダウンロード |

## 機能種別

設定管理 / ルーティング制御

## 入力仕様

### 入力パラメータ

#### routes.yaml構造

```yaml
routes:
  /custom-page/:
    template: custom
    data: page.custom

collections:
  /:
    permalink: /{slug}/
    template: index

  /blog/:
    permalink: /blog/{slug}/
    filter: tag:blog
    template: blog

taxonomies:
  tag: /tag/{slug}/
  author: /author/{slug}/
```

| セクション | 説明 | 設定項目 |
|-----------|------|---------|
| routes | 静的ルートの定義 | template, data, content_type |
| collections | 動的コレクションの定義 | permalink, filter, template, data, limit, order |
| taxonomies | タグ・著者ページのURL設定 | tag, author のURLパターン |

### 入力データソース

- `content/settings/routes.yaml`ファイル
- Admin API経由でのファイルアップロード

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| content | String | routes.yamlファイルの内容（ダウンロード時） |

### 出力先

- フロントエンドルーター（ルーティング設定の適用）
- Admin APIレスポンス（ファイルダウンロード）
- settingsテーブル（routes_hash）

## 処理フロー

### 処理シーケンス

```
1. 初期化処理
   └─ デフォルト routes.yaml の存在確認
   └─ 存在しない場合はデフォルト設定をコピー
   └─ routes.yaml の読み込みと解析
   └─ バリデーション実行
   └─ URLジェネレーターの構築

2. ファイルアップロード（Admin API）
   └─ アップロードファイルのバリデーション
   └─ 既存ファイルのバックアップ作成
   └─ 新ファイルの保存
   └─ URLジェネレーターのリセット
   └─ フロントエンドの再読み込み
   └─ 起動完了の待機
   └─ routes_hashの更新

3. ファイルダウンロード（Admin API）
   └─ routes.yaml の読み込み
   └─ 内容を返却
```

### フローチャート

```mermaid
flowchart TD
    A[Ghost起動] --> B{routes.yaml存在?}
    B -->|No| C[デフォルト設定をコピー]
    B -->|Yes| D[ファイル読み込み]
    C --> D
    D --> E[YAML解析]
    E --> F{バリデーション}
    F -->|成功| G[URLジェネレーター構築]
    F -->|失敗| H[エラー発生]
    G --> I[フロントエンドルーター設定]
    I --> J[起動完了]

    K[ファイルアップロード] --> L[バリデーション]
    L --> M{有効?}
    M -->|No| N[エラー返却]
    M -->|Yes| O[バックアップ作成]
    O --> P[ファイル保存]
    P --> Q[URLジェネレーターリセット]
    Q --> R[フロントエンド再読み込み]
    R --> S{起動完了待機}
    S -->|成功| T[routes_hash更新]
    S -->|失敗| U[バックアップから復元]
    U --> V[エラー返却]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-34-01 | デフォルト設定 | routes.yamlが存在しない場合、デフォルト設定を使用 | 初期化時 |
| BR-34-02 | YAML形式のみ | 設定はYAML形式のみサポート | ファイル読み込み時 |
| BR-34-03 | バックアップ作成 | 更新時は既存ファイルのバックアップを作成 | ファイルアップロード時 |
| BR-34-04 | 自動ロールバック | 設定適用後に問題が発生した場合、バックアップから復元 | フロントエンド再読み込み失敗時 |
| BR-34-05 | 起動待機 | 設定変更後はURLサービスの完了を最大5回確認 | ファイルアップロード後 |
| BR-34-06 | ハッシュ管理 | デフォルト設定のハッシュ値で変更を検出 | 設定比較時 |

### 計算ロジック

- routes_hash: `crypto.createHash('md5').update(JSON.stringify(settings), 'binary').digest('hex')`
- デフォルトハッシュ: `3d180d52c663d173a6be791ef411ed01`

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| ハッシュ保存 | settings | UPDATE | routes_hashの更新 |

### ファイル操作詳細

| 操作 | ファイル | 処理内容 | 備考 |
|-----|---------|---------|------|
| 読み込み | content/settings/routes.yaml | ルーティング設定読み込み | 起動時・API呼び出し時 |
| 書き込み | content/settings/routes.yaml | アップロードされた設定の保存 | Admin API経由 |
| バックアップ | routes-backup.yaml | 更新前のファイルをバックアップ | 上書き前に作成 |
| コピー | default-routes.yaml | デフォルト設定をコピー | 初回起動時 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | InternalServerError | YAML読み込み失敗 | 「Error trying to load YAML setting」エラーを返却 |
| - | InternalServerError | フロントエンド再読み込みがタイムアウト | 「Could not load routes.yaml file」エラー、バックアップから復元 |

### リトライ仕様

- フロントエンド再読み込み後、URLサービスの完了を最大5回（1秒間隔）確認
- タイムアウト時はバックアップファイルから自動復元

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

- ファイル更新はバックアップ作成後に実行
- 更新失敗時はバックアップから自動復元

## パフォーマンス要件

- 設定変更時はフロントエンド全体の再読み込みが発生
- URLサービスの再構築に時間がかかる場合がある（大規模サイトの場合）

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

- 管理者権限を持つユーザーのみが設定を変更可能
- 不正な設定によるサイトダウンを防ぐため、自動ロールバック機能を実装

## 備考

- routes.yamlの詳細な構文はGhost公式ドキュメントを参照
- デフォルト設定はGhostインストールディレクトリ内に保持
- 設定変更後はサイト全体のキャッシュが無効化される

---

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

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

### 推奨読解順序

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

まず、ルート設定のデータ構造と型定義を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | default-routes.yaml | `ghost/core/core/server/services/route-settings/default-routes.yaml` | デフォルトのルーティング設定（routes, collections, taxonomies） |
| 1-2 | settings-loader.js | `ghost/core/core/server/services/route-settings/settings-loader.js` | **56-60行目**: RouteSettings型定義（routes, collections, taxonomies） |

**読解のコツ**:
- default-routes.yamlでデフォルト設定の構造を確認
- JSDocコメントで型定義が記載されている

#### Step 2: サービス初期化を理解する

ルート設定サービスの初期化処理を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | index.js | `ghost/core/core/server/services/route-settings/index.js` | **7-52行目**: 初期化処理、各クラスの生成と連携 |

**主要処理フロー**:
1. **14行目**: SettingsPathManagerの生成（設定ファイルパス管理）
2. **15行目**: SettingsLoaderの生成（YAML読み込み）
3. **16-20行目**: RouteSettingsの生成（メイン処理）
4. **21-26行目**: DefaultSettingsManagerの生成（デフォルト設定管理）
5. **28行目**: ensureSettingsFileExists()でデフォルト設定の配置

#### Step 3: ファイル読み込みを理解する

YAMLファイルの読み込みと解析処理を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | settings-loader.js | `ghost/core/core/server/services/route-settings/settings-loader.js` | **28-50行目**: loadSettings関数 |
| 3-2 | yaml-parser.js | `ghost/core/core/server/services/route-settings/yaml-parser.js` | YAML解析処理 |
| 3-3 | validate.js | `ghost/core/core/server/services/route-settings/validate.js` | 設定のバリデーション |

**主要処理フロー**:
1. **30行目**: fs.readFile()でYAML読み込み
2. **33行目**: parseYaml()でオブジェクトに変換
3. **36行目**: validate()でバリデーション実行

#### Step 4: 設定適用を理解する

設定ファイルのアップロードと適用処理を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | route-settings.js | `ghost/core/core/server/services/route-settings/route-settings.js` | **26-168行目**: RouteSettingsクラス |

**主要処理フロー**:
1. **94-147行目**: `setFromFilePath()` - ファイルアップロード処理
2. **95行目**: バックアップ作成
3. **96行目**: ファイル保存
4. **98行目**: URLジェネレーターリセット
5. **109行目**: フロントエンド再読み込み
6. **120-137行目**: 起動完了待機（最大5回、1秒間隔）
7. **141-145行目**: 失敗時のロールバック処理

#### Step 5: Admin APIを理解する

設定のアップロード・ダウンロードAPIを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | settings.js | `ghost/core/core/server/api/endpoints/settings.js` | **150-181行目**: upload/downloadエンドポイント |

**主要処理フロー**:
1. **150-161行目**: `upload` - ファイルアップロード、routes_hash同期
2. **163-181行目**: `download` - ファイルダウンロード（YAML形式）

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

```
Ghost起動
    │
    └─ route-settings/index.js init()
           │
           ├─ DefaultSettingsManager.ensureSettingsFileExists()
           │      └─ デフォルトroutes.yamlのコピー
           │
           └─ (フロントエンド起動時)
                  └─ SettingsLoader.loadSettings()
                         ├─ fs.readFile()
                         ├─ parseYaml()
                         └─ validate()

Admin API upload
    │
    └─ routeSettings.api.setFromFilePath()
           │
           ├─ createBackupFile()
           │
           ├─ saveFile()
           │
           ├─ urlService.resetGenerators()
           │
           ├─ bridge.reloadFrontend()
           │
           └─ isBlogRunning() (待機ループ)
                  │
                  └─ (失敗時) restoreBackupFile()

Admin API download
    │
    └─ routeSettings.api.get()
           └─ readFile()
```

### データフロー図

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

routes.yaml ─────────▶ SettingsLoader.loadSettings()
                              │
                              ▼
                        parseYaml()
                              │
                              ▼
                        validate()
                              │
                              ▼
                        URLジェネレーター構築 ───▶ フロントエンドルーター
                              │
                              ▼
                        routes_hash計算 ───────▶ settingsテーブル

Admin API upload ────▶ setFromFilePath()
                              │
                              ├─▶ バックアップ作成
                              │
                              ├─▶ ファイル保存
                              │
                              └─▶ フロントエンド再読み込み
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| index.js | `ghost/core/core/server/services/route-settings/index.js` | ソース | サービスエントリーポイント |
| route-settings.js | `ghost/core/core/server/services/route-settings/route-settings.js` | ソース | メインロジック |
| settings-loader.js | `ghost/core/core/server/services/route-settings/settings-loader.js` | ソース | YAML読み込み |
| yaml-parser.js | `ghost/core/core/server/services/route-settings/yaml-parser.js` | ソース | YAML解析 |
| validate.js | `ghost/core/core/server/services/route-settings/validate.js` | ソース | バリデーション |
| settings-path-manager.js | `ghost/core/core/server/services/route-settings/settings-path-manager.js` | ソース | パス管理 |
| default-settings-manager.js | `ghost/core/core/server/services/route-settings/default-settings-manager.js` | ソース | デフォルト設定管理 |
| default-routes.yaml | `ghost/core/core/server/services/route-settings/default-routes.yaml` | 設定 | デフォルトルート設定 |
| settings.js | `ghost/core/core/server/api/endpoints/settings.js` | ソース | Admin APIエンドポイント |
