# 機能設計書 66-メンテナンスページ

## 概要

本ドキュメントは、Shopware Storefrontにおけるメンテナンスモード時のページを表示する機能について、その処理内容、入出力仕様、およびコード構造を記述した機能設計書である。

### 本機能の処理概要

本機能は、販売チャネルがメンテナンスモードに設定されている場合に、一般ユーザーに対してメンテナンスページを表示する機能である。

**業務上の目的・背景**：ECサイトの運用において、システム更新やデータ移行などのメンテナンス作業中は、一般ユーザーのアクセスを制限する必要がある。メンテナンスページ機能により、作業中であることをユーザーに適切に通知し、一時的にショップを閉鎖できる。同時に、ホワイトリストIPからのアクセスは許可することで、管理者は作業中のサイト確認が可能。

**機能の利用シーン**：
- システムアップデート中のショップ一時閉鎖
- 大規模なデータ移行作業中
- 緊急メンテナンス時
- 特定イベント前のサイト準備中

**主要な処理内容**：
1. リクエストを受信（`/maintenance`または`/maintenance/singlepage/{id}`）
2. メンテナンスモードの状態確認
3. ホワイトリストIPの確認
4. メンテナンスページ（CMSページ）の読み込み
5. 503ステータスでレスポンスを返却

**関連システム・外部連携**：SystemConfigService、MaintenanceModeResolver、CMSページローダー

**権限による制御**：メンテナンスモード中でもアクセス許可（`ATTRIBUTE_IS_ALLOWED_IN_MAINTENANCE`）

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 29 | メンテナンスページ | 主機能 | メンテナンスモード時の表示（MaintenanceController） |

## 機能種別

画面表示 / アクセス制御

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| id | string | No | CMSページID（singlepageの場合） | 有効なUUID形式 |
| redirectTo | string | No | リダイレクト先パラメータ | - |
| request | Request | Yes | HTTPリクエストオブジェクト | - |
| context | SalesChannelContext | Yes | 販売チャネルコンテキスト | - |

### 入力データソース

- システム設定: `core.basicInformation.maintenancePage`（メンテナンスCMSページID）
- リクエスト属性: `SALES_CHANNEL_MAINTENANCE_IP_WHITLELIST`
- MaintenanceModeResolver: メンテナンス状態の判定

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| page | MaintenancePage | メンテナンスページオブジェクト |
| page.cmsPage | CmsPageEntity | CMSページエンティティ |

### 出力先

- HTMLレスポンス
- テンプレート: `@Storefront/storefront/page/error/error-maintenance.html.twig`
- HTTPステータス: 503 Service Temporarily Unavailable
- Retry-Afterヘッダー: 3600（秒）

## 処理フロー

### 処理シーケンス

```
1. MaintenanceController がリクエストを受信
   ├─ renderMaintenancePage(): /maintenance
   └─ renderSinglePage(): /maintenance/singlepage/{id}

2. renderMaintenancePage() の場合
   ├─ MaintenanceModeResolver::shouldRedirectToShop() の確認
   │   └─ メンテナンスモード解除/ホワイトリストIPの場合はショップにリダイレクト
   ├─ SystemConfigからメンテナンスCMSページIDを取得
   ├─ CMSページIDがない場合
   │   └─ デフォルトのメンテナンスページをレンダリング
   ├─ CMSページIDがある場合
   │   └─ MaintenancePageLoader::load() でCMSページを取得
   ├─ MaintenancePageLoadedHook を実行
   ├─ 503ステータス、Retry-Afterヘッダーを設定
   └─ ホワイトリストIPヘッダーを設定

3. renderSinglePage() の場合
   ├─ idパラメータのバリデーション
   ├─ MaintenancePageLoader::load() でCMSページを取得
   ├─ MaintenancePageLoadedHook を実行
   └─ ホワイトリストIPヘッダーを設定
```

### フローチャート

