From 32bff77fd0b62ee50a73a934fe2622dd1d183ef4 Mon Sep 17 00:00:00 2001 From: byGalax Date: Fri, 15 May 2026 22:02:29 +0200 Subject: [PATCH] feat(db): conversation_keys can target user_id alongside legacy device_id MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Note: not applied locally — push via pnpm prod:migrate when ready. --- ...00002_conversation_keys_user_recipient.sql | 76 +++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 supabase/migrations/20260515000002_conversation_keys_user_recipient.sql diff --git a/supabase/migrations/20260515000002_conversation_keys_user_recipient.sql b/supabase/migrations/20260515000002_conversation_keys_user_recipient.sql new file mode 100644 index 0000000..b9338a3 --- /dev/null +++ b/supabase/migrations/20260515000002_conversation_keys_user_recipient.sql @@ -0,0 +1,76 @@ +-- Add per-user recipient/sender columns so conv-keys can be wrapped to a +-- user identity instead of a specific device. Old per-device columns stay +-- nullable through the transition; a follow-up migration drops them once +-- migration telemetry shows >=95% adoption. + +alter table public.conversation_keys + add column if not exists recipient_user_id uuid references auth.users(id) on delete cascade, + add column if not exists sender_user_id uuid references auth.users(id) on delete restrict; + +alter table public.conversation_keys + alter column recipient_device_id drop not null, + alter column sender_device_id drop not null; + +create unique index if not exists conversation_keys_user_recipient_uniq + on public.conversation_keys(conversation_id, recipient_user_id, key_version) + where recipient_user_id is not null; + +create index if not exists conversation_keys_recipient_user_idx + on public.conversation_keys(recipient_user_id); + +drop policy if exists conversation_keys_select_owner on public.conversation_keys; +create policy conversation_keys_select_owner + on public.conversation_keys + for select + to authenticated + using ( + recipient_user_id = auth.uid() + or exists ( + select 1 + from public.devices d + where d.id = recipient_device_id + and d.user_id = auth.uid() + ) + ); + +drop policy if exists conversation_keys_insert_member on public.conversation_keys; +create policy conversation_keys_insert_member + on public.conversation_keys + for insert + to authenticated + with check ( + exists ( + select 1 + from public.conversation_members m + where m.conversation_id = conversation_keys.conversation_id + and m.user_id = auth.uid() + and m.accepted = true + ) + and ( + sender_user_id = auth.uid() + or exists ( + select 1 + from public.devices d + where d.id = sender_device_id + and d.user_id = auth.uid() + ) + ) + and ( + (recipient_user_id is not null and exists ( + select 1 + from public.conversation_members m + where m.conversation_id = conversation_keys.conversation_id + and m.user_id = recipient_user_id + and m.accepted = true + )) + or (recipient_device_id is not null and exists ( + select 1 + from public.devices d + join public.conversation_members m + on m.user_id = d.user_id + and m.conversation_id = conversation_keys.conversation_id + where d.id = recipient_device_id + and m.accepted = true + )) + ) + );