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: 注意事項
- 依存パッケージを忘れず追加