# 通知設計書 21-アップデートチェック重要アラート

## 概要

本ドキュメントは、Ghost CMSにおけるアップデートチェック重要アラート通知機能の設計仕様を記載する。

### 本通知の処理概要

Ghost更新チェックサービス（updates.ghost.org）から重要なアラートを受信した際に、サイト管理者に対してメール通知を送信する機能である。セキュリティ修正や重大な更新情報など、サイト運営に影響を与える重要な情報を確実に管理者へ届けることを目的としている。

**業務上の目的・背景**：Ghost CMSはオープンソースのブログプラットフォームであり、セキュリティ脆弱性や重要なアップデート情報を迅速にサイト管理者に伝える必要がある。この通知機能により、管理者は重要なセキュリティパッチの適用やバージョンアップを適切なタイミングで実施でき、サイトの安全性と安定性を維持できる。

**通知の送信タイミング**：毎日1回定期実行されるアップデートチェックジョブにより、Ghost公式の更新チェックサービス（updates.ghost.org）へリクエストが送信される。レスポンスに含まれる通知メッセージのうち、`type: "alert"`のものがメール送信の対象となる。

**通知の受信者**：Ghostインスタンスに登録されているアクティブなユーザーのうち、「Owner」または「Administrator」ロールを持つユーザー全員にメールが送信される。

**通知内容の概要**：更新チェックサービスから返されるカスタム通知メッセージがそのままメール本文として使用される。通常、セキュリティ脆弱性の警告、緊急のアップデート要請、サービスに関する重要なお知らせなどが含まれる。

**期待されるアクション**：管理者はメール内容を確認し、必要に応じてGhostインスタンスのアップデート、セキュリティ対策の実施、または設定変更などの対応を行う。

## 通知種別

メール通知

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 非同期（ジョブスケジューラー経由） |
| 優先度 | 高 |
| リトライ | 無し（メール送信失敗時はログ出力のみ） |

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

1. `users`テーブルから`status: active`のユーザーを全件取得
2. 関連する`roles`テーブルを参照し、`name`が「Owner」または「Administrator」のロールを持つユーザーをフィルタリング
3. フィルタリングされた各ユーザーの`email`アドレスに対してメールを送信

```javascript
const {users} = await this.api.users.browse({
    limit: 'all',
    include: ['roles'],
    filter: 'status:active'
});

const adminEmails = users
    .filter(user => ['Owner', 'Administrator'].includes(user.roles[0].name))
    .map(user => user.email);
```

## 通知テンプレート

### メール通知の場合

| 項目 | 内容 |
|-----|------|
| 送信元アドレス | Ghostメール設定に依存（`config.mail`） |
| 送信元名称 | Ghostメール設定に依存 |
| 件名 | `Action required: Critical alert from Ghost instance {siteUrl}` |
| 形式 | HTML（テキストフォールバック付き） |

### 本文テンプレート

```html
{notification.message}
```

本文は更新チェックサービスから返されるメッセージがそのまま使用される。`forceTextContent: true`オプションによりテキスト形式でも送信される。

### 添付ファイル

なし

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| siteUrl | Ghostサイトの公開URL | config設定（`urlUtils.urlFor('home', true)`） | Yes |
| notification.message | 通知メッセージ本文 | 更新チェックサービスレスポンス | Yes |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| スケジュール | cronジョブ（毎日1回） | `type: "alert"`の通知が含まれる場合 | 更新チェックサービスからのレスポンスに重要アラートが含まれる場合にメール送信 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| プライバシー設定無効 | `privacy.useUpdateCheck: false`の場合、更新チェック自体が実行されない |
| 環境制限 | `NODE_ENV`が`development`または`production`以外の場合はチェックがスキップされる |
| 通知タイプ | `type`が`alert`以外の場合はメール送信されない（管理画面通知のみ） |
| チェック間隔 | `next_update_check`設定値が現在時刻より未来の場合はスキップ |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[cronジョブ起動] --> B{環境チェック}
    B -->|NG| C[終了]
    B -->|OK| D{プライバシー設定チェック}
    D -->|無効| C
    D -->|有効| E{next_update_checkチェック}
    E -->|未達| C
    E -->|到達| F[updateCheckRequest実行]
    F --> G[サービスレスポンス受信]
    G --> H{notificationsループ}
    H --> I{type == alert?}
    I -->|Yes| J[adminEmailsを取得]
    J --> K[各管理者にメール送信]
    K --> L[notifications.add API呼出]
    I -->|No| L
    L --> M{次の通知}
    M -->|あり| H
    M -->|なし| N[next_update_check更新]
    N --> O[終了]
