# 通知設計書 18-セッション切断通知

## 概要

本ドキュメントは、Decap CMS において、ユーザーの認証セッションが無効になった場合に表示される持続的なエラートースト通知の設計を記述する。

### 本通知の処理概要

セッション切断通知は、定期的なバックエンドステータスチェックにおいてユーザーの認証ステータスが無効（`status.auth.status === false`）であることを検出した場合に、セッション切断をユーザーに通知する持続的なトースト通知である。本通知は自動消去されず、ユーザーが手動で消去するか、再ログインするまで表示され続ける。

**業務上の目的・背景**：Decap CMS ではOAuth認証（GitHub、GitLab等）を使用してバックエンドサービスにアクセスする。認証トークンの有効期限切れ、セッションの無効化、またはバックエンド側でのアクセス権限変更等により、ユーザーのセッションが無効になることがある。この状態でエントリの保存や公開を行おうとすると失敗するため、セッション切断を事前に通知し、データのバックアップと再ログインを促す必要がある。

**通知の送信タイミング**：`checkBackendStatus` アクション内で `backend.status()` の結果として `status.auth.status === false` が返された場合に送信される。APIステータスチェックの後に実行される認証チェックの一環として発火する。ただし、既に同じキー（`ui.toast.onLoggedOut`）の通知が存在する場合は重複送信されない。

**通知の受信者**：セッションが無効になった当該ユーザー。定期的なステータスチェックはバックグラウンドで実行されるため、明示的なユーザー操作なしに通知が表示される。

**通知内容の概要**：i18nキー `ui.toast.onLoggedOut` に対応するローカライズされたエラーメッセージが表示される。英語では "You have been logged out, please back up any data and login again"、日本語では "ログアウトされています。データをバックアップし、再度ログインしてください。" となる。

**期待されるアクション**：ユーザーは編集中のデータをコピー等でバックアップし、その後CMS画面からログアウト→再ログインの操作を行うことが期待される。

## 通知種別

アプリ内トースト通知（react-toastify ベース） - 持続表示型

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 同期（Redux dispatch） |
| 優先度 | 最高 |
| リトライ | なし（通知自体のリトライはなし。ステータスチェックは定期的に繰り返される） |

### 送信先決定ロジック

バックグラウンドの定期ステータスチェックにより、認証セッションが無効になったユーザーに対してトースト通知を表示する。重複防止ロジックにより、同じ通知が複数表示されることはない。

## 通知テンプレート

### メール通知の場合

該当なし（本通知はアプリ内トースト通知のみ）

### 本文テンプレート

```
You have been logged out, please back up any data and login again
```

日本語の場合：
```
ログアウトされています。データをバックアップし、再度ログインしてください。
```

### 添付ファイル

該当なし

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| （なし） | 本通知にはテンプレート変数はない | - | - |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| バックグラウンドポーリング | 定期的なバックエンドステータスチェック | `status.auth.status === false` かつ同キーの既存通知がない場合 | `checkBackendStatus` thunkアクション内の認証チェック部分で発火 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| 既に同キーの通知が存在する場合 | `existingNotification` が見つかった場合、新たな通知は送信されない（行78）。重複防止ロジック |
| 認証ステータスが有効な場合 | `status.auth.status !== false` の場合、通知は送信されない |
| バックエンドAPIがダウンしている場合 | `status.api.status === false` の場合、APIダウン通知の処理後に `statusSuccess` が dispatch されて早期リターンする可能性がある（行64） |
| フェッチ中の場合 | `state.status.isFetching` が true の場合、ステータスチェック自体がスキップされる |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[バックグラウンド定期ポーリング] --> B[checkBackendStatus thunk dispatch]
    B --> C{isFetching?}
    C -->|Yes| D[早期リターン]
    C -->|No| E[statusRequest dispatch]
    E --> F[backend.status 呼び出し]
    F --> G{api.status}
    G -->|false| H[APIダウン処理 + statusSuccess + リターン]
    G -->|true| I[APIダウン通知消去処理]
    I --> J{auth.status === false?}
    J -->|No| K[statusSuccess dispatch]
    J -->|Yes| L{既存のセッション切断通知あり?}
    L -->|Yes| M[通知送信スキップ]
    L -->|No| N[addNotification dispatch - onLoggedOut]
    N --> O[持続的トースト表示 - 自動消去なし]
    O --> P[statusSuccess dispatch]
    M --> P
    K --> Q[終了]
    P --> Q
    D --> Q
    H --> Q
