# 機能設計書 192-直接アップロード

## 概要

本ドキュメントは、ChatwootにおけるActiveStorage直接アップロード機能の設計を記載したものである。この機能により、クライアントはサーバーを経由せずにファイルを直接ストレージサービス（S3等）にアップロードできる。

### 本機能の処理概要

**業務上の目的・背景**：大容量ファイルのアップロード時にサーバーリソースを消費することを避けるため、クライアントからストレージサービスに直接ファイルをアップロードする仕組みが必要である。この機能により、アプリケーションサーバーの負荷を軽減し、ファイルアップロードのスケーラビリティを向上させる。特に会話内での画像やドキュメントの共有において、ユーザー体験の向上に寄与する。

**機能の利用シーン**：エージェントやカスタマーが会話中に画像、動画、ドキュメントなどの大容量ファイルを添付する際に利用される。クライアント側でファイルを直接ストレージにアップロードすることで、アップロード中も他の操作を継続できる。

**主要な処理内容**：
1. クライアントからのダイレクトアップロードリクエスト受信
2. アカウントと会話の検証
3. ActiveStorageのダイレクトアップロード署名付きURLの生成
4. クライアントへの署名付きURLの返却

**関連システム・外部連携**：ActiveStorageを通じたクラウドストレージサービス（Amazon S3、Google Cloud Storage、Azure Blob Storage等）との連携。

**権限による制御**：アカウントに所属し、指定された会話にアクセス可能なユーザーのみがダイレクトアップロードを実行可能。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 11 | 会話詳細画面 | 主画面 | 会話内でのファイル添付・アップロード |

## 機能種別

ファイル操作（ダイレクトアップロード）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| conversation_id | Integer | Yes | 会話のdisplay_id | 存在する会話であること |
| blob[filename] | String | Yes | ファイル名 | ActiveStorage標準 |
| blob[byte_size] | Integer | Yes | ファイルサイズ（バイト） | ActiveStorage標準 |
| blob[checksum] | String | Yes | ファイルのMD5チェックサム（Base64） | ActiveStorage標準 |
| blob[content_type] | String | Yes | ファイルのMIMEタイプ | ActiveStorage標準 |

### 入力データソース

- クライアントからのAPIリクエスト（JSON形式）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| direct_upload[url] | String | ダイレクトアップロード用の署名付きURL |
| direct_upload[headers] | Object | アップロード時に必要なHTTPヘッダー |
| signed_id | String | ActiveStorage Blobの署名付きID |

### 出力先

- JSON形式でAPIレスポンスとして返却

## 処理フロー

### 処理シーケンス

```
1. リクエスト受信
   └─ EnsureCurrentAccountHelperによるアカウント検証
2. 会話の検証
   └─ display_idで会話を検索・取得
3. アクセス権限チェック
   └─ アカウントと会話の存在確認
4. 親クラスのcreate処理呼び出し
   └─ ActiveStorage::DirectUploadsController#create
5. 署名付きURL生成
   └─ ストレージサービス用の署名付きアップロードURL生成
6. レスポンス返却
   └─ 署名付きURLとBlob情報を含むJSONを返却
```

### フローチャート