```

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

### 参照テーブル一覧

| テーブル名 | 用途 | 備考 |
|-----------|------|------|
| settings | 更新チェック設定の取得 | `next_update_check`, `db_hash`, `active_theme`キー |
| users | 管理者メールアドレス取得 | `status:active`でフィルタ |
| roles | ユーザーロール確認 | Owner/Administratorを特定 |
| posts | 統計情報収集 | 投稿数カウント（オプトイン時） |

### テーブル別参照項目詳細

#### settings

| 参照項目（カラム名） | 用途 | 取得条件 |
|-------------------|------|---------|
| value | 設定値の取得 | `key = 'next_update_check'`、`key = 'db_hash'`、`key = 'active_theme'` |

#### users

| 参照項目（カラム名） | 用途 | 取得条件 |
|-------------------|------|---------|
| id | ユーザー識別 | `status = 'active'` |
| email | メール送信先 | rolesがOwner/Administrator |
| created_at | ブログ作成日特定 | 統計情報用 |

### 更新テーブル一覧

| テーブル名 | 操作 | 概要 |
|-----------|------|------|
| settings | UPDATE | `next_update_check`の更新 |
| settings | UPDATE | `notifications`配列への追加 |

#### settings更新詳細

| 操作 | 項目（カラム名） | 更新値 | 備考 |
|-----|-----------------|-------|------|
| UPDATE | value | 次回チェックタイムスタンプ | `key = 'next_update_check'`、現在時刻 + 24時間 |
| UPDATE | value | 通知配列にJSON追加 | `key = 'notifications'` |

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| HTTPリクエスト失敗 | 更新チェックサービスへの接続エラー | エラーログ出力、`next_update_check`を更新して次回チェックを設定 |
| 404レスポンス | 通知が存在しない | 正常終了として扱い、空の通知配列を返す |
| メール送信失敗 | SMTPエラーなど | エラーログ出力、処理は継続（他の管理者へのメール送信は試行） |
| JSONパースエラー | 不正なレスポンス形式 | エラーログ出力、`next_update_check`を更新 |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | 0回（HTTPリクエストタイムアウト1000ms） |
| リトライ間隔 | N/A |
| リトライ対象エラー | N/A（次回ジョブ実行まで待機） |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | 制限なし |
| 1日あたり上限 | 1回（ジョブスケジュールによる制限） |

### 配信時間帯

ジョブスケジューラーによりランダムな時刻（秒/分/時を0-59/0-59/0-23でランダム選択）で毎日1回実行される。これにより更新チェックサービスへのアクセス集中を防止している。

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

- 更新チェックサービスへ送信される統計情報はMD5ハッシュ化されたブログIDを使用
- `privacy.useUpdateCheck: false`設定によりデータ送信をオプトアウト可能
- メール送信は管理者権限を持つユーザーのみに限定
- ワーカースレッドで実行され、メインプロセスから分離

## 備考

- 通知グループ機能により、特定のグループ（例: "migration"）向けのカスタム通知をフィルタリング可能
- `forceUpdate`設定により、環境やスケジュールに関係なく強制的にチェックを実行可能
- 受信した通知はメール送信と同時に管理画面通知（`notifications.add`）としても登録される

---

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

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

### 推奨読解順序

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

まず、更新チェックサービスから返されるレスポンスの構造と、通知データの形式を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | update-check-service.js | `ghost/core/core/server/services/update-check/update-check-service.js` | 218-237行目のコメントでレスポンス構造が説明されている |

**読解のコツ**: レスポンス構造のコメント（218-237行目）を先に読み、`id`, `version`, `messages`, `created_at`, `custom`, `next_check`の各フィールドの意味を把握する。

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

処理の起点となるファイル・関数を特定する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | index.js | `ghost/core/core/server/services/update-check/index.js` | ジョブスケジューリングと初期化処理 |
| 2-2 | run-update-check.js | `ghost/core/core/server/services/update-check/run-update-check.js` | ワーカースレッドでの実行フロー |

**主要処理フロー**:
1. **75-86行目（index.js）**: `scheduleRecurringJobs()`でcronジョブを登録
2. **31-63行目（run-update-check.js）**: ワーカースレッドでの初期化と実行

#### Step 3: メイン処理ロジックを理解する

更新チェックの核心となるサービスクラスの処理を追う。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | update-check-service.js | `ghost/core/core/server/services/update-check/update-check-service.js` | UpdateCheckServiceクラス全体 |

**主要処理フロー**:
- **373-391行目**: `check()`メソッド - エントリーポイント
- **165-209行目**: `updateCheckRequest()`メソッド - HTTPリクエスト送信
- **249-303行目**: `updateCheckResponse()`メソッド - レスポンス処理
- **311-364行目**: `createCustomNotification()`メソッド - 通知作成とメール送信
- **343-358行目**: アラートタイプ判定とメール送信処理

#### Step 4: 管理者取得ロジックを理解する

メール送信先となる管理者ユーザーの取得処理を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | update-check-service.js | `ghost/core/core/server/services/update-check/update-check-service.js` | 318-327行目 |

**主要処理フロー**:
- **318-322行目**: ユーザー一覧取得（`api.users.browse`）
- **324-326行目**: Owner/Administratorロールでフィルタリング

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

```
scheduleRecurringJobs() [index.js:75-86]
    │
    └─ jobsService.addJob() → run-update-check.js
           │
           ├─ models.init()
           ├─ permissions.init()
           ├─ settings.init()
           ├─ tiers.init()
           │
           └─ updateCheck() [index.js:23-73]
                  │
                  └─ UpdateCheckService.check() [update-check-service.js:373-391]
                         │
                         ├─ api.settings.read('next_update_check')
                         │
                         ├─ updateCheckRequest() [165-209]
                         │      │
                         │      ├─ updateCheckData() [102-154]
                         │      │      └─ 統計情報収集
                         │      │
                         │      └─ request() → updates.ghost.org
                         │
                         └─ updateCheckResponse() [249-303]
                                │
                                ├─ api.settings.edit('next_update_check')
                                │
                                └─ createCustomNotification() [311-364]
                                       │
                                       ├─ api.users.browse() → 管理者取得
                                       │
                                       ├─ [type === 'alert'] sendEmail() → メール送信
                                       │
                                       └─ api.notifications.add() → 管理画面通知
