186 lines
7.0 KiB
PL/PgSQL
186 lines
7.0 KiB
PL/PgSQL
-- Forum Votes & Reputation Migration for BroadcastBeat
|
|
-- Adds upvote/downvote on threads and replies, and reputation points on user_profiles
|
|
|
|
-- ============================================================
|
|
-- 1. ADD REPUTATION COLUMN TO USER_PROFILES
|
|
-- ============================================================
|
|
ALTER TABLE public.user_profiles
|
|
ADD COLUMN IF NOT EXISTS reputation INTEGER DEFAULT 0;
|
|
|
|
-- ============================================================
|
|
-- 2. FORUM VOTES TABLE
|
|
-- ============================================================
|
|
CREATE TABLE IF NOT EXISTS public.forum_votes (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
user_id UUID NOT NULL REFERENCES public.user_profiles(id) ON DELETE CASCADE,
|
|
target_type TEXT NOT NULL CHECK (target_type IN ('thread', 'reply')),
|
|
target_id UUID NOT NULL,
|
|
vote_value SMALLINT NOT NULL CHECK (vote_value IN (1, -1)),
|
|
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
-- Unique: one vote per user per target
|
|
CREATE UNIQUE INDEX IF NOT EXISTS idx_forum_votes_unique
|
|
ON public.forum_votes (user_id, target_type, target_id);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_forum_votes_target
|
|
ON public.forum_votes (target_type, target_id);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_forum_votes_user_id
|
|
ON public.forum_votes (user_id);
|
|
|
|
-- ============================================================
|
|
-- 3. ADD VOTE SCORE COLUMNS TO THREADS AND REPLIES
|
|
-- ============================================================
|
|
ALTER TABLE public.forum_threads
|
|
ADD COLUMN IF NOT EXISTS upvotes INTEGER DEFAULT 0,
|
|
ADD COLUMN IF NOT EXISTS downvotes INTEGER DEFAULT 0,
|
|
ADD COLUMN IF NOT EXISTS vote_score INTEGER DEFAULT 0;
|
|
|
|
ALTER TABLE public.forum_replies
|
|
ADD COLUMN IF NOT EXISTS upvotes INTEGER DEFAULT 0,
|
|
ADD COLUMN IF NOT EXISTS downvotes INTEGER DEFAULT 0,
|
|
ADD COLUMN IF NOT EXISTS vote_score INTEGER DEFAULT 0;
|
|
|
|
-- ============================================================
|
|
-- 4. FUNCTIONS
|
|
-- ============================================================
|
|
|
|
-- Recalculate vote counts on thread/reply after vote change
|
|
CREATE OR REPLACE FUNCTION public.update_vote_counts()
|
|
RETURNS TRIGGER
|
|
LANGUAGE plpgsql
|
|
SECURITY DEFINER
|
|
AS $$
|
|
DECLARE
|
|
v_target_type TEXT;
|
|
v_target_id UUID;
|
|
v_up INTEGER;
|
|
v_down INTEGER;
|
|
v_author_id UUID;
|
|
v_old_vote SMALLINT := 0;
|
|
v_new_vote SMALLINT := 0;
|
|
v_rep_delta INTEGER := 0;
|
|
BEGIN
|
|
-- Determine target info
|
|
IF TG_OP = 'DELETE' THEN
|
|
v_target_type := OLD.target_type;
|
|
v_target_id := OLD.target_id;
|
|
v_old_vote := OLD.vote_value;
|
|
ELSIF TG_OP = 'INSERT' THEN
|
|
v_target_type := NEW.target_type;
|
|
v_target_id := NEW.target_id;
|
|
v_new_vote := NEW.vote_value;
|
|
ELSIF TG_OP = 'UPDATE' THEN
|
|
v_target_type := NEW.target_type;
|
|
v_target_id := NEW.target_id;
|
|
v_old_vote := OLD.vote_value;
|
|
v_new_vote := NEW.vote_value;
|
|
END IF;
|
|
|
|
-- Recalculate vote totals
|
|
SELECT
|
|
COALESCE(SUM(CASE WHEN vote_value = 1 THEN 1 ELSE 0 END), 0),
|
|
COALESCE(SUM(CASE WHEN vote_value = -1 THEN 1 ELSE 0 END), 0)
|
|
INTO v_up, v_down
|
|
FROM public.forum_votes
|
|
WHERE target_type = v_target_type AND target_id = v_target_id;
|
|
|
|
-- Update the appropriate table
|
|
IF v_target_type = 'thread' THEN
|
|
UPDATE public.forum_threads
|
|
SET upvotes = v_up,
|
|
downvotes = v_down,
|
|
vote_score = v_up - v_down,
|
|
updated_at = CURRENT_TIMESTAMP
|
|
WHERE id = v_target_id;
|
|
|
|
SELECT author_id INTO v_author_id
|
|
FROM public.forum_threads WHERE id = v_target_id LIMIT 1;
|
|
ELSIF v_target_type = 'reply' THEN
|
|
UPDATE public.forum_replies
|
|
SET upvotes = v_up,
|
|
downvotes = v_down,
|
|
vote_score = v_up - v_down,
|
|
updated_at = CURRENT_TIMESTAMP
|
|
WHERE id = v_target_id;
|
|
|
|
SELECT author_id INTO v_author_id
|
|
FROM public.forum_replies WHERE id = v_target_id LIMIT 1;
|
|
END IF;
|
|
|
|
-- Update author reputation (only for authenticated authors)
|
|
IF v_author_id IS NOT NULL THEN
|
|
-- Calculate reputation delta: upvote = +10, downvote = -2
|
|
v_rep_delta := (v_new_vote - v_old_vote) * CASE
|
|
WHEN (v_new_vote > 0 OR v_old_vote > 0) THEN 10
|
|
ELSE 2
|
|
END;
|
|
|
|
-- More precise: +10 per upvote gained, -10 per upvote lost, -2 per downvote gained, +2 per downvote lost
|
|
v_rep_delta := 0;
|
|
IF v_new_vote = 1 AND v_old_vote = 0 THEN v_rep_delta := 10; END IF;
|
|
IF v_new_vote = 0 AND v_old_vote = 1 THEN v_rep_delta := -10; END IF;
|
|
IF v_new_vote = -1 AND v_old_vote = 0 THEN v_rep_delta := -2; END IF;
|
|
IF v_new_vote = 0 AND v_old_vote = -1 THEN v_rep_delta := 2; END IF;
|
|
IF v_new_vote = 1 AND v_old_vote = -1 THEN v_rep_delta := 12; END IF;
|
|
IF v_new_vote = -1 AND v_old_vote = 1 THEN v_rep_delta := -12; END IF;
|
|
|
|
IF v_rep_delta != 0 THEN
|
|
UPDATE public.user_profiles
|
|
SET reputation = GREATEST(0, COALESCE(reputation, 0) + v_rep_delta)
|
|
WHERE id = v_author_id;
|
|
END IF;
|
|
END IF;
|
|
|
|
RETURN COALESCE(NEW, OLD);
|
|
END;
|
|
$$;
|
|
|
|
-- ============================================================
|
|
-- 5. ENABLE RLS
|
|
-- ============================================================
|
|
ALTER TABLE public.forum_votes ENABLE ROW LEVEL SECURITY;
|
|
|
|
-- ============================================================
|
|
-- 6. RLS POLICIES
|
|
-- ============================================================
|
|
|
|
-- Public can read all votes (for counts)
|
|
DROP POLICY IF EXISTS "public_read_forum_votes" ON public.forum_votes;
|
|
CREATE POLICY "public_read_forum_votes"
|
|
ON public.forum_votes FOR SELECT TO public USING (true);
|
|
|
|
-- Authenticated users can insert their own votes
|
|
DROP POLICY IF EXISTS "authenticated_insert_forum_votes" ON public.forum_votes;
|
|
CREATE POLICY "authenticated_insert_forum_votes"
|
|
ON public.forum_votes FOR INSERT TO authenticated
|
|
WITH CHECK (user_id = auth.uid());
|
|
|
|
-- Users can update their own votes
|
|
DROP POLICY IF EXISTS "users_update_own_forum_votes" ON public.forum_votes;
|
|
CREATE POLICY "users_update_own_forum_votes"
|
|
ON public.forum_votes FOR UPDATE TO authenticated
|
|
USING (user_id = auth.uid())
|
|
WITH CHECK (user_id = auth.uid());
|
|
|
|
-- Users can delete their own votes
|
|
DROP POLICY IF EXISTS "users_delete_own_forum_votes" ON public.forum_votes;
|
|
CREATE POLICY "users_delete_own_forum_votes"
|
|
ON public.forum_votes FOR DELETE TO authenticated
|
|
USING (user_id = auth.uid());
|
|
|
|
-- Service role full access
|
|
DROP POLICY IF EXISTS "service_role_manage_forum_votes" ON public.forum_votes;
|
|
CREATE POLICY "service_role_manage_forum_votes"
|
|
ON public.forum_votes FOR ALL TO service_role USING (true) WITH CHECK (true);
|
|
|
|
-- ============================================================
|
|
-- 7. TRIGGERS
|
|
-- ============================================================
|
|
DROP TRIGGER IF EXISTS on_forum_vote_change ON public.forum_votes;
|
|
CREATE TRIGGER on_forum_vote_change
|
|
AFTER INSERT OR UPDATE OR DELETE ON public.forum_votes
|
|
FOR EACH ROW EXECUTE FUNCTION public.update_vote_counts();
|