59 lines
2.4 KiB
PL/PgSQL
59 lines
2.4 KiB
PL/PgSQL
-- View-once media: per-attachment opt-in. When a recipient opens the
|
|
-- attachment for the first time we delete the storage object and replace
|
|
-- it with a tombstone marker (kept_until / viewed_at). The encrypted
|
|
-- attachment row stays so the bubble can render "Angesehen".
|
|
|
|
alter table public.message_attachments
|
|
add column if not exists view_once boolean not null default false,
|
|
add column if not exists viewed_at timestamptz null,
|
|
add column if not exists viewed_by uuid null references auth.users(id) on delete set null;
|
|
|
|
-- Mark-viewed RPC: server-authoritative so the recipient can't replay the
|
|
-- decrypted blob across devices. Atomic: only the FIRST viewer wins. The
|
|
-- function returns the previous `viewed_at` so the client can distinguish
|
|
-- first-open (got it) from already-viewed (too late, sorry).
|
|
create or replace function public.mark_attachment_viewed(p_attachment_id uuid)
|
|
returns jsonb
|
|
language plpgsql
|
|
security definer
|
|
set search_path = public
|
|
as $$
|
|
declare
|
|
caller uuid := auth.uid();
|
|
att public.message_attachments%rowtype;
|
|
msg public.messages%rowtype;
|
|
prev timestamptz;
|
|
begin
|
|
if caller is null then raise exception 'not authenticated'; end if;
|
|
|
|
select * into att from public.message_attachments where id = p_attachment_id for update;
|
|
if not found then raise exception 'attachment not found'; end if;
|
|
if not att.view_once then return jsonb_build_object('view_once', false); end if;
|
|
|
|
select * into msg from public.messages where id = att.message_id;
|
|
if not found then raise exception 'orphan attachment'; end if;
|
|
if not public.is_conversation_member(msg.conversation_id) then
|
|
raise exception 'not a member';
|
|
end if;
|
|
-- Sender opening their own view-once doesn't "burn" it — they sent it.
|
|
if msg.sender_id = caller then
|
|
return jsonb_build_object('view_once', true, 'viewed_at', att.viewed_at, 'self', true);
|
|
end if;
|
|
|
|
if att.viewed_at is not null then
|
|
return jsonb_build_object('view_once', true, 'viewed_at', att.viewed_at, 'already', true);
|
|
end if;
|
|
|
|
update public.message_attachments
|
|
set viewed_at = now(),
|
|
viewed_by = caller
|
|
where id = p_attachment_id
|
|
returning viewed_at into prev;
|
|
|
|
return jsonb_build_object('view_once', true, 'viewed_at', prev);
|
|
end;
|
|
$$;
|
|
|
|
revoke execute on function public.mark_attachment_viewed(uuid) from public, anon;
|
|
grant execute on function public.mark_attachment_viewed(uuid) to authenticated;
|