SCALE — Build Lab
開発パターン · SQL MIGRATION

Supabase スナップショット移行

CATEGORY開発パターン TYPESQL Migration EFFORT90〜180分 DIFFICULTY
PRIMARY CODE
tsx · scale-crm:SUPABASE_SNAPSHOTS_MIGRATION.sql
-- =====================================================================
-- SCALE CRM — スナップショット保管テーブル
-- =====================================================================
-- 実行方法:
--   1. Supabase Dashboard → SQL Editor を開く
--   2. このファイル全体をコピペ
--   3. Run ボタンで実行
--
-- 目的:
--   SCALE CRM の状態を定期的にスナップショット保存し、
--   事故時やバグ時に瞬時に過去の状態に復元できる仕組みを作る。
--
-- 保存頻度:
--   - 編集発生時から30分経過ごとに自動
--   - ユーザー手動(設定画面「💾 今すぐスナップショット」)
--   - ブラウザ閉じる前(beforeunload)
--   - 復元実行前(safety_before_restore)
--
-- 保存期間:
--   - ローカル: 直近20件
--   - Supabase: 無制限(但し下記の pruning ロジックで自動整理推奨)
-- =====================================================================

-- 1. スナップショットテーブル
CREATE TABLE IF NOT EXISTS app_snapshots (
  id TEXT PRIMARY KEY,                    -- 's_xxxxx' 形式
  ts TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  "user" TEXT,                            -- 作成者(currentUser の名前)
  label TEXT,                             -- 'auto' | '手動' | 'exit' | 'safety_before_restore' 等
  keys_count INT DEFAULT 0,               -- 保存したキー数
  data JSONB NOT NULL,                    -- 全体状態( sb_* キー群 )
  created_at TIMESTAMPTZ DEFAULT NOW()
);

-- 2. インデックス(時系列取得高速化)
CREATE INDEX IF NOT EXISTS idx_app_snapshots_ts ON app_snapshots(ts DESC);
CREATE INDEX IF NOT EXISTS idx_app_snapshots_user ON app_snapshots("user", ts DESC);
CREATE INDEX IF NOT EXISTS idx_app_snapshots_label ON app_snapshots(label, ts DESC);

-- 3. RLS ポリシー(app_data と同じ方針)
ALTER TABLE app_snapshots ENABLE ROW LEVEL SECURITY;

DROP POLICY IF EXISTS "Allow all access" ON app_snapshots;
DROP POLICY IF EXISTS "app_snapshots_all_anon" ON app_snapshots;

CREATE POLICY "app_snapshots_all_anon"
  ON app_snapshots FOR ALL
  TO anon
  USING (true)
  WITH CHECK (true);

CREATE POLICY "app_snapshots_all_authenticated"
  ON app_snapshots FOR ALL
  TO authenticated
  USING (true)
  WITH CHECK (true);

-- 4. 古いスナップショット自動削除関数
--    30日より古い 'auto' ラベルのスナップショットを削除
--    ただし 'safety_*' や '手動' ラベルは保持(重要度高)
CREATE OR REPLACE FUNCTION prune_old_snapshots()
RETURNS void AS $$
BEGIN
  DELETE FROM app_snapshots
  WHERE ts < NOW() - INTERVAL '30 days'
    AND label IN ('auto', 'exit');
END;
$$ LANGUAGE plpgsql;

-- 5. Daily クリーンアップ Cron(Supabase pg_cron 利用可能な場合)
-- SELECT cron.schedule(
--   'prune_old_snapshots_daily',
--   '0 3 * * *',
--   'SELECT prune_old_snapshots()'
-- );
-- ※ pg_cron が使えない場合は、手動 or フロント側でトリガー


-- =====================================================================
-- 動作確認クエリ
-- =====================================================================

-- 最新10件のスナップショット一覧
-- SELECT id, ts, "user", label, keys_count FROM app_snapshots ORDER BY ts DESC LIMIT 10;

-- 直近7日間のスナップショット数(ラベル別)
-- SELECT label, COUNT(*) FROM app_snapshots WHERE ts > NOW() - INTERVAL '7 days' GROUP BY label;

-- 合計ストレージサイズ確認(JSONBの大きさ)
-- SELECT pg_size_pretty(pg_total_relation_size('app_snapshots'));

-- 手動クリーンアップ(30日以上前のauto削除)
-- SELECT prune_old_snapshots();

前提条件
Tailwind CSS v4TypeScript 5
USE CASES
  • Supabase 利用システム全般

Supabase スナップショット移行

:LiTarget: 用途

Supabase でスナップショット機能を実装するための SQL マイグレーション。

:LiSparkle: 特徴

  • スナップショットテーブル
  • 世代管理
  • 復元クエリ
  • ストレージ最適化

