alter table public.profiles add column description text not null default '' check (char_length(description) <= 500), add column avatar_path text check (avatar_path is null or avatar_path = id::text || '/avatar'), add column updated_at timestamptz not null default now(); -- Avatars are public profile media, but only the owning authenticated user may -- create, replace, or remove the one deterministic object in their folder. insert into storage.buckets(id, name, public, file_size_limit, allowed_mime_types) values ( 'profile-avatars', 'profile-avatars', true, 2097152, array['image/jpeg', 'image/png', 'image/webp'] ) on conflict (id) do update set public = excluded.public, file_size_limit = excluded.file_size_limit, allowed_mime_types = excluded.allowed_mime_types; create policy dng_profile_avatar_insert on storage.objects for insert to authenticated with check ( bucket_id = 'profile-avatars' and name = (select auth.uid())::text || '/avatar' ); create policy dng_profile_avatar_select_own on storage.objects for select to authenticated using ( bucket_id = 'profile-avatars' and name = (select auth.uid())::text || '/avatar' ); create policy dng_profile_avatar_update on storage.objects for update to authenticated using ( bucket_id = 'profile-avatars' and name = (select auth.uid())::text || '/avatar' ) with check ( bucket_id = 'profile-avatars' and name = (select auth.uid())::text || '/avatar' ); create policy dng_profile_avatar_delete on storage.objects for delete to authenticated using ( bucket_id = 'profile-avatars' and name = (select auth.uid())::text || '/avatar' ); create or replace function public.dng_schema_version() returns integer language sql stable security definer set search_path = '' as $$ select 9; $$; revoke all on function public.dng_schema_version() from public, anon, authenticated; grant execute on function public.dng_schema_version() to service_role; notify pgrst, 'reload schema';