Spaces:
Running
Running
| -- Nova AI - Supabase Database Schema | |
| -- Run this in Supabase SQL Editor | |
| -- Enable UUID extension | |
| CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; | |
| -- Profiles table (extends Supabase auth.users) | |
| CREATE TABLE IF NOT EXISTS profiles ( | |
| id UUID REFERENCES auth.users ON DELETE CASCADE PRIMARY KEY, | |
| email TEXT UNIQUE, | |
| display_name TEXT, | |
| avatar_url TEXT, | |
| -- Subscription | |
| subscription_tier TEXT DEFAULT 'free' CHECK (subscription_tier IN ('free', 'pro')), | |
| tokens_used INTEGER DEFAULT 0, | |
| tokens_limit INTEGER DEFAULT 20, | |
| tokens_reset_at TIMESTAMPTZ DEFAULT (NOW() + INTERVAL '30 days'), | |
| -- Consent | |
| consent_given BOOLEAN DEFAULT FALSE, | |
| consent_given_at TIMESTAMPTZ, | |
| data_collection_consent BOOLEAN DEFAULT FALSE, | |
| terms_accepted_at TIMESTAMPTZ, | |
| -- Preferences | |
| theme TEXT DEFAULT 'system', | |
| notifications_enabled BOOLEAN DEFAULT TRUE, | |
| -- Admin | |
| is_admin BOOLEAN DEFAULT FALSE, | |
| -- Timestamps | |
| created_at TIMESTAMPTZ DEFAULT NOW(), | |
| updated_at TIMESTAMPTZ DEFAULT NOW() | |
| ); | |
| -- Chat history | |
| CREATE TABLE IF NOT EXISTS chat_history ( | |
| id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), | |
| user_id UUID REFERENCES profiles(id) ON DELETE CASCADE, | |
| title TEXT DEFAULT 'New Chat', | |
| messages JSONB DEFAULT '[]'::jsonb, | |
| model_id TEXT, | |
| is_local BOOLEAN DEFAULT FALSE, | |
| created_at TIMESTAMPTZ DEFAULT NOW(), | |
| updated_at TIMESTAMPTZ DEFAULT NOW() | |
| ); | |
| -- Blocked IPs (security) | |
| CREATE TABLE IF NOT EXISTS blocked_ips ( | |
| id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), | |
| ip_address TEXT NOT NULL, | |
| device_info JSONB DEFAULT '{}'::jsonb, | |
| user_agent TEXT, | |
| reason TEXT, | |
| attempt_count INTEGER DEFAULT 1, | |
| blocked_at TIMESTAMPTZ DEFAULT NOW(), | |
| blocked_by UUID REFERENCES profiles(id), | |
| is_active BOOLEAN DEFAULT TRUE | |
| ); | |
| -- Subscriptions | |
| CREATE TABLE IF NOT EXISTS subscriptions ( | |
| id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), | |
| user_id UUID REFERENCES profiles(id) ON DELETE CASCADE, | |
| tier TEXT NOT NULL CHECK (tier IN ('free', 'pro', 'addon')), | |
| razorpay_subscription_id TEXT, | |
| razorpay_order_id TEXT, | |
| amount INTEGER, -- in paise | |
| currency TEXT DEFAULT 'INR', | |
| status TEXT DEFAULT 'active' CHECK (status IN ('active', 'cancelled', 'expired', 'pending')), | |
| started_at TIMESTAMPTZ DEFAULT NOW(), | |
| expires_at TIMESTAMPTZ, | |
| created_at TIMESTAMPTZ DEFAULT NOW() | |
| ); | |
| -- Token purchases (add-ons) | |
| CREATE TABLE IF NOT EXISTS token_purchases ( | |
| id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), | |
| user_id UUID REFERENCES profiles(id) ON DELETE CASCADE, | |
| tokens_purchased INTEGER NOT NULL, | |
| amount INTEGER NOT NULL, -- in paise | |
| razorpay_payment_id TEXT, | |
| status TEXT DEFAULT 'pending' CHECK (status IN ('pending', 'completed', 'failed')), | |
| created_at TIMESTAMPTZ DEFAULT NOW() | |
| ); | |
| -- Admin notifications | |
| CREATE TABLE IF NOT EXISTS admin_notifications ( | |
| id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), | |
| type TEXT NOT NULL, -- 'security', 'system', 'user', 'payment' | |
| title TEXT NOT NULL, | |
| message TEXT, | |
| metadata JSONB DEFAULT '{}'::jsonb, | |
| is_read BOOLEAN DEFAULT FALSE, | |
| created_at TIMESTAMPTZ DEFAULT NOW() | |
| ); | |
| -- Task queue (for background processing tracking) | |
| CREATE TABLE IF NOT EXISTS task_queue ( | |
| id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), | |
| user_id UUID REFERENCES profiles(id) ON DELETE CASCADE, | |
| task_type TEXT NOT NULL, -- 'chat', 'image', 'analysis' | |
| task_data JSONB NOT NULL, | |
| result JSONB, | |
| status TEXT DEFAULT 'pending' CHECK (status IN ('pending', 'processing', 'completed', 'failed')), | |
| error_message TEXT, | |
| created_at TIMESTAMPTZ DEFAULT NOW(), | |
| completed_at TIMESTAMPTZ | |
| ); | |
| -- API usage logs | |
| CREATE TABLE IF NOT EXISTS api_usage ( | |
| id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), | |
| user_id UUID REFERENCES profiles(id) ON DELETE SET NULL, | |
| endpoint TEXT NOT NULL, | |
| method TEXT, | |
| ip_address TEXT, | |
| tokens_used INTEGER DEFAULT 0, | |
| response_time_ms INTEGER, | |
| status_code INTEGER, | |
| created_at TIMESTAMPTZ DEFAULT NOW() | |
| ); | |
| -- Create indexes for performance | |
| CREATE INDEX IF NOT EXISTS idx_profiles_email ON profiles(email); | |
| CREATE INDEX IF NOT EXISTS idx_chat_history_user ON chat_history(user_id); | |
| CREATE INDEX IF NOT EXISTS idx_chat_history_updated ON chat_history(updated_at DESC); | |
| CREATE INDEX IF NOT EXISTS idx_blocked_ips_address ON blocked_ips(ip_address); | |
| CREATE INDEX IF NOT EXISTS idx_blocked_ips_active ON blocked_ips(is_active); | |
| CREATE INDEX IF NOT EXISTS idx_subscriptions_user ON subscriptions(user_id); | |
| CREATE INDEX IF NOT EXISTS idx_task_queue_user ON task_queue(user_id); | |
| CREATE INDEX IF NOT EXISTS idx_task_queue_status ON task_queue(status); | |
| CREATE INDEX IF NOT EXISTS idx_api_usage_user ON api_usage(user_id); | |
| CREATE INDEX IF NOT EXISTS idx_api_usage_created ON api_usage(created_at DESC); | |
| CREATE INDEX IF NOT EXISTS idx_admin_notifications_unread ON admin_notifications(is_read) WHERE is_read = FALSE; | |
| -- Row Level Security (RLS) | |
| ALTER TABLE profiles ENABLE ROW LEVEL SECURITY; | |
| ALTER TABLE chat_history ENABLE ROW LEVEL SECURITY; | |
| ALTER TABLE subscriptions ENABLE ROW LEVEL SECURITY; | |
| ALTER TABLE token_purchases ENABLE ROW LEVEL SECURITY; | |
| ALTER TABLE task_queue ENABLE ROW LEVEL SECURITY; | |
| -- Profiles: Users can read/update their own profile | |
| CREATE POLICY "Users can view own profile" ON profiles | |
| FOR SELECT USING (auth.uid() = id); | |
| CREATE POLICY "Users can update own profile" ON profiles | |
| FOR UPDATE USING (auth.uid() = id); | |
| -- Chat history: Users can manage their own chats | |
| CREATE POLICY "Users can view own chats" ON chat_history | |
| FOR SELECT USING (auth.uid() = user_id); | |
| CREATE POLICY "Users can insert own chats" ON chat_history | |
| FOR INSERT WITH CHECK (auth.uid() = user_id); | |
| CREATE POLICY "Users can update own chats" ON chat_history | |
| FOR UPDATE USING (auth.uid() = user_id); | |
| CREATE POLICY "Users can delete own chats" ON chat_history | |
| FOR DELETE USING (auth.uid() = user_id); | |
| -- Subscriptions: Users can view their own | |
| CREATE POLICY "Users can view own subscriptions" ON subscriptions | |
| FOR SELECT USING (auth.uid() = user_id); | |
| -- Token purchases: Users can view their own | |
| CREATE POLICY "Users can view own purchases" ON token_purchases | |
| FOR SELECT USING (auth.uid() = user_id); | |
| -- Task queue: Users can manage their own tasks | |
| CREATE POLICY "Users can view own tasks" ON task_queue | |
| FOR SELECT USING (auth.uid() = user_id); | |
| CREATE POLICY "Users can insert own tasks" ON task_queue | |
| FOR INSERT WITH CHECK (auth.uid() = user_id); | |
| -- Function to handle new user signup | |
| CREATE OR REPLACE FUNCTION handle_new_user() | |
| RETURNS TRIGGER AS $$ | |
| BEGIN | |
| INSERT INTO profiles (id, email, display_name) | |
| VALUES ( | |
| NEW.id, | |
| NEW.email, | |
| COALESCE(NEW.raw_user_meta_data->>'full_name', split_part(NEW.email, '@', 1)) | |
| ); | |
| RETURN NEW; | |
| END; | |
| $$ LANGUAGE plpgsql SECURITY DEFINER; | |
| -- Trigger for new user signup | |
| DROP TRIGGER IF EXISTS on_auth_user_created ON auth.users; | |
| CREATE TRIGGER on_auth_user_created | |
| AFTER INSERT ON auth.users | |
| FOR EACH ROW EXECUTE FUNCTION handle_new_user(); | |
| -- Function to update tokens_used | |
| CREATE OR REPLACE FUNCTION use_tokens(p_user_id UUID, p_tokens INTEGER) | |
| RETURNS BOOLEAN AS $$ | |
| DECLARE | |
| v_tokens_used INTEGER; | |
| v_tokens_limit INTEGER; | |
| BEGIN | |
| SELECT tokens_used, tokens_limit INTO v_tokens_used, v_tokens_limit | |
| FROM profiles WHERE id = p_user_id; | |
| IF v_tokens_used + p_tokens > v_tokens_limit THEN | |
| RETURN FALSE; | |
| END IF; | |
| UPDATE profiles | |
| SET tokens_used = tokens_used + p_tokens, updated_at = NOW() | |
| WHERE id = p_user_id; | |
| RETURN TRUE; | |
| END; | |
| $$ LANGUAGE plpgsql SECURITY DEFINER; | |
| -- Function to reset monthly tokens | |
| CREATE OR REPLACE FUNCTION reset_monthly_tokens() | |
| RETURNS void AS $$ | |
| BEGIN | |
| UPDATE profiles | |
| SET tokens_used = 0, tokens_reset_at = NOW() + INTERVAL '30 days' | |
| WHERE tokens_reset_at < NOW(); | |
| END; | |
| $$ LANGUAGE plpgsql SECURITY DEFINER; | |
| -- Admin check function | |
| CREATE OR REPLACE FUNCTION is_admin(p_user_id UUID) | |
| RETURNS BOOLEAN AS $$ | |
| BEGIN | |
| RETURN EXISTS (SELECT 1 FROM profiles WHERE id = p_user_id AND is_admin = TRUE); | |
| END; | |
| $$ LANGUAGE plpgsql SECURITY DEFINER; | |