# 機能設計書 156-SSL設定

## 概要

本ドキュメントは、Chatwootのヘルプセンターにおける「SSL設定」機能の設計仕様を定義する。この機能により、カスタムドメインを使用するポータルにSSL証明書を自動的にプロビジョニングし、HTTPSによるセキュアなアクセスを実現する。

### 本機能の処理概要

**業務上の目的・背景**：
企業がヘルプセンターをカスタムドメイン（例：help.example.com）で公開する場合、SSL証明書が必要となる。証明書の取得・更新は専門知識が必要で手間がかかるため、Cloudflare連携により自動化することで、管理者の負担を軽減し、セキュアなヘルプセンター公開を容易にする。

**機能の利用シーン**：
- 管理者がカスタムドメインを設定した後、SSL証明書の発行状況を確認する場面
- DNS設定が正しく完了しているか検証する場面
- SSL証明書の検証エラーを確認して対処する場面

**主要な処理内容**：
1. Cloudflare APIを通じてカスタムホスト名のSSL状態を確認
2. ポータルのssl_settingsに状態を保存
3. 検証エラーがある場合は詳細を返却

**関連システム・外部連携**：
- Cloudflare API：カスタムホスト名のSSL証明書管理
- DNS：CNAMEレコードによるドメイン検証

**権限による制御**：
- SSL状態確認：アカウントに所属する全ユーザーがアクセス可能

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 98 | ポータル設定画面 | 主画面 | カスタムドメイン用SSL設定 |

## 機能種別

外部API連携 / 状態確認 / Enterprise機能

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| id | String | Yes | ポータルのslug | Portal存在確認、custom_domain設定済み確認 |

### 入力データソース

- URL パラメータ：ポータルのslug
- API：`GET /api/v1/accounts/:account_id/portals/:id/ssl_status`（Enterprise版）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| status | String | SSL証明書の状態（cf_status） |
| verification_errors | Array | 検証エラーの詳細（cf_verification_errors） |

### 出力先

- JSON API レスポンス

## 処理フロー

### 処理シーケンス

```
1. SSL状態確認
   └─ リクエスト受信 → ポータル取得 → カスタムドメイン確認 → Cloudflare API呼び出し → ssl_settings更新 → レスポンス返却
```

### フローチャート

```mermaid
flowchart TD
    A[GET /portals/:id/ssl_status] --> B[fetch_portal]
    B --> C{Portalが存在?}
    C -->|No| D[404 Not Found]
    C -->|Yes| E[check_authorization]
    E --> F{custom_domain設定済み?}
    F -->|No| G[422 custom_domain_not_configured]
    F -->|Yes| H[Cloudflare::CheckCustomHostnameService.perform]
    H --> I{エラー?}
    I -->|Yes| J[422 エラー詳細]
    I -->|No| K[ssl_settingsから状態取得]
    K --> L[JSON Response: status, verification_errors]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-156-01 | カスタムドメイン必須 | SSL状態確認にはcustom_domainが設定されている必要がある | ssl_status実行時 |
| BR-156-02 | Enterprise機能 | この機能はEnterprise版でのみ利用可能 | ssl_status実行時 |
| BR-156-03 | ssl_settings保存 | Cloudflareから取得した状態はssl_settingsカラムに保存 | Cloudflare API応答時 |

### 計算ロジック

特になし

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| ssl_status | portals | SELECT | ポータル情報取得 |
| ssl_status | portals | UPDATE | ssl_settings更新（Cloudflare応答後） |

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

#### portals

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | custom_domain | - | カスタムドメイン取得 |
| SELECT | ssl_settings | - | 現在のSSL設定取得 |
| UPDATE | ssl_settings | cf_status, cf_verification_errors | Cloudflare応答で更新 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 422 | UnprocessableEntity | custom_domainが未設定 | カスタムドメインを設定 |
| 422 | UnprocessableEntity | Cloudflare APIエラー | Cloudflare設定を確認 |
| 404 | NotFound | 指定slugのPortalが存在しない | 正しいslugを指定 |

### リトライ仕様

Cloudflare API呼び出し失敗時のリトライはサービス側で実装（詳細は要確認）

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

- ssl_settingsの更新は単一UPDATE
- Cloudflare API呼び出しは外部依存のため非トランザクション

## パフォーマンス要件

- Cloudflare API呼び出しのため、レスポンス時間は外部依存
- 一般的に1-3秒程度

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

- Cloudflare APIキーの安全な管理
- SSL検証エラー情報の適切な表示（機密情報の漏洩防止）

## 備考

- この機能はEnterprise版でのみ利用可能
- Cloudflare::CheckCustomHostnameServiceの詳細実装は別ファイルで定義
- OSS版ではこのエンドポイントは存在しない

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | portal.rb | `app/models/portal.rb` | ssl_settingsカラムの定義 |

**読解のコツ**:
- **15行目**: `ssl_settings :jsonb not null` - SSL設定を格納するJSONBカラム

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | portals_controller.rb | `enterprise/app/controllers/enterprise/api/v1/accounts/portals_controller.rb` | ssl_statusアクション |

**主要処理フロー**:
- **2-14行目**: ssl_status - SSL状態確認のエントリーポイント

```ruby
def ssl_status
  return render_could_not_create_error(I18n.t('portals.ssl_status.custom_domain_not_configured')) if @portal.custom_domain.blank?

  result = Cloudflare::CheckCustomHostnameService.new(portal: @portal).perform

  return render_could_not_create_error(result[:errors]) if result[:errors].present?

  ssl_settings = @portal.ssl_settings || {}
  render json: {
    status: ssl_settings['cf_status'],
    verification_errors: ssl_settings['cf_verification_errors']
  }
end
```

#### Step 3: 認可ポリシーを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | portal_policy.rb | `app/policies/portal_policy.rb` | ssl_status?メソッド |

**主要処理フロー**:
- **34-36行目**: ssl_status? - アカウントユーザーであれば許可

```ruby
def ssl_status?
  @account.users.include?(@user)
end
```

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

```
Enterprise::Api::V1::Accounts::PortalsController#ssl_status
    │
    ├─ before_action :fetch_portal
    │      └─ Current.account.portals.find_by(slug:)
    │
    ├─ before_action :check_authorization
    │      └─ PortalPolicy#ssl_status?
    │
    ├─ custom_domain.blank? チェック
    │      └─ 422 custom_domain_not_configured
    │
    └─ Cloudflare::CheckCustomHostnameService.new(portal:).perform
           │
           ├─ Cloudflare API呼び出し
           │
           └─ @portal.ssl_settings 更新
                  ├─ cf_status
                  └─ cf_verification_errors
```

### データフロー図

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

GET Request         →   ssl_status               →   JSON Response
(portal_slug)            │                            { status:,
                        ├─ custom_domain確認           verification_errors: }
                        │
                        ├─ Cloudflare API
                        │     │
                        │     └─ SSL証明書状態取得
                        │
                        └─ ssl_settings更新
                                       ↓
                              [portals.ssl_settings UPDATE]
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| portal.rb | `app/models/portal.rb` | モデル | ssl_settingsカラム定義 |
| portals_controller.rb | `enterprise/app/controllers/enterprise/api/v1/accounts/portals_controller.rb` | コントローラー | ssl_statusアクション（Enterprise） |
| portal_policy.rb | `app/policies/portal_policy.rb` | ポリシー | ssl_status?認可 |
| check_custom_hostname_service.rb | `enterprise/app/services/cloudflare/check_custom_hostname_service.rb` | サービス | Cloudflare API連携（推定） |
