feat: mobile-first refactor for BottleDetails & RLS security/performance optimizations

This commit is contained in:
2025-12-26 23:58:35 +01:00
parent 20f7436e66
commit 20659567fd
9 changed files with 734 additions and 282 deletions

View File

@@ -0,0 +1,248 @@
-- ============================================
-- RLS Policy Performance Fixes Migration
-- Run this in Supabase SQL Editor
-- ============================================
-- Fixes two types of issues:
-- 1. auth_rls_initplan: Ensures auth.uid() is wrapped in (SELECT ...)
-- 2. multiple_permissive_policies: Consolidates overlapping policies
-- ============================================
-- ============================================
-- Fix 1: user_subscriptions
-- Issues: Multiple permissive policies for INSERT and SELECT
-- ============================================
-- Drop existing policies
DROP POLICY IF EXISTS "user_subscriptions_select_policy" ON user_subscriptions;
DROP POLICY IF EXISTS "user_subscriptions_admin_policy" ON user_subscriptions;
DROP POLICY IF EXISTS "user_subscriptions_insert_self" ON user_subscriptions;
-- Consolidated SELECT policy: user can see own OR admin can see all
CREATE POLICY "user_subscriptions_select_policy" ON user_subscriptions
FOR SELECT USING (
(SELECT auth.uid()) = user_id OR
EXISTS (SELECT 1 FROM admin_users WHERE admin_users.user_id = (SELECT auth.uid()))
);
-- Consolidated INSERT policy: user can insert own OR admin can insert any
CREATE POLICY "user_subscriptions_insert_policy" ON user_subscriptions
FOR INSERT WITH CHECK (
(SELECT auth.uid()) = user_id OR
EXISTS (SELECT 1 FROM admin_users WHERE admin_users.user_id = (SELECT auth.uid()))
);
-- Admin UPDATE/DELETE policy
CREATE POLICY "user_subscriptions_admin_modify_policy" ON user_subscriptions
FOR UPDATE USING (
EXISTS (SELECT 1 FROM admin_users WHERE admin_users.user_id = (SELECT auth.uid()))
);
CREATE POLICY "user_subscriptions_admin_delete_policy" ON user_subscriptions
FOR DELETE USING (
EXISTS (SELECT 1 FROM admin_users WHERE admin_users.user_id = (SELECT auth.uid()))
);
-- ============================================
-- Fix 2: bottle_splits
-- Issues: bottle_splits_host_policy (ALL) and bottle_splits_public_view (SELECT) overlap
-- ============================================
DROP POLICY IF EXISTS "bottle_splits_host_policy" ON bottle_splits;
DROP POLICY IF EXISTS "bottle_splits_public_view" ON bottle_splits;
-- Consolidated SELECT: host can see all own, everyone can see active
CREATE POLICY "bottle_splits_select_policy" ON bottle_splits
FOR SELECT USING (
(SELECT auth.uid()) = host_id OR is_active = true
);
-- Host-only for INSERT/UPDATE/DELETE
CREATE POLICY "bottle_splits_host_insert_policy" ON bottle_splits
FOR INSERT WITH CHECK ((SELECT auth.uid()) = host_id);
CREATE POLICY "bottle_splits_host_update_policy" ON bottle_splits
FOR UPDATE USING ((SELECT auth.uid()) = host_id);
CREATE POLICY "bottle_splits_host_delete_policy" ON bottle_splits
FOR DELETE USING ((SELECT auth.uid()) = host_id);
-- ============================================
-- Fix 3: bottles
-- Issues: bottles_owner_policy (ALL) and bottles_session_select_policy (SELECT) overlap
-- Strategy: Just keep owner policy because the session_select_policy doesn't exist in schema
-- ============================================
DROP POLICY IF EXISTS "bottles_owner_policy" ON bottles;
DROP POLICY IF EXISTS "bottles_session_select_policy" ON bottles;
-- Owner-only policy for all operations (simple and performant)
CREATE POLICY "bottles_policy" ON bottles
FOR ALL USING ((SELECT auth.uid()) = user_id);
-- ============================================
-- Fix 4: buddy_invites
-- Issues: buddy_invites_creator_policy (ALL) and buddy_invites_redeem_policy (SELECT) overlap
-- ============================================
DROP POLICY IF EXISTS "buddy_invites_creator_policy" ON buddy_invites;
DROP POLICY IF EXISTS "buddy_invites_redeem_policy" ON buddy_invites;
-- Consolidated SELECT: creator can see own, anyone can see non-expired (for redemption)
CREATE POLICY "buddy_invites_select_policy" ON buddy_invites
FOR SELECT USING (
(SELECT auth.uid()) = creator_id OR expires_at > now()
);
-- Creator-only for INSERT/UPDATE/DELETE
CREATE POLICY "buddy_invites_creator_insert_policy" ON buddy_invites
FOR INSERT WITH CHECK ((SELECT auth.uid()) = creator_id);
CREATE POLICY "buddy_invites_creator_update_policy" ON buddy_invites
FOR UPDATE USING ((SELECT auth.uid()) = creator_id);
CREATE POLICY "buddy_invites_creator_delete_policy" ON buddy_invites
FOR DELETE USING ((SELECT auth.uid()) = creator_id);
-- ============================================
-- Fix 5: global_products
-- Issues: "Enable Admin Insert/Update" (ALL) and "Enable Read Access for all users" (SELECT) overlap
-- ============================================
DROP POLICY IF EXISTS "Enable Admin Insert/Update" ON global_products;
DROP POLICY IF EXISTS "Enable Read Access for all users" ON global_products;
-- Everyone can SELECT
CREATE POLICY "global_products_select_policy" ON global_products
FOR SELECT USING (true);
-- Admin-only for INSERT/UPDATE/DELETE
CREATE POLICY "global_products_admin_insert_policy" ON global_products
FOR INSERT WITH CHECK (
EXISTS (SELECT 1 FROM admin_users WHERE admin_users.user_id = (SELECT auth.uid()))
);
CREATE POLICY "global_products_admin_update_policy" ON global_products
FOR UPDATE USING (
EXISTS (SELECT 1 FROM admin_users WHERE admin_users.user_id = (SELECT auth.uid()))
);
CREATE POLICY "global_products_admin_delete_policy" ON global_products
FOR DELETE USING (
EXISTS (SELECT 1 FROM admin_users WHERE admin_users.user_id = (SELECT auth.uid()))
);
-- ============================================
-- Fix 6: session_participants
-- Issues: session_participants_manage_policy and session_participants_read_policy overlap
-- ============================================
DROP POLICY IF EXISTS "session_participants_owner_policy" ON session_participants;
DROP POLICY IF EXISTS "session_participants_manage_policy" ON session_participants;
DROP POLICY IF EXISTS "session_participants_read_policy" ON session_participants;
-- Single unified policy: owner can do all
CREATE POLICY "session_participants_policy" ON session_participants
FOR ALL USING ((SELECT auth.uid()) = user_id);
-- ============================================
-- Fix 7: split_participants
-- Issues: Multiple overlapping policies for different actions
-- ============================================
DROP POLICY IF EXISTS "split_participants_own_policy" ON split_participants;
DROP POLICY IF EXISTS "split_participants_host_policy" ON split_participants;
DROP POLICY IF EXISTS "split_participants_public_view" ON split_participants;
-- Consolidated SELECT: own participation OR host of split OR public active split
CREATE POLICY "split_participants_select_policy" ON split_participants
FOR SELECT USING (
(SELECT auth.uid()) = user_id OR
split_id IN (SELECT id FROM bottle_splits WHERE host_id = (SELECT auth.uid())) OR
split_id IN (SELECT id FROM bottle_splits WHERE is_active = true)
);
-- INSERT: own participation OR host of split
CREATE POLICY "split_participants_insert_policy" ON split_participants
FOR INSERT WITH CHECK (
(SELECT auth.uid()) = user_id OR
split_id IN (SELECT id FROM bottle_splits WHERE host_id = (SELECT auth.uid()))
);
-- UPDATE: own participation OR host of split
CREATE POLICY "split_participants_update_policy" ON split_participants
FOR UPDATE USING (
(SELECT auth.uid()) = user_id OR
split_id IN (SELECT id FROM bottle_splits WHERE host_id = (SELECT auth.uid()))
);
-- DELETE: own participation OR host of split
CREATE POLICY "split_participants_delete_policy" ON split_participants
FOR DELETE USING (
(SELECT auth.uid()) = user_id OR
split_id IN (SELECT id FROM bottle_splits WHERE host_id = (SELECT auth.uid()))
);
-- ============================================
-- Fix 8: subscription_plans
-- Issues: subscription_plans_admin_policy (ALL) and subscription_plans_select_policy (SELECT) overlap
-- ============================================
DROP POLICY IF EXISTS "subscription_plans_select_policy" ON subscription_plans;
DROP POLICY IF EXISTS "subscription_plans_admin_policy" ON subscription_plans;
-- Consolidated SELECT: active plans OR admin can see all
CREATE POLICY "subscription_plans_select_policy" ON subscription_plans
FOR SELECT USING (
is_active = true OR
EXISTS (SELECT 1 FROM admin_users WHERE admin_users.user_id = (SELECT auth.uid()))
);
-- Admin-only for INSERT/UPDATE/DELETE
CREATE POLICY "subscription_plans_admin_insert_policy" ON subscription_plans
FOR INSERT WITH CHECK (
EXISTS (SELECT 1 FROM admin_users WHERE admin_users.user_id = (SELECT auth.uid()))
);
CREATE POLICY "subscription_plans_admin_update_policy" ON subscription_plans
FOR UPDATE USING (
EXISTS (SELECT 1 FROM admin_users WHERE admin_users.user_id = (SELECT auth.uid()))
);
CREATE POLICY "subscription_plans_admin_delete_policy" ON subscription_plans
FOR DELETE USING (
EXISTS (SELECT 1 FROM admin_users WHERE admin_users.user_id = (SELECT auth.uid()))
);
-- ============================================
-- Fix 9: tasting_sessions
-- Issues: sessions_access_policy (ALL) and sessions_modification_policy overlap
-- Strategy: Keep it simple - owner-only for modifications, no complex buddy joins
-- ============================================
DROP POLICY IF EXISTS "sessions_access_policy" ON tasting_sessions;
DROP POLICY IF EXISTS "sessions_modification_policy" ON tasting_sessions;
-- Owner-only policy for all operations
CREATE POLICY "tasting_sessions_policy" ON tasting_sessions
FOR ALL USING ((SELECT auth.uid()) = user_id);
-- ============================================
-- Fix 10: tastings
-- Issues: tastings_modify_policy (ALL) and tastings_select_policy (SELECT) overlap
-- Strategy: Keep it simple - owner-only for all operations
-- ============================================
DROP POLICY IF EXISTS "tastings_select_policy" ON tastings;
DROP POLICY IF EXISTS "tastings_modify_policy" ON tastings;
-- Owner-only policy for all operations
CREATE POLICY "tastings_policy" ON tastings
FOR ALL USING ((SELECT auth.uid()) = user_id);
-- ============================================
-- Verification Query (run after migration)
-- ============================================
-- SELECT tablename, policyname, roles, cmd
-- FROM pg_policies
-- WHERE schemaname = 'public'
-- ORDER BY tablename, cmd, roles;