leader board + setting
This commit is contained in:
119
supabase/migrations/002_gamification.sql
Normal file
119
supabase/migrations/002_gamification.sql
Normal file
@@ -0,0 +1,119 @@
|
||||
-- Migration 002: Gamification — Xu system, XP, streak, leaderboard
|
||||
-- Run in Supabase Dashboard → SQL Editor (after 001_user_progress.sql)
|
||||
|
||||
-- ============================================================
|
||||
-- User gamification state (one row per user)
|
||||
-- ============================================================
|
||||
CREATE TABLE IF NOT EXISTS user_gamification (
|
||||
user_id UUID PRIMARY KEY REFERENCES auth.users(id) ON DELETE CASCADE,
|
||||
xp INT NOT NULL DEFAULT 0,
|
||||
level TEXT NOT NULL DEFAULT 'beginner'
|
||||
CHECK (level IN ('beginner', 'bronze', 'silver', 'gold', 'master')),
|
||||
streak INT NOT NULL DEFAULT 0,
|
||||
longest_streak INT NOT NULL DEFAULT 0,
|
||||
last_active DATE,
|
||||
xu INT NOT NULL DEFAULT 50, -- welcome bonus
|
||||
freeze_count INT NOT NULL DEFAULT 0,
|
||||
created_at TIMESTAMPTZ DEFAULT now()
|
||||
);
|
||||
|
||||
-- ============================================================
|
||||
-- Xu transaction log (immutable audit trail)
|
||||
-- ============================================================
|
||||
CREATE TABLE IF NOT EXISTS xu_transactions (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
|
||||
type TEXT NOT NULL
|
||||
CHECK (type IN (
|
||||
'earn_welcome', 'earn_daily', 'earn_streak', 'earn_ads',
|
||||
'spend_freeze', 'spend_writing', 'spend_test'
|
||||
)),
|
||||
amount INT NOT NULL, -- positive = earn, negative = spend
|
||||
balance INT NOT NULL, -- xu balance after this transaction
|
||||
description TEXT,
|
||||
created_at TIMESTAMPTZ DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_xu_transactions_user_date
|
||||
ON xu_transactions(user_id, created_at DESC);
|
||||
|
||||
-- ============================================================
|
||||
-- Weekly leaderboard (reset every week)
|
||||
-- ============================================================
|
||||
CREATE TABLE IF NOT EXISTS weekly_leaderboard (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
|
||||
week_start DATE NOT NULL,
|
||||
xp_earned INT NOT NULL DEFAULT 0,
|
||||
rank INT,
|
||||
UNIQUE (user_id, week_start)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_leaderboard_week_xp
|
||||
ON weekly_leaderboard(week_start, xp_earned DESC);
|
||||
|
||||
-- ============================================================
|
||||
-- Row Level Security
|
||||
-- ============================================================
|
||||
|
||||
-- user_gamification
|
||||
ALTER TABLE user_gamification ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
CREATE POLICY "Users can read own gamification"
|
||||
ON user_gamification FOR SELECT
|
||||
USING (auth.uid() = user_id);
|
||||
|
||||
CREATE POLICY "Users can insert own gamification"
|
||||
ON user_gamification FOR INSERT
|
||||
WITH CHECK (auth.uid() = user_id);
|
||||
|
||||
CREATE POLICY "Users can update own gamification"
|
||||
ON user_gamification FOR UPDATE
|
||||
USING (auth.uid() = user_id);
|
||||
|
||||
-- xu_transactions (immutable — no UPDATE/DELETE)
|
||||
ALTER TABLE xu_transactions ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
CREATE POLICY "Users can read own xu transactions"
|
||||
ON xu_transactions FOR SELECT
|
||||
USING (auth.uid() = user_id);
|
||||
|
||||
CREATE POLICY "Users can insert own xu transactions"
|
||||
ON xu_transactions FOR INSERT
|
||||
WITH CHECK (auth.uid() = user_id);
|
||||
|
||||
-- weekly_leaderboard (public read, own write)
|
||||
ALTER TABLE weekly_leaderboard ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
CREATE POLICY "Anyone can read leaderboard"
|
||||
ON weekly_leaderboard FOR SELECT
|
||||
USING (true);
|
||||
|
||||
CREATE POLICY "Users can insert own leaderboard"
|
||||
ON weekly_leaderboard FOR INSERT
|
||||
WITH CHECK (auth.uid() = user_id);
|
||||
|
||||
CREATE POLICY "Users can update own leaderboard"
|
||||
ON weekly_leaderboard FOR UPDATE
|
||||
USING (auth.uid() = user_id);
|
||||
|
||||
-- ============================================================
|
||||
-- Trigger: auto-create gamification row on new user signup
|
||||
-- ============================================================
|
||||
CREATE OR REPLACE FUNCTION handle_new_user_gamification()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
INSERT INTO user_gamification (user_id, xu)
|
||||
VALUES (NEW.id, 50);
|
||||
|
||||
INSERT INTO xu_transactions (user_id, type, amount, balance, description)
|
||||
VALUES (NEW.id, 'earn_welcome', 50, 50, 'Welcome bonus');
|
||||
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql SECURITY DEFINER;
|
||||
|
||||
CREATE TRIGGER on_auth_user_created_gamification
|
||||
AFTER INSERT ON auth.users
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION handle_new_user_gamification();
|
||||
Reference in New Issue
Block a user