# 通知設計書 13-rejected

## 概要

本ドキュメントは、Etherpadにおけるメッセージ拒否通知「rejected」の設計仕様を定義する。この通知は、サーバーがクライアントから送信されたメッセージを拒否した際に表示されるモーダルダイアログである。

### 本通知の処理概要

この通知はサーバーがクライアントのメッセージを正当な理由で拒否した際に、ユーザーにその事実を通知するためのアプリ内モーダル通知である。

**業務上の目的・背景**：共同編集環境において、クライアントとサーバー間の状態不整合は様々な理由で発生しうる。例えば、サーバーが更新された際にクライアントが古いプロトコルでメッセージを送信した場合や、セッション情報の不整合（Author IDの変更など）が検出された場合である。この通知により、ユーザーは技術的な問題が発生したことを認識し、ページのリロードによる再接続を試みることができる。

**通知の送信タイミング**：主に`handleMessage`関数内でセッション検証中にAuthor IDの不一致が検出された場合（337-348行目）に送信される。具体的には、セッション開始時に記録されたAuthor IDと、後続のメッセージ処理時に取得されたAuthor IDが異なる場合に発生する。

**通知の受信者**：メッセージが拒否された特定のクライアント。ブロードキャストではなく、該当するソケットに対してのみ送信される。

**通知内容の概要**：「You have been disconnected.」というタイトル（一般的な切断メッセージ）と、「The server rejected a message that was sent by your browser.」という説明文、および「The server may have been updated while you were viewing the pad, or maybe there is a bug in Etherpad. Try reloading the page.」という原因説明が表示される。

**期待されるアクション**：ユーザーはこの通知を確認し、ページをリロードして再接続を試みる。リロードにより新しいセッションが開始され、問題が解決することが期待される。

## 通知種別

アプリ内通知（モーダルダイアログ）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 同期（WebSocket経由のリアルタイム通知） |
| 優先度 | 高 |
| リトライ | 無 |

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

メッセージが拒否された特定のソケットに対してのみ`socket.emit('message', {disconnect: 'rejected'})`で送信される。

## 通知テンプレート

### モーダル通知の場合

| 項目 | 内容 |
|-----|------|
| タイトル | pad.modals.disconnected（共通の切断タイトル） |
| 説明 | pad.modals.rejected.explanation（ローカライズキー） |
| 原因 | pad.modals.rejected.cause（ローカライズキー） |
| 表示形式 | モーダルダイアログ + オーバーレイ |

### 本文テンプレート

```html
<div class="rejected">
  <h1 data-l10n-id="pad.modals.disconnected"></h1>
  <h2 data-l10n-id="pad.modals.rejected.explanation"></h2>
  <p data-l10n-id="pad.modals.rejected.cause"></p>
</div>
```

### 添付ファイル

