データベーススキーマ
Subseek で使用する全テーブルの定義。ORM は Drizzle ORM、DB は Turso(SQLite)。
Better Auth が管理する users テーブルは FK 参照用に最小限のみ定義し、マイグレーション対象からは除外している。
ER図
テーブル定義
users(参照専用)
Better Auth が管理するテーブル。FK 参照に必要な id のみ定義。
| カラム | 型 | 制約 | 説明 |
|---|---|---|---|
id | TEXT | PK, NOT NULL | ユーザーID |
channels
YouTube チャンネルのメタデータを管理する。
| カラム | 型 | 制約 | デフォルト | 説明 |
|---|---|---|---|---|
id | TEXT | PK, NOT NULL | - | YouTube チャンネルID(UCxxxxxx) |
title | TEXT | NOT NULL | - | チャンネル名 |
handle | TEXT | - | - | カスタムURL(@handle) |
thumbnail_url | TEXT | - | - | サムネイル画像URL |
subtitle_status | TEXT | NOT NULL | 'pending' | 字幕取得状態: pending / processing / completed / failed |
video_count | INTEGER | NOT NULL | 0 | 動画数(YouTube API から取得) |
created_at | TEXT | NOT NULL | new Date().toISOString() | 作成日時 |
updated_at | TEXT | NOT NULL | new Date().toISOString() | 更新日時 |
インデックス:
| インデックス名 | カラム |
|---|---|
idx_channels_title | title |
idx_channels_handle | handle |
idx_channels_subtitle_status | subtitle_status |
videos
動画メタデータ・字幕取得状態を管理する。
| カラム | 型 | 制約 | デフォルト | 説明 |
|---|---|---|---|---|
id | TEXT | PK, NOT NULL | - | YouTube 動画ID(11文字) |
channel_id | TEXT | NOT NULL, FK → channels.id | - | 所属チャンネルID |
title | TEXT | NOT NULL | - | 動画タイトル |
thumbnail_url | TEXT | - | - | サムネイル画像URL |
duration_seconds | INTEGER | NOT NULL | - | 動画の長さ(秒) |
published_at | TEXT | NOT NULL | - | 公開日時(ISO 8601) |
subtitle_status | TEXT | NOT NULL | 'pending' | 字幕取得状態: pending / completed / failed / unavailable / deleted |
created_at | TEXT | NOT NULL | new Date().toISOString() | 作成日時 |
updated_at | TEXT | NOT NULL | new Date().toISOString() | 更新日時 |
インデックス:
| インデック ス名 | カラム |
|---|---|
idx_videos_channel_id | channel_id |
idx_videos_published_at | published_at |
idx_videos_subtitle_status | subtitle_status |
idx_videos_channel_subtitle | channel_id, subtitle_status |
subtitle_status 状態遷移:
user_video_access
ユーザーの動画アクセス権(クレジット消費履歴)を管理する。
| カラム | 型 | 制約 | 説明 |
|---|---|---|---|
user_id | TEXT | PK, NOT NULL, FK → users.id | ユーザーID |
video_id | TEXT | PK, NOT NULL, FK → videos.id | 動画ID |
credits | INTEGER | NOT NULL | 消費したクレジット数 |
created_at | TEXT | NOT NULL | 作成日時 |
updated_at | TEXT | NOT NULL | 更新日時 |
expires_at | TEXT | NOT NULL | アクセス権の有効期限(created_at + 4週間) |
主キー: (user_id, video_id) の複合主キー
billing_accounts
課金情報(プラン・クレジット残高・Stripe 顧客)を管理する(users と 1:1 関係)。
認証ユーザー情報とは別ドメインとして分離しており、独立した主キー id を持つ。将来 organization が所有者になった場合に所有者カラムを付け替えるだけで対応できる設計。user 削除時も billing レコードを残す運用を想定するため CASCADE しない。設計方針は ADR: organization 将来導入と billing 分離戦略 を参照。
| カラム | 型 | 制約 | デフォルト | 説明 |
|---|---|---|---|---|
id | TEXT | PK, NOT NULL | UUID v4 (crypto.randomUUID()) | billing アカウントID |
user_id | TEXT | NOT NULL, UNIQUE, FK → users.id | - | ユーザーID(1 ユーザー 1 billing account) |
stripe_customer_id | TEXT | UNIQUE, NULL 許容 | - | Stripe Customer ID(未接続ユーザーは NULL) |
plan | TEXT | NOT NULL | 'free' | プラン: free / plus / pro |
credit_balance | INTEGER | NOT NULL | 50 | クレジット残高(Free プランの上限値) |
created_at | TEXT | NOT NULL | new Date().toISOString() | 作成日時 |
updated_at | TEXT | NOT NULL | new Date().toISOString() | 更新日時 |
ユニーク制約:
| 制約名 | カラム |
|---|---|
uq_billing_accounts_user_id | user_id |
uq_billing_accounts_stripe_customer_id | stripe_customer_id |
favorite_channels
ユーザーのお気に入りチャンネルを管理する。
| カラム | 型 | 制約 | 説明 |
|---|---|---|---|
user_id | TEXT | PK, NOT NULL, FK → users.id (CASCADE) | ユーザーID |
channel_id | TEXT | PK, NOT NULL, FK → channels.id | チャンネルID |
created_at | TEXT | NOT NULL | 作成日時 |
主キー: (user_id, channel_id) の複合主キー
インデックス:
| インデックス名 | カラム |
|---|---|
idx_favorite_channels_user_created | user_id, created_at |
search_histories
ユーザーの検索履歴を管理する。同一検索条件(userId, query, channels, target, sort)に対してユニーク制約を持ち、重複する検索条件は searched_at を更新する。
| カラム | 型 | 制約 | デフォルト | 説明 |
|---|---|---|---|---|
id | INTEGER | PK, NOT NULL, AUTOINCREMENT | - | 検索履歴ID |
user_id | TEXT | NOT NULL, FK → users.id (CASCADE) | - | ユーザーID |
query | TEXT | NOT NULL | - | 検索キーワード |
channels | TEXT | NOT NULL | '' | 検索対象チャンネルID一覧(カンマ区切り。全チャンネル検索時は空文字) |
target | TEXT | NOT NULL | 'description,subtitle,title' | 検索対象(ソート済みカンマ区切り) |
sort | TEXT | NOT NULL | 'relevance' | 並び順 |
searched_at | TEXT | NOT NULL | - | 検索実行日時(ISO 8601) |
created_at | TEXT | NOT NULL | new Date().toISOString() | 作成日時 |
updated_at | TEXT | NOT NULL | new Date().toISOString() | 更新日時 |
ユニーク制約:
| 制約名 | カラム |
|---|---|
uq_search_histories_condition | user_id, query, channels, target, sort |
インデックス:
| インデックス名 | カラム |
|---|---|
idx_search_histories_user_searched | user_id, searched_at |
スキーマ定義ファイル
実装は packages/db/src/schema/schema.ts を参照。