initial commit: rocket.new export of broadcastbeat
This commit is contained in:
129
supabase/migrations/20260322200000_user_suspensions.sql
Normal file
129
supabase/migrations/20260322200000_user_suspensions.sql
Normal file
@@ -0,0 +1,129 @@
|
||||
-- User Suspensions Migration
|
||||
-- Adds user_suspensions table for admin suspend/ban controls
|
||||
|
||||
-- 1. Create suspension type enum
|
||||
DROP TYPE IF EXISTS public.suspension_type CASCADE;
|
||||
CREATE TYPE public.suspension_type AS ENUM ('suspended', 'banned');
|
||||
|
||||
-- 2. Create user_suspensions table
|
||||
CREATE TABLE IF NOT EXISTS public.user_suspensions (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
user_id UUID NOT NULL REFERENCES public.user_profiles(id) ON DELETE CASCADE,
|
||||
suspended_by UUID NOT NULL REFERENCES public.user_profiles(id) ON DELETE CASCADE,
|
||||
suspension_type public.suspension_type NOT NULL DEFAULT 'suspended',
|
||||
reason TEXT NOT NULL,
|
||||
duration_days INTEGER NULL, -- NULL = permanent (ban)
|
||||
expires_at TIMESTAMPTZ NULL, -- NULL = permanent
|
||||
is_active BOOLEAN NOT NULL DEFAULT true,
|
||||
lifted_at TIMESTAMPTZ NULL,
|
||||
lifted_by UUID NULL REFERENCES public.user_profiles(id) ON DELETE SET NULL,
|
||||
lift_reason TEXT NULL,
|
||||
email_sent BOOLEAN NOT NULL DEFAULT false,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
-- 3. Indexes
|
||||
CREATE INDEX IF NOT EXISTS idx_user_suspensions_user_id ON public.user_suspensions(user_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_user_suspensions_active ON public.user_suspensions(user_id, is_active);
|
||||
CREATE INDEX IF NOT EXISTS idx_user_suspensions_expires_at ON public.user_suspensions(expires_at) WHERE is_active = true;
|
||||
|
||||
-- 4. Add suspension_status column to user_profiles if not exists
|
||||
ALTER TABLE public.user_profiles
|
||||
ADD COLUMN IF NOT EXISTS suspension_status TEXT DEFAULT NULL,
|
||||
ADD COLUMN IF NOT EXISTS suspension_reason TEXT DEFAULT NULL,
|
||||
ADD COLUMN IF NOT EXISTS suspended_until TIMESTAMPTZ DEFAULT NULL;
|
||||
|
||||
-- 5. Helper function: check if user is currently suspended
|
||||
CREATE OR REPLACE FUNCTION public.is_user_suspended(p_user_id UUID)
|
||||
RETURNS BOOLEAN
|
||||
LANGUAGE sql
|
||||
STABLE
|
||||
SECURITY DEFINER
|
||||
AS $$
|
||||
SELECT EXISTS (
|
||||
SELECT 1 FROM public.user_suspensions
|
||||
WHERE user_id = p_user_id
|
||||
AND is_active = true
|
||||
AND (expires_at IS NULL OR expires_at > now())
|
||||
)
|
||||
$$;
|
||||
|
||||
-- 6. Helper function: get active suspension for user
|
||||
CREATE OR REPLACE FUNCTION public.get_active_suspension(p_user_id UUID)
|
||||
RETURNS TABLE(
|
||||
suspension_id UUID,
|
||||
suspension_type TEXT,
|
||||
reason TEXT,
|
||||
expires_at TIMESTAMPTZ,
|
||||
created_at TIMESTAMPTZ
|
||||
)
|
||||
LANGUAGE sql
|
||||
STABLE
|
||||
SECURITY DEFINER
|
||||
AS $$
|
||||
SELECT id, suspension_type::TEXT, reason, expires_at, created_at
|
||||
FROM public.user_suspensions
|
||||
WHERE user_id = p_user_id
|
||||
AND is_active = true
|
||||
AND (expires_at IS NULL OR expires_at > now())
|
||||
ORDER BY created_at DESC
|
||||
LIMIT 1
|
||||
$$;
|
||||
|
||||
-- 7. Function to auto-expire suspensions
|
||||
CREATE OR REPLACE FUNCTION public.expire_suspensions()
|
||||
RETURNS void
|
||||
LANGUAGE plpgsql
|
||||
SECURITY DEFINER
|
||||
AS $$
|
||||
BEGIN
|
||||
UPDATE public.user_suspensions
|
||||
SET is_active = false, updated_at = now()
|
||||
WHERE is_active = true
|
||||
AND expires_at IS NOT NULL
|
||||
AND expires_at <= now();
|
||||
|
||||
-- Sync user_profiles suspension_status
|
||||
UPDATE public.user_profiles
|
||||
SET suspension_status = NULL,
|
||||
suspension_reason = NULL,
|
||||
suspended_until = NULL
|
||||
WHERE id IN (
|
||||
SELECT DISTINCT user_id FROM public.user_suspensions
|
||||
WHERE is_active = false
|
||||
AND expires_at IS NOT NULL
|
||||
AND expires_at <= now()
|
||||
)
|
||||
AND NOT public.is_user_suspended(id);
|
||||
END;
|
||||
$$;
|
||||
|
||||
-- 8. Enable RLS
|
||||
ALTER TABLE public.user_suspensions ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
-- 9. RLS Policies
|
||||
DROP POLICY IF EXISTS "admins_manage_suspensions" ON public.user_suspensions;
|
||||
CREATE POLICY "admins_manage_suspensions"
|
||||
ON public.user_suspensions
|
||||
FOR ALL
|
||||
TO authenticated
|
||||
USING (
|
||||
EXISTS (
|
||||
SELECT 1 FROM public.user_profiles
|
||||
WHERE id = auth.uid() AND role IN ('admin', 'moderator')
|
||||
)
|
||||
)
|
||||
WITH CHECK (
|
||||
EXISTS (
|
||||
SELECT 1 FROM public.user_profiles
|
||||
WHERE id = auth.uid() AND role IN ('admin', 'moderator')
|
||||
)
|
||||
);
|
||||
|
||||
DROP POLICY IF EXISTS "users_view_own_suspension" ON public.user_suspensions;
|
||||
CREATE POLICY "users_view_own_suspension"
|
||||
ON public.user_suspensions
|
||||
FOR SELECT
|
||||
TO authenticated
|
||||
USING (user_id = auth.uid());
|
||||
Reference in New Issue
Block a user