-- Guest access still uses a real authenticated Supabase user. This keeps the -- existing ownership checks, RLS policies, and multiplayer membership model. -- Email accounts remain restricted to the allowlist. create or replace function public.create_profile_for_allowlisted_user() returns trigger language plpgsql security definer set search_path = '' as $$ declare v_is_anonymous boolean := coalesce(new.is_anonymous, false); v_display_name text; begin if not v_is_anonymous and ( new.email is null or not exists ( select 1 from public.allowlist where email = lower(btrim(new.email)) ) ) then raise exception using errcode = '42501', message = 'This email is not invited to the Dungeons & Ground alpha.'; end if; v_display_name := case when v_is_anonymous then 'Guest ' || upper(substr(replace(new.id::text, '-', ''), 1, 6)) else coalesce(nullif(btrim(new.raw_user_meta_data ->> 'display_name'), ''), 'Adventurer') end; insert into public.profiles(id, display_name) values (new.id, v_display_name) on conflict (id) do nothing; return new; end; $$; revoke all on function public.create_profile_for_allowlisted_user() from public; -- An anonymous account can be upgraded later. Recheck the allowlist on the -- relevant auth.users transition so linking an email cannot bypass the alpha -- gate. Unrelated token and metadata updates do not fire this trigger. create or replace function public.enforce_alpha_email_allowlist() returns trigger language plpgsql security definer set search_path = '' as $$ begin if not coalesce(new.is_anonymous, false) and ( new.email is null or not exists ( select 1 from public.allowlist where email = lower(btrim(new.email)) ) ) then raise exception using errcode = '42501', message = 'This email is not invited to the Dungeons & Ground alpha.'; end if; return new; end; $$; revoke all on function public.enforce_alpha_email_allowlist() from public; drop trigger if exists enforce_alpha_email_allowlist on auth.users; create trigger enforce_alpha_email_allowlist before update of email, is_anonymous on auth.users for each row when ( old.email is distinct from new.email or old.is_anonymous is distinct from new.is_anonymous ) execute function public.enforce_alpha_email_allowlist(); -- The schema may be installed after Auth users already exist. Backfill only -- anonymous users and explicitly allowlisted email accounts; do not silently -- enable unrelated accounts. insert into public.profiles(id, display_name) select auth_user.id, case when coalesce(auth_user.is_anonymous, false) then 'Guest ' || upper(substr(replace(auth_user.id::text, '-', ''), 1, 6)) else coalesce(nullif(btrim(auth_user.raw_user_meta_data ->> 'display_name'), ''), 'Adventurer') end from auth.users auth_user where coalesce(auth_user.is_anonymous, false) or exists ( select 1 from public.allowlist where allowlist.email = lower(btrim(auth_user.email)) ) on conflict (id) do nothing; notify pgrst, 'reload schema';