-- 17-vehicle-photos.sql -- Idempotent migration: add vehicle_photos table for multiple photos per vehicle. -- Each vehicle can have multiple photos with ordering support. -- Create vehicle_photos table create table if not exists public.vehicle_photos ( id uuid primary key default gen_random_uuid(), vehicle_id uuid not null references public.vehicles(id) on delete cascade, photo_url text not null default '', photo_path text not null, display_order integer not null default 0, is_primary boolean not null default false, created_at timestamptz not null default now() ); create index if not exists vehicle_photos_vehicle_id_idx on public.vehicle_photos(vehicle_id, display_order); -- Enable RLS alter table public.vehicle_photos enable row level security; -- Drop existing policies to ensure idempotency drop policy if exists "vehicle_photos_public_read" on public.vehicle_photos; drop policy if exists "vehicle_photos_admin_read" on public.vehicle_photos; drop policy if exists "vehicle_photos_admin_insert" on public.vehicle_photos; drop policy if exists "vehicle_photos_admin_delete" on public.vehicle_photos; drop policy if exists "vehicle_photos_admin_update" on public.vehicle_photos; -- Public can read all photos create policy "vehicle_photos_public_read" on public.vehicle_photos for select to anon using (true); -- Authenticated (admin) full access create policy "vehicle_photos_admin_read" on public.vehicle_photos for select to authenticated using (true); create policy "vehicle_photos_admin_insert" on public.vehicle_photos for insert to authenticated with check (true); create policy "vehicle_photos_admin_update" on public.vehicle_photos for update to authenticated using (true) with check (true); create policy "vehicle_photos_admin_delete" on public.vehicle_photos for delete to authenticated using (true); -- Grants grant select on public.vehicle_photos to anon, authenticated; grant insert, update, delete on public.vehicle_photos to authenticated; grant all on public.vehicle_photos to service_role; -- Migrate existing vehicle photo_url/photo_path to vehicle_photos table -- This ensures existing vehicles get their photo into the new table insert into public.vehicle_photos (vehicle_id, photo_url, photo_path, display_order, is_primary) select id, photo_url, coalesce(photo_path, 'legacy'), 0, true from public.vehicles where photo_url != '' and photo_path is not null on conflict do nothing; -- RPC: set primary photo for a vehicle (unsets others) create or replace function public.set_primary_vehicle_photo( p_vehicle_id uuid, p_photo_id uuid ) returns void language plpgsql security invoker as $$ begin update public.vehicle_photos set is_primary = false where vehicle_id = p_vehicle_id; update public.vehicle_photos set is_primary = true where id = p_photo_id and vehicle_id = p_vehicle_id; end; $$; -- RPC: re-order photos for a vehicle create or replace function public.reorder_vehicle_photos( p_vehicle_id uuid, p_photo_orders jsonb -- [{id: uuid, order: int}, ...] ) returns void language plpgsql security invoker as $$ declare rec jsonb; begin for rec in select * from jsonb_array_elements(p_photo_orders) loop update public.vehicle_photos set display_order = (rec->>'order')::int where id = (rec->>'id')::uuid and vehicle_id = p_vehicle_id; end loop; end; $$;