d3ro-voice/server/supabase/migrations/20260821000012_system_push_dispatch.sql
2026-08-29 18:33:45 +09:00

83 lines
2.4 KiB
PL/PgSQL

BEGIN;
ALTER TABLE public.push_dispatch_attempts
ADD COLUMN actor_kind text NOT NULL DEFAULT 'user'
CHECK (actor_kind IN ('user', 'system'));
ALTER TABLE public.push_dispatch_attempts
ALTER COLUMN caller_id DROP NOT NULL,
ADD CONSTRAINT push_dispatch_attempt_actor_check CHECK (
(actor_kind = 'user' AND caller_id IS NOT NULL)
OR (actor_kind = 'system' AND caller_id IS NULL)
);
CREATE INDEX idx_push_dispatch_attempts_system_event
ON public.push_dispatch_attempts(event_type, resource_id, created_at DESC)
WHERE actor_kind = 'system';
CREATE OR REPLACE FUNCTION public.reserve_system_push_dispatch(
push_event_type text,
push_resource_id uuid
)
RETURNS jsonb
LANGUAGE plpgsql
SECURITY DEFINER
SET search_path = ''
AS $$
DECLARE
normalized_event_type text := lower(btrim(push_event_type));
existing_attempt_id uuid;
created_attempt_id uuid;
BEGIN
IF auth.role() <> 'service_role' THEN
RAISE EXCEPTION 'service_role_required' USING ERRCODE = '42501';
END IF;
IF push_resource_id IS NULL OR normalized_event_type NOT IN (
'transcription.completed',
'team.invite.created',
'billing.status.changed'
) THEN
RAISE EXCEPTION 'invalid_push_event' USING ERRCODE = '22023';
END IF;
PERFORM pg_advisory_xact_lock(
hashtextextended(normalized_event_type || ':' || push_resource_id::text, 73049)
);
SELECT attempt.id INTO existing_attempt_id
FROM public.push_dispatch_attempts AS attempt
WHERE attempt.actor_kind = 'system'
AND attempt.event_type = normalized_event_type
AND attempt.resource_id = push_resource_id
AND attempt.created_at > now() - interval '30 seconds'
ORDER BY attempt.created_at DESC
LIMIT 1;
IF existing_attempt_id IS NOT NULL THEN
RETURN jsonb_build_object(
'reserved', false,
'duplicate', true,
'attempt_id', existing_attempt_id
);
END IF;
INSERT INTO public.push_dispatch_attempts (
caller_id, actor_kind, event_type, resource_id
) VALUES (
NULL, 'system', normalized_event_type, push_resource_id
)
RETURNING id INTO created_attempt_id;
RETURN jsonb_build_object(
'reserved', true,
'duplicate', false,
'attempt_id', created_attempt_id
);
END;
$$;
REVOKE ALL ON FUNCTION public.reserve_system_push_dispatch(text, uuid)
FROM PUBLIC, anon, authenticated;
GRANT EXECUTE ON FUNCTION public.reserve_system_push_dispatch(text, uuid)
TO service_role;
COMMIT;