# 機能設計書 8-パッド削除

## 概要

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

### 本機能の処理概要

指定されたパッドを完全に削除する機能である。パッドの全リビジョン、チャット履歴、読み取り専用リンクなど、パッドに関連するすべてのデータが削除される。

**業務上の目的・背景**：
不要になったパッドや古いドキュメントを削除してストレージを解放する必要がある。また、誤って作成されたパッドや、プライバシー上の理由でデータを完全に消去したい場合にも利用される。本機能により、パッドとその関連データを確実に削除できる。

**機能の利用シーン**：
- 不要なパッドを削除してストレージを解放する場合
- プロジェクト終了後にドキュメントをクリーンアップする場合
- 誤って作成されたパッドを削除する場合
- データ保持ポリシーに基づく削除を行う場合

**主要な処理内容**：
1. パッドの存在確認
2. 接続中のユーザーをパッドからキック
3. グループパッドの場合、グループからパッドエントリを削除
4. 読み取り専用リンクの削除
5. 全チャットメッセージの削除
6. 全リビジョンの削除
7. 著者のパッドリストから削除
8. パッド本体の削除

**関連システム・外部連携**：
- ueberDB2データベースによるデータ削除
- Socket.IO（接続中ユーザーのキック）
- ReadOnlyManager（読み取り専用リンク削除）
- AuthorManager（著者パッドリスト更新）

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

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 2 | パッド編集画面 | 補助機能 | 設定ポップアップ内のDelete padボタンでパッドを削除 |
| 10 | パッド管理画面 | 補助機能 | 削除ボタンでパッドを削除 |

## 機能種別

CRUD操作（Delete）- カスケード削除

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| padID | string | Yes | 削除対象のパッドID | 存在するパッドIDであること |
| apikey | string | Yes（APIキー認証時） | API認証キー | 設定ファイルのAPIキーと一致すること |
| authorization | string | Yes（OAuth2認証時） | Bearerトークン | 有効なJWTトークンであること |

### 入力データソース

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

## 出力仕様

### 出力データ

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

### 出力先

HTTPレスポンス（JSON形式）

## 処理フロー

### 処理シーケンス

```
1. API認証
   └─ APIキーまたはOAuth2トークンを検証
2. パッド取得
   └─ getPadSafe(padID, true)で存在確認と取得
   └─ 存在しなければエラー返却
3. ユーザーキック
   └─ kickSessionsFromPad(padID)で接続中ユーザーを切断
4. グループパッド処理
   └─ $を含むIDの場合、グループのpadsから削除
5. 読み取り専用リンク削除
   └─ readonly2pad:{roID}とpad2readonly:{padID}を削除
6. チャット削除（並列・バッチ処理）
   └─ pad:{padID}:chat:{0..chatHead}を削除
7. リビジョン削除（並列・バッチ処理）
   └─ pad:{padID}:revs:{0..head}を削除
8. 著者パッドリスト更新
   └─ 全著者のパッドリストから削除
9. パッド本体削除
   └─ padManager.removePad(padID)
10. フック実行
    └─ padRemove フックを実行
11. レスポンス返却
```

### フローチャート

