# 機能設計書 32-共有機能

## 概要

本ドキュメントは、Liam DBアプリケーションにおけるデザインセッションの公開共有機能について記述する。本機能は、組織メンバーが作成したスキーマ設計セッションを、認証不要の公開リンクとして外部に共有できる仕組みを提供する。

### 本機能の処理概要

**業務上の目的・背景**：データベース設計の成果物を、チーム外のステークホルダー（クライアント、外部レビュアー等）と共有する必要がある。認証を必要とせずにセッション内容を閲覧可能にすることで、設計の承認プロセスやフィードバック収集を円滑化する。また、設計ナレッジの公開共有による開発コミュニティへの貢献も可能となる。

**機能の利用シーン**：
- 設計セッションの成果物をクライアントに共有し、レビューを依頼する場面
- 社内の他チームにデータベース設計を共有し、フィードバックを求める場面
- 公開ナレッジとしてデータベース設計パターンを共有する場面
- 共有設定を変更し、非公開に戻す場面

**主要な処理内容**：
1. 公開共有の有効化：public_share_settingsテーブルにレコードを挿入
2. 公開共有の無効化：public_share_settingsテーブルからレコードを削除
3. 共有状態の確認：public_share_settingsテーブルを参照して公開状態を判定
4. 公開リンクの生成とコピー：クライアントサイドでURLを構築しクリップボードにコピー
5. 公開セッション閲覧：認証不要でセッション内容を表示

**関連システム・外部連携**：
- Supabase：データベース操作およびRLSによるアクセス制御
- クリップボードAPI：公開リンクのコピー機能

**権限による制御**：
- 共有設定の変更：セッションにアクセス可能な組織メンバーのみ
- 公開セッションの閲覧：認証不要（匿名アクセス可能）
- 検索エンジンインデックス：robots metaタグでnoindex設定

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 14 | デザインセッション詳細画面 | 主画面 | セッションの公開共有設定 |
| 15 | 公開デザインセッション詳細画面 | 主画面 | 公開共有されたデザインセッションの閲覧 |

## 機能種別

CRUD操作 / データ連携（リンク共有）

## 入力仕様

### 入力パラメータ

#### 共有設定の変更

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| designSessionId | string | Yes | デザインセッションID | UUID形式 |

#### 公開状態の確認

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| designSessionId | string | Yes | デザインセッションID | UUID形式 |

#### 公開セッション閲覧

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| id | string | Yes | デザインセッションID（URLパラメータ） | Valibotで検証 |

### 入力データソース

- 画面操作（ShareDialogのドロップダウン選択）
- URLパラメータ（公開セッションID）

## 出力仕様

### 出力データ

#### 共有設定変更結果

| 項目名 | 型 | 説明 |
|--------|-----|------|
| success | boolean | 処理成功フラグ |
| error | string | エラーメッセージ（失敗時） |
| isPublic | boolean | 変更後の公開状態 |

#### 公開状態確認結果

| 項目名 | 型 | 説明 |
|--------|-----|------|
| isPublic | boolean | 公開状態フラグ |

### 出力先

- 画面表示（ShareDialog、公開セッションページ）
- データベース（public_share_settingsテーブル）
- クリップボード（公開リンク）

## 処理フロー

### 処理シーケンス

#### 公開共有の有効化フロー

```
1. ShareDialogで「Anyone with the link」を選択
   └─ handlePublicClick関数呼び出し
2. togglePublicShare実行
   └─ usePublicShareServerActionフック内の処理
3. enablePublicShare Server Action実行
   └─ 認証確認、セッション存在確認
4. public_share_settingsテーブルにINSERT
   └─ design_session_idを登録
5. UIの状態を更新
   └─ isPublicをtrueに変更
```

#### 公開共有の無効化フロー