:LiCode: 実コード(SCALE Base より自動抽出)

:LiInfo: scale-crm:SUPABASE_SNAPSHOTS_MIGRATION.sql の中身そのもの。コピペ即可。

-- =====================================================================
-- SCALE CRM — スナップショット保管テーブル
-- =====================================================================
-- 実行方法:
--   1. Supabase Dashboard → SQL Editor を開く
--   2. このファイル全体をコピペ
--   3. Run ボタンで実行
--
-- 目的:
--   SCALE CRM の状態を定期的にスナップショット保存し、
--   事故時やバグ時に瞬時に過去の状態に復元できる仕組みを作る。
--
-- 保存頻度:
--   - 編集発生時から30分経過ごとに自動
--   - ユーザー手動(設定画面「💾 今すぐスナップショット」)
--   - ブラウザ閉じる前(beforeunload)
--   - 復元実行前(safety_before_restore)
--
-- 保存期間:
--   - ローカル: 直近20件
--   - Supabase: 無制限(但し下記の pruning ロジックで自動整理推奨)
-- =====================================================================

-- 1. スナップショットテーブル
CREATE TABLE IF NOT EXISTS app_snapshots (
  id TEXT PRIMARY KEY,                    -- 's_xxxxx' 形式
  ts TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  "user" TEXT,                            -- 作成者(currentUser の名前)
  label TEXT,                             -- 'auto' | '手動' | 'exit' | 'safety_before_restore'
  keys_count INT DEFAULT 0,               -- 保存したキー数
  data JSONB NOT NULL,                    -- 全体状態( sb_* キー群 )
  created_at TIMESTAMPTZ DEFAULT NOW()
);

-- 2. インデックス(時系列取得高速化)
CREATE INDEX IF NOT EXISTS idx_app_snapshots_ts ON app_snapshots(ts DESC);
CREATE INDEX IF NOT EXISTS idx_app_snapshots_user ON app_snapshots("user", ts DESC);
CREATE INDEX IF NOT EXISTS idx_app_snapshots_label ON app_snapshots(label, ts DESC);

-- 3. RLS ポリシー(app_data と同じ方針)
ALTER TABLE app_snapshots ENABLE ROW LEVEL SECURITY;

DROP POLICY IF EXISTS "Allow all access" ON app_snapshots;
DROP POLICY IF EXISTS "app_snapshots_all_anon" ON app_snapshots;

CREATE POLICY "app_snapshots_all_anon"
  ON app_snapshots FOR ALL
  TO anon
  USING (true)
  WITH CHECK (true);

CREATE POLICY "app_snapshots_all_authenticated"
  ON app_snapshots FOR ALL
  TO authenticated
  USING (true)
  WITH CHECK (true);

-- 4. 古いスナップショット自動削除関数
--    30日より古い 'auto' ラベルのスナップショットを削除
--    ただし 'safety_*''手動' ラベルは保持(重要度高)
CREATE OR REPLACE FUNCTION prune_old_snapshots()
RETURNS void AS $$
BEGIN
  DELETE FROM app_snapshots
  WHERE ts < NOW() - INTERVAL '30 days'
    AND label IN ('auto', 'exit');
END;
$$ LANGUAGE plpgsql;

-- 5. Daily クリーンアップ Cron(Supabase pg_cron 利用可能な場合)
-- SELECT cron.schedule(
--   'prune_old_snapshots_daily',
--   '0 3 * * *',
--   'SELECT prune_old_snapshots()'
-- );
-- ※ pg_cron が使えない場合は、手動 or フロント側でトリガー


-- =====================================================================
-- 動作確認クエリ
-- =====================================================================

-- 最新10件のスナップショット一覧
-- SELECT id, ts, "user", label, keys_count FROM app_snapshots ORDER BY ts DESC LIMIT 10;

-- 直近7日間のスナップショット数(ラベル別)
-- SELECT label, COUNT(*) FROM app_snapshots WHERE ts > NOW() - INTERVAL '7 days' GROUP BY label;

-- 合計ストレージサイズ確認(JSONBの大きさ)
-- SELECT pg_size_pretty(pg_total_relation_size('app_snapshots'));

-- 手動クリーンアップ(30日以上前のauto削除)
-- SELECT prune_old_snapshots();

:LiFolder: ソースファイルのパス

/Users/oogushiyuuki/株式会社SCALE/scale-lead/SUPABASE_SNAPSHOTS_MIGRATION.sql

:LiHandPointer: 使い方

対象プロジェクトに該当ファイルをコピーして、props を流し込むだけ。

:LiAlertCircle: 注意事項

  • 依存パッケージを忘れず追加