-- ================================================ -- SUPABASE DATABASE SCHEMA FOR SSG WHATSAPP AI -- ================================================ -- Enable UUID extension CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; -- ================================================ -- TABLE: categories -- Purpose: Store business categories -- ================================================ CREATE TABLE categories ( id SERIAL PRIMARY KEY, name VARCHAR(255) NOT NULL UNIQUE, description TEXT, icon VARCHAR(50), display_order INTEGER DEFAULT 0, is_active BOOLEAN DEFAULT TRUE, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() ); CREATE INDEX idx_categories_name ON categories(name); CREATE INDEX idx_categories_active ON categories(is_active); -- ================================================ -- TABLE: locations -- Purpose: Store geographical locations -- ================================================ CREATE TABLE locations ( id SERIAL PRIMARY KEY, name VARCHAR(255) NOT NULL UNIQUE, country VARCHAR(100), region VARCHAR(100), latitude DECIMAL(10, 8), longitude DECIMAL(11, 8), display_order INTEGER DEFAULT 0, is_active BOOLEAN DEFAULT TRUE, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() ); CREATE INDEX idx_locations_name ON locations(name); CREATE INDEX idx_locations_country ON locations(country); CREATE INDEX idx_locations_active ON locations(is_active); -- ================================================ -- TABLE: companies -- Purpose: Store company listings -- ================================================ CREATE TABLE companies ( id SERIAL PRIMARY KEY, company_name VARCHAR(255) NOT NULL, description TEXT, website_link VARCHAR(500), advertising_tier VARCHAR(20) DEFAULT 'Free' CHECK (advertising_tier IN ('Premium', 'Enhanced', 'Logo', 'Free')), category_id INTEGER NOT NULL REFERENCES categories(id) ON DELETE CASCADE, location_id INTEGER NOT NULL REFERENCES locations(id) ON DELETE CASCADE, keywords TEXT[], contact_email VARCHAR(255), contact_phone VARCHAR(50), address TEXT, logo_url VARCHAR(500), is_active BOOLEAN DEFAULT TRUE, featured BOOLEAN DEFAULT FALSE, views_count INTEGER DEFAULT 0, clicks_count INTEGER DEFAULT 0, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() ); CREATE INDEX idx_companies_category ON companies(category_id); CREATE INDEX idx_companies_location ON companies(location_id); CREATE INDEX idx_companies_tier ON companies(advertising_tier); CREATE INDEX idx_companies_active ON companies(is_active); CREATE INDEX idx_companies_keywords ON companies USING GIN(keywords); -- ================================================ -- TABLE: users -- Purpose: Store user profiles and subscription info -- ================================================ CREATE TABLE users ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), phone_number VARCHAR(20) UNIQUE NOT NULL, full_name VARCHAR(255), email VARCHAR(255), role VARCHAR(50) DEFAULT 'boat_owner', vessel_size VARCHAR(50), vessel_name VARCHAR(255), subscription_tier VARCHAR(20) DEFAULT 'free' CHECK (subscription_tier IN ('free', 'premium')), subscription_start_date TIMESTAMP WITH TIME ZONE, subscription_end_date TIMESTAMP WITH TIME ZONE, queries_today INTEGER DEFAULT 0, max_daily_queries INTEGER DEFAULT 10, total_queries INTEGER DEFAULT 0, onboarding_completed BOOLEAN DEFAULT FALSE, preferences JSONB DEFAULT '{}', created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), last_active_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() ); -- Index for fast phone number lookups CREATE INDEX idx_users_phone_number ON users(phone_number); CREATE INDEX idx_users_subscription_tier ON users(subscription_tier); CREATE INDEX idx_users_last_active ON users(last_active_at); -- ================================================ -- TABLE: conversations -- Purpose: Store message history for context -- ================================================ CREATE TABLE conversations ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, role VARCHAR(20) NOT NULL CHECK (role IN ('user', 'assistant', 'system')), content TEXT NOT NULL, content_type VARCHAR(20) DEFAULT 'text' CHECK (content_type IN ('text', 'image', 'audio', 'video')), media_url TEXT, metadata JSONB DEFAULT '{}', tokens_used INTEGER, model_used VARCHAR(100), created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() ); -- Indexes for conversation retrieval CREATE INDEX idx_conversations_user_id ON conversations(user_id); CREATE INDEX idx_conversations_created_at ON conversations(created_at DESC); CREATE INDEX idx_conversations_user_created ON conversations(user_id, created_at DESC); -- ================================================ -- TABLE: search_queries -- Purpose: Log searches for analytics -- ================================================ CREATE TABLE search_queries ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, category_id VARCHAR(10), category_name VARCHAR(255), location_id VARCHAR(10), location_name VARCHAR(255), search_term TEXT, results_count INTEGER DEFAULT 0, top_result_company VARCHAR(255), user_query TEXT, extracted_intent JSONB, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() ); -- Indexes for analytics queries CREATE INDEX idx_search_queries_user_id ON search_queries(user_id); CREATE INDEX idx_search_queries_category ON search_queries(category_name); CREATE INDEX idx_search_queries_location ON search_queries(location_name); CREATE INDEX idx_search_queries_created_at ON search_queries(created_at DESC); -- ================================================ -- TABLE: api_logs -- Purpose: Track API calls for debugging/monitoring -- ================================================ CREATE TABLE api_logs ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), user_id UUID REFERENCES users(id) ON DELETE SET NULL, endpoint VARCHAR(255) NOT NULL, method VARCHAR(10) NOT NULL, request_params JSONB, response_status INTEGER, response_time_ms INTEGER, error_message TEXT, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() ); CREATE INDEX idx_api_logs_created_at ON api_logs(created_at DESC); CREATE INDEX idx_api_logs_endpoint ON api_logs(endpoint); -- ================================================ -- TABLE: user_feedback -- Purpose: Store user feedback and ratings -- ================================================ CREATE TABLE user_feedback ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, conversation_id UUID REFERENCES conversations(id) ON DELETE SET NULL, rating INTEGER CHECK (rating >= 1 AND rating <= 5), feedback_text TEXT, feedback_type VARCHAR(50) CHECK (feedback_type IN ('helpful', 'not_helpful', 'incorrect', 'bug', 'feature_request', 'other')), created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() ); CREATE INDEX idx_user_feedback_user_id ON user_feedback(user_id); CREATE INDEX idx_user_feedback_rating ON user_feedback(rating); CREATE INDEX idx_user_feedback_created_at ON user_feedback(created_at DESC); -- ================================================ -- TABLE: system_metrics -- Purpose: Track system performance metrics -- ================================================ CREATE TABLE system_metrics ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), metric_type VARCHAR(100) NOT NULL, metric_value NUMERIC, metadata JSONB DEFAULT '{}', created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() ); CREATE INDEX idx_system_metrics_type ON system_metrics(metric_type); CREATE INDEX idx_system_metrics_created_at ON system_metrics(created_at DESC); -- ================================================ -- TABLE: subscription_history -- Purpose: Track subscription changes and payments -- ================================================ CREATE TABLE subscription_history ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, previous_tier VARCHAR(20), new_tier VARCHAR(20) NOT NULL, change_reason VARCHAR(255), amount_paid NUMERIC(10, 2), payment_method VARCHAR(50), payment_status VARCHAR(50), valid_from TIMESTAMP WITH TIME ZONE NOT NULL, valid_until TIMESTAMP WITH TIME ZONE, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() ); CREATE INDEX idx_subscription_history_user_id ON subscription_history(user_id); CREATE INDEX idx_subscription_history_created_at ON subscription_history(created_at DESC); -- ================================================ -- FUNCTIONS & TRIGGERS -- ================================================ -- Function to update updated_at timestamp CREATE OR REPLACE FUNCTION update_updated_at_column() RETURNS TRIGGER AS $$ BEGIN NEW.updated_at = NOW(); RETURN NEW; END; $$ language 'plpgsql'; -- Trigger for users table CREATE TRIGGER update_users_updated_at BEFORE UPDATE ON users FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); -- Function to reset daily query count CREATE OR REPLACE FUNCTION reset_daily_queries() RETURNS void AS $$ BEGIN UPDATE users SET queries_today = 0; END; $$ language 'plpgsql'; -- Function to increment user queries CREATE OR REPLACE FUNCTION increment_user_query(p_user_id UUID) RETURNS void AS $$ BEGIN UPDATE users SET queries_today = queries_today + 1, total_queries = total_queries + 1, last_active_at = NOW() WHERE id = p_user_id; END; $$ language 'plpgsql'; -- Function to get conversation context (last N messages) CREATE OR REPLACE FUNCTION get_conversation_context( p_user_id UUID, p_limit INTEGER DEFAULT 10 ) RETURNS TABLE ( role VARCHAR(20), content TEXT, created_at TIMESTAMP WITH TIME ZONE ) AS $$ BEGIN RETURN QUERY SELECT c.role, c.content, c.created_at FROM conversations c WHERE c.user_id = p_user_id ORDER BY c.created_at DESC LIMIT p_limit; END; $$ language 'plpgsql'; -- ================================================ -- ROW LEVEL SECURITY (RLS) POLICIES -- ================================================ -- Enable RLS on all tables ALTER TABLE users ENABLE ROW LEVEL SECURITY; ALTER TABLE conversations ENABLE ROW LEVEL SECURITY; ALTER TABLE search_queries ENABLE ROW LEVEL SECURITY; ALTER TABLE user_feedback ENABLE ROW LEVEL SECURITY; ALTER TABLE subscription_history ENABLE ROW LEVEL SECURITY; -- Policy: Users can only read their own data CREATE POLICY "Users can view own data" ON users FOR SELECT USING (auth.uid()::text = id::text); -- Policy: Users can update their own profile CREATE POLICY "Users can update own profile" ON users FOR UPDATE USING (auth.uid()::text = id::text); -- Policy: Users can view their own conversations CREATE POLICY "Users can view own conversations" ON conversations FOR SELECT USING (auth.uid()::text = user_id::text); -- Policy: Users can view their own search queries CREATE POLICY "Users can view own searches" ON search_queries FOR SELECT USING (auth.uid()::text = user_id::text); -- ================================================ -- COMMENTS FOR DOCUMENTATION -- ================================================ COMMENT ON TABLE users IS 'Stores user profiles, subscription status, and usage metrics'; COMMENT ON TABLE conversations IS 'Message history for providing conversation context to AI'; COMMENT ON TABLE search_queries IS 'Logs all search queries for analytics and improvement'; COMMENT ON TABLE api_logs IS 'Tracks API performance and errors for monitoring'; COMMENT ON TABLE user_feedback IS 'Stores user feedback and ratings for quality improvement'; COMMENT ON TABLE system_metrics IS 'System-wide performance and usage metrics'; COMMENT ON TABLE subscription_history IS 'Audit trail of subscription changes and payments'; COMMENT ON COLUMN users.preferences IS 'JSON object storing user preferences like language, notification settings'; COMMENT ON COLUMN conversations.metadata IS 'JSON object storing additional message metadata like processing time, model version'; COMMENT ON COLUMN search_queries.extracted_intent IS 'JSON object containing the extracted category, location, and search term';