-- Base table grants for the API roles. The Supabase CLI's current default -- (auto_expose_new_tables unset) no longer auto-grants table privileges to -- anon/authenticated/service_role, so without these GRANTs the RLS policies -- from the initial schema migration are unreachable (every request fails at -- the privilege check before RLS is even evaluated). This is the documented -- "explicit grants + RLS" pattern Supabase recommends going forward. -- -- Unlike the previous blanket grant, these privileges are scoped to match -- what each role's RLS policies actually permit, so table-level GRANTs are a -- real second layer of defense rather than a no-op rubber stamp on top of -- RLS: anon/authenticated can only select the public-read tables and insert -- into bookings/inquiries; only authenticated (narrowed further by RLS's -- admin-only policies to the single admin UID) gets full read/write on -- everything. service_role still needs an explicit full grant here: table -- privileges and RLS are two independent checks in Postgres — service_role's -- BYPASSRLS role attribute only skips the RLS policy check, it does not -- substitute for the base GRANT privilege check, so without this it cannot -- touch these tables at all (e.g. the test suite's service_role seed step). grant select on public.products, public.services, public.schedule_template, public.schedule_exceptions, public.booked_slots to anon, authenticated; grant insert on public.bookings, public.inquiries to anon, authenticated; grant select, insert, update, delete on public.products, public.services, public.schedule_template, public.schedule_exceptions, public.bookings, public.booked_slots, public.inquiries to authenticated, service_role; -- Keeps the public, PII-free booked_slots table in sync whenever a booking -- is created. security definer + explicit search_path per Supabase's -- documented safe pattern for definer functions. create or replace function public.sync_booked_slot() returns trigger language plpgsql security definer set search_path = '' as $$ begin insert into public.booked_slots (date, time) values (new.date, new.time) on conflict do nothing; return new; end; $$; create trigger booking_created_sync_slot after insert on public.bookings for each row execute function public.sync_booked_slot();