# 機能設計書 91-Explore連携

## 概要

本ドキュメントは、Ghost ExploreディレクトリへのGhostサイト情報の登録・同期機能について、その設計・仕様を詳細に記載したものである。

### 本機能の処理概要

Explore連携機能は、GhostサイトをGhost Exploreディレクトリ（公開サイトディレクトリ）に登録し、サイト情報を定期的に同期する機能である。これにより、Ghostコミュニティ内でサイトを発見可能にし、他のGhostユーザーとの接続を促進する。

**業務上の目的・背景**：Ghost Exploreは、Ghostで構築されたサイトを発見・共有できる公開ディレクトリである。サイト運営者がExploreに登録することで、新規読者の獲得やコミュニティとのネットワーク構築が可能になる。また、Ghost公式がエコシステムの成長を把握するためのデータ収集にも活用される。

**機能の利用シーン**：
- サイト運営者がGhost Exploreディレクトリへの掲載を希望する場合
- Ghostサーバー起動時に自動的にサイト情報をExploreに送信する場合
- サイト統計（メンバー数、MRR、投稿数など）をExploreと同期する場合
- 管理画面のExplore設定画面でサイト情報を確認する場合

**主要な処理内容**：
1. サイト基本情報（タイトル、URL、アイコン、説明文など）の収集
2. サイト統計情報（メンバー数、MRR、投稿数など）の収集
3. Exploreエンドポイントへのデータ送信（Ping）
4. 管理APIを通じたExploreデータの取得・表示

**関連システム・外部連携**：Ghost Explore API（外部サービス）とのHTTP通信によりデータを送受信する。Stripe連携が有効な場合はMRR（月間定期収益）情報も送信される。

**権限による制御**：Explore APIへのアクセスにはAdministrator以上のロールが必要。explore_pingおよびexplore_ping_growth設定によりデータ送信の有無を制御可能。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 53 | Explore設定 | 主機能 | Ghost Exploreディレクトリへの掲載設定 |
| 74 | Explore画面 | 主機能 | Ghost Exploreとの連携・閲覧 |
| 75 | Explore接続画面 | 主機能 | Ghost Exploreへの接続処理 |

## 機能種別

データ連携 / 外部API通信 / 設定管理

## 入力仕様

### 入力パラメータ

#### Explore Ping サービス

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| explore_ping | boolean | Yes | Exploreへのping送信有効/無効 | true/false |
| explore_ping_growth | boolean | Yes | 成長データ（メンバー数・MRR）の送信有無 | true/false |

#### Explore API エンドポイント

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| (認証のみ) | - | Yes | Admin API認証が必要 | 権限チェック |

### 入力データソース

- 設定キャッシュ（settingsCache）: explore_ping, explore_ping_growth, site_uuid, active_theme, facebook, twitter, stripe_connect_livemode
- 設定ファイル（config）: url, explore:update_url
- MembersService: メンバー数統計
- PostsService: 投稿統計（投稿総数、最新投稿日、初投稿日）
- StatsService: MRR統計
- StripeService: Stripe連携状態
- PublicConfigService: サイト情報（title, description, icon, accent_color, locale）
- UserModel: オーナーメールアドレス

## 出力仕様

### 出力データ

#### Explore Ping ペイロード

| 項目名 | 型 | 説明 |
|--------|-----|------|
| ghost | string | Ghostバージョン |
| site_uuid | string | サイト固有ID |
| url | string | サイトURL |
| theme | string | アクティブテーマ名 |
| facebook | string | Facebookアカウント |
| twitter | string | Twitterアカウント |
| posts_total | number/null | 公開投稿総数 |
| posts_last | string/null | 最新投稿日時（ISO 8601） |
| posts_first | string/null | 初投稿日時（ISO 8601） |
| members_total | number/null | メンバー総数（growth有効時のみ） |
| mrr | array | MRR情報（通貨別、livemode時のみ） |

#### Explore API レスポンス

| 項目名 | 型 | 説明 |
|--------|-----|------|
| version | string | Ghostバージョン |
| total_members | number | メンバー総数 |
| mrr_stats | object | MRR履歴データ |
| site.description | string | サイト説明文 |
| site.icon | string | サイトアイコンURL |
| site.title | string | サイトタイトル |
| site.url | string | サイトURL |
| site.accent_color | string | アクセントカラー |
| site.locale | string | サイトロケール |
| stripe.configured | boolean | Stripe設定有無 |
| stripe.livemode | boolean | Stripeライブモード有無 |
| most_recently_published_at | string/null | 最新公開日時 |
| total_posts_published | number/null | 公開投稿総数 |
| owner_email | string/null | オーナーメールアドレス |

### 出力先

