freemium F3b: /saas/usuarios (donos) + notify_all_devs
- saas_list_account_owners(): 1 linha/tenant com dono (master), nome/slug/email/ plano + selo "novo" (24h). Dev-only (is_saas_admin), cast text p/ varchar. - notify_all_devs() insere em notifications_sistema p/ cada saas_admin - trigger AFTER INSERT/UPDATE OF status em subscriptions avisa os devs com deeplink /saas/usuarios - testado em ROLLBACK: lista + notify ao inserir subscription Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,116 @@
|
||||
-- =============================================================================
|
||||
-- Freemium F3b — /saas/usuarios (donos por tenant) + notificação aos devs
|
||||
--
|
||||
-- ⚠️ APLICAR COMO supabase_admin (lê auth.users.email + cria trigger em
|
||||
-- public.subscriptions; notify_user_sistema é chamada por SECURITY DEFINER).
|
||||
--
|
||||
-- • saas_list_account_owners(): 1 linha por tenant com o DONO (master),
|
||||
-- nome/slug/e-mail/plano + selo "novo" (24h). Dev-only (is_saas_admin).
|
||||
-- • notify_all_devs(): insere em notifications_sistema p/ cada saas_admin.
|
||||
-- • trigger em subscriptions: avisa os devs quando nasce/muda uma assinatura,
|
||||
-- com deeplink pra /saas/usuarios.
|
||||
-- =============================================================================
|
||||
|
||||
BEGIN;
|
||||
|
||||
-- 1) Donos por tenant (dev-only) ---------------------------------------------
|
||||
CREATE OR REPLACE FUNCTION public.saas_list_account_owners()
|
||||
RETURNS TABLE (
|
||||
tenant_id uuid,
|
||||
slug text,
|
||||
tenant_name text,
|
||||
kind text,
|
||||
owner_id uuid,
|
||||
owner_name text,
|
||||
owner_email text,
|
||||
plan_key text,
|
||||
created_at timestamptz,
|
||||
is_new boolean
|
||||
)
|
||||
LANGUAGE plpgsql STABLE SECURITY DEFINER SET search_path TO 'public','pg_temp'
|
||||
AS $$
|
||||
BEGIN
|
||||
IF NOT public.is_saas_admin() THEN
|
||||
RAISE EXCEPTION 'forbidden' USING ERRCODE = '42501';
|
||||
END IF;
|
||||
|
||||
RETURN QUERY
|
||||
SELECT t.id, t.slug::text, t.name::text, t.kind::text,
|
||||
owner.user_id, pr.full_name::text, au.email::text,
|
||||
COALESCE(vas.plan_key, ps.plan_key)::text,
|
||||
t.created_at,
|
||||
(t.created_at > now() - interval '24 hours')
|
||||
FROM public.tenants t
|
||||
LEFT JOIN LATERAL (
|
||||
SELECT tm.user_id
|
||||
FROM public.tenant_members tm
|
||||
WHERE tm.tenant_id = t.id AND tm.role = 'tenant_admin' AND tm.status = 'active'
|
||||
ORDER BY tm.created_at ASC
|
||||
LIMIT 1
|
||||
) owner ON true
|
||||
LEFT JOIN public.profiles pr ON pr.id = owner.user_id
|
||||
LEFT JOIN auth.users au ON au.id = owner.user_id
|
||||
LEFT JOIN public.v_tenant_active_subscription vas ON vas.tenant_id = t.id
|
||||
LEFT JOIN LATERAL (
|
||||
SELECT s.plan_key FROM public.subscriptions s
|
||||
WHERE s.user_id = owner.user_id AND s.status = 'active' AND s.tenant_id IS NULL
|
||||
ORDER BY s.created_at DESC LIMIT 1
|
||||
) ps ON true
|
||||
ORDER BY t.created_at DESC;
|
||||
END $$;
|
||||
ALTER FUNCTION public.saas_list_account_owners() OWNER TO supabase_admin;
|
||||
REVOKE ALL ON FUNCTION public.saas_list_account_owners() FROM PUBLIC;
|
||||
GRANT EXECUTE ON FUNCTION public.saas_list_account_owners() TO authenticated, service_role;
|
||||
|
||||
-- 2) notify_all_devs ----------------------------------------------------------
|
||||
CREATE OR REPLACE FUNCTION public.notify_all_devs(
|
||||
p_type text, p_payload jsonb, p_ref_id uuid DEFAULT NULL, p_ref_table text DEFAULT NULL
|
||||
)
|
||||
RETURNS int LANGUAGE plpgsql SECURITY DEFINER SET search_path TO 'public','pg_temp'
|
||||
AS $$
|
||||
DECLARE r record; n int := 0;
|
||||
BEGIN
|
||||
FOR r IN SELECT user_id FROM public.saas_admins LOOP
|
||||
PERFORM public.notify_user_sistema(r.user_id, p_type, p_payload, NULL, p_ref_id, p_ref_table);
|
||||
n := n + 1;
|
||||
END LOOP;
|
||||
RETURN n;
|
||||
END $$;
|
||||
ALTER FUNCTION public.notify_all_devs(text, jsonb, uuid, text) OWNER TO supabase_admin;
|
||||
|
||||
-- 3) trigger em subscriptions -------------------------------------------------
|
||||
CREATE OR REPLACE FUNCTION public.trg_notify_devs_subscription()
|
||||
RETURNS trigger LANGUAGE plpgsql SECURITY DEFINER SET search_path TO 'public','pg_temp'
|
||||
AS $$
|
||||
DECLARE v_slug text; v_title text;
|
||||
BEGIN
|
||||
-- só em INSERT ou quando o status muda
|
||||
IF TG_OP = 'UPDATE' AND NEW.status IS NOT DISTINCT FROM OLD.status THEN
|
||||
RETURN NEW;
|
||||
END IF;
|
||||
|
||||
SELECT t.slug INTO v_slug FROM public.tenants t WHERE t.id = NEW.tenant_id;
|
||||
|
||||
v_title := CASE WHEN TG_OP = 'INSERT' THEN 'Nova assinatura' ELSE 'Assinatura atualizada' END;
|
||||
|
||||
PERFORM public.notify_all_devs(
|
||||
'subscription_' || lower(TG_OP),
|
||||
jsonb_build_object(
|
||||
'title', v_title,
|
||||
'detail', NEW.plan_key || ' · ' || NEW.status || COALESCE(' · ' || v_slug, ''),
|
||||
'deeplink', '/saas/usuarios',
|
||||
'plan_key', NEW.plan_key,
|
||||
'status', NEW.status
|
||||
),
|
||||
NEW.id, 'subscriptions'
|
||||
);
|
||||
RETURN NEW;
|
||||
END $$;
|
||||
ALTER FUNCTION public.trg_notify_devs_subscription() OWNER TO supabase_admin;
|
||||
|
||||
DROP TRIGGER IF EXISTS trg_subscriptions_notify_devs ON public.subscriptions;
|
||||
CREATE TRIGGER trg_subscriptions_notify_devs
|
||||
AFTER INSERT OR UPDATE OF status ON public.subscriptions
|
||||
FOR EACH ROW EXECUTE FUNCTION public.trg_notify_devs_subscription();
|
||||
|
||||
COMMIT;
|
||||
Reference in New Issue
Block a user