# 機能設計書 131-Web Widget

## 概要

本ドキュメントは、Chatwootシステムにおける「Web Widget」機能の設計仕様を記載する。Web Widgetは、顧客のWebサイトに埋め込んでリアルタイムチャットを提供するためのチャネル機能である。

### 本機能の処理概要

Web Widgetは、企業が自社のWebサイトに埋め込み可能なチャットウィジェットを提供し、Webサイト訪問者とリアルタイムでコミュニケーションを取ることを可能にする機能である。

**業務上の目的・背景**：
企業がWebサイト上で顧客とリアルタイムにコミュニケーションを取る必要性が高まっている。Web Widgetは、訪問者が問い合わせやサポートを求める際の障壁を下げ、顧客満足度の向上と売上機会の創出を実現する。また、24時間365日対応可能なチャット環境を構築することで、従来のメールや電話サポートの負荷軽減にも貢献する。

**機能の利用シーン**：
- Webサイト訪問者が商品やサービスについて質問する場合
- カスタマーサポートチームがリアルタイムで問い合わせに対応する場合
- 営業担当者がリード獲得のためにプロアクティブなコミュニケーションを行う場合
- プリチャットフォームで訪問者の情報を事前収集する場合

**主要な処理内容**：
1. Webサイトへの埋め込み用JavaScriptスクリプトの生成
2. Widget設定（色、ウェルカムメッセージ、返信予想時間等）の管理
3. プリチャットフォームの設定とカスタマイズ
4. HMACによるユーザー認証（オプション）
5. 許可ドメインの制限設定
6. 機能フラグ（添付ファイル、絵文字ピッカー、会話終了ボタン等）の制御
7. ポータル（ヘルプセンター）との連携

**関連システム・外部連携**：
- フロントエンドWidget SDK（JavaScript）
- WebSocket接続によるリアルタイム通信
- ActionCable（Rails）によるリアルタイムメッセージング
- ポータル（ヘルプセンター）機能との連携

**権限による制御**：
- インボックスの作成・編集にはAdministrator権限が必要
- エージェントはインボックスメンバーとして割り当てられることで会話に参加可能
- HMACトークンの表示・管理はAdministratorのみ

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 47 | インボックス一覧画面 | 参照画面 | インボックスの一覧表示 |
| 48 | インボックス新規作成画面 | 主画面 | 新しいWebWidgetインボックスの作成 |
| 49 | チャネル設定画面 | 参照画面 | チャネルタイプの詳細設定 |
| 50 | インボックス設定画面 | 主画面 | 既存インボックスの詳細設定 |
| 114 | ホーム画面（Widget） | 参照画面 | ウィジェットホーム画面表示 |
| 115 | プリチャットフォーム画面（Widget） | 参照画面 | チャット前の情報入力 |
| 116 | メッセージ画面（Widget） | 参照画面 | チャットメッセージの送受信 |

## 機能種別

CRUD操作 / 設定管理 / リアルタイム通信

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| website_url | String | Yes | ウィジェット設置先URL | URL形式必須 |
| widget_color | String | Yes | ウィジェットのテーマ色 | デフォルト: #1f93ff |
| welcome_title | String | No | ウェルカムメッセージのタイトル | 任意の文字列 |
| welcome_tagline | String | No | ウェルカムメッセージのサブタイトル | 任意の文字列 |
| reply_time | Integer | No | 予想返信時間 | 0: 数分, 1: 数時間, 2: 1日 |
| pre_chat_form_enabled | Boolean | No | プリチャットフォーム有効化 | デフォルト: false |
| pre_chat_form_options | JSONB | No | プリチャットフォーム設定 | 定義されたスキーマに従う |
| continuity_via_email | Boolean | No | メールによる継続対応 | デフォルト: true |
| hmac_mandatory | Boolean | No | HMAC認証の必須化 | デフォルト: false |
| allowed_domains | Text | No | 許可ドメイン（カンマ区切り） | デフォルト: "" |
| selected_feature_flags | Array | No | 有効化する機能フラグ | attachments, emoji_picker, end_conversation, use_inbox_avatar_for_bot |

### 入力データソース

- 管理画面でのユーザー入力
- API経由でのプログラマティック設定

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| website_token | String | ウィジェット識別用トークン（自動生成） |
| hmac_token | String | HMAC検証用シークレットトークン（自動生成） |
| web_widget_script | String | 埋め込み用JavaScriptコード |
| inbox_id | Integer | 作成されたインボックスID |

### 出力先

- 管理画面への表示
- API レスポンス（JSON形式）
- 埋め込み用スクリプト（JavaScript）

## 処理フロー

### 処理シーケンス

