Files
avbeat-com/supabase/migrations/20260322120000_user_badges.sql
2026-05-07 16:39:17 +00:00

157 lines
6.0 KiB
PL/PgSQL

-- User Badges Migration for BroadcastBeat
-- Adds earned badges (Contributor, Mentor, Expert, Moderator) based on reputation and activity
-- ============================================================
-- 1. USER BADGES TABLE
-- ============================================================
CREATE TABLE IF NOT EXISTS public.user_badges (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES public.user_profiles(id) ON DELETE CASCADE,
badge_type TEXT NOT NULL CHECK (badge_type IN ('contributor', 'mentor', 'expert', 'moderator')),
awarded_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
awarded_reason TEXT
);
CREATE UNIQUE INDEX IF NOT EXISTS idx_user_badges_unique
ON public.user_badges (user_id, badge_type);
CREATE INDEX IF NOT EXISTS idx_user_badges_user_id
ON public.user_badges (user_id);
-- ============================================================
-- 2. FUNCTION: EVALUATE AND AWARD BADGES
-- ============================================================
CREATE OR REPLACE FUNCTION public.evaluate_user_badges(p_user_id UUID)
RETURNS VOID
LANGUAGE plpgsql
SECURITY DEFINER
AS $$
DECLARE
v_reputation INTEGER;
v_thread_count INTEGER;
v_reply_count INTEGER;
v_upvotes_received INTEGER;
v_is_moderator BOOLEAN := false;
BEGIN
-- Get reputation
SELECT COALESCE(reputation, 0) INTO v_reputation
FROM public.user_profiles WHERE id = p_user_id;
-- Get thread count
SELECT COUNT(*) INTO v_thread_count
FROM public.forum_threads WHERE author_id = p_user_id;
-- Get reply count
SELECT COUNT(*) INTO v_reply_count
FROM public.forum_replies WHERE author_id = p_user_id;
-- Get total upvotes received
SELECT COALESCE(SUM(upvotes), 0) INTO v_upvotes_received
FROM (
SELECT upvotes FROM public.forum_threads WHERE author_id = p_user_id
UNION ALL
SELECT upvotes FROM public.forum_replies WHERE author_id = p_user_id
) AS all_posts;
-- Check moderator role (if user_roles table exists)
IF EXISTS (
SELECT 1 FROM information_schema.tables
WHERE table_schema = 'public' AND table_name = 'user_roles'
) THEN
SELECT EXISTS(
SELECT 1 FROM public.user_roles
WHERE user_id = p_user_id AND role IN ('moderator', 'admin')
) INTO v_is_moderator;
END IF;
-- Award CONTRIBUTOR badge: reputation >= 100 OR (threads >= 5 AND replies >= 10)
IF v_reputation >= 100 OR (v_thread_count >= 5 AND v_reply_count >= 10) THEN
INSERT INTO public.user_badges (user_id, badge_type, awarded_reason)
VALUES (p_user_id, 'contributor', 'Reached 100 reputation or posted 5+ threads and 10+ replies')
ON CONFLICT (user_id, badge_type) DO NOTHING;
END IF;
-- Award MENTOR badge: reputation >= 300 OR upvotes_received >= 50
IF v_reputation >= 300 OR v_upvotes_received >= 50 THEN
INSERT INTO public.user_badges (user_id, badge_type, awarded_reason)
VALUES (p_user_id, 'mentor', 'Reached 300 reputation or received 50+ upvotes')
ON CONFLICT (user_id, badge_type) DO NOTHING;
END IF;
-- Award EXPERT badge: reputation >= 1000 OR (upvotes_received >= 200 AND thread_count >= 20)
IF v_reputation >= 1000 OR (v_upvotes_received >= 200 AND v_thread_count >= 20) THEN
INSERT INTO public.user_badges (user_id, badge_type, awarded_reason)
VALUES (p_user_id, 'expert', 'Reached 1000 reputation or received 200+ upvotes with 20+ threads')
ON CONFLICT (user_id, badge_type) DO NOTHING;
END IF;
-- Award MODERATOR badge: has moderator/admin role
IF v_is_moderator THEN
INSERT INTO public.user_badges (user_id, badge_type, awarded_reason)
VALUES (p_user_id, 'moderator', 'Assigned moderator or admin role')
ON CONFLICT (user_id, badge_type) DO NOTHING;
END IF;
END;
$$;
-- ============================================================
-- 3. TRIGGER FUNCTION: AUTO-EVALUATE BADGES ON REPUTATION CHANGE
-- ============================================================
CREATE OR REPLACE FUNCTION public.trigger_evaluate_badges()
RETURNS TRIGGER
LANGUAGE plpgsql
SECURITY DEFINER
AS $$
BEGIN
PERFORM public.evaluate_user_badges(NEW.id);
RETURN NEW;
END;
$$;
-- ============================================================
-- 4. ENABLE RLS
-- ============================================================
ALTER TABLE public.user_badges ENABLE ROW LEVEL SECURITY;
-- ============================================================
-- 5. RLS POLICIES
-- ============================================================
DROP POLICY IF EXISTS "public_read_user_badges" ON public.user_badges;
CREATE POLICY "public_read_user_badges"
ON public.user_badges FOR SELECT TO public USING (true);
DROP POLICY IF EXISTS "service_role_manage_user_badges" ON public.user_badges;
CREATE POLICY "service_role_manage_user_badges"
ON public.user_badges FOR ALL TO service_role USING (true) WITH CHECK (true);
-- ============================================================
-- 6. TRIGGER: EVALUATE BADGES WHEN REPUTATION CHANGES
-- ============================================================
DROP TRIGGER IF EXISTS on_reputation_change_evaluate_badges ON public.user_profiles;
CREATE TRIGGER on_reputation_change_evaluate_badges
AFTER UPDATE OF reputation ON public.user_profiles
FOR EACH ROW
WHEN (OLD.reputation IS DISTINCT FROM NEW.reputation)
EXECUTE FUNCTION public.trigger_evaluate_badges();
-- ============================================================
-- 7. BACKFILL BADGES FOR EXISTING USERS
-- ============================================================
DO $$
DECLARE
v_user_id UUID;
BEGIN
IF EXISTS (
SELECT 1 FROM information_schema.tables
WHERE table_schema = 'public' AND table_name = 'user_profiles'
) THEN
FOR v_user_id IN SELECT id FROM public.user_profiles LOOP
PERFORM public.evaluate_user_badges(v_user_id);
END LOOP;
RAISE NOTICE 'Badge backfill completed for all existing users.';
END IF;
EXCEPTION
WHEN OTHERS THEN
RAISE NOTICE 'Badge backfill failed: %', SQLERRM;
END $$;