```

### データフロー図

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

Ghost設定             UpdateCheckService
(settings) ─────────▶ .check()
                          │
                          ▼
updates.ghost.org ◀── updateCheckRequest() ──▶ レスポンスJSON
                          │
                          ▼
                    updateCheckResponse()
                          │
                          ▼
                    createCustomNotification()
                          │
                    ┌─────┴─────┐
                    ▼           ▼
              [type=alert]  [その他]
                    │           │
                    ▼           │
              sendEmail() ──────┼─────────────▶ 管理者メール
                    │           │
                    └─────┬─────┘
                          ▼
              api.notifications.add() ─────────▶ 管理画面通知
                          │
                          ▼
              api.settings.edit() ─────────────▶ 設定更新
                                                (next_update_check)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| update-check-service.js | `ghost/core/core/server/services/update-check/update-check-service.js` | ソース | 更新チェックのメインロジック |
| index.js | `ghost/core/core/server/services/update-check/index.js` | ソース | サービス初期化とジョブスケジューリング |
| run-update-check.js | `ghost/core/core/server/services/update-check/run-update-check.js` | ソース | ワーカースレッド実行スクリプト |
| notifications.js | `ghost/core/core/server/services/notifications/notifications.js` | ソース | 管理画面通知の管理 |
| default-settings.json | `ghost/core/core/server/data/schema/default-settings/default-settings.json` | 設定 | 通知設定のデフォルト値 |
