# 機能設計書 6-パッド作成

## 概要

本ドキュメントは、Etherpad APIにおける「パッド作成」機能の設計仕様を記載する。

### 本機能の処理概要

新しいパッドを作成する機能である。パッドはEtherpadにおける共同編集ドキュメントの基本単位であり、本機能により任意のIDと初期テキストでパッドを作成できる。

**業務上の目的・背景**：
Etherpadの中核機能として、共同編集のためのドキュメント（パッド）を作成する必要がある。本機能により、プログラムからパッドを作成でき、外部システムとの連携やテンプレートベースのパッド生成が可能になる。

**機能の利用シーン**：
- 外部システムからパッドを自動作成する場合
- テンプレートテキストを持つパッドを作成する場合
- パッドIDを指定して作成したい場合
- バッチ処理で複数パッドを作成する場合

**主要な処理内容**：
1. パッドIDのバリデーション（グループパッド形式の除外、特殊文字チェック）
2. パッドの重複確認
3. パッドの初期化と初期テキストの設定
4. データベースへの保存
5. パッドキャッシュへの登録

**関連システム・外部連携**：
- ueberDB2データベースによるデータ永続化
- フック機構（padDefaultContent, padCreate, padLoad）

**権限による制御**：
APIキーまたはOAuth2トークンによる認証が必要。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 1 | トップページ（パッド作成） | 主機能 | 新規パッド名を入力してパッドを作成する |
| 10 | パッド管理画面 | 補助機能 | 新規パッド作成ダイアログからパッドを作成 |

## 機能種別

CRUD操作（Create）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| padID | string | Yes | 作成するパッドのID | 1-50文字、$や特殊文字を含まないこと |
| text | string | No | パッドの初期テキスト | 100,000文字以下 |
| authorId | string | No | 作成者の著者ID | 空文字列可 |
| apikey | string | Yes（APIキー認証時） | API認証キー | 設定ファイルのAPIキーと一致すること |

### 入力データソース

HTTP POSTリクエスト（`/api/2/pads`）のボディから取得。

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| code | number | 処理結果コード（0: 成功） |
| message | string | 処理結果メッセージ（"ok"） |
| data | null | 作成処理のため戻り値なし |

### 出力先

HTTPレスポンス（JSON形式）

## 処理フロー

### 処理シーケンス

```
1. API認証
   └─ APIキーまたはOAuth2トークンを検証
2. パッドIDバリデーション
   └─ $を含まないことを確認（グループパッド形式除外）
   └─ 特殊文字（/、?、&、#）を含まないことを確認
3. パッド重複確認
   └─ doesPadExists(padID)を呼び出し
   └─ 存在する場合はエラー返却
4. パッド作成
   └─ padManager.getPad(padID, text, authorId)を呼び出し
   └─ 内部でPad.init()が実行される
5. データベース保存
   └─ pad.saveToDatabase()が自動実行
6. フック実行
   └─ padCreate フックを実行
7. レスポンス返却
```

### フローチャート

