Initial commit

This commit is contained in:
belviskhoremk
2026-02-22 21:59:37 +00:00
commit 5bd496d355
27 changed files with 4172 additions and 0 deletions

192
supabase_schema.sql Normal file
View File

@@ -0,0 +1,192 @@
-- ============================================================
-- CONTEXTA - Supabase Database Schema
-- Run this in your Supabase SQL Editor
-- ============================================================
-- Enable UUID extension
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
-- ─── Companies ────────────────────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS companies (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
owner_id UUID REFERENCES auth.users(id) ON DELETE CASCADE,
name VARCHAR(255) NOT NULL,
website VARCHAR(255),
industry VARCHAR(100),
logo_url TEXT,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
-- ─── Subscriptions ────────────────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS subscriptions (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
user_id UUID REFERENCES auth.users(id) ON DELETE CASCADE UNIQUE,
plan VARCHAR(50) DEFAULT 'free',
status VARCHAR(50) DEFAULT 'active',
stripe_customer_id VARCHAR(255) UNIQUE,
stripe_subscription_id VARCHAR(255),
stripe_price_id VARCHAR(255),
current_period_start TIMESTAMPTZ,
current_period_end TIMESTAMPTZ,
chatbots_published INT DEFAULT 0,
conversations_used INT DEFAULT 0,
trial_end TIMESTAMPTZ,
canceled_at TIMESTAMPTZ,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
-- ─── Chatbots ─────────────────────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS chatbots (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
company_id UUID REFERENCES companies(id) ON DELETE CASCADE,
name VARCHAR(255) NOT NULL,
description TEXT,
system_prompt TEXT,
model VARCHAR(200) DEFAULT 'accounts/fireworks/models/llama-v3p1-70b-instruct',
temperature DECIMAL(3,2) DEFAULT 0.70,
max_tokens INT DEFAULT 1000,
primary_color VARCHAR(20) DEFAULT '#6366f1',
welcome_message TEXT DEFAULT 'Hello! How can I help you today?',
category VARCHAR(100),
industry VARCHAR(100),
languages JSONB DEFAULT '["en"]',
visibility VARCHAR(50) DEFAULT 'preview',
is_published BOOLEAN DEFAULT FALSE,
qdrant_collection_name VARCHAR(255) UNIQUE,
average_rating DECIMAL(3,1),
total_conversations INT DEFAULT 0,
is_featured BOOLEAN DEFAULT FALSE,
published_at TIMESTAMPTZ,
unpublished_at TIMESTAMPTZ,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
-- ─── Documents ────────────────────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS documents (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
chatbot_id UUID REFERENCES chatbots(id) ON DELETE CASCADE,
file_name VARCHAR(500) NOT NULL,
file_type VARCHAR(50),
file_size BIGINT DEFAULT 0,
file_url TEXT,
chunk_count INT DEFAULT 0,
status VARCHAR(50) DEFAULT 'pending',
error_message TEXT,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- ─── Conversations ────────────────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS conversations (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
chatbot_id UUID REFERENCES chatbots(id) ON DELETE CASCADE,
user_id UUID REFERENCES auth.users(id) ON DELETE SET NULL,
session_id UUID,
language VARCHAR(20) DEFAULT 'en',
message_count INT DEFAULT 0,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
-- ─── Messages ─────────────────────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS messages (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
conversation_id UUID REFERENCES conversations(id) ON DELETE CASCADE,
role VARCHAR(50) NOT NULL,
content TEXT NOT NULL,
sources JSONB,
model VARCHAR(200),
tokens_used INT DEFAULT 0,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- ─── Indexes ──────────────────────────────────────────────────────────────────
CREATE INDEX IF NOT EXISTS idx_companies_owner ON companies(owner_id);
CREATE INDEX IF NOT EXISTS idx_chatbots_company ON chatbots(company_id);
CREATE INDEX IF NOT EXISTS idx_chatbots_published ON chatbots(is_published, visibility);
CREATE INDEX IF NOT EXISTS idx_documents_chatbot ON documents(chatbot_id);
CREATE INDEX IF NOT EXISTS idx_conversations_chatbot ON conversations(chatbot_id);
CREATE INDEX IF NOT EXISTS idx_conversations_session ON conversations(session_id);
CREATE INDEX IF NOT EXISTS idx_messages_conversation ON messages(conversation_id);
CREATE INDEX IF NOT EXISTS idx_subscriptions_user ON subscriptions(user_id);
-- ─── RLS Policies ─────────────────────────────────────────────────────────────
ALTER TABLE companies ENABLE ROW LEVEL SECURITY;
ALTER TABLE subscriptions ENABLE ROW LEVEL SECURITY;
ALTER TABLE chatbots ENABLE ROW LEVEL SECURITY;
ALTER TABLE documents ENABLE ROW LEVEL SECURITY;
ALTER TABLE conversations ENABLE ROW LEVEL SECURITY;
ALTER TABLE messages ENABLE ROW LEVEL SECURITY;
-- Companies: users can only see/edit their own
CREATE POLICY "companies_own" ON companies
FOR ALL USING (auth.uid() = owner_id);
-- Subscriptions: users can only see their own
CREATE POLICY "subscriptions_own" ON subscriptions
FOR ALL USING (auth.uid() = user_id);
-- Chatbots: owners can manage, public can read published
CREATE POLICY "chatbots_owner" ON chatbots
FOR ALL USING (
company_id IN (SELECT id FROM companies WHERE owner_id = auth.uid())
);
CREATE POLICY "chatbots_public_read" ON chatbots
FOR SELECT USING (is_published = TRUE AND visibility = 'published');
-- Documents: only chatbot owners
CREATE POLICY "documents_owner" ON documents
FOR ALL USING (
chatbot_id IN (
SELECT c.id FROM chatbots c
JOIN companies co ON c.company_id = co.id
WHERE co.owner_id = auth.uid()
)
);
-- Conversations & Messages: open for anonymous users to create
CREATE POLICY "conversations_insert" ON conversations FOR INSERT WITH CHECK (true);
CREATE POLICY "conversations_select" ON conversations FOR SELECT USING (true);
CREATE POLICY "messages_insert" ON messages FOR INSERT WITH CHECK (true);
CREATE POLICY "messages_select" ON messages FOR SELECT USING (true);
-- ─── Marketplace View ─────────────────────────────────────────────────────────
CREATE OR REPLACE VIEW marketplace_chatbots AS
SELECT
c.id, c.name, c.description, c.category, c.industry,
c.languages, c.primary_color, c.welcome_message,
c.average_rating, c.total_conversations, c.is_featured,
c.published_at, c.created_at,
co.name AS company_name, co.logo_url AS company_logo
FROM chatbots c
JOIN companies co ON c.company_id = co.id
JOIN subscriptions s ON co.owner_id = s.user_id
WHERE
c.visibility = 'published'
AND c.is_published = TRUE
AND s.status = 'active'
AND s.plan IN ('starter', 'pro', 'enterprise');
-- ─── Auto-unpublish on subscription cancel ────────────────────────────────────
CREATE OR REPLACE FUNCTION unpublish_on_subscription_end()
RETURNS TRIGGER AS $$
BEGIN
IF NEW.status IN ('canceled', 'unpaid', 'past_due') THEN
UPDATE chatbots
SET visibility = 'preview', is_published = FALSE
WHERE company_id IN (
SELECT id FROM companies WHERE owner_id = NEW.user_id
);
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;
DROP TRIGGER IF EXISTS subscription_status_change ON subscriptions;
CREATE TRIGGER subscription_status_change
AFTER UPDATE ON subscriptions
FOR EACH ROW
WHEN (OLD.status IS DISTINCT FROM NEW.status)
EXECUTE FUNCTION unpublish_on_subscription_end();