- Ghost Explore API（外部サービス）へのHTTP POST
- Admin APIレスポンス（管理画面への表示用）

## 処理フロー

### 処理シーケンス

```
1. サーバー起動時
   └─ ExplorePingService.init() が呼び出される
2. Labs フラグ確認
   └─ labs.isSet('explore') をチェック
3. 設定確認
   └─ explore:update_url と explore_ping 設定を確認
4. ペイロード構築
   └─ 各サービスからデータを収集
5. HTTPリクエスト送信
   └─ Explore APIへPOSTリクエスト
6. レスポンスログ出力
   └─ 成功/失敗をログに記録
```

### フローチャート

```mermaid
flowchart TD
    A[サーバー起動] --> B{Labs 'explore' 有効?}
    B -->|No| Z[終了]
    B -->|Yes| C{explore_url 設定済み?}
    C -->|No| D[警告ログ出力]
    D --> Z
    C -->|Yes| E{explore_ping 有効?}
    E -->|No| F[情報ログ出力]
    F --> Z
    E -->|Yes| G[ペイロード構築]
    G --> H[投稿統計取得]
    H --> I{explore_ping_growth 有効?}
    I -->|Yes| J[メンバー統計取得]
    J --> K{Stripe livemode?}
    K -->|Yes| L[MRR取得]
    K -->|No| M[MRR空配列]
    I -->|No| N[成長データ省略]
    L --> O[HTTP POST送信]
    M --> O
    N --> O
    O --> P{送信成功?}
    P -->|Yes| Q[成功ログ]
    P -->|No| R[エラーログ]
    Q --> Z
    R --> Z
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | Labs制御 | exploreラボフラグが有効な場合のみping送信 | サーバー起動時 |
| BR-02 | Ping有効化 | explore_ping設定がtrueの場合のみ送信 | ペイロード送信前 |
| BR-03 | 成長データ制御 | explore_ping_growth設定がtrueの場合のみメンバー・MRR送信 | ペイロード構築時 |
| BR-04 | MRRデータ制限 | stripe_connect_livemodeがtrueの場合のみMRRデータ送信 | MRR取得時 |
| BR-05 | エラー継続 | 統計取得エラー時はnullを設定し処理継続 | 統計取得時 |

### 計算ロジック

特になし（統計データは各サービスから取得）

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| Exploreデータ取得 | users | SELECT | オーナーメールアドレス取得 |
| Exploreデータ取得 | settings | SELECT | 各種設定値取得（settingsCache経由） |

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

#### users

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | email | role = 'Owner' AND status = 'all' | オーナーユーザーのメール取得 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | 警告 | explore_url未設定 | ログ出力して処理終了 |
| - | 警告 | 投稿統計取得失敗 | nullを設定して処理継続 |
| - | 警告 | メンバー統計取得失敗 | nullを設定して処理継続 |
| - | 警告 | HTTPリクエスト失敗 | エラーログ出力して処理終了 |

### リトライ仕様

現行実装ではリトライ機能なし。サーバー起動時に1回のみ実行される。

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

データベースへの書き込み操作がないため、トランザクション管理は不要。

## パフォーマンス要件

- サーバー起動時に非同期で実行されるため、起動処理をブロックしない
- HTTPリクエストのタイムアウトは標準設定に従う

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

- オーナーメールアドレスがExplore APIに送信される（プライバシー考慮が必要）
- MRRデータはStripe livemodeの場合のみ送信（テストデータの混入防止）
- Admin APIアクセスには認証・権限チェックが必要
- explore_ping, explore_ping_growth設定により送信データを制御可能

## 備考

- 現行実装ではサーバー起動時に1回のみ実行。将来的にはスケジュール実行が予定されている
- Labs機能であり、有効化にはlabs設定が必要

---

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

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

### 推奨読解順序

#### Step 1: 設定・依存関係を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | default-settings.json | `ghost/core/core/server/data/schema/default-settings/default-settings.json` | explore_ping, explore_ping_growth設定のデフォルト値 |
| 1-2 | fixtures.json | `ghost/core/core/server/data/schema/fixtures/fixtures.json` | exploreに対する権限設定 |

**読解のコツ**: 設定ファイルでexplore関連の設定を検索し、デフォルト値と型定義を確認する。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | index.js (explore-ping) | `ghost/core/core/server/services/explore-ping/index.js` | サービス初期化とping実行のエントリーポイント |
| 2-2 | index.js (explore) | `ghost/core/core/server/services/explore/index.js` | ExploreServiceの依存関係注入 |

**主要処理フロー**:
1. **27-34行目**: init()関数でサービス作成とping実行
2. **33行目**: explorePingService.ping()を非同期で実行（結果を待たない）

#### Step 3: Explore Pingサービスを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | explore-ping-service.js | `ghost/core/core/server/services/explore-ping/explore-ping-service.js` | ping送信のメインロジック |

**主要処理フロー**:
- **32-88行目**: constructPayload()でペイロード構築
- **42-60行目**: 投稿統計取得（エラー時はnull設定）
- **62-85行目**: 成長データ取得（explore_ping_growth有効時）
- **69-76行目**: MRRデータ取得（livemode時のみ実データ）
- **90-109行目**: makeRequest()でHTTPリクエスト送信
- **111-129行目**: ping()でフロー制御

#### Step 4: Exploreサービス（API）を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | explore-service.js | `ghost/core/core/server/services/explore/explore-service.js` | Admin API用データ取得 |
| 4-2 | explore.js (endpoint) | `ghost/core/core/server/api/endpoints/explore.js` | APIエンドポイント定義 |
| 4-3 | explore.js (serializer) | `ghost/core/core/server/api/endpoints/utils/serializers/output/explore.js` | レスポンスシリアライズ |

**主要処理フロー**:
- **25-58行目**: fetchData()でExploreデータ収集
- **26-27行目**: メンバー数とMRR履歴取得
- **29行目**: PublicConfigServiceからサイト情報取得
- **49-52行目**: 投稿統計取得
- **54-55行目**: オーナーメールアドレス取得

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

```
[サーバー起動]
    │
    └─ explore-ping/index.js::init()
           │
           ├─ createService()
           │      └─ new ExplorePingService({...})
           │
           └─ explorePingService.ping()
                  │
                  ├─ labs.isSet('explore')
                  ├─ config.get('explore:update_url')
                  ├─ settingsCache.get('explore_ping')
                  │
                  └─ constructPayload()
                         │
                         ├─ posts.stats.getTotalPostsPublished()
                         ├─ posts.stats.getMostRecentlyPublishedPostDate()
                         ├─ posts.stats.getFirstPublishedPostDate()
                         │
                         └─ (explore_ping_growth有効時)
                                ├─ members.stats.getTotalMembers()
                                └─ statsService.api.mrr.getCurrentMrr()

