# 機能設計書：76-Ghost Admin

## 1. 概要

### 1.1 機能の目的
Ghost Adminは、Ghostブログプラットフォームの管理画面を提供するEmber.jsベースのSPA（シングルページアプリケーション）です。コンテンツ管理、会員管理、サイト設定、分析など、サイト運営に必要なすべての管理機能を統合的に提供します。

### 1.2 機能の範囲
- 記事・ページの作成・編集・削除（Lexicalエディタ）
- タグ管理
- 会員（Members）管理
- ユーザー（スタッフ）管理
- サイト設定（React Admin-Xアプリ連携）
- 分析ダッシュボード（Stats）
- ActivityPub連携
- テーマ管理
- 統合・Webhook管理
- セッション認証・権限管理

### 1.3 関連画面
| 画面No | 画面名 | 関連度 |
|--------|--------|--------|
| 1-30 | 管理画面全般 | 高 |
| 31-50 | 記事・ページ編集画面 | 高 |
| 51-70 | 会員管理画面 | 高 |
| 71-92 | 設定画面 | 高 |

## 2. 機能種別
- 種別：管理画面（SPA）
- 分類：管理画面
- カテゴリ：フルスタック管理アプリケーション

## 3. 入力仕様

### 3.1 認証情報
| フィールド | 型 | 必須 | 説明 |
|------------|-----|------|------|
| identification | string | Yes | ユーザーのメールアドレス |
| password | string | Yes | パスワード |
| token | string | No | 2FA検証トークン |

### 3.2 主要クエリパラメータ（記事一覧）
| パラメータ | 型 | 説明 |
|-----------|-----|------|
| type | string | フィルタタイプ（draft/published/scheduled/sent/featured） |
| visibility | string | アクセス制限（public/members/[paid,tiers]） |
| author | string | 著者スラグ |
| tag | string | タグスラグ |
| order | string | 並び順 |

## 4. 出力仕様

### 4.1 レンダリング出力
- DOM要素：`#ember-app`にEmberアプリケーションをマウント
- HTML5 History APIによるルーティング
- adminRoot: `/ghost/`配下で動作

### 4.2 APIエンドポイント連携
- Ghost Admin API: `/ghost/api/admin/`
- セッションAPI: `/ghost/api/admin/session`
- ファイルアップロード: `/ghost/api/admin/images/upload`

## 5. 処理フロー

### 5.1 アプリケーション起動フロー
```
1. Ember Application初期化
   ├─ loadInitializers(): 初期化処理
   ├─ Application.extend(): カスタムイベント設定
   └─ rootElement: #ember-app

2. Router設定
   ├─ locationType: HTML5 History API
   ├─ rootURL: /ghost/
   └─ Route定義（posts, pages, members, settings-x等）

3. 認証チェック
   ├─ AuthenticatedRoute.beforeModel()
   │   └─ session.requireAuthentication()
   └─ 未認証: signin ルートへリダイレクト
```

### 5.2 セッション認証フロー
```
1. Cookie Authenticator
   ├─ authenticate({identification, password, token})
   │   ├─ POST /ghost/api/admin/session
   │   └─ トークンのみ: PUT /ghost/api/admin/session/verify
   └─ invalidate(): DELETE /ghost/api/admin/session

2. SessionService
   ├─ populateUser(): ユーザー情報取得
   ├─ postAuthPreparation()
   │   ├─ configManager.fetchAuthenticated()
   │   ├─ feature.fetch()
   │   ├─ settings.fetch()
   │   ├─ membersUtils.fetch()
   │   └─ frontend.loginIfNeeded()
   └─ loadServerNotifications()
```

### 5.3 記事一覧取得フロー
```
1. PostsRoute.model(params)
   ├─ _getTypeFilters(): ステータスフィルタ生成
   ├─ filterParams設定（tag, visibility, authors）
   └─ infinity.model()でページネーション付きモデル生成

2. PostsWithAnalytics.afterInfinityModel(posts)
   ├─ 公開記事フィルタ
   ├─ postAnalytics.loadVisitorCounts()
   └─ postAnalytics.loadMemberCounts()

3. setupController()
   ├─ ユーザー一覧バックグラウンドロード
   ├─ selectionList設定
   └─ _fetchAnalyticsForPosts()
```

## 6. ビジネスルール

### 6.1 権限ルール
| ルールID | ルール内容 | 実装箇所 |
|----------|-----------|----------|
| BR-01 | Author/Contributorは自分の記事のみ表示 | routes/posts.js:99-103 |
| BR-02 | Author/Contributorは通知を受け取らない | services/session.js:137 |
| BR-03 | Author/Contributorは選択リスト無効 | routes/posts.js:150-151 |

### 6.2 記事フィルタルール
| タイプ | ステータスフィルタ |
|--------|-------------------|
| draft | status:draft |
| published | status:published |
| scheduled | status:scheduled |
| sent | status:sent |
| featured | featured:true |

### 6.3 並び順ルール
| 並び順 | 適用対象 |
|--------|----------|
| published_at desc | scheduled, published, sent |
| updated_at desc | draft |

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

