diff --git a/.env.example b/.env.example index fcd49e62..93202e4f 100644 --- a/.env.example +++ b/.env.example @@ -38,10 +38,12 @@ VITE_ENABLE_DEMO_MODE=true # ======================================== # SUPABASE CONFIGURATION # ======================================== -# Get your credentials from: https://supabase.com/dashboard +# Get these from: https://supabase.com/dashboard/project/vsyrpfiwhjvahofxwytr/settings/api +# 1. Project URL: Copy from "Project URL" section +# 2. Anon key: Copy from "Project API keys" → "anon" → "public" -VITE_SUPABASE_URL=your_supabase_project_url -VITE_SUPABASE_ANON_KEY=your_supabase_anon_key +VITE_SUPABASE_URL=https://vsyrpfiwhjvahofxwytr.supabase.co +VITE_SUPABASE_ANON_KEY=your_supabase_anon_key_here # ======================================== # SUBSTRATE ASSET IDs diff --git a/supabase/README.md b/supabase/README.md new file mode 100644 index 00000000..006f003f --- /dev/null +++ b/supabase/README.md @@ -0,0 +1,202 @@ +# Supabase Setup Guide + +## Overview + +Bu klasör Supabase database setup için gerekli migration dosyalarını içerir. + +--- + +## Quick Start + +### 1. Supabase Dashboard'a Giriş + +1. https://supabase.com/dashboard adresine gidin +2. PezkuwiChain projesini açın: https://supabase.com/dashboard/project/vsyrpfiwhjvahofxwytr + +### 2. SQL Editor'ı Açın + +1. Sol menüden **SQL Editor** sekmesine tıklayın +2. **New Query** butonuna tıklayın + +### 3. Migration Script'ini Çalıştırın + +1. `supabase/migrations/001_initial_schema.sql` dosyasını açın +2. Tüm içeriği kopyalayın +3. Supabase SQL Editor'a yapıştırın +4. Sağ alttaki **Run** butonuna tıklayın + +### 4. Sonuçları Kontrol Edin + +Migration başarılıysa şu mesajı göreceksiniz: +``` +Database schema created successfully! +``` + +--- + +## Oluşturulan Tablolar + +### 1. `profiles` Tablosu +Kullanıcı profil bilgilerini ve referral verilerini saklar: +- `id` - User ID (auth.users'a reference) +- `username` - Benzersiz kullanıcı adı +- `email` - Email adresi +- `full_name` - Tam ad +- `avatar_url` - Avatar resmi URL'si +- `referred_by` - Referans kodu (kim davet etti) +- `referral_code` - Kendi referans kodu (otomatik oluşturulur) +- `referral_count` - Kaç kişi davet etti +- `total_referral_rewards` - Toplam kazanılan ödüller + +### 2. `admin_roles` Tablosu +Admin ve moderator rol atamalarını saklar: +- `id` - Benzersiz ID +- `user_id` - User ID +- `role` - Rol: 'admin', 'super_admin', 'moderator' +- `granted_by` - Rolü kim verdi +- `granted_at` - Ne zaman verildi + +### 3. `wallets` Tablosu +Kullanıcı wallet adreslerini saklar: +- `id` - Benzersiz ID +- `user_id` - User ID +- `address` - Wallet adresi +- `network` - Network adı (pezkuwichain, polkadot, etc.) +- `is_primary` - Primary wallet mı? +- `nickname` - Wallet nickname'i + +### 4. `referral_history` Tablosu +Referral ödüllerini ve geçmişini takip eder: +- `id` - Benzersiz ID +- `referrer_id` - Davet eden user ID +- `referred_user_id` - Davet edilen user ID +- `referral_code` - Kullanılan referral code +- `reward_amount` - Ödül miktarı +- `reward_token` - Ödül token'ı (PEZ, HEZ, etc.) +- `reward_claimed` - Ödül talep edildi mi? + +--- + +## Automatic Features + +### 1. Referral Code Auto-Generation +Her kullanıcı kaydolduğunda otomatik olarak benzersiz 8 karakterli bir referral code oluşturulur. + +### 2. Row Level Security (RLS) +Tüm tablolarda RLS etkin: +- Kullanıcılar sadece kendi verilerini görebilir/düzenleyebilir +- Admin'ler admin_roles tablosuna erişebilir +- Public profiller herkes tarafından görülebilir + +### 3. Timestamp Updates +Profile güncellendiğinde `updated_at` otomatik olarak güncellenir. + +--- + +## Test Etme + +### 1. Sign Up Testi + +1. Web uygulamasını başlatın: `npm run dev` +2. `/login` sayfasına gidin +3. **Sign Up** sekmesine tıklayın +4. Yeni kullanıcı bilgilerini girin: + - Full Name: Test User + - Email: test@example.com + - Password: Test1234! + - Referral Code: (opsiyonel) +5. **Create Account** butonuna tıklayın + +### 2. Database'i Kontrol Edin + +1. Supabase Dashboard → **Table Editor** sekmesine gidin +2. `profiles` tablosunu seçin +3. Yeni kaydın oluştuğunu doğrulayın +4. `referral_code` alanının otomatik doldurulduğunu kontrol edin + +### 3. Login Testi + +1. Oluşturduğunuz email ve password ile login olun +2. Başarılı giriş yapabildiğinizi doğrulayın + +--- + +## Admin Rolü Ekleme + +Bir kullanıcıya admin rolü vermek için: + +1. Supabase Dashboard → **SQL Editor** +2. Şu SQL'i çalıştırın: + +```sql +-- Get user ID first +SELECT id, email FROM auth.users WHERE email = 'info@pezkuwichain.io'; + +-- Then add admin role (replace USER_ID with actual ID) +INSERT INTO public.admin_roles (user_id, role, granted_by) +VALUES ('USER_ID', 'super_admin', 'USER_ID') +ON CONFLICT (user_id) DO UPDATE SET role = 'super_admin'; +``` + +--- + +## Troubleshooting + +### Problem: "relation 'profiles' already exists" + +**Çözüm**: Tablolar zaten oluşturulmuş. Sorun yok, devam edebilirsiniz. + +### Problem: "duplicate key value violates unique constraint" + +**Çözüm**: Bu kayıt zaten var. Normal bir durum. + +### Problem: Sign up başarılı ama profile oluşmadı + +**Çözüm**: +1. AuthContext.tsx'deki `signUp` fonksiyonunu kontrol edin (line 148-186) +2. Browser console'da hata mesajlarını kontrol edin +3. Supabase Dashboard → **Logs** → **Postgres Logs**'u inceleyin + +### Problem: "Invalid JWT token" + +**Çözüm**: `.env` dosyasındaki `VITE_SUPABASE_ANON_KEY` değerini kontrol edin. + +--- + +## Security Notes + +### 🔒 Row Level Security (RLS) + +Tüm tablolar RLS ile korunuyor: +- Kullanıcılar sadece kendi verilerine erişebilir +- Public veriler (profil bilgileri) herkes tarafından görülebilir +- Admin rolleri sadece admin'ler tarafından görülebilir + +### 🔑 API Keys + +- **anon key**: Frontend'de kullanılır, RLS kurallarına tabidir +- **service_role key**: ASLA frontend'de kullanmayın! Server-side only. + +### 📝 Best Practices + +1. **Production'da**: + - `VITE_ENABLE_DEMO_MODE=false` yapın + - Demo credentials'ları kaldırın + - Service role key'i asla commit etmeyin + +2. **Development'ta**: + - Test verileri ile çalışın + - Real user data kullanmayın + +--- + +## Additional Resources + +- [Supabase Documentation](https://supabase.com/docs) +- [Row Level Security Guide](https://supabase.com/docs/guides/auth/row-level-security) +- [Database Functions](https://supabase.com/docs/guides/database/functions) + +--- + +**Last Updated**: 2025-01-28 +**Version**: 1.0.0 diff --git a/supabase/migrations/001_initial_schema.sql b/supabase/migrations/001_initial_schema.sql new file mode 100644 index 00000000..f41c8038 --- /dev/null +++ b/supabase/migrations/001_initial_schema.sql @@ -0,0 +1,255 @@ +-- ======================================== +-- PezkuwiChain - Initial Database Schema +-- ======================================== +-- Run this in Supabase SQL Editor to set up required tables +-- Dashboard → SQL Editor → New Query → Paste & Run + +-- ======================================== +-- 1. PROFILES TABLE +-- ======================================== +-- Stores user profile information and referral data + +CREATE TABLE IF NOT EXISTS public.profiles ( + id UUID REFERENCES auth.users(id) ON DELETE CASCADE, + username TEXT UNIQUE NOT NULL, + email TEXT, + full_name TEXT, + avatar_url TEXT, + referred_by TEXT, + referral_code TEXT UNIQUE, + referral_count INTEGER DEFAULT 0, + total_referral_rewards DECIMAL(20, 4) DEFAULT 0, + created_at TIMESTAMP WITH TIME ZONE DEFAULT TIMEZONE('utc'::text, NOW()) NOT NULL, + updated_at TIMESTAMP WITH TIME ZONE DEFAULT TIMEZONE('utc'::text, NOW()) NOT NULL, + PRIMARY KEY (id) +); + +-- Create index for faster lookups +CREATE INDEX IF NOT EXISTS idx_profiles_username ON public.profiles(username); +CREATE INDEX IF NOT EXISTS idx_profiles_email ON public.profiles(email); +CREATE INDEX IF NOT EXISTS idx_profiles_referral_code ON public.profiles(referral_code); +CREATE INDEX IF NOT EXISTS idx_profiles_referred_by ON public.profiles(referred_by); + +-- Enable Row Level Security +ALTER TABLE public.profiles ENABLE ROW LEVEL SECURITY; + +-- Drop existing policies if they exist +DROP POLICY IF EXISTS "Users can view their own profile" ON public.profiles; +DROP POLICY IF EXISTS "Users can update their own profile" ON public.profiles; +DROP POLICY IF EXISTS "Public profiles are viewable by everyone" ON public.profiles; + +-- Create policies +CREATE POLICY "Users can view their own profile" + ON public.profiles FOR SELECT + USING (auth.uid() = id); + +CREATE POLICY "Users can update their own profile" + ON public.profiles FOR UPDATE + USING (auth.uid() = id); + +CREATE POLICY "Public profiles are viewable by everyone" + ON public.profiles FOR SELECT + USING (true); + +-- ======================================== +-- 2. ADMIN ROLES TABLE +-- ======================================== +-- Stores admin and moderator role assignments + +CREATE TABLE IF NOT EXISTS public.admin_roles ( + id UUID DEFAULT gen_random_uuid() PRIMARY KEY, + user_id UUID REFERENCES auth.users(id) ON DELETE CASCADE, + role TEXT NOT NULL CHECK (role IN ('admin', 'super_admin', 'moderator')), + granted_by UUID REFERENCES auth.users(id), + granted_at TIMESTAMP WITH TIME ZONE DEFAULT TIMEZONE('utc'::text, NOW()) NOT NULL, + UNIQUE(user_id) +); + +-- Create index for faster lookups +CREATE INDEX IF NOT EXISTS idx_admin_roles_user_id ON public.admin_roles(user_id); + +-- Enable Row Level Security +ALTER TABLE public.admin_roles ENABLE ROW LEVEL SECURITY; + +-- Drop existing policies if they exist +DROP POLICY IF EXISTS "Admins can view admin roles" ON public.admin_roles; +DROP POLICY IF EXISTS "Super admins can manage admin roles" ON public.admin_roles; + +-- Create policies +CREATE POLICY "Admins can view admin roles" + ON public.admin_roles FOR SELECT + USING ( + EXISTS ( + SELECT 1 FROM public.admin_roles + WHERE user_id = auth.uid() AND role IN ('admin', 'super_admin') + ) + ); + +CREATE POLICY "Super admins can manage admin roles" + ON public.admin_roles FOR ALL + USING ( + EXISTS ( + SELECT 1 FROM public.admin_roles + WHERE user_id = auth.uid() AND role = 'super_admin' + ) + ); + +-- ======================================== +-- 3. WALLETS TABLE +-- ======================================== +-- Stores user wallet addresses and metadata + +CREATE TABLE IF NOT EXISTS public.wallets ( + id UUID DEFAULT gen_random_uuid() PRIMARY KEY, + user_id UUID REFERENCES auth.users(id) ON DELETE CASCADE, + address TEXT NOT NULL, + network TEXT NOT NULL DEFAULT 'pezkuwichain', + is_primary BOOLEAN DEFAULT false, + nickname TEXT, + created_at TIMESTAMP WITH TIME ZONE DEFAULT TIMEZONE('utc'::text, NOW()) NOT NULL, + last_used_at TIMESTAMP WITH TIME ZONE, + UNIQUE(user_id, address, network) +); + +-- Create index for faster lookups +CREATE INDEX IF NOT EXISTS idx_wallets_user_id ON public.wallets(user_id); +CREATE INDEX IF NOT EXISTS idx_wallets_address ON public.wallets(address); + +-- Enable Row Level Security +ALTER TABLE public.wallets ENABLE ROW LEVEL SECURITY; + +-- Drop existing policies if they exist +DROP POLICY IF EXISTS "Users can view their own wallets" ON public.wallets; +DROP POLICY IF EXISTS "Users can manage their own wallets" ON public.wallets; + +-- Create policies +CREATE POLICY "Users can view their own wallets" + ON public.wallets FOR SELECT + USING (auth.uid() = user_id); + +CREATE POLICY "Users can manage their own wallets" + ON public.wallets FOR ALL + USING (auth.uid() = user_id); + +-- ======================================== +-- 4. REFERRAL TRACKING TABLE +-- ======================================== +-- Tracks referral rewards and history + +CREATE TABLE IF NOT EXISTS public.referral_history ( + id UUID DEFAULT gen_random_uuid() PRIMARY KEY, + referrer_id UUID REFERENCES auth.users(id) ON DELETE CASCADE, + referred_user_id UUID REFERENCES auth.users(id) ON DELETE CASCADE, + referral_code TEXT NOT NULL, + reward_amount DECIMAL(20, 4) DEFAULT 0, + reward_token TEXT DEFAULT 'PEZ', + reward_claimed BOOLEAN DEFAULT false, + claimed_at TIMESTAMP WITH TIME ZONE, + created_at TIMESTAMP WITH TIME ZONE DEFAULT TIMEZONE('utc'::text, NOW()) NOT NULL, + UNIQUE(referred_user_id) +); + +-- Create index for faster lookups +CREATE INDEX IF NOT EXISTS idx_referral_history_referrer ON public.referral_history(referrer_id); +CREATE INDEX IF NOT EXISTS idx_referral_history_referred ON public.referral_history(referred_user_id); + +-- Enable Row Level Security +ALTER TABLE public.referral_history ENABLE ROW LEVEL SECURITY; + +-- Drop existing policies if they exist +DROP POLICY IF EXISTS "Users can view their referral history" ON public.referral_history; + +-- Create policies +CREATE POLICY "Users can view their referral history" + ON public.referral_history FOR SELECT + USING (auth.uid() = referrer_id OR auth.uid() = referred_user_id); + +-- ======================================== +-- 5. FUNCTIONS +-- ======================================== + +-- Function to auto-generate referral code on profile creation +CREATE OR REPLACE FUNCTION public.generate_referral_code() +RETURNS TRIGGER AS $$ +DECLARE + new_code TEXT; + code_exists BOOLEAN; +BEGIN + -- Generate a random 8-character referral code + LOOP + new_code := UPPER(SUBSTRING(MD5(RANDOM()::TEXT) FROM 1 FOR 8)); + + -- Check if code already exists + SELECT EXISTS(SELECT 1 FROM public.profiles WHERE referral_code = new_code) INTO code_exists; + + -- Exit loop if code is unique + EXIT WHEN NOT code_exists; + END LOOP; + + NEW.referral_code := new_code; + RETURN NEW; +END; +$$ LANGUAGE plpgsql SECURITY DEFINER; + +-- Function to update updated_at timestamp +CREATE OR REPLACE FUNCTION public.update_updated_at_column() +RETURNS TRIGGER AS $$ +BEGIN + NEW.updated_at = NOW(); + RETURN NEW; +END; +$$ LANGUAGE plpgsql; + +-- ======================================== +-- 6. TRIGGERS +-- ======================================== + +-- Drop existing triggers if they exist +DROP TRIGGER IF EXISTS trigger_generate_referral_code ON public.profiles; +DROP TRIGGER IF EXISTS trigger_update_profiles_updated_at ON public.profiles; + +-- Auto-generate referral code when profile is created +CREATE TRIGGER trigger_generate_referral_code + BEFORE INSERT ON public.profiles + FOR EACH ROW + WHEN (NEW.referral_code IS NULL) + EXECUTE FUNCTION public.generate_referral_code(); + +-- Auto-update updated_at timestamp +CREATE TRIGGER trigger_update_profiles_updated_at + BEFORE UPDATE ON public.profiles + FOR EACH ROW + EXECUTE FUNCTION public.update_updated_at_column(); + +-- ======================================== +-- 7. INITIAL DATA (OPTIONAL) +-- ======================================== +-- Add founder as super admin (if exists) + +-- Note: Replace 'founder-001' with actual founder user ID after first login +-- This is commented out by default for security + +-- INSERT INTO public.admin_roles (user_id, role, granted_by) +-- VALUES ('founder-001', 'super_admin', 'founder-001') +-- ON CONFLICT (user_id) DO NOTHING; + +-- ======================================== +-- SUCCESS MESSAGE +-- ======================================== +DO $$ +BEGIN + RAISE NOTICE '========================================'; + RAISE NOTICE 'Database schema created successfully!'; + RAISE NOTICE '========================================'; + RAISE NOTICE 'Tables created:'; + RAISE NOTICE ' - profiles'; + RAISE NOTICE ' - admin_roles'; + RAISE NOTICE ' - wallets'; + RAISE NOTICE ' - referral_history'; + RAISE NOTICE ''; + RAISE NOTICE 'Next steps:'; + RAISE NOTICE '1. Test sign up on the web app'; + RAISE NOTICE '2. Check if profile is created automatically'; + RAISE NOTICE '3. Assign admin role manually if needed'; + RAISE NOTICE '========================================'; +END $$;