Spaces:
Running
Running
| -- OpenCode Tables for Supabase | |
| -- Run this in Supabase SQL Editor | |
| -- Sessions table | |
| CREATE TABLE IF NOT EXISTS opencode_sessions ( | |
| id TEXT PRIMARY KEY, | |
| user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE, | |
| title TEXT NOT NULL, | |
| agent_id TEXT DEFAULT 'build', | |
| provider_id TEXT, | |
| model_id TEXT, | |
| created_at TIMESTAMPTZ DEFAULT NOW(), | |
| updated_at TIMESTAMPTZ DEFAULT NOW() | |
| ); | |
| CREATE INDEX IF NOT EXISTS idx_opencode_sessions_user_id ON opencode_sessions(user_id); | |
| CREATE INDEX IF NOT EXISTS idx_opencode_sessions_updated_at ON opencode_sessions(updated_at DESC); | |
| -- Messages table | |
| CREATE TABLE IF NOT EXISTS opencode_messages ( | |
| id TEXT PRIMARY KEY, | |
| session_id TEXT NOT NULL REFERENCES opencode_sessions(id) ON DELETE CASCADE, | |
| role TEXT NOT NULL CHECK (role IN ('user', 'assistant')), | |
| content TEXT, | |
| provider_id TEXT, | |
| model_id TEXT, | |
| input_tokens INTEGER, | |
| output_tokens INTEGER, | |
| error TEXT, | |
| created_at TIMESTAMPTZ DEFAULT NOW() | |
| ); | |
| CREATE INDEX IF NOT EXISTS idx_opencode_messages_session_id ON opencode_messages(session_id); | |
| CREATE INDEX IF NOT EXISTS idx_opencode_messages_created_at ON opencode_messages(session_id, created_at); | |
| -- Message parts (text, tool_call, tool_result) | |
| CREATE TABLE IF NOT EXISTS opencode_message_parts ( | |
| id TEXT PRIMARY KEY, | |
| message_id TEXT NOT NULL REFERENCES opencode_messages(id) ON DELETE CASCADE, | |
| type TEXT NOT NULL CHECK (type IN ('text', 'tool_call', 'tool_result')), | |
| content TEXT, | |
| tool_call_id TEXT, | |
| tool_name TEXT, | |
| tool_args JSONB, | |
| tool_output TEXT, | |
| created_at TIMESTAMPTZ DEFAULT NOW() | |
| ); | |
| CREATE INDEX IF NOT EXISTS idx_opencode_message_parts_message_id ON opencode_message_parts(message_id); | |
| -- Usage tracking (replaces sandbox_usage) | |
| CREATE TABLE IF NOT EXISTS opencode_usage ( | |
| id UUID PRIMARY KEY DEFAULT gen_random_uuid(), | |
| user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE, | |
| usage_date DATE NOT NULL DEFAULT CURRENT_DATE, | |
| input_tokens INTEGER DEFAULT 0, | |
| output_tokens INTEGER DEFAULT 0, | |
| request_count INTEGER DEFAULT 0, | |
| UNIQUE(user_id, usage_date) | |
| ); | |
| CREATE INDEX IF NOT EXISTS idx_opencode_usage_user_date ON opencode_usage(user_id, usage_date); | |
| -- Row Level Security | |
| ALTER TABLE opencode_sessions ENABLE ROW LEVEL SECURITY; | |
| ALTER TABLE opencode_messages ENABLE ROW LEVEL SECURITY; | |
| ALTER TABLE opencode_message_parts ENABLE ROW LEVEL SECURITY; | |
| ALTER TABLE opencode_usage ENABLE ROW LEVEL SECURITY; | |
| -- RLS Policies: Users can only access their own data | |
| CREATE POLICY "Users can CRUD their own sessions" | |
| ON opencode_sessions FOR ALL | |
| USING (auth.uid() = user_id); | |
| CREATE POLICY "Users can CRUD messages in their sessions" | |
| ON opencode_messages FOR ALL | |
| USING ( | |
| EXISTS ( | |
| SELECT 1 FROM opencode_sessions | |
| WHERE opencode_sessions.id = opencode_messages.session_id | |
| AND opencode_sessions.user_id = auth.uid() | |
| ) | |
| ); | |
| CREATE POLICY "Users can CRUD parts in their messages" | |
| ON opencode_message_parts FOR ALL | |
| USING ( | |
| EXISTS ( | |
| SELECT 1 FROM opencode_messages | |
| JOIN opencode_sessions ON opencode_sessions.id = opencode_messages.session_id | |
| WHERE opencode_messages.id = opencode_message_parts.message_id | |
| AND opencode_sessions.user_id = auth.uid() | |
| ) | |
| ); | |
| CREATE POLICY "Users can access their own usage" | |
| ON opencode_usage FOR ALL | |
| USING (auth.uid() = user_id); | |
| -- Function to increment usage (atomic) | |
| CREATE OR REPLACE FUNCTION increment_opencode_usage( | |
| p_user_id UUID, | |
| p_input_tokens INTEGER DEFAULT 0, | |
| p_output_tokens INTEGER DEFAULT 0 | |
| ) | |
| RETURNS void AS $$ | |
| BEGIN | |
| INSERT INTO opencode_usage (user_id, usage_date, input_tokens, output_tokens, request_count) | |
| VALUES (p_user_id, CURRENT_DATE, p_input_tokens, p_output_tokens, 1) | |
| ON CONFLICT (user_id, usage_date) | |
| DO UPDATE SET | |
| input_tokens = opencode_usage.input_tokens + EXCLUDED.input_tokens, | |
| output_tokens = opencode_usage.output_tokens + EXCLUDED.output_tokens, | |
| request_count = opencode_usage.request_count + 1; | |
| END; | |
| $$ LANGUAGE plpgsql SECURITY DEFINER; | |
| -- Function to get daily usage | |
| CREATE OR REPLACE FUNCTION get_opencode_usage(p_user_id UUID) | |
| RETURNS TABLE(input_tokens INTEGER, output_tokens INTEGER, request_count INTEGER) AS $$ | |
| BEGIN | |
| RETURN QUERY | |
| SELECT | |
| COALESCE(u.input_tokens, 0)::INTEGER, | |
| COALESCE(u.output_tokens, 0)::INTEGER, | |
| COALESCE(u.request_count, 0)::INTEGER | |
| FROM opencode_usage u | |
| WHERE u.user_id = p_user_id AND u.usage_date = CURRENT_DATE; | |
| END; | |
| $$ LANGUAGE plpgsql SECURITY DEFINER; | |
| -- Update timestamp trigger | |
| CREATE OR REPLACE FUNCTION update_updated_at() | |
| RETURNS TRIGGER AS $$ | |
| BEGIN | |
| NEW.updated_at = NOW(); | |
| RETURN NEW; | |
| END; | |
| $$ LANGUAGE plpgsql; | |
| CREATE TRIGGER opencode_sessions_updated_at | |
| BEFORE UPDATE ON opencode_sessions | |
| FOR EACH ROW EXECUTE FUNCTION update_updated_at(); | |