The migrations did not describe this database, and could not be made to.
Three findings, in the order they surfaced:
* 25 functions declared across six migrations — all recorded as applied — did
not exist. Their tables did. Two were reached by the app, so merchant tier
upgrades and post-trade reputation updates had been quietly dead. This only
came to light because a user hit "Could not find the function
public.upsert_user_profile(...)" while toggling a notification setting.
* admin_roles has three conflicting definitions across the set and production
matches none of them. 001 says (id, user_id, role, granted_by, granted_at),
COMBINED says (user_id, role, created_at), production has
(id, user_id, role, permissions, created_at, updated_at).
* Applied to an empty database, five migrations fail. The legacy 0NN filenames
sort before the 14-digit timestamps they depend on — "013" < "20241117054600"
— so 013 runs before the migration creating the table it alters. The set
could never have been replayed from scratch.
So it could not be tested, could not rebuild the database, and did not match what
was running. Widening or patching it would have been dressing up a history that
was already fiction.
The baseline is a pg_dump of the live public schema, which matches production by
construction. Privileges are included deliberately: the REVOKEs on
lock_escrow_internal, release_escrow_internal, refund_escrow_internal and
request_withdraw are the 20260725030000 hardening, and dropping them would hand
fund movement back to anon.
Verified step by step against production before committing:
- applies to an empty database cleanly, after three real obstacles were fixed
(extensions live in the `extensions` schema, supabase_admin membership,
platform-level ALTER DEFAULT PRIVILEGES that cannot apply outside Supabase)
- produces 83 tables / 39 functions / 222 indexes / 360 policies — identical
counts to production
- recorded as applied in supabase_migrations without touching the 37 existing
rows, then dry-run confirmed "up to date — no pending migrations", so the
next deploy will not try to replay it over live tables
- drift check now reports "every declared function is present"; the known-gaps
list drops from 22 entries to zero
The old files move to migrations/archive/ rather than being deleted — they are
the only record of why parts of this look the way they do. Their README says
plainly not to run them, and why.
CI now applies migrations to an empty Postgres on every PR. That check was
impossible while the old set was the starting point; it is the thing that stops
this class of drift from being discovered by a user again.
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ş
- https://supabase.com/dashboard adresine gidin
- PezkuwiChain projesini açın: https://supabase.com/dashboard/project/vsyrpfiwhjvahofxwytr
2. SQL Editor'ı Açın
- Sol menüden SQL Editor sekmesine tıklayın
- New Query butonuna tıklayın
3. Migration Script'ini Çalıştırın
supabase/migrations/001_initial_schema.sqldosyasını açın- Tüm içeriği kopyalayın
- Supabase SQL Editor'a yapıştırın
- 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 adresifull_name- Tam adavatar_url- Avatar resmi URL'sireferred_by- Referans kodu (kim davet etti)referral_code- Kendi referans kodu (otomatik oluşturulur)referral_count- Kaç kişi davet ettitotal_referral_rewards- Toplam kazanılan ödüller
2. admin_roles Tablosu
Admin ve moderator rol atamalarını saklar:
id- Benzersiz IDuser_id- User IDrole- Rol: 'admin', 'super_admin', 'moderator'granted_by- Rolü kim verdigranted_at- Ne zaman verildi
3. wallets Tablosu
Kullanıcı wallet adreslerini saklar:
id- Benzersiz IDuser_id- User IDaddress- Wallet adresinetwork- 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 IDreferrer_id- Davet eden user IDreferred_user_id- Davet edilen user IDreferral_code- Kullanılan referral codereward_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
- Web uygulamasını başlatın:
npm run dev /loginsayfasına gidin- Sign Up sekmesine tıklayın
- Yeni kullanıcı bilgilerini girin:
- Full Name: Test User
- Email: test@example.com
- Password: Test1234!
- Referral Code: (opsiyonel)
- Create Account butonuna tıklayın
2. Database'i Kontrol Edin
- Supabase Dashboard → Table Editor sekmesine gidin
profilestablosunu seçin- Yeni kaydın oluştuğunu doğrulayın
referral_codealanının otomatik doldurulduğunu kontrol edin
3. Login Testi
- Oluşturduğunuz email ve password ile login olun
- Başarılı giriş yapabildiğinizi doğrulayın
Admin Rolü Ekleme
Bir kullanıcıya admin rolü vermek için:
- Supabase Dashboard → SQL Editor
- Şu SQL'i çalıştırın:
-- 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:
- AuthContext.tsx'deki
signUpfonksiyonunu kontrol edin (line 148-186) - Browser console'da hata mesajlarını kontrol edin
- 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
-
Production'da:
VITE_ENABLE_DEMO_MODE=falseyapın- Demo credentials'ları kaldırın
- Service role key'i asla commit etmeyin
-
Development'ta:
- Test verileri ile çalışın
- Real user data kullanmayın
Additional Resources
Last Updated: 2025-01-28 Version: 1.0.0