[Admin API: GET /explore/]
    │
    └─ explore.js::read()
           │
           └─ exploreService.fetchData()
                  │
                  ├─ MembersService.stats.getTotalMembers()
                  ├─ StatsService.api.getMRRHistory()
                  ├─ PublicConfigService.site
                  ├─ PostsService.stats.getMostRecentlyPublishedPostDate()
                  ├─ PostsService.stats.getTotalPostsPublished()
                  └─ UserModel.findOne({role: 'Owner'})
```

### データフロー図

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

設定キャッシュ ──────────┐
  - explore_ping        │
  - explore_ping_growth │
  - site_uuid          │
  - active_theme       │
                       ▼
config設定 ────────────► ExplorePingService ──────► Explore API
  - url                       │                      (HTTPリクエスト)
  - explore:update_url       │
                             │
PostsService ─────────────────┤
  - 投稿統計                  │
                             │
MembersService ───────────────┤
  - メンバー数               │
                             │
StatsService ─────────────────┘
  - MRR統計


[Admin API]

MembersService ──────────┐
StatsService ────────────┤
PublicConfigService ─────┼───► ExploreService ──────► Admin API Response
PostsService ────────────┤          │                    (JSON)
StripeService ───────────┤          │
UserModel ───────────────┘          │
                                    ▼
                              frame.response
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| explore-ping-service.js | `ghost/core/core/server/services/explore-ping/explore-ping-service.js` | ソース | Explore pingサービス実装 |
| index.js | `ghost/core/core/server/services/explore-ping/index.js` | ソース | Explore pingサービス初期化 |
| explore-service.js | `ghost/core/core/server/services/explore/explore-service.js` | ソース | Explore APIサービス実装 |
| index.js | `ghost/core/core/server/services/explore/index.js` | ソース | Explore APIサービス初期化 |
| explore.js | `ghost/core/core/server/api/endpoints/explore.js` | ソース | APIエンドポイント定義 |
| explore.js | `ghost/core/core/server/api/endpoints/utils/serializers/output/explore.js` | ソース | レスポンスシリアライザ |
| default-settings.json | `ghost/core/core/server/data/schema/default-settings/default-settings.json` | 設定 | デフォルト設定値 |
| fixtures.json | `ghost/core/core/server/data/schema/fixtures/fixtures.json` | 設定 | 権限フィクスチャ |
| explore-ping-service.test.js | `ghost/core/test/unit/server/services/explore-ping/explore-ping-service.test.js` | テスト | ユニットテスト |
