-- ============================================================ -- RLS Policies — Sistema de Recolección Inteligente -- Ejecutar en: Supabase > SQL Editor -- Regla innegociable: el ciudadano NUNCA ve coordenadas ni datos ajenos. -- ============================================================ -- ------------------------------------------------------------ -- 1. Tabla public.users (espejo de auth.users con rol) -- ------------------------------------------------------------ CREATE TABLE IF NOT EXISTS public.users ( id UUID PRIMARY KEY REFERENCES auth.users(id) ON DELETE CASCADE, email TEXT, phone TEXT, role TEXT DEFAULT 'citizen' CHECK (role IN ('citizen', 'driver', 'admin')), created_at TIMESTAMPTZ DEFAULT NOW() ); ALTER TABLE public.users ENABLE ROW LEVEL SECURITY; -- Cada usuario ve y edita solo su propia fila DROP POLICY IF EXISTS "users_select_own" ON public.users; CREATE POLICY "users_select_own" ON public.users FOR SELECT USING (auth.uid() = id); DROP POLICY IF EXISTS "users_update_own" ON public.users; CREATE POLICY "users_update_own" ON public.users FOR UPDATE USING (auth.uid() = id); -- El backend (service_role) inserta al registrar; no necesita policy -- porque service_role bypasea RLS por diseño. -- ------------------------------------------------------------ -- 2. Tabla public.addresses -- ------------------------------------------------------------ CREATE TABLE IF NOT EXISTS public.addresses ( id UUID DEFAULT gen_random_uuid() PRIMARY KEY, user_id UUID NOT NULL REFERENCES public.users(id) ON DELETE CASCADE, label TEXT NOT NULL, calle TEXT NOT NULL, colonia TEXT NOT NULL, route_id TEXT NOT NULL, verified BOOLEAN DEFAULT FALSE, verified_method TEXT, verified_at TIMESTAMPTZ, created_at TIMESTAMPTZ DEFAULT NOW() ); ALTER TABLE public.addresses ENABLE ROW LEVEL SECURITY; -- Ciudadano: solo lee sus domicilios; admin lee todos DROP POLICY IF EXISTS "addresses_select" ON public.addresses; CREATE POLICY "addresses_select" ON public.addresses FOR SELECT USING ( auth.uid() = user_id OR (SELECT role FROM public.users WHERE id = auth.uid()) = 'admin' ); -- Ciudadano solo inserta domicilios propios DROP POLICY IF EXISTS "addresses_insert" ON public.addresses; CREATE POLICY "addresses_insert" ON public.addresses FOR INSERT WITH CHECK (auth.uid() = user_id); -- Ciudadano solo modifica los suyos (para verified=true tras OCR) DROP POLICY IF EXISTS "addresses_update" ON public.addresses; CREATE POLICY "addresses_update" ON public.addresses FOR UPDATE USING ( auth.uid() = user_id OR (SELECT role FROM public.users WHERE id = auth.uid()) = 'admin' ); -- ------------------------------------------------------------ -- 3. Tabla public.route_positions ← ÚNICA QUE TIENE LAT/LNG -- Solo admin puede leerla. Regla innegociable #1 y #4. -- ------------------------------------------------------------ ALTER TABLE public.route_positions ENABLE ROW LEVEL SECURITY; DROP POLICY IF EXISTS "route_positions_admin_only" ON public.route_positions; CREATE POLICY "route_positions_admin_only" ON public.route_positions FOR SELECT USING ( (SELECT role FROM public.users WHERE id = auth.uid()) = 'admin' ); -- Sin policy para INSERT/UPDATE/DELETE → el backend usa service_role para el seed. -- ------------------------------------------------------------ -- 4. Tabla public.notifications -- ------------------------------------------------------------ CREATE TABLE IF NOT EXISTS public.notifications ( id UUID DEFAULT gen_random_uuid() PRIMARY KEY, user_id UUID REFERENCES public.users(id) ON DELETE CASCADE, route_id TEXT, type TEXT, payload JSONB, sent_at TIMESTAMPTZ DEFAULT NOW() ); ALTER TABLE public.notifications ENABLE ROW LEVEL SECURITY; DROP POLICY IF EXISTS "notifications_select" ON public.notifications; CREATE POLICY "notifications_select" ON public.notifications FOR SELECT USING ( auth.uid() = user_id OR (SELECT role FROM public.users WHERE id = auth.uid()) = 'admin' ); -- ------------------------------------------------------------ -- 5. Tabla public.feedback -- Las quejas van a target_unit_id (unidad), NUNCA al chofer. -- ------------------------------------------------------------ CREATE TABLE IF NOT EXISTS public.feedback ( id UUID DEFAULT gen_random_uuid() PRIMARY KEY, user_id UUID REFERENCES public.users(id) ON DELETE SET NULL, address_id UUID REFERENCES public.addresses(id) ON DELETE SET NULL, type TEXT, target_unit_id INT, -- unidad (no chofer): privacidad del chofer message TEXT, rating SMALLINT CHECK (rating BETWEEN 1 AND 5), created_at TIMESTAMPTZ DEFAULT NOW() ); ALTER TABLE public.feedback ENABLE ROW LEVEL SECURITY; DROP POLICY IF EXISTS "feedback_select" ON public.feedback; CREATE POLICY "feedback_select" ON public.feedback FOR SELECT USING ( auth.uid() = user_id OR (SELECT role FROM public.users WHERE id = auth.uid()) = 'admin' ); DROP POLICY IF EXISTS "feedback_insert" ON public.feedback; CREATE POLICY "feedback_insert" ON public.feedback FOR INSERT WITH CHECK (auth.uid() = user_id); -- ------------------------------------------------------------ -- 5b. Tabla public.incidents (reportes ciudadanos) -- ------------------------------------------------------------ ALTER TABLE public.incidents ENABLE ROW LEVEL SECURITY; DROP POLICY IF EXISTS "incidents_select" ON public.incidents; CREATE POLICY "incidents_select" ON public.incidents FOR SELECT USING ( auth.uid() = user_id OR (SELECT role FROM public.users WHERE id = auth.uid()) = 'admin' ); DROP POLICY IF EXISTS "incidents_insert" ON public.incidents; CREATE POLICY "incidents_insert" ON public.incidents FOR INSERT WITH CHECK (auth.uid() = user_id); DROP POLICY IF EXISTS "incidents_update_admin" ON public.incidents; CREATE POLICY "incidents_update_admin" ON public.incidents FOR UPDATE USING ( (SELECT role FROM public.users WHERE id = auth.uid()) = 'admin' ); -- ------------------------------------------------------------ -- 6. Índices útiles para rendimiento -- ------------------------------------------------------------ CREATE INDEX IF NOT EXISTS idx_addresses_user_id ON public.addresses(user_id); CREATE INDEX IF NOT EXISTS idx_addresses_route_id ON public.addresses(route_id); CREATE INDEX IF NOT EXISTS idx_notifications_user ON public.notifications(user_id); CREATE INDEX IF NOT EXISTS idx_feedback_user ON public.feedback(user_id);