```
1. ShareDialogで「Private」を選択
   └─ handlePrivateClick関数呼び出し
2. togglePublicShare実行
   └─ usePublicShareServerActionフック内の処理
3. disablePublicShare Server Action実行
   └─ 認証確認
4. public_share_settingsテーブルからDELETE
   └─ 該当レコードを削除
5. UIの状態を更新
   └─ isPublicをfalseに変更
```

#### 公開セッション閲覧フロー

```
1. 公開リンクへアクセス
   └─ /public/design_sessions/[id]
2. パラメータのバリデーション
   └─ Valibotでid検証
3. public_share_settings確認
   └─ 公開設定されているか確認
4. セッションデータ取得
   └─ design_sessions、building_schemas、versionsを取得
5. スキーマパース
   └─ schemaSchemaでバリデーション
6. 公開ページレンダリング
   └─ PublicLayoutとViewModeProvider（mode="public"）
```

### フローチャート

```mermaid
flowchart TD
    A[共有ダイアログを開く] --> B{現在の状態}
    B -->|Private| C[Public選択]
    B -->|Public| D[Private選択]
    C --> E[enablePublicShare呼び出し]
    D --> F[disablePublicShare呼び出し]
    E --> G{認証確認}
    F --> G
    G -->|失敗| H[エラー表示]
    G -->|成功| I{セッション存在確認}
    I -->|不存在| J[エラー: Session not found]
    I -->|存在| K[DB操作実行]
    K --> L[状態更新]
    L --> M{Public?}
    M -->|Yes| N[コピーリンクボタン表示]
    M -->|No| O[コピーリンクボタン非表示]

    P[公開リンクアクセス] --> Q[パラメータ検証]
    Q -->|失敗| R[404ページ]
    Q -->|成功| S[public_share_settings確認]
    S -->|未公開| R
    S -->|公開| T[セッションデータ取得]
    T --> U[スキーマパース]
    U -->|失敗| R
    U -->|成功| V[公開ページ表示]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-32-01 | 認証必須（設定変更） | 共有設定の変更には認証済みユーザーが必要 | enablePublicShare、disablePublicShare実行時 |
| BR-32-02 | セッションアクセス権 | 自組織のセッションのみ共有設定可能 | RLSポリシーで制御 |
| BR-32-03 | 匿名アクセス許可 | 公開設定されたセッションは認証不要で閲覧可能 | public_share_settingsにレコード存在時 |
| BR-32-04 | 検索エンジン除外 | 公開ページは検索エンジンにインデックスされない | robots meta noindex設定 |
| BR-32-05 | キャッシュ設定 | 公開ページは60秒ごとに再検証 | revalidate = 60 |
| BR-32-06 | 1:1関係 | 1つのセッションに対して1つの公開設定のみ | isOneToOne: true |

### 計算ロジック

公開リンクの構築：
```typescript
const publicUrl = `${window.location.origin}${urlgen('public/design_sessions/[id]', { id: designSessionId })}`
```

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 公開有効化 | public_share_settings | INSERT | 公開設定レコードの作成 |
| 公開無効化 | public_share_settings | DELETE | 公開設定レコードの削除 |
| 状態確認 | public_share_settings | SELECT | 公開設定の存在確認 |
| セッション確認 | design_sessions | SELECT | セッション存在確認 |
| 公開閲覧 | design_sessions, building_schemas, building_schema_versions | SELECT | セッションデータの取得 |

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

#### public_share_settings

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | design_session_id | 入力値 | セッションIDを登録 |
| INSERT | created_at | デフォルト値 | 作成日時 |
| SELECT | design_session_id = 引数値 | 存在確認 | |
| DELETE | design_session_id = 引数値 | レコード削除 | |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | 認証エラー | 未認証状態で設定変更 | 'Authentication required'メッセージ |
| - | セッション不存在 | 指定IDのセッションが存在しない | 'Session not found'メッセージ |
| - | DB操作エラー | INSERT/DELETE失敗 | error.messageを返却 |
| - | パラメータエラー | 公開ページのID不正 | 404ページへリダイレクト |
| - | 非公開アクセス | 公開設定されていないセッションへのアクセス | 404ページへリダイレクト |

### リトライ仕様

リトライ機構は実装されていない。エラー発生時はユーザーが再度操作を行う必要がある。

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

単一テーブルへの単一操作のため、明示的なトランザクション管理は不要。

## パフォーマンス要件

- 共有設定の変更：サブセカンドでの応答が期待される
- 公開ページ閲覧：revalidate=60により、60秒ごとにキャッシュが更新される
- 初回アクセス時は複数テーブルからのデータ取得があるため、数秒の遅延が発生する可能性がある

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

- 公開設定の変更はRLSポリシーにより組織メンバーのみに制限
- 公開ページへの匿名アクセスはcreatePublicServerClientを使用
- 公開ページはrobots metaタグでnoindex設定され、検索エンジンからの発見を防止
- 公開リンクはUUID形式のセッションIDを含み、推測困難
- 公開設定はいつでも無効化可能で、即座にアクセス不可となる

## 備考

- 公開ページはViewModeProvider（mode="public"）でラップされ、編集機能が制限される
- 公開閲覧時はaiAgentの解析結果（initialAnalyzedRequirements）も表示される
- コピー後のフィードバックは2秒後に自動消去

---

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

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

### 推奨読解順序

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

共有機能で使用されるテーブル構造を理解することが重要。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | database.types.ts | `frontend/internal-packages/db/supabase/database.types.ts` | public_share_settingsテーブルの型定義（548-570行目）を確認 |

**読解のコツ**: public_share_settingsはdesign_sessionsと1:1の関係（isOneToOne: true）を持ち、公開設定はセッションIDをプライマリキー的に使用している。

#### Step 2: UIコンポーネントを理解する

共有ダイアログの構造とユーザーインタラクションを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | ShareButton.tsx | `frontend/apps/app/components/SessionDetailPage/components/Output/components/Header/ShareButton.tsx` | 共有ボタンの表示とダイアログ制御 |
| 2-2 | ShareDialog.tsx | `frontend/apps/app/components/ShareDialog/ShareDialog.tsx` | 共有設定UIの全体構造 |

**主要処理フロー**:
1. **32-38行目（ShareButton.tsx）**: 公開モードの場合はサインインを促し、認証済みの場合はダイアログを開く
2. **50-79行目（ShareDialog.tsx）**: Private/Publicの切り替えハンドラー
3. **72-79行目**: クリップボードへのリンクコピー処理

#### Step 3: カスタムフックを理解する

状態管理とServer Actionの呼び出しロジックを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | usePublicShareServerAction.ts | `frontend/apps/app/hooks/usePublicShareServerAction.ts` | 共有状態管理フック |

**主要処理フロー**:
- **13-70行目**: フックの全体構造
- **33-64行目**: togglePublicShare関数 - neverthrowのfromPromiseを使用したエラーハンドリング
- **38-49行目**: 現在の状態に応じてenable/disableを切り替え

#### Step 4: Server Actionsを理解する

バックエンド処理のロジックを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | actions.ts | `frontend/apps/app/features/public-share/actions.ts` | 共有設定のServer Actions |

**主要処理フロー**:
- **5-16行目**: checkPublicShareStatus - 公開状態の確認（publicクライアント使用）
- **18-47行目**: enablePublicShare - 公開有効化（認証必須、セッション存在確認）
- **49-68行目**: disablePublicShare - 公開無効化（認証必須）

#### Step 5: 公開ページを理解する

認証不要の公開閲覧機能を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | page.tsx | `frontend/apps/app/app/public/design_sessions/[id]/page.tsx` | 公開ページのルーティング |
| 5-2 | PublicSessionDetailPage.tsx | `frontend/apps/app/components/PublicSessionDetailPage/PublicSessionDetailPage.tsx` | 公開ページの実装 |

**主要処理フロー**:
- **11-13行目（page.tsx）**: robots metaでnoindex設定
- **16行目**: revalidate=60でキャッシュ制御
- **23-34行目（PublicSessionDetailPage.tsx）**: public_share_settings確認
- **36-80行目**: セッションデータ、スキーマの取得とパース
- **109-142行目**: ViewModeProvider mode="public"でラップ

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

```
ShareButton（共有ボタン）
    │
    └─ ShareDialog（共有ダイアログ）
           │
           ├─ usePublicShareServerAction（状態管理フック）
           │      ├─ enablePublicShare（Server Action）
           │      │      ├─ supabase.auth.getUser()
           │      │      ├─ supabase.from('design_sessions').select()
           │      │      └─ supabase.from('public_share_settings').insert()
           │      │
           │      └─ disablePublicShare（Server Action）
           │             ├─ supabase.auth.getUser()
           │             └─ supabase.from('public_share_settings').delete()
           │
           └─ copyLink（クリップボードコピー）
                  └─ navigator.clipboard.writeText()

