-- ======================================== -- 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 $$;