```
1. インボックス作成リクエスト受信
   └─ 管理者がチャネルタイプ「web_widget」を選択

2. バリデーション実行
   └─ website_url の存在確認
   └─ widget_color の存在確認

3. チャネル作成
   └─ Channel::WebWidget レコード作成
   └─ website_token 自動生成（SecureRandom）
   └─ hmac_token 自動生成（SecureRandom）

4. インボックス作成
   └─ Inbox レコード作成
   └─ チャネルとの関連付け

5. プリチャットフォームオプション初期化
   └─ デフォルトフィールド（Email, Full name, Phone number）設定

6. 埋め込みスクリプト生成
   └─ website_token を含むJavaScriptコード生成
```

### フローチャート

```mermaid
flowchart TD
    A[開始: インボックス作成リクエスト] --> B{チャネルタイプ = web_widget?}
    B -->|No| C[他チャネル処理へ]
    B -->|Yes| D[バリデーション実行]
    D --> E{必須パラメータ存在?}
    E -->|No| F[エラーレスポンス]
    E -->|Yes| G[Channel::WebWidget作成]
    G --> H[website_token生成]
    H --> I[hmac_token生成]
    I --> J[Inbox作成]
    J --> K[プリチャットフォームオプション初期化]
    K --> L[成功レスポンス]
    L --> M[終了]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-131-01 | 一意トークン | website_tokenとhmac_tokenは一意である必要がある | チャネル作成時 |
| BR-131-02 | ドメイン制限 | allowed_domainsが設定されている場合、指定ドメイン以外からのアクセスは拒否 | ウィジェットアクセス時 |
| BR-131-03 | HMAC必須時認証 | hmac_mandatoryがtrueの場合、HMAC署名なしのリクエストは拒否 | ウィジェットAPI呼び出し時 |
| BR-131-04 | 機能フラグデフォルト | feature_flagsはデフォルトで7（attachments, emoji_picker, end_conversationが有効） | チャネル作成時 |

### 計算ロジック

機能フラグはビットフラグとして実装されている:
- 1: attachments（添付ファイル機能）
- 2: emoji_picker（絵文字ピッカー）
- 4: end_conversation（会話終了ボタン）
- 8: use_inbox_avatar_for_bot（ボット用インボックスアバター使用）

デフォルト値7 = 1 + 2 + 4（attachments, emoji_picker, end_conversationが有効）

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| チャネル作成 | channel_web_widgets | INSERT | 新規WebWidgetチャネル作成 |
| インボックス作成 | inboxes | INSERT | インボックス作成とチャネル関連付け |
| 設定更新 | channel_web_widgets | UPDATE | ウィジェット設定の更新 |
| 削除 | channel_web_widgets | DELETE | チャネル削除（非同期） |
| 削除 | inboxes | DELETE | インボックス削除（非同期） |

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

#### channel_web_widgets

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | account_id | Current.account.id | アカウントID |
| INSERT | website_url | params[:website_url] | 設置先URL |
| INSERT | widget_color | params[:widget_color] or '#1f93ff' | テーマ色 |
| INSERT | website_token | SecureRandom自動生成 | 一意制約あり |
| INSERT | hmac_token | SecureRandom自動生成 | 一意制約あり |
| INSERT | feature_flags | 7 | デフォルト値 |
| UPDATE | 各設定カラム | params値 | 許可された属性のみ |

#### inboxes

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | name | 'Website' or params[:name] | インボックス名 |
| INSERT | channel_type | 'Channel::WebWidget' | チャネルタイプ |
| INSERT | channel_id | 作成されたチャネルID | ポリモーフィック関連 |
| INSERT | account_id | Current.account.id | アカウントID |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 422 | Validation Error | website_urlが空 | 必須項目の入力を促す |
| 422 | Validation Error | website_tokenの重複 | 再生成（システム内部処理） |
| 403 | Forbidden | HMAC検証失敗 | 正しいHMAC署名で再リクエスト |
| 403 | Forbidden | 許可ドメイン外からのアクセス | 許可ドメインリストの更新 |

### リトライ仕様

トークン生成時に重複が発生した場合、has_secure_tokenにより自動的に再生成される。

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

チャネル作成とインボックス作成はActiveRecord::Base.transactionでラップされ、いずれかが失敗した場合はロールバックされる。

## パフォーマンス要件

- ウィジェットスクリプトの読み込み: 500ms以内
- API レスポンス: 200ms以内
- website_tokenとhmac_tokenにはユニークインデックスが設定

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

- HMAC認証によるユーザー検証が可能
- 許可ドメインによるアクセス制限
- website_tokenは公開されるが、hmac_tokenは管理者のみアクセス可能
- CSRFトークンによる保護（管理API）

## 備考

- ウィジェットはVue.jsで実装されたSPA
- WebSocket（ActionCable）によるリアルタイム通信
- ポータル（ヘルプセンター）と連携可能

---

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

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

### 推奨読解順序

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

まず、Web Widgetのデータモデルとデータベーススキーマを理解することが重要である。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | web_widget.rb | `app/models/channel/web_widget.rb` | チャネルモデルの定義、EDITABLE_ATTRS、feature_flags |
| 1-2 | channelable.rb | `app/models/concerns/channelable.rb` | 共通のチャネル機能（belongs_to :account, has_one :inbox） |

**読解のコツ**:
- `has_flags`メソッドはFlagShihTzuライブラリによるビットフラグ実装
- `has_secure_token`はRailsの機能で、一意のトークンを自動生成
- `EDITABLE_ATTRS`は更新可能な属性のホワイトリスト

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

管理APIのコントローラーがエントリーポイントとなる。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | inboxes_controller.rb | `app/controllers/api/v1/accounts/inboxes_controller.rb` | インボックスCRUD操作の実装 |

**主要処理フロー**:
1. **30-42行目**: create アクション - チャネル作成とインボックス作成をトランザクション内で実行
2. **45-51行目**: update アクション - インボックスとチャネルの更新
3. **107-110行目**: create_channel - チャネルタイプに応じたチャネル作成
4. **113-115行目**: allowed_channel_types - 許可されるチャネルタイプの定義

#### Step 3: Widget API層を理解する

Widgetからのリクエストを処理するAPIコントローラーを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | base_controller.rb | `app/controllers/api/v1/widget/base_controller.rb` | Widget API基底クラス、認証処理 |
| 3-2 | configs_controller.rb | `app/controllers/api/v1/widget/configs_controller.rb` | Widget設定取得、連絡先作成 |

**主要処理フロー**:
- **4-7行目**: create アクション - 連絡先作成とトークン発行
- **28-33行目**: build_contact - ContactInboxWithContactBuilderを使用した連絡先作成
- **35-38行目**: set_token - Widgetトークンサービスによるトークン生成

#### Step 4: モデルの詳細を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | web_widget.rb | `app/models/channel/web_widget.rb` | 全体構造の再確認 |

**主要処理フロー**:
- **29-33行目**: テーブル名とEDITABLE_ATTRSの定義
- **42-43行目**: バリデーション（website_url, widget_color必須）
- **47-48行目**: secure token生成（website_token, hmac_token）
- **50-55行目**: feature_flags（ビットフラグ）の定義
- **57行目**: reply_time の enum定義
- **63-81行目**: web_widget_script - 埋め込み用JavaScriptの生成
- **83-100行目**: validate_pre_chat_options - プリチャットフォームオプションの初期化
- **102-107行目**: create_contact_inbox - 連絡先インボックス作成

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

```
Api::V1::Accounts::InboxesController#create
    |
    +-- create_channel
    |       |
    |       +-- Channel::WebWidget.create!
    |               |
    |               +-- has_secure_token :website_token
    |               +-- has_secure_token :hmac_token
    |               +-- validate_pre_chat_options (before_validation)
    |
    +-- Current.account.inboxes.build
            |
            +-- Inbox.save!