```mermaid
flowchart TD
    A[リクエスト受信] --> B[current_account検証]
    B --> C[conversation検証]
    C --> D{会話が存在する?}
    D -->|No| E[処理終了（何も返さない）]
    D -->|Yes| F{アカウントが存在する?}
    F -->|No| E
    F -->|Yes| G[親クラスのcreate呼び出し]
    G --> H[ActiveStorage署名付きURL生成]
    H --> I[レスポンス返却]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-192-01 | 会話存在確認 | 指定されたdisplay_idの会話が現在のアカウントに存在すること | 常時 |
| BR-192-02 | アカウント検証 | 現在のアカウントが設定されていること | 常時 |

### 計算ロジック

なし

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 会話検索 | conversations | SELECT | display_idによる会話の検索 |
| Blob作成 | active_storage_blobs | INSERT | ダイレクトアップロード用Blob作成 |

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

#### conversations

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | display_id | params[:conversation_id]と一致 | Current.account.conversationsからの検索 |

#### active_storage_blobs

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | filename | blob[filename]パラメータ | - |
| INSERT | content_type | blob[content_type]パラメータ | - |
| INSERT | byte_size | blob[byte_size]パラメータ | - |
| INSERT | checksum | blob[checksum]パラメータ | - |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | 無応答 | 会話が存在しない場合 | 処理を終了（レスポンスなし） |
| - | 無応答 | アカウントが存在しない場合 | 処理を終了（レスポンスなし） |

### リトライ仕様

なし

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

- ActiveStorage Blobの作成は単一トランザクションで実行
- ダイレクトアップロード自体はクライアント側で実行されるため、サーバー側でのトランザクション管理は限定的

## パフォーマンス要件

- 署名付きURLの生成は高速に行われる（ストレージサービスへの接続不要）
- ダイレクトアップロードによりアプリケーションサーバーの負荷を軽減

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

- 認証済みユーザーのみアクセス可能
- 会話へのアクセス権限が必要
- 署名付きURLには有効期限が設定される
- チェックサムによるファイル整合性検証

## 備考

- このAPIはActiveStorage::DirectUploadsControllerを継承しており、RailsのActiveStorageダイレクトアップロード機能を活用
- 実際のファイルアップロードはクライアント側で署名付きURLに対して行われる

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | direct_uploads_controller.rb | `app/controllers/api/v1/accounts/conversations/direct_uploads_controller.rb` | ダイレクトアップロードの制御フロー |

**主要処理フロー**:
1. **1行目**: `ActiveStorage::DirectUploadsController`を継承
2. **2行目**: `EnsureCurrentAccountHelper`のinclude
3. **3-4行目**: `before_action`でアカウントと会話を検証
4. **6-10行目**: `create`アクション - 会話とアカウントの存在確認後に親クラス呼び出し
5. **14-16行目**: `conversation`メソッド - display_idによる会話検索

**読解のコツ**: このコントローラーはRailsの`ActiveStorage::DirectUploadsController`を継承している。`super`呼び出しにより、親クラスの`create`メソッドが署名付きURLの生成と返却を担当する。

#### Step 2: 親クラスを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | direct_uploads_controller.rb | `activestorage` gem内 | ActiveStorageの標準ダイレクトアップロード処理 |

**読解のコツ**: ActiveStorageのダイレクトアップロードは、クライアントがサーバーに対してBlob情報を送信し、サーバーが署名付きURLを返却する仕組み。クライアントは返却されたURLに対してファイルを直接アップロードする。

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

```
Api::V1::Accounts::Conversations::DirectUploadsController#create
    │
    ├─ EnsureCurrentAccountHelper#current_account (before_action)
    │
    ├─ conversation (before_action)
    │      └─ Current.account.conversations.find_by(display_id: ...)
    │
    └─ super (親クラスのcreate)
           └─ ActiveStorage::DirectUploadsController#create
                  └─ ActiveStorage::Blob.create_before_direct_upload!
```

### データフロー図

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

conversation_id      ───▶ conversation検証
                              │
blob[filename]       ───▶     │
blob[byte_size]      ───▶ DirectUploadsController#create
blob[checksum]       ───▶     │
blob[content_type]   ───▶     ▼
                         ActiveStorage::DirectUploadsController#create
                              │
                              ▼
                         Blob.create_before_direct_upload!
                              │
                              ▼
                         署名付きURL生成 ───▶ { direct_upload: {url, headers}, signed_id }
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| direct_uploads_controller.rb | `app/controllers/api/v1/accounts/conversations/direct_uploads_controller.rb` | ソース | ダイレクトアップロードのコントローラー |
| ensure_current_account_helper.rb | `app/controllers/concerns/ensure_current_account_helper.rb` | ソース | アカウント検証ヘルパー |
| routes.rb | `config/routes.rb` | 設定 | ルーティング定義（141行目） |