/public/design_sessions/[id]（公開ページルート）
    │
    └─ PublicSessionDetailPage
           ├─ createPublicServerClient()
           ├─ supabase.from('public_share_settings').select()
           ├─ supabase.from('design_sessions').select()
           ├─ supabase.from('building_schemas').select()
           ├─ supabase.from('building_schema_versions').select()
           └─ SessionDetailPageClient（ViewMode: public）
```

### データフロー図

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

共有有効化:
ドロップダウン選択 ───▶ togglePublicShare ───▶ enablePublicShare
      │                                            │
      │                                            ├─ 認証確認
      │                                            ├─ セッション確認
      │                                            └─ INSERT public_share_settings
      │                                                      │
      └─────────────────────────────────────────────────────▶ isPublic: true

公開リンクコピー:
コピーボタンクリック ───▶ copyLink ───▶ navigator.clipboard ───▶ クリップボード

公開閲覧:
公開URL ───▶ PublicSessionDetailPage
                │
                ├───▶ public_share_settings確認
                │            │
                │           ▼
                ├───▶ design_sessions取得
                │            │
                │           ▼
                ├───▶ building_schemas取得
                │            │
                │           ▼
                └───▶ SessionDetailPageClient ───▶ 公開ページ表示
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| ShareButton.tsx | `frontend/apps/app/components/SessionDetailPage/components/Output/components/Header/ShareButton.tsx` | ソース | 共有ボタンコンポーネント |
| ShareDialog.tsx | `frontend/apps/app/components/ShareDialog/ShareDialog.tsx` | ソース | 共有ダイアログUI |
| ShareDialog.module.css | `frontend/apps/app/components/ShareDialog/ShareDialog.module.css` | スタイル | 共有ダイアログスタイル |
| actions.ts | `frontend/apps/app/features/public-share/actions.ts` | ソース | 共有設定Server Actions |
| usePublicShareServerAction.ts | `frontend/apps/app/hooks/usePublicShareServerAction.ts` | ソース | 共有状態管理フック |
| page.tsx | `frontend/apps/app/app/public/design_sessions/[id]/page.tsx` | ソース | 公開ページルーティング |
| PublicSessionDetailPage.tsx | `frontend/apps/app/components/PublicSessionDetailPage/PublicSessionDetailPage.tsx` | ソース | 公開ページ実装 |
| PublicLayout.tsx | `frontend/apps/app/components/PublicLayout/PublicLayout.tsx` | ソース | 公開ページレイアウト |
| ViewModeContext.tsx | `frontend/apps/app/components/SessionDetailPage/contexts/ViewModeContext.tsx` | ソース | 表示モードコンテキスト |
| database.types.ts | `frontend/internal-packages/db/supabase/database.types.ts` | 型定義 | テーブル型定義 |