Api::V1::Widget::ConfigsController#create
    |
    +-- set_web_widget (before_action)
    |       |
    |       +-- WebsiteTokenHelper
    |
    +-- build_contact
    |       |
    |       +-- @web_widget.create_contact_inbox
    |               |
    |               +-- ContactInboxWithContactBuilder.new.perform
    |
    +-- set_token
            |
            +-- Widget::TokenService.new.generate_token
```

### データフロー図

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

管理者入力           --> InboxesController#create     --> Inbox + Channel作成
(website_url,            |                                 |
 widget_color等)         +-- Channel::WebWidget.create!    +-- website_token
                         +-- Inbox.create!                  +-- hmac_token
                                                            +-- web_widget_script

Widgetアクセス       --> Widget::ConfigsController    --> トークン + 設定
(website_token)          |                                 |
                         +-- set_web_widget                +-- JWT Token
                         +-- build_contact                 +-- Widget Config
                         +-- set_token
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| web_widget.rb | `app/models/channel/web_widget.rb` | モデル | WebWidgetチャネルのデータモデル |
| channelable.rb | `app/models/concerns/channelable.rb` | Concern | チャネル共通機能 |
| inboxes_controller.rb | `app/controllers/api/v1/accounts/inboxes_controller.rb` | コントローラー | インボックスCRUD API |
| base_controller.rb | `app/controllers/api/v1/widget/base_controller.rb` | コントローラー | Widget API基底クラス |
| configs_controller.rb | `app/controllers/api/v1/widget/configs_controller.rb` | コントローラー | Widget設定API |
| App.vue | `app/javascript/widget/App.vue` | Vue | WidgetメインVueコンポーネント |
| router.js | `app/javascript/widget/router.js` | JavaScript | Widgetルーティング |
| index.js | `app/javascript/widget/store/index.js` | JavaScript | Vuexストア |
| conversation.js | `app/javascript/widget/api/conversation.js` | JavaScript | 会話API呼び出し |
| message.js | `app/javascript/widget/api/message.js` | JavaScript | メッセージAPI呼び出し |
