# 通知設計書 5-メールアドレス変更確認メール

## 概要

本ドキュメントは、Ghost CMSにおけるメールアドレス変更確認メール（Update Email確認メール）の設計仕様を記載する。既存会員がメールアドレスを変更する際に送信される確認メールの送信ロジック、テンプレート構造、およびデータフローを定義する。

### 本通知の処理概要

本通知は、既存会員がアカウントのメールアドレスを変更する際に、新しいメールアドレスの所有権を確認するためのリンクを含むメールを送信する機能である。

**業務上の目的・背景**：会員がメールアドレスを変更する際に、新しいメールアドレスが本人のものであることを確認する。これにより、アカウントの乗っ取りや誤ったメールアドレスへの変更を防止する。

**通知の送信タイミング**：会員がPortalまたは会員向けAPI経由でメールアドレスの変更を要求した時点でトリガーされる。APIエンドポイント `POST /members/api/member/email` が呼び出される。

**通知の受信者**：変更先の新しいメールアドレスに送信される。現在のメールアドレスではなく、新しいメールアドレスの所有権を確認するため。

**通知内容の概要**：メールには新しいメールアドレスの確認用マジックリンクが含まれる。リンクをクリックすることでメールアドレスの変更が確定する。リンクは24時間で有効期限が切れる。

**期待されるアクション**：受信者はメール内の「Confirm email address」ボタンをクリックしてメールアドレスの変更を確定する。

## 通知種別

メール通知（トランザクションメール）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 同期 |
| 優先度 | 高 |
| リトライ | 無 |

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

1. 認証済み会員からのリクエストであることを確認
2. 新しいメールアドレスをリクエストボディから取得
3. 新しいメールアドレスに確認メールを送信

## 通知テンプレート

### メール通知の場合

| 項目 | 内容 |
|-----|------|
| 送信元アドレス | サイト設定の`members_support_address`または`noreply@[サイトドメイン]` |
| 送信元名称 | サイトタイトル |
| 件名 | `Confirm your email address` |
| 形式 | HTML + テキスト（マルチパート） |

### 本文テンプレート

```html
件名: Confirm your email address

本文:
Hey there,

Please confirm your email address with this link:

[Confirm email address ボタン]

For your security, the link will expire in 24 hours time.

---
You can also copy & paste this URL into your browser:
{magicLinkUrl}

If you did not make this request, you can simply delete this message.
This email address will not be used.

This message was sent from {siteDomain} to {email}.
```

### 添付ファイル

| ファイル名 | 形式 | 条件 | 説明 |
|----------|------|------|------|
| なし | - | - | 本通知には添付ファイルはない |

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| email | 送信先メールアドレス（新しいアドレス） | リクエストボディ | Yes |
| url | マジックリンクURL | tokenProvider.create() + getSigninURL() | Yes |
| accentColor | サイトのアクセントカラー | settingsCache.get('accent_color') | No（デフォルト: #15212A） |
| siteDomain | サイトドメイン | urlUtils.getSiteUrl()から抽出 | Yes |
| siteUrl | サイトURL | urlUtils.getSiteUrl() | Yes |

**注意**: このテンプレートにはsiteTitleが含まれていない（件名に{siteTitle}がない）

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| API呼び出し | POST /members/api/member/email | 認証済み会員からのリクエスト | メールアドレス変更リクエスト |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| 未認証 | 会員としてログインしていない場合 |
| 無効なメールアドレス | 新しいメールアドレスが不正形式の場合 |
| 同一メールアドレス | 現在のメールアドレスと同じ場合 |
| 既存メールアドレス | 他の会員が既に使用しているメールアドレスの場合 |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[POST /members/api/member/email] --> B{認証確認}
    B -->|未認証| C[401 Unauthorized]
    B -->|認証済み| D[新メールアドレス取得]
    D --> E{バリデーション}
    E -->|失敗| F[400 Bad Request]
    E -->|成功| G{既存チェック}
    G -->|既に使用中| H[エラー]
    G -->|使用可能| I[トークン生成]
    I --> J[update-emailテンプレート展開]
    J --> K[新メールアドレスにメール送信]
    K --> L[200 OK]