```

## データベース参照・更新仕様

### 参照テーブル一覧

| テーブル名 | 用途 | 備考 |
|-----------|------|------|
| Redux Store: notifications | 重複通知チェック | `notifications.notifications` から `ui.toast.onLoggedOut` キーの既存通知を検索 |
| Redux Store: status | フェッチ中チェック | `status.isFetching` で重複チェック実行を防止 |

### テーブル別参照項目詳細

#### Redux Store: notifications

| 参照項目 | 用途 | 取得条件 |
|---------|------|---------|
| notifications.notifications | 既存のセッション切断通知の存在チェック | `n.message?.key === 'ui.toast.onLoggedOut'` で `find` 検索 |

### 更新テーブル一覧

| テーブル名 | 操作 | 概要 |
|-----------|------|------|
| Redux Store: notifications | INSERT | セッション切断検知時に通知オブジェクトを追加 |
| Redux Store: status | UPDATE | STATUS_SUCCESS でステータス情報を更新 |

#### Redux Store 更新詳細

| 操作 | 項目 | 更新値 | 備考 |
|-----|------|-------|------|
| INSERT | notifications.notifications[] | `{ id: uuid(), message: { key: 'ui.toast.onLoggedOut' }, type: 'error' }` | `dismissAfter` 未設定のため持続表示。`details` も未使用 |

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| ステータスチェック自体の失敗 | `backend.status()` が例外をスローした場合 | `statusFailure(error)` を dispatch。セッション切断通知は表示されない |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | 該当なし |
| リトライ間隔 | 該当なし |
| リトライ対象エラー | 該当なし |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | 実質1件（重複防止ロジックにより同時に1件のみ表示） |
| 1日あたり上限 | なし |

### 配信時間帯

制限なし（バックグラウンドポーリングにより随時発生）

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

- 本通知は認証セッションの無効化に関する情報であり、セキュリティ上重要な通知である。
- 通知メッセージ自体には機密情報は含まれない（固定文字列のみ）。
- セッション切断後もブラウザ上に編集データが残っている可能性があるため、通知メッセージでデータのバックアップを促している。

## 備考

- **持続表示型通知**：`dismissAfter` が設定されていないため、トーストは自動消去されない。ユーザーが手動で閉じるか、再ログイン後にページがリロードされるまで表示され続ける。
- **重複防止**：`find` メソッドで既存通知の有無をチェックしている。No.17（バックエンドサービス障害通知）が `filter` + `length` を使用しているのに対し、本通知は `find` を使用している点が異なる。
- **APIダウンとの関係**：`status.api.status === false` の場合、行64で `return dispatch(statusSuccess(status))` により早期リターンが発生する。そのため、APIダウン時には認証チェック（本通知の発火条件）に到達しない可能性がある。
- **No.17との共存**：No.17（バックエンドサービス障害通知）とNo.18（セッション切断通知）は同じ `checkBackendStatus` 関数内で処理されるが、APIダウン時は早期リターンにより本通知は発火しない。APIは正常だが認証が無効の場合にのみ本通知が発火する。
- 本通知の i18nキーは `ui.toast.onLoggedOut` であり、多言語対応済みである。

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | notifications.ts (actions) | `packages/decap-cms-core/src/actions/notifications.ts` | `NotificationPayload` の型を理解する。本通知では `dismissAfter` と `details` の両方が未設定である |
| 1-2 | status.ts | `packages/decap-cms-core/src/actions/status.ts` | `statusSuccess` のペイロード型（行18-21）で `status.auth.status` の構造を理解する |

**読解のコツ**: 本通知はNo.17（バックエンドサービス障害通知）と同じ `checkBackendStatus` 関数内に含まれるが、実行順序としてはAPIステータスチェックの後に認証チェックが行われる点に注意。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | status.ts | `packages/decap-cms-core/src/actions/status.ts` | `checkBackendStatus` 関数（行35-95）。特に認証チェック部分（行72-88）に注目 |

**主要処理フロー**:
1. **行45**: `backend.status()` でバックエンドステータスを取得
2. **行52-64**: APIステータスチェック（No.17の処理）。APIダウン時は行64で早期リターン
3. **行72**: `status.auth.status === false` で認証エラーを判定
4. **行74**: i18nキー変数 `key` に `'ui.toast.onLoggedOut'` を設定
5. **行75-77**: 既存通知の検索（`find` メソッドで重複チェック）
6. **行78-87**: 既存通知がない場合のみ `addNotification` を dispatch（**本通知の発火箇所**）
7. **行90**: `statusSuccess` を dispatch

#### Step 3: 重複防止ロジックを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | status.ts | `packages/decap-cms-core/src/actions/status.ts` | 行75-77: `state.notifications.notifications.find()` で既存通知を検索。`n.message?.key === key` で同じi18nキーの通知を検出 |
| 3-2 | status.ts | `packages/decap-cms-core/src/actions/status.ts` | 行78: `if (!existingNotification)` の場合のみ新規通知を送信 |

**読解のコツ**: No.17（バックエンドサービス障害通知）は `filter` + `length === 0` で判定するのに対し、本通知は `find` + `!existingNotification` で判定する。同じ機能だが実装パターンが異なる。

#### Step 4: 通知ディスパッチの仕組みを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | notifications.ts (actions) | `packages/decap-cms-core/src/actions/notifications.ts` | `addNotification` 関数（行18-19） |
| 4-2 | notifications.ts (reducers) | `packages/decap-cms-core/src/reducers/notifications.ts` | `NOTIFICATION_SEND` ケース（行40-47） |

#### Step 5: UIレンダリングを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | Notifications.tsx | `packages/decap-cms-core/src/components/UI/Notifications.tsx` | 行39: `autoClose: notification.dismissAfter` が `undefined` の場合、react-toastify はトーストを自動消去しない。行47-51: `dismissAfter` が undefined の場合は setTimeout での自動 dismiss も実行されない |

#### Step 6: i18nメッセージを確認する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 6-1 | index.js (en) | `packages/decap-cms-locales/src/en/index.js` | 行283: `onLoggedOut: 'You have been logged out, please back up any data and login again'` |
| 6-2 | index.js (ja) | `packages/decap-cms-locales/src/ja/index.js` | 行270: `onLoggedOut: 'ログアウトされています。データをバックアップし、再度ログインしてください。'` |

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

```
checkBackendStatus (status.ts:35)
    |
    +-- statusRequest (status.ts:12)
    |
    +-- backend.status() (バックエンドAPI: ステータス取得)
    |
    +-- [APIステータスチェック] (行52-64)
    |       +-- [APIダウン時] addNotification - onBackendDown (No.17)
    |       +-- [APIダウン時] return statusSuccess (早期リターン)
    |
    +-- [APIダウン通知消去] (行65-70)
    |
    +-- [認証ステータスチェック] (行72-88)
    |       |
    |       +-- 既存通知チェック (行75-77)
    |       |
    |       +-- [通知なし] addNotification (notifications.ts:18)
    |               |
    |               +-- NOTIFICATION_SEND → reducer (reducers/notifications.ts:40)
    |                       |
    |                       +-- Notifications.tsx useEffect → toast() (持続表示)
    |
    +-- statusSuccess (status.ts:18)
