-- R1 Konto und Profil: profiles, Anlage beim Registrieren. create table public.profiles ( id uuid primary key references auth.users (id) on delete cascade, display_name text not null check (char_length(display_name) between 1 and 30), bio text check (char_length(bio) <= 300), city text check (char_length(city) <= 100), city_visible boolean not null default true, avatar_path text, locale text not null default 'de' check (locale in ('de', 'en')), terms_accepted_at timestamptz not null, age_confirmed_at timestamptz not null, deleted_at timestamptz, created_at timestamptz not null default now(), updated_at timestamptz not null default now() ); create trigger set_updated_at before update on public.profiles for each row execute function public.set_updated_at(); -- Profil automatisch anlegen. Die Registrierung scheitert ohne Altersbestätigung (ab 16) -- und ohne Zustimmung zu den Nutzungsbedingungen. Die App sendet beide als user_metadata. create or replace function public.handle_new_user() returns trigger language plpgsql security definer set search_path = '' as $$ declare meta jsonb := coalesce(new.raw_user_meta_data, '{}'::jsonb); name text; begin if coalesce(meta ->> 'age_confirmed', '') <> 'true' then raise exception 'age_confirmation_required'; end if; if coalesce(meta ->> 'terms_accepted', '') <> 'true' then raise exception 'terms_acceptance_required'; end if; name := left(btrim(coalesce( nullif(btrim(meta ->> 'display_name'), ''), nullif(split_part(new.email, '@', 1), ''), 'Spieler' )), 30); insert into public.profiles (id, display_name, locale, terms_accepted_at, age_confirmed_at) values ( new.id, name, case when meta ->> 'locale' in ('de', 'en') then meta ->> 'locale' else 'de' end, now(), now() ); return new; end; $$; revoke all on function public.handle_new_user() from public, anon, authenticated; create trigger on_auth_user_created after insert on auth.users for each row execute function public.handle_new_user(); -- Zugriff: Jeder sieht nur das eigene Profil. Fremde Profile gibt es nur über die View -- public_profiles (siehe Migration plays), und dort nur für Mitspieler. alter table public.profiles enable row level security; revoke all on public.profiles from anon, authenticated; grant select on public.profiles to authenticated; -- Nur diese Spalten darf der Nutzer ändern; Bestätigungen und Löschmarke nie. grant update (display_name, bio, city, city_visible, avatar_path, locale) on public.profiles to authenticated; create policy profiles_select_own on public.profiles for select to authenticated using (id = (select auth.uid())); create policy profiles_update_own on public.profiles for update to authenticated using (id = (select auth.uid())) with check (id = (select auth.uid()));