nkbmusic/supabase/migrations/20261030000000_profiles.sql
metatroncubeswdev 178d2d00ab Nanga Kathai: Flutter app, self-hosted backend and song tools
- Flutter app (Android/iOS): home, explore, albums, search, player with
  equalizer, mini player, sign up/sign in with email codes, profile,
  encrypted offline downloads, About page
- server/: one-command deploy of self-hosted Supabase behind HestiaCP/nginx
  (or Caddy), migrations, backups, email templates
- supabase/migrations: schema, row-level security, profiles
- tools/: song scanner and importer

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 10:39:26 -04:00

41 lines
1.9 KiB
PL/PgSQL

-- 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/<user id>/<file>. 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);