本機能はEmber Dataを通じてGhost Admin APIと通信します。直接のデータベースアクセスは行いません。

### 7.1 主要モデル
| モデル | 説明 |
|--------|------|
| post | 記事 |
| page | ページ |
| tag | タグ |
| user | ユーザー |
| member | 会員 |
| setting | 設定 |
| newsletter | ニュースレター |
| tier | 料金プラン |
| integration | 統合 |
| webhook | Webhook |

## 8. エラー処理

### 8.1 認証エラー
| エラー種別 | 対応 |
|------------|------|
| 401 Unauthorized | セッション無効化、signinへリダイレクト |
| セッション復元失敗 | invalidate()呼び出し |
| 2FA検証失敗 | エラーメッセージ表示 |

### 8.2 React Admin-Xコンポーネントエラー
```javascript
// admin-x-component.js ErrorHandler
static getDerivedStateFromError() {
    return {hasError: true};
}
// エラー時はフォールバックUIを表示
```

### 8.3 Sentry統合
- `@sentry/ember`によるエラートラッキング
- 認証後にユーザーロール情報を付加
- `config.sentry_dsn`で有効化

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

本機能はクライアントサイドSPAのため、トランザクション管理はGhost CoreのAPIが担当します。
Ember DataのStore経由でCRUD操作を実行し、APIがトランザクションを管理します。

## 10. パフォーマンス要件

### 10.1 ページネーション
- 記事一覧: 30件/ページ
- infinity.modelによる無限スクロール

### 10.2 バックグラウンドロード
- ユーザー一覧: 非同期ロード
- タグ: 100件ずつページネーション
- Koenigエディタ: 事前フェッチ

### 10.3 React Admin-Xアプリ
- 動的インポート（Suspense + lazy loading）
- ビデオローディングインジケータ

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

### 11.1 認証
- Cookie認証（ember-simple-auth）
- 2FA対応（token検証）
- セッションエンドポイント: `/ghost/api/admin/session`

### 11.2 認可
- ルートレベルでの認証要求（AuthenticatedRoute）
- ユーザーロールによる機能制限
- Owner/Administrator/Editor/Author/Contributor

### 11.3 外部サービス連携
- Unsplash API: Client-IDヘッダー認証
- Sentry: DSNベースの設定

## 12. 備考

### 12.1 技術スタック
- Ember.js 3.24（Octane Edition）
- Ember Data 3.24
- ember-simple-auth 5.0
- ember-concurrency（タスク管理）
- ember-infinity（無限スクロール）
- @tryghost/koenig-lexical（リッチテキストエディタ）
- React 18（Admin-Xコンポーネント）

### 12.2 Admin-Xマイクロフロントエンド
Ghost AdminはEmber.jsベースですが、新機能はReactベースのAdmin-Xアプリとして開発されています。

| パッケージ | 機能 |
|-----------|------|
| @tryghost/admin-x-settings | 設定画面 |
| @tryghost/activitypub | ActivityPub管理 |
| @tryghost/posts | 記事分析 |
| @tryghost/stats | サイト分析 |

### 12.3 ルート構成
| ルート | パス | 説明 |
|--------|------|------|
| home | / | ホーム |
| posts | /posts | 記事一覧 |
| pages | /pages | ページ一覧 |
| lexical-editor | /editor | エディタ |
| tags | /tags | タグ管理 |
| members | /members | 会員管理 |
| settings-x | /settings | 設定（Admin-X） |
| stats-x | /analytics | 分析（Admin-X） |
| activitypub-x | /activitypub | ActivityPub（Admin-X） |

---

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

### 推奨読解順序

#### 1. アプリケーションエントリーポイント
**ファイル**: `ghost/admin/app/app.js`

```
Application.extend() (18-34行目)
├── Resolver: ember-resolver
├── modulePrefix: config設定
├── customEvents: タッチイベント無効化
└── rootElement: #ember-app
```

#### 2. ルーター設定
**ファイル**: `ghost/admin/app/router.js`

```
Router.map() (11-96行目)
├── home (12行目)
├── signin/signout (17-19行目)
├── posts/pages (36-45行目)
├── lexical-editor (47-50行目)
├── tags (52-54行目)
├── members (84-89行目)
├── settings-x (60-62行目): Admin-X React
├── stats-x (28-30行目): Admin-X React
└── activitypub-x (64-66行目): Admin-X React
```

#### 3. 認証システム
**ファイル**: `ghost/admin/app/authenticators/cookie.js`

```
Authenticator (6-62行目)
├── authenticate() (22-48行目)
│   ├── トークンのみ: PUT /session/verify
│   └── ID/パスワード: POST /session
└── invalidate() (51-60行目): DELETE /session
```

**ファイル**: `ghost/admin/app/services/session.js`

