cb153165c3
- 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>
117 lines
4.5 KiB
PL/PgSQL
117 lines
4.5 KiB
PL/PgSQL
-- =============================================================================
|
|
-- 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;
|