-- Profile page: more optional details, the name given at sign-up, photos, and account deletion. alter table profiles add column phone text, add column hatti_name text, -- village / hatti, free text add column avatar_url text, add column updated_at timestamptz not null default now(); -- Use the name typed at sign-up (user metadata), falling back to the email's first part. create or replace function handle_new_user() returns trigger language plpgsql security definer set search_path = public as $$ begin insert into profiles (id, display_name) values (new.id, coalesce(nullif(trim(new.raw_user_meta_data ->> 'display_name'), ''), split_part(new.email, '@', 1))); return new; end $$; -- App stores require in-app account deletion. Removes the login; profile, likes, -- playlists and history go with it (on delete cascade). create function delete_my_account() returns void language plpgsql security definer set search_path = public as $$ begin if auth.uid() is null then raise exception 'not signed in'; end if; delete from storage.objects where bucket_id = 'avatars' and (storage.foldername(name))[1] = auth.uid()::text; delete from auth.users where id = auth.uid(); end $$; revoke all on function delete_my_account() from public, anon; grant execute on function delete_my_account() to authenticated; -- Profile photos: avatars//. Public to read, each user writes only their own folder. insert into storage.buckets (id, name, public, file_size_limit, allowed_mime_types) values ('avatars', 'avatars', true, 2097152, array['image/jpeg','image/png','image/webp']) on conflict (id) do nothing; create policy nk_own_avatar on storage.objects for all to authenticated using (bucket_id = 'avatars' and (storage.foldername(name))[1] = auth.uid()::text) with check (bucket_id = 'avatars' and (storage.foldername(name))[1] = auth.uid()::text);