なし

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| pad.modals.disconnected | 通知タイトル「You have been disconnected.」 | src/locales/*.json | Yes |
| pad.modals.rejected.explanation | 説明文「The server rejected a message...」 | src/locales/*.json | Yes |
| pad.modals.rejected.cause | 原因説明「The server may have been updated...」 | src/locales/*.json | Yes |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| セッション検証 | Author ID不一致検出 | セッション開始時と現在のAuthor IDが異なる | トークンまたはセッションIDの問題 |
| メッセージ処理 | 不正なメッセージ形式 | サーバーがメッセージを処理できない | プロトコル不整合の可能性 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| 正常なセッション | Author IDが一致し、メッセージが正常に処理される場合 |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[クライアントからメッセージ受信] --> B[セッション情報取得]
    B --> C{thisSession.author存在?}
    C -->|Yes| D{現在のauthorIDと一致?}
    C -->|No| E[新しいauthorIDを設定]
    D -->|一致| F[通常のメッセージ処理]
    D -->|不一致| G[disconnect:rejected送信]
    G --> H[エラーログ出力]
    H --> I[例外スロー]
    E --> F
```

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

### 参照テーブル一覧

| テーブル名 | 用途 | 備考 |
|-----------|------|------|
| globalAuthor:{authorId} | 著者情報の参照 | セッション検証時 |

### 更新テーブル一覧

なし

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| Author ID不一致 | セッション中にAuthor IDが変更された | rejectedを送信し処理中断 |
| 不正なトークン | トークンが無効または期限切れ | rejectedを送信し処理中断 |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | 0（リトライなし） |
| リトライ間隔 | N/A |
| リトライ対象エラー | N/A |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | 制限なし |
| 1日あたり上限 | 制限なし |

### 配信時間帯

制限なし（セキュリティ関連の通知は即座に送信される必要がある）

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

- Author ID不一致の検出はセキュリティ上重要（なりすまし防止）
- ログには詳細なエラー情報（ソケットID、IPアドレス、両方のAuthor ID）が記録される
- ユーザー名がある場合はログに含まれる
- この通知はセキュリティインシデントの可能性を示唆するため、ログの監視が推奨される

## 備考

- この通知にはForce reconnectボタンは表示されない
- 自動再接続タイマーも適用されない（with_reconnect_timerクラスがない）
- ユーザーは手動でページをリロードする必要がある
- サーバー更新後のプロトコル不整合を防ぐため、ページリロードが推奨される

---

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

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

### 推奨読解順序

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

まず、セッション情報の構造を理解することが重要である。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | PadMessageHandler.ts | `src/node/handler/PadMessageHandler.ts` | sessioninfos構造体（77-93行目のコメント）を確認 |

**読解のコツ**: sessioninfosのauthor属性がどのように設定・検証されるかに注目する。

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

Author ID検証の実装を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | PadMessageHandler.ts | `src/node/handler/PadMessageHandler.ts` | handleMessage関数内のAuthor ID検証（337-348行目） |

**主要処理フロー**:
1. **331-332行目**: securityManager.checkAccessでauthorIDを取得
2. **337行目**: `if (thisSession.author != null && thisSession.author !== authorID)`で不一致を検出
3. **338行目**: `socket.emit('message', {disconnect: 'rejected'})`で通知
4. **339-348行目**: エラーログ出力と例外スロー

#### Step 3: クライアント側の受信処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | pad_connectionstatus.ts | `src/static/js/pad_connectionstatus.ts` | disconnected関数でのメッセージ処理（73行目にrejected含む） |
| 3-2 | pad_modals.ts | `src/static/js/pad_modals.ts` | showModal関数でモーダル表示 |

#### Step 4: HTML構造を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | pad.html | `src/templates/pad.html` | rejected用のdiv要素（299-303行目）を確認 |

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

```
handleMessage(socket, message)
    │
    ├─ securityManager.checkAccess(...)
    │      │
    │      └─ authorID取得
    │
    ├─ [Author ID検証]
    │      │
    │      └─ thisSession.author !== authorID?
    │             │
    │             ├─ [一致] → 通常処理続行
    │             │
    │             └─ [不一致]
    │                    │
    │                    ├─ socket.emit({disconnect: 'rejected'})
    │                    └─ throw new Error(詳細メッセージ)
    │
    └─ [クライアント側]
           │
           ├─ pad_connectionstatus.disconnected('rejected')
           │      │
           │      └─ padmodals.showModal('rejected')
           │
           └─ padmodals.showOverlay()
```

### データフロー図

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

クライアントメッセージ ───▶ handleMessage ───▶ securityManager.checkAccess
                                                        │
                                                        ▼
                                               Author ID取得
                                                        │
                                               ┌────────┴────────┐
                                               │                 │
                                        [ID一致]            [ID不一致]
                                               │                 │
                                               ▼                 ▼
                                          通常処理      disconnect:rejected送信
                                                                 │
                                                                 ▼
                                                        クライアント受信
                                                                 │
                                                                 ▼
                                                        モーダル表示
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| PadMessageHandler.ts | `src/node/handler/PadMessageHandler.ts` | ソース | サーバー側メッセージ処理、Author ID検証 |
| SecurityManager.js | `src/node/db/SecurityManager.js` | ソース | アクセス権限とAuthor ID取得 |
| pad_connectionstatus.ts | `src/static/js/pad_connectionstatus.ts` | ソース | クライアント側接続状態管理 |
| pad_modals.ts | `src/static/js/pad_modals.ts` | ソース | モーダルダイアログ表示制御 |
| pad.html | `src/templates/pad.html` | テンプレート | モーダルHTML構造定義 |
| en.json | `src/locales/en.json` | 設定 | ローカライズ文字列 |
