feat: profile avatar upload + share_conv_keys rpc + favicon + smtp tweaks
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
-- 1) Test-Daten-Wipe: alle conversation/message Daten weg, User+Devices+
|
||||
-- Friendships+Invites bleiben erhalten so dass bestehende Logins weiter
|
||||
-- funktionieren. Cascade räumt mitlaufende rows (envelopes, attachments,
|
||||
-- reactions, reads, conversation_keys, conversation_members).
|
||||
|
||||
truncate
|
||||
public.message_reactions,
|
||||
public.message_reads,
|
||||
public.message_attachments,
|
||||
public.messages,
|
||||
public.conversation_keys,
|
||||
public.conversation_members,
|
||||
public.conversations
|
||||
restart identity cascade;
|
||||
|
||||
-- 2) RPC: share conv-key bundles to multiple recipients atomically.
|
||||
-- Runs as SECURITY DEFINER so RLS doesn't reject individual rows. The
|
||||
-- function itself enforces the same invariants as the policy. This avoids
|
||||
-- the per-row 403 console spam we got with direct inserts.
|
||||
|
||||
create or replace function public.share_conv_keys(
|
||||
p_conv_id uuid,
|
||||
p_sender_device_id uuid,
|
||||
p_key_version int,
|
||||
p_bundles jsonb
|
||||
) returns int
|
||||
language plpgsql
|
||||
security definer
|
||||
set search_path = public
|
||||
as $$
|
||||
declare
|
||||
caller uuid := auth.uid();
|
||||
bundle jsonb;
|
||||
recipient_user_id uuid;
|
||||
inserted int := 0;
|
||||
recipient_did uuid;
|
||||
enc_key_hex text;
|
||||
nonce_hex text;
|
||||
begin
|
||||
if caller is null then
|
||||
raise exception 'not authenticated';
|
||||
end if;
|
||||
|
||||
-- Caller must be an accepted member of the conversation.
|
||||
if not exists (
|
||||
select 1
|
||||
from public.conversation_members
|
||||
where conversation_id = p_conv_id
|
||||
and user_id = caller
|
||||
and accepted = true
|
||||
) then
|
||||
raise exception 'caller is not an accepted member of %', p_conv_id;
|
||||
end if;
|
||||
|
||||
-- Sender device must belong to the caller.
|
||||
if not exists (
|
||||
select 1
|
||||
from public.devices
|
||||
where id = p_sender_device_id
|
||||
and user_id = caller
|
||||
) then
|
||||
raise exception 'sender_device % not owned by caller', p_sender_device_id;
|
||||
end if;
|
||||
|
||||
-- Iterate bundles and only insert ones whose recipient is a current
|
||||
-- accepted member. Silently skip otherwise (no error → no 403 noise).
|
||||
for bundle in
|
||||
select * from jsonb_array_elements(p_bundles)
|
||||
loop
|
||||
recipient_did := (bundle->>'recipient_device_id')::uuid;
|
||||
enc_key_hex := bundle->>'encrypted_key';
|
||||
nonce_hex := bundle->>'nonce';
|
||||
|
||||
select user_id
|
||||
into recipient_user_id
|
||||
from public.devices
|
||||
where id = recipient_did;
|
||||
if recipient_user_id is null then continue; end if;
|
||||
|
||||
if not exists (
|
||||
select 1
|
||||
from public.conversation_members
|
||||
where conversation_id = p_conv_id
|
||||
and user_id = recipient_user_id
|
||||
and accepted = true
|
||||
) then continue; end if;
|
||||
|
||||
insert into public.conversation_keys
|
||||
(conversation_id, recipient_device_id, key_version,
|
||||
sender_device_id, encrypted_key, nonce)
|
||||
values
|
||||
(p_conv_id, recipient_did, p_key_version,
|
||||
p_sender_device_id,
|
||||
decode(enc_key_hex, 'hex'),
|
||||
decode(nonce_hex, 'hex'))
|
||||
on conflict (conversation_id, recipient_device_id, key_version)
|
||||
do nothing;
|
||||
|
||||
if found then
|
||||
inserted := inserted + 1;
|
||||
end if;
|
||||
end loop;
|
||||
|
||||
return inserted;
|
||||
end;
|
||||
$$;
|
||||
|
||||
revoke execute on function public.share_conv_keys(uuid, uuid, int, jsonb) from public, anon;
|
||||
grant execute on function public.share_conv_keys(uuid, uuid, int, jsonb) to authenticated;
|
||||
Reference in New Issue
Block a user