```

### データフロー図

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

バックグラウンド ────────> checkBackendStatus thunk ────────> Redux dispatch
ポーリング                       |                                    |
                         backend.status()                             |
                                 |                            [認証無効時]
                         status = {                           NOTIFICATION_SEND
                           api: {                              (dismissAfter なし)
                             status: true,                           |
                             statusPage: '...'                reducer: notifications
                           },                                        |
                           auth: {                            Notifications コンポーネント
                             status: false  <──               react-toastify toast()
                           }                   認証無効!              |
                         }                                    持続的トースト表示
                                 |                            「ログアウトされています。
                         addNotification({                     データをバックアップし、
                           message: {                          再度ログインしてください。」
                             key: 'ui.toast.
                               onLoggedOut'
                           },
                           type: 'error'
                         })
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| status.ts | `packages/decap-cms-core/src/actions/status.ts` | ソース | バックエンドステータスチェックのthunkアクション。認証無効検知時に通知を dispatch する |
| notifications.ts (actions) | `packages/decap-cms-core/src/actions/notifications.ts` | ソース | `addNotification` アクションクリエーター |
| notifications.ts (reducers) | `packages/decap-cms-core/src/reducers/notifications.ts` | ソース | 通知ステートの管理リデューサー |
| Notifications.tsx | `packages/decap-cms-core/src/components/UI/Notifications.tsx` | ソース | トースト通知のUIコンポーネント（持続表示サポート） |
| index.js (en) | `packages/decap-cms-locales/src/en/index.js` | i18n | 英語翻訳定義 |
| index.js (ja) | `packages/decap-cms-locales/src/ja/index.js` | i18n | 日本語翻訳定義 |