```mermaid
flowchart TD
    A[DELETE /api/2/pads] --> B{認証検証}
    B -->|失敗| C[401 Unauthorized]
    B -->|成功| D[getPadSafe padID, true]
    D --> E{パッド存在?}
    E -->|No| F[400 padID does not exist]
    E -->|Yes| G[kickSessionsFromPad]
    G --> H{グループパッド?}
    H -->|Yes| I[グループからパッド削除]
    H -->|No| J[readonly削除]
    I --> J
    J --> K[チャット削除 × chatHead]
    K --> L[リビジョン削除 × head]
    L --> M[著者パッドリスト更新]
    M --> N[padManager.removePad]
    N --> O[padRemove hook]
    O --> P["返却 {data: null}"]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-001 | 強制切断 | 削除前に接続中ユーザーを切断 | 常に |
| BR-002 | グループ連動 | グループパッドの場合、グループからも削除 | $を含むpadID |
| BR-003 | 完全削除 | リビジョン、チャット、readonly全て削除 | 常に |
| BR-004 | 著者連動 | 著者のパッドリストからも削除 | 常に |
| BR-005 | バッチ削除 | リビジョン・チャットは500件ずつバッチ削除 | 常に |

### 計算ロジック

- グループID抽出: `padID.substring(0, padID.indexOf('$'))`

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| パッド取得 | pad:{padID} | SELECT | 削除対象の確認 |
| グループ更新 | group:{groupID} | UPDATE | padsからパッド削除 |
| readonly削除 | readonly2pad:{roID} | DELETE | 読み取り専用リンク削除 |
| pad2readonly削除 | pad2readonly:{padID} | DELETE | 逆引きリンク削除 |
| チャット削除 | pad:{padID}:chat:{n} | DELETE | 全チャット削除 |
| リビジョン削除 | pad:{padID}:revs:{n} | DELETE | 全リビジョン削除 |
| 著者更新 | author:{authorID} | UPDATE | パッドリストから削除 |
| パッド削除 | pad:{padID} | DELETE | パッド本体削除 |

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

#### pad:{padID}

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | - | 全体 | 存在確認 |
| DELETE | - | 全体 | レコード削除 |

#### group:{groupID}

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPDATE | pads | パッドID削除 | delete group.pads[padID] |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 1 | パラメータエラー | padIDが存在しない | 正しいpadIDを指定 |
| 4 | 認証エラー | APIキーが無効 | 正しいAPIキーを指定 |
| 2 | 内部エラー | データベース操作失敗 | サーバーログを確認 |

### リトライ仕様

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

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

完全なトランザクション管理は行われない。削除処理は複数のPromiseで並列実行される。途中で失敗した場合、部分的な削除状態になる可能性がある。

## パフォーマンス要件

- レスポンス時間: リビジョン数・チャット数に依存（数秒かかる場合あり）
- バッチサイズ: 500件ずつの並列削除でリソース使用を制限

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

- APIキーまたはOAuth2トークンによる認証が必須
- 削除操作は不可逆のため、呼び出し元での確認が推奨される
- 接続中ユーザーは強制切断される

## 備考

- パッド削除は物理削除であり、復旧は不可能
- 大量のリビジョンを持つパッドの削除は時間がかかる

---

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

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

### 推奨読解順序

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

削除対象となるデータ構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | Pad.ts | `src/node/db/Pad.ts` | Padクラスの構造とremove()メソッド |

**読解のコツ**: パッドは複数のデータと関連している：本体（`pad:{padID}`）、リビジョン（`pad:{padID}:revs:{n}`）、チャット（`pad:{padID}:chat:{n}`）、読み取り専用リンク（`readonly2pad`、`pad2readonly`）。

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

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

**主要処理フロー**:
1. **660-680行目（RestAPI.ts）**: DELETE /pads のルーティング定義
2. **56行目（APIHandler.ts）**: `deletePad: ['padID']` パラメータ定義

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | API.ts | `src/node/db/API.ts` | deletePad関数の実装 |
| 3-2 | Pad.ts | `src/node/db/Pad.ts` | remove関数の実装 |

**主要処理フロー**:
- **532-535行目（API.ts）**: deletePad関数の実装
  - **533行目**: `getPadSafe(padID, true)` でパッド取得
  - **534行目**: `pad.remove()` で削除実行
- **557-615行目（Pad.ts）**: remove関数の実装
  - **562行目**: `kickSessionsFromPad(padID)` でユーザーキック
  - **570-580行目**: グループパッド処理
  - **583-586行目**: 読み取り専用リンク削除
  - **590-592行目**: チャット削除（timesLimit使用）
  - **596-598行目**: リビジョン削除（timesLimit使用）
  - **601-603行目**: 著者パッドリスト更新
  - **606行目**: `padManager.removePad(padID)` で本体削除

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

```
DELETE /api/2/pads (RestAPI.ts)
    │
    ├─ APIHandler.handle() (APIHandler.ts:161)
    │      │
    │      ├─ 認証検証 (APIHandler.ts:175-200)
    │      │
    │      └─ api.deletePad(padID) (API.ts:532)
    │              │
    │              ├─ getPadSafe(padID, true)
    │              │
    │              └─ pad.remove() (Pad.ts:557)
    │                      │
    │                      ├─ kickSessionsFromPad(padID)
    │                      │
    │                      ├─ [グループパッド] db.set(`group:${groupID}`)
    │                      │
    │                      ├─ readOnlyManager.getReadOnlyId(padID)
    │                      │      └─ db.remove(`readonly2pad:${roID}`)
    │                      │
    │                      ├─ db.remove(`pad2readonly:${padID}`)
    │                      │
    │                      ├─ timesLimit: db.remove(`pad:chat:${i}`) × chatHead
    │                      │
    │                      ├─ timesLimit: db.remove(`pad:revs:${i}`) × head
    │                      │
    │                      ├─ authorManager.removePad(authorId, padID) × N
    │                      │
    │                      ├─ padManager.removePad(padID)
    │                      │
    │                      └─ hooks.aCallAll('padRemove')
    │
    └─ Response: {code: 0, message: "ok", data: null}
```

### データフロー図

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

HTTP DELETE     ─────▶ RestAPI.ts
/api/2/pads            ルーティング
                           │
padID           ─────▶ API.ts
apikey/token           getPadSafe()
                           │
                       Pad.ts remove()
                       │
                       ├─ kickSessionsFromPad()
                       │
                       ├─ [グループ] group.pads削除
                       │
                       ├─ readonly削除
                       │
                       ├─ chat × N 削除
                       │
                       ├─ revs × M 削除
                       │
                       ├─ 著者リスト更新
                       │
                       └─ padManager.removePad()
                              │
                              ▼
                       {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` | ソース | deletePad実装 |
| Pad.ts | `src/node/db/Pad.ts` | ソース | remove実装 |
| PadManager.ts | `src/node/db/PadManager.ts` | ソース | removePad実装 |
| PadMessageHandler.ts | `src/node/handler/PadMessageHandler.ts` | ソース | kickSessionsFromPad |
| ReadOnlyManager.ts | `src/node/db/ReadOnlyManager.ts` | ソース | readonly削除 |
| AuthorManager.ts | `src/node/db/AuthorManager.ts` | ソース | 著者パッドリスト更新 |
| DB.ts | `src/node/db/DB.ts` | ソース | データベースアクセス層 |