```

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

### 参照テーブル一覧

| テーブル名 | 用途 | 備考 |
|-----------|------|------|
| members | 現在の会員情報取得 | idで検索 |
| members | 新メールアドレスの重複チェック | emailで検索 |
| settings | サイト設定取得 | settingsCacheから取得 |

### 更新テーブル一覧

| テーブル名 | 操作 | 概要 |
|-----------|------|------|
| tokens | INSERT | マジックリンクトークンの保存 |

**注意**: membersテーブルの更新（email列の変更）は、このメール送信時点では行われない。会員がマジックリンクをクリックして確認を完了した時点で更新される。

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| UnauthorizedError | 未認証リクエスト | 401エラーを返却 |
| BadRequestError | メールアドレスが不正 | 400エラーを返却 |
| ConflictError | メールアドレスが既に使用中 | 409エラーを返却 |
| EENVELOPE | メール送信エラー | エラーログ出力 |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | 0 |
| リトライ間隔 | - |
| リトライ対象エラー | - |

## 配信設定

### レート制限

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

### 配信時間帯

特定の配信時間帯制限はない。

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

- マジックリンクトークンは24時間で有効期限切れ
- トークンには旧メールアドレス（oldEmail）と新メールアドレスの両方が含まれる
- 認証済み会員のみがメールアドレス変更を要求可能
- 新しいメールアドレスが既に使用中の場合はエラー
- 確認完了までは旧メールアドレスが有効のまま

## 備考

- メールアドレス変更は二段階プロセス：1) 変更リクエスト（確認メール送信）、2) 確認リンククリック（実際の変更）
- 確認完了時、MemberEmailChangeEventが発行される
- 旧メールアドレスへの通知は行われない（セキュリティ要件により検討が必要な場合あり）

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | update-email.js | `ghost/core/core/server/services/members/emails/update-email.js` | siteTitleがないシンプルなテンプレート構造 |

**読解のコツ**: update-email.jsは他のテンプレートと異なりsiteTitleを受け取らない。汎用的な「Confirm your email address」件名を使用。

#### Step 2: APIコントローラーを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | member-controller.js | `ghost/core/core/server/services/members/members-api/controllers/member-controller.js` | updateEmailAddress()メソッドの実装 |

**主要処理フロー**:
- 認証トークンの検証
- 新メールアドレスのバリデーション
- sendEmailWithMagicLink呼び出し（type='updateEmail'）

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

```
POST /members/api/member/email
    │
    ├─ MemberController.updateEmailAddress()
    │      │
    │      ├─ tokenService.decodeToken() - 認証確認
    │      │
    │      ├─ 新メールアドレスバリデーション
    │      │
    │      └─ sendEmailWithMagicLink()
    │             │
    │             └─ MagicLink.sendMagicLink()
    │                    │
    │                    └─ update-email.js テンプレート

--- マジックリンククリック後 ---

GET /members/api/session (token + oldEmail付き)
    │
    └─ getMemberDataFromMagicLinkToken()
           │
           ├─ tokenProvider.validate()
           │
           └─ users.update({email: newEmail}) - メールアドレス更新
```

### データフロー図

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

リクエストボディ ───────▶ MemberController
  - email (新)                 │
  + 認証トークン               ▼
                          認証確認
                               │
membersテーブル ◀────── 現在の会員情報取得
  (oldEmail)                   │
                               ▼
                         重複チェック
                               │
                               ▼
                         トークン生成
                         （email, oldEmail含む）
                               │
                               ▼
                         メール送信 ─────────▶ 新メールアドレス
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| update-email.js | `ghost/core/core/server/services/members/emails/update-email.js` | テンプレート | メールアドレス変更確認メールのHTMLテンプレート |
| member-controller.js | `ghost/core/core/server/services/members/members-api/controllers/member-controller.js` | コントローラー | updateEmailAddress APIエンドポイント |
| members-api.js | `ghost/core/core/server/services/members/members-api/members-api.js` | サービス | getMemberDataFromMagicLinkTokenでのメール変更処理 |
| member-repository.js | `ghost/core/core/server/services/members/members-api/repositories/member-repository.js` | リポジトリ | 会員データ更新 |