```mermaid
flowchart TD
    A[POST /api/2/pads] --> B{認証検証}
    B -->|失敗| C[401 Unauthorized]
    B -->|成功| D{padIDに$が含まれる?}
    D -->|Yes| E["400 createPad can't create group pads"]
    D -->|No| F{特殊文字が含まれる?}
    F -->|Yes| G[400 malformed padID]
    F -->|No| H[doesPadExists padID]
    H --> I{パッド存在?}
    I -->|Yes| J[400 padID does already exist]
    I -->|No| K["padManager.getPad(padID, text, authorId)"]
    K --> L[Pad.init text, authorId]
    L --> M[padCreate hook]
    M --> N["返却 {data: null}"]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-001 | グループパッド除外 | createPadではグループパッド（$含む）は作成不可 | 常に |
| BR-002 | 特殊文字禁止 | URLで問題になる特殊文字（/、?、&、#）は禁止 | 常に |
| BR-003 | 重複禁止 | 同じpadIDのパッドは作成不可 | 常に |
| BR-004 | デフォルトテキスト | textが未指定の場合はsettings.defaultPadTextを使用 | text未指定時 |
| BR-005 | テキスト長制限 | 初期テキストは100,000文字以下 | text指定時 |

### 計算ロジック

- パッドID正規表現: `/^(g.[a-zA-Z0-9]{16}\$)?[^$]{1,50}$/`
- 特殊文字チェック: `/(\/|\?|&|#)/`

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| パッド存在確認 | pad:{padID} | SELECT | 重複チェック |
| パッド作成 | pad:{padID} | INSERT | パッド本体作成 |
| リビジョン作成 | pad:{padID}:revs:0 | INSERT | 初期リビジョン作成 |
| 著者登録 | author:{authorId} | UPDATE | 著者のパッドリストに追加 |

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

#### pad:{padID}

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | atext | 初期テキストのattributed text | 属性付きテキスト |
| INSERT | pool | 初期属性プール | AttributePool |
| INSERT | head | 0 | 初期リビジョン番号 |
| INSERT | chatHead | -1 | チャットなし |
| INSERT | publicStatus | false | 非公開 |
| INSERT | savedRevisions | [] | 保存済みリビジョンなし |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 1 | パラメータエラー | padIDに$が含まれる | createGroupPadを使用 |
| 1 | パラメータエラー | padIDに特殊文字が含まれる | 特殊文字を除去 |
| 1 | パラメータエラー | padIDが既に存在する | 別のpadIDを指定 |
| 1 | パラメータエラー | textが文字列でない | 文字列を指定 |
| 1 | パラメータエラー | textが100,000文字超 | テキストを短縮 |
| 4 | 認証エラー | APIキーが無効 | 正しいAPIキーを指定 |

### リトライ仕様

データベース操作失敗時のリトライはueberDB2層で処理される。

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

パッド作成はPad.init()とappendRevision()で処理される。完全なトランザクション管理は行われないが、単一パッドの作成は通常アトミックに完了する。

## パフォーマンス要件

- レスポンス時間: 通常200ms以下
- 初期テキストのサイズに応じて処理時間が増加

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

- APIキーまたはOAuth2トークンによる認証が必須
- パッドIDの長さ制限（1-50文字）でDoS攻撃を軽減
- テキスト長制限（100,000文字）でリソース枯渇を防止

## 備考

- グループパッドを作成する場合はcreateGroupPad APIを使用
- APIバージョン1.3.0からauthorIdパラメータが追加

---

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

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

### 推奨読解順序

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

パッドデータの構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | Pad.ts | `src/node/db/Pad.ts` | Padクラスのプロパティ（atext, pool, head等） |
| 1-2 | PadType.ts | `src/node/types/PadType.ts` | パッドの型定義 |

**読解のコツ**: Padクラスは`atext`（属性付きテキスト）、`pool`（属性プール）、`head`（ヘッドリビジョン）などのプロパティを持つ。`init()`メソッドで初期化される。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | RestAPI.ts | `src/node/handler/RestAPI.ts` | POST /pads のルーティング |
| 2-2 | APIHandler.ts | `src/node/handler/APIHandler.ts` | createPadパラメータ定義 |

**主要処理フロー**:
1. **1307-1333行目（RestAPI.ts）**: POST /pads のルーティング定義
2. **139行目（APIHandler.ts）**: `createPad: ['padID', 'text', 'authorId']` パラメータ定義

#### Step 3: コアロジックを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | API.ts | `src/node/db/API.ts` | createPad関数の実装 |
| 3-2 | PadManager.ts | `src/node/db/PadManager.ts` | getPad関数（パッド作成も担当） |
| 3-3 | Pad.ts | `src/node/db/Pad.ts` | init関数の実装 |

**主要処理フロー**:
- **506-521行目（API.ts）**: createPad関数の実装
  - **508-511行目**: $を含むpadIDのチェック
  - **514-516行目**: 特殊文字チェック
  - **520行目**: `getPadSafe(padID, false, text, authorId)` 呼び出し
- **109-144行目（PadManager.ts）**: getPad関数
  - **111-113行目**: padID有効性チェック
  - **116-126行目**: textバリデーション
  - **136行目**: `new Pad.Pad(id)` でPadインスタンス作成
  - **139行目**: `pad.init(text, authorId)` で初期化
- **382-401行目（Pad.ts）**: init関数
  - **384行目**: 既存パッドの読み込み
  - **391-398行目**: 新規パッドの場合の初期化

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

```
POST /api/2/pads (RestAPI.ts)
    │
    ├─ APIHandler.handle() (APIHandler.ts:161)
    │      │
    │      ├─ 認証検証 (APIHandler.ts:175-200)
    │      │
    │      └─ api.createPad(padID, text, authorId) (API.ts:506)
    │              │
    │              ├─ バリデーション（$、特殊文字）
    │              │
    │              └─ getPadSafe(padID, false, text, authorId) (API.ts:894)
    │                      │
    │                      ├─ padManager.isValidPadId(padID)
    │                      │
    │                      ├─ padManager.doesPadExists(padID)
    │                      │
    │                      └─ padManager.getPad(padID, text, authorId)
    │                              │
    │                              └─ Pad.init(text, authorId) (Pad.ts:382)
    │                                      │
    │                                      ├─ hooks.aCallAll('padDefaultContent')
    │                                      │
    │                                      └─ appendRevision(firstChangeset)
    │
    └─ Response: {code: 0, message: "ok", data: null}
```

### データフロー図

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

HTTP POST       ─────▶ RestAPI.ts
/api/2/pads            ルーティング
                           │
padID           ─────▶ API.ts
text                   バリデーション
authorId               （$、特殊文字、重複）
                           │
                       PadManager.ts
                       │ isValidPadId()
                       │ doesPadExists()
                       │ new Pad()
                           │
                       Pad.ts
                       │ init()
                       │ appendRevision()
                       │ saveToDatabase()
                           │
                           ▼
                       {data: null} ─────▶ JSON Response
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| RestAPI.ts | `src/node/handler/RestAPI.ts` | ソース | APIルーティング定義 |
| APIHandler.ts | `src/node/handler/APIHandler.ts` | ソース | API認証・パラメータ処理 |
| API.ts | `src/node/db/API.ts` | ソース | createPad実装 |
| PadManager.ts | `src/node/db/PadManager.ts` | ソース | パッド管理・キャッシュ |
| Pad.ts | `src/node/db/Pad.ts` | ソース | Padクラス実装 |
| DB.ts | `src/node/db/DB.ts` | ソース | データベースアクセス層 |
| Settings.ts | `src/node/utils/Settings.ts` | ソース | defaultPadText設定 |