```mermaid
flowchart TD
    A[リクエスト受信] --> B{エンドポイント}
    B -->|/maintenance| C[renderMaintenancePage]
    B -->|/maintenance/singlepage/{id}| D[renderSinglePage]

    C --> E{shouldRedirectToShop?}
    E -->|Yes| F{redirectToあり?}
    F -->|Yes| G[createActionResponse]
    F -->|No| H[ホームにリダイレクト]
    E -->|No| I[maintenanceLayoutId取得]
    I --> J{layoutIdあり?}
    J -->|No| K[デフォルトテンプレート]
    J -->|Yes| L[MaintenancePageLoader::load]
    L --> M[MaintenancePageLoadedHook]
    K --> N[503ステータス設定]
    M --> N
    N --> O[Retry-Afterヘッダー]
    O --> P[ホワイトリストIPヘッダー]
    P --> Q[レスポンス返却]
    G --> Q
    H --> Q

    D --> R{idあり?}
    R -->|No| S[RoutingException]
    R -->|Yes| T[MaintenancePageLoader::load]
    T --> U[MaintenancePageLoadedHook]
    U --> V[ホワイトリストIPヘッダー]
    V --> Q
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-001 | メンテナンスモード判定 | メンテナンスモードが解除された場合はショップにリダイレクト | 常時 |
| BR-002 | ホワイトリストIP | ホワイトリストIPからのアクセスはメンテナンス中でもショップにリダイレクト可能 | 常時 |
| BR-003 | 503ステータス | メンテナンスページは503ステータスで返却 | 常時 |
| BR-004 | Retry-After | Retry-Afterヘッダーを3600秒（1時間）に設定 | 常時 |
| BR-005 | デフォルトページ | CMSページが設定されていない場合はデフォルトテンプレートを使用 | CMSページ未設定時 |

### 計算ロジック

特になし

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| CMSページ取得 | cms_page | SELECT | メンテナンスCMSページの取得 |

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

#### cms_page

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | id, sections, blocks, slots等 | id = {メンテナンスCMSページID} | 関連するセクション、ブロック、スロットも取得 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| FRAMEWORK__ROUTING_MISSING_REQUEST_PARAMETER | RoutingException | singlepage呼び出し時にidパラメータ未指定 | パラメータを指定する |
| CMS__PAGE_NOT_FOUND | PageNotFoundException | 指定されたCMSページが存在しない | CMSページを作成する |

### リトライ仕様

リトライ処理なし（読み取り専用処理のため）

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

読み取り専用のためトランザクション制御なし

## パフォーマンス要件

- HTTPキャッシュ対応（`PlatformRequest::ATTRIBUTE_HTTP_CACHE => true`）
- レスポンス時間：200ms以内推奨

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

- メンテナンスモード中でもアクセス許可のルートとして設定
- ホワイトリストIPによるアクセス制御
- ホワイトリストIPはレスポンスヘッダーに含まれる（HTTPキャッシュ用）

## 備考

- メンテナンスページはCMSで自由にカスタマイズ可能
- CMSページが設定されていない場合はデフォルトのエラーテンプレートを使用
- Retry-Afterヘッダーにより検索エンジンに一時的な停止を通知

---

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

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

### 推奨読解順序

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

メンテナンスページのデータ構造を理解することが重要である。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | MaintenancePage.php | `src/Storefront/Page/Maintenance/MaintenancePage.php` | ページの構造（cmsPageを含むPageクラス） |

**読解のコツ**: MaintenancePageはPageクラスを継承し、CmsPageEntityを保持する。CMSページが設定されていない場合はnullとなる。

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

処理の起点となるコントローラーを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | MaintenanceController.php | `src/Storefront/Controller/MaintenanceController.php` | renderMaintenancePage(), renderSinglePage()メソッド |

**主要処理フロー**:
1. **38-46行目**: ルート定義 - `/maintenance`、メンテナンス中許可
2. **47-88行目**: renderMaintenancePage() - メインのメンテナンスページ処理
3. **49-55行目**: shouldRedirectToShop()でリダイレクト判定
4. **57-70行目**: CMSページIDがない場合のデフォルトレンダリング
5. **73-87行目**: CMSページがある場合の処理
6. **93-120行目**: renderSinglePage() - スタンドアロンCMSページ

#### Step 3: ページローダーを理解する

メンテナンスページデータの読み込み処理を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | MaintenancePageLoader.php | `src/Storefront/Page/Maintenance/MaintenancePageLoader.php` | load()メソッド（33-54行目） |

**主要処理フロー**:
- **35-37行目**: GenericPageLoaderで共通ページデータを取得
- **39-44行目**: CMSページローダーでCMSページを取得
- **46行目**: CMSページをセット
- **48行目**: MaintenancePageLoadedEventをディスパッチ

#### Step 4: メンテナンスモード判定を理解する

メンテナンスモードの判定ロジックを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | MaintenanceModeResolver.php | `src/Storefront/Framework/Routing/MaintenanceModeResolver.php` | shouldRedirectToShop()メソッド |

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

```
MaintenanceController
    │
    ├─ renderMaintenancePage()
    │      ├─ MaintenanceModeResolver::shouldRedirectToShop()
    │      │      └─ メンテナンス状態/IPチェック
    │      ├─ SystemConfigService::getString('maintenancePage')
    │      ├─ MaintenancePageLoader::load() [CMSページありの場合]
    │      │      ├─ GenericPageLoader::load()
    │      │      └─ SalesChannelCmsPageLoader::load()
    │      ├─ hook(MaintenancePageLoadedHook)
    │      └─ renderStorefront() → error-maintenance.html.twig
    │
    └─ renderSinglePage()
           ├─ MaintenancePageLoader::load()
           ├─ hook(MaintenancePageLoadedHook)
           └─ renderStorefront() → single-cms-page.html.twig
```

### データフロー図

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

Request (/maintenance) ─────────┐
                                ▼
SalesChannelContext ──────▶ MaintenanceController ──────▶ HTML Response
                                │                              │
                                ▼                              │
                         MaintenanceModeResolver               │
                                │                              │
                                ▼ (メンテナンス中)              │
                         SystemConfig                          │
                                │                              │
                                ▼                              │
                         MaintenancePageLoader                 │
                                │                              ▼
                                ├─ GenericPageLoader      error-maintenance.html.twig
                                └─ CmsPageLoader               │
                                       │                       │
                                       ▼                       ▼
                                MaintenancePage          503 Status
                                       │                 Retry-After: 3600
                                       └─ cmsPage        IP Whitelist Header
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| MaintenanceController.php | `src/Storefront/Controller/MaintenanceController.php` | コントローラー | メンテナンスページ表示のエントリーポイント |
| MaintenancePageLoader.php | `src/Storefront/Page/Maintenance/MaintenancePageLoader.php` | ローダー | メンテナンスページデータの読み込み |
| MaintenancePage.php | `src/Storefront/Page/Maintenance/MaintenancePage.php` | ページ | メンテナンスページのデータ構造 |
| MaintenancePageLoadedEvent.php | `src/Storefront/Page/Maintenance/MaintenancePageLoadedEvent.php` | イベント | メンテナンスページ読み込み完了イベント |
| MaintenancePageLoadedHook.php | `src/Storefront/Page/Maintenance/MaintenancePageLoadedHook.php` | フック | App Scripts用拡張ポイント |
| MaintenanceModeResolver.php | `src/Storefront/Framework/Routing/MaintenanceModeResolver.php` | リゾルバ | メンテナンスモード判定 |
