mirror of
https://github.com/pezkuwichain/pwap.git
synced 2026-04-22 20:37:56 +00:00
c48ded7ff2
Restructured the project to support multiple frontend applications: - Move web app to web/ directory - Create pezkuwi-sdk-ui/ for Polkadot SDK clone (planned) - Create mobile/ directory for mobile app development - Add shared/ directory with common utilities, types, and blockchain code - Update README.md with comprehensive documentation - Remove obsolete DKSweb/ directory This monorepo structure enables better code sharing and organized development across web, mobile, and SDK UI projects.
256 lines
8.7 KiB
PL/PgSQL
256 lines
8.7 KiB
PL/PgSQL
-- ========================================
|
|
-- 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 $$;
|