```
SessionService (11-165行目)
├── populateUser() (33-41行目)
├── postAuthPreparation() (43-78行目)
│   ├── configManager.fetchAuthenticated()
│   ├── feature.fetch()
│   ├── settings.fetch()
│   └── membersUtils.fetch()
├── handleAuthentication() (80-95行目)
├── requireAuthentication() (105-118行目)
└── loadServerNotifications() (135-149行目)
```

#### 4. 認証済みルートベース
**ファイル**: `ghost/admin/app/routes/authenticated.js`

```
AuthenticatedRoute (6-19行目)
└── beforeModel() (10-18行目)
    └── session.requireAuthentication()
```

#### 5. 記事管理ルート
**ファイル**: `ghost/admin/app/routes/posts.js`

```
PostsRoute (43-255行目)
├── queryParams (50-56行目): フィルタパラメータ
├── model() (78-133行目): データ取得
│   ├── _getTypeFilters(): ステータスフィルタ
│   ├── infinity.model(): ページネーション
│   └── PostsWithAnalytics: 分析データ付加
└── setupController() (136-159行目)
```

#### 6. Admin-Xコンポーネント統合
**ファイル**: `ghost/admin/app/components/admin-x/admin-x-component.js`

```
AdminXComponent (125-214行目)
├── importComponent() (48-89行目): 動的インポート
├── fetchComponent() (91-122行目): Suspense対応
├── externalNavigate (153-158行目): Ember→React遷移
├── additionalProps() (168行目): 追加プロパティ
└── ReactComponent() (170-213行目): レンダリング
    ├── ErrorHandler: エラーバウンダリ
    └── Suspense: ローディング状態
```

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

```
[Browser]
    │
    ▼
app.js::Application
    │
    ├── router.js::Router.map()
    │       │
    │       ├── [未認証ルート]
    │       │   ├── signin
    │       │   ├── signup
    │       │   └── reset
    │       │
    │       └── [認証済みルート]
    │           └── authenticated.js::beforeModel()
    │               │
    │               └── session.requireAuthentication()
    │
    └── services/session.js::SessionService
            │
            ├── authenticators/cookie.js
            │   ├── authenticate() → POST /session
            │   └── invalidate() → DELETE /session
            │
            ├── populateUser() → GET /users/me
            │
            └── postAuthPreparation()
                ├── configManager.fetchAuthenticated()
                ├── feature.fetch()
                ├── settings.fetch()
                └── membersUtils.fetch()

[記事管理]
routes/posts.js::PostsRoute
    │
    ├── model()
    │   ├── _getTypeFilters()
    │   └── infinity.model()
    │       └── PostsWithAnalytics
    │           └── postAnalytics.loadVisitorCounts()
    │
    └── setupController()
        └── _fetchAnalyticsForPosts()

[Admin-Xアプリ]
admin-x-component.js::AdminXComponent
    │
    ├── importComponent()
    │   └── dynamic import (https://...)
    │
    └── ReactComponent()
        ├── ErrorHandler
        └── Suspense
            └── AdminXApp
```

### データフロー図

```
[ユーザー操作]
    │
    ▼
[Ember Router]
    │
    ├── Route.model() ─────────────────┐
    │                                   │
    │                                   ▼
    │                           [Ghost Admin API]
    │                                   │
    │                                   ▼
    │                           [Ember Data Store]
    │                                   │
    ├── Controller ◄───────────────────┘
    │       │
    │       └── @tracked プロパティ
    │               │
    │               ▼
    └── Template/Component
            │
            ├── [Ember Components]
            │
            └── [Admin-X React Components]
                    │
                    ├── react-render modifier
                    │
                    └── AdminXComponent
                            │
                            ├── framework props
                            │   ├── ghostVersion
                            │   ├── externalNavigate
                            │   └── unsplashConfig
                            │
                            └── designSystem props
                                ├── fetchKoenigLexical
                                └── darkMode
```

### 関連ファイル一覧

| ファイルパス | 役割 | 重要度 |
|-------------|------|--------|
| ghost/admin/app/app.js | アプリケーションエントリー | 高 |
| ghost/admin/app/router.js | ルート定義 | 高 |
| ghost/admin/app/authenticators/cookie.js | Cookie認証 | 高 |
| ghost/admin/app/services/session.js | セッション管理 | 高 |
| ghost/admin/app/routes/authenticated.js | 認証済みルートベース | 高 |
| ghost/admin/app/routes/posts.js | 記事一覧ルート | 高 |
| ghost/admin/app/controllers/posts.js | 記事一覧コントローラ | 高 |
| ghost/admin/app/components/admin-x/admin-x-component.js | Admin-X統合 | 高 |
| ghost/admin/app/adapters/application.js | Ember Dataアダプタ | 中 |
| ghost/admin/app/models/*.js | データモデル定義 | 中 |
| ghost/admin/app/routes/settings-x.js | 設定ルート（Admin-X） | 中 |
| ghost/admin/app/routes/stats-x.js | 分析ルート（Admin-X） | 中 |
| ghost/admin/app/utils/ghost-paths.js | パスユーティリティ | 中 |
| ghost/admin/package.json | 依存関係定義 | 中 |
