-- ============================================================================ -- Private storage bucket for encrypted attachment blobs. -- Files are stored under `{conversation_id}/{attachment_id}.bin`. -- The conversation_id is parsed from the object name so RLS can reuse the -- existing is_conversation_member helper. -- ============================================================================ insert into storage.buckets (id, name, public, file_size_limit) values ('chat-attachments', 'chat-attachments', false, 10 * 1024 * 1024) on conflict (id) do update set public = excluded.public, file_size_limit = excluded.file_size_limit; -- Helper: extract conversation_id from the object name safely. create or replace function public.attachment_object_conv_id(object_name text) returns uuid language sql immutable as $$ select case when object_name is null or position('/' in object_name) = 0 then null else (split_part(object_name, '/', 1))::uuid end; $$; -- Policies on storage.objects for this bucket only. drop policy if exists "chat_attachments_select_member" on storage.objects; create policy "chat_attachments_select_member" on storage.objects for select to authenticated using ( bucket_id = 'chat-attachments' and public.is_conversation_member(public.attachment_object_conv_id(name)) ); drop policy if exists "chat_attachments_insert_member" on storage.objects; create policy "chat_attachments_insert_member" on storage.objects for insert to authenticated with check ( bucket_id = 'chat-attachments' and public.is_conversation_member(public.attachment_object_conv_id(name)) ); drop policy if exists "chat_attachments_delete_sender" on storage.objects; create policy "chat_attachments_delete_sender" on storage.objects for delete to authenticated using ( bucket_id = 'chat-attachments' and owner = auth.uid() );