Changeset 0.5.0 (#35)
This commit is contained in:
408
server/database/migrations/001_full_schema.sql
Normal file
408
server/database/migrations/001_full_schema.sql
Normal file
@@ -0,0 +1,408 @@
|
||||
-- ==========================================
|
||||
-- Chat Switchboard - PostgreSQL Schema
|
||||
-- ==========================================
|
||||
|
||||
-- Enable extensions
|
||||
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
|
||||
CREATE EXTENSION IF NOT EXISTS "pgcrypto";
|
||||
CREATE EXTENSION IF NOT EXISTS "vector"; -- For embeddings (pgvector)
|
||||
|
||||
-- ==========================================
|
||||
-- Core Tables
|
||||
-- ==========================================
|
||||
|
||||
-- Users
|
||||
CREATE TABLE users (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
username VARCHAR(50) UNIQUE NOT NULL,
|
||||
email VARCHAR(255) UNIQUE NOT NULL,
|
||||
password_hash TEXT NOT NULL,
|
||||
display_name VARCHAR(100),
|
||||
avatar_url TEXT,
|
||||
role VARCHAR(20) DEFAULT 'user', -- user, admin, moderator
|
||||
is_active BOOLEAN DEFAULT true,
|
||||
settings JSONB DEFAULT '{}'::jsonb,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
last_login_at TIMESTAMP WITH TIME ZONE
|
||||
);
|
||||
|
||||
CREATE INDEX idx_users_username ON users(username);
|
||||
CREATE INDEX idx_users_email ON users(email);
|
||||
|
||||
-- API Configurations (user's API keys)
|
||||
CREATE TABLE api_configs (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
user_id UUID REFERENCES users(id) ON DELETE CASCADE,
|
||||
name VARCHAR(100) NOT NULL, -- "OpenAI", "Claude", "Local Ollama"
|
||||
provider VARCHAR(50) NOT NULL, -- openai, anthropic, ollama, openrouter
|
||||
endpoint TEXT NOT NULL,
|
||||
api_key_encrypted TEXT, -- Encrypted at rest
|
||||
model_default VARCHAR(100),
|
||||
config JSONB DEFAULT '{}'::jsonb, -- Custom settings per provider
|
||||
is_active BOOLEAN DEFAULT true,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_api_configs_user ON api_configs(user_id);
|
||||
|
||||
-- ==========================================
|
||||
-- Feature 1: CHATS (User to LLM)
|
||||
-- ==========================================
|
||||
|
||||
CREATE TABLE chats (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
user_id UUID REFERENCES users(id) ON DELETE CASCADE,
|
||||
title VARCHAR(500) NOT NULL,
|
||||
model VARCHAR(100), -- Current model for this chat
|
||||
api_config_id UUID REFERENCES api_configs(id) ON DELETE SET NULL,
|
||||
system_prompt TEXT,
|
||||
settings JSONB DEFAULT '{}'::jsonb, -- temperature, max_tokens, etc
|
||||
is_archived BOOLEAN DEFAULT false,
|
||||
is_pinned BOOLEAN DEFAULT false,
|
||||
folder VARCHAR(100), -- For organization
|
||||
tags TEXT[], -- For filtering
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_chats_user ON chats(user_id);
|
||||
CREATE INDEX idx_chats_updated ON chats(updated_at DESC);
|
||||
CREATE INDEX idx_chats_tags ON chats USING GIN(tags);
|
||||
|
||||
CREATE TABLE chat_messages (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
chat_id UUID REFERENCES chats(id) ON DELETE CASCADE,
|
||||
role VARCHAR(20) NOT NULL, -- user, assistant, system, tool
|
||||
content TEXT NOT NULL,
|
||||
model VARCHAR(100), -- Which model generated this (for assistant)
|
||||
tokens_used INTEGER,
|
||||
tool_calls JSONB, -- Function calls made
|
||||
metadata JSONB DEFAULT '{}'::jsonb, -- thinking_blocks, attachments, etc
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_chat_messages_chat ON chat_messages(chat_id, created_at);
|
||||
|
||||
-- Model routing history (for analytics/debugging)
|
||||
CREATE TABLE model_routing_log (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
chat_id UUID REFERENCES chats(id) ON DELETE CASCADE,
|
||||
message_id UUID REFERENCES chat_messages(id) ON DELETE CASCADE,
|
||||
requested_model VARCHAR(100),
|
||||
routed_model VARCHAR(100),
|
||||
reason TEXT, -- "cost_optimization", "context_length", "manual", "fallback"
|
||||
latency_ms INTEGER,
|
||||
cost_usd NUMERIC(10, 6),
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_routing_log_chat ON model_routing_log(chat_id);
|
||||
|
||||
-- ==========================================
|
||||
-- Feature 2: CHANNELS (User to User + AI)
|
||||
-- ==========================================
|
||||
|
||||
CREATE TABLE channels (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
name VARCHAR(100) UNIQUE NOT NULL,
|
||||
description TEXT,
|
||||
type VARCHAR(20) DEFAULT 'public', -- public, private, dm
|
||||
owner_id UUID REFERENCES users(id) ON DELETE SET NULL,
|
||||
settings JSONB DEFAULT '{}'::jsonb, -- ai_participants, webhooks, etc
|
||||
is_archived BOOLEAN DEFAULT false,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_channels_name ON channels(name);
|
||||
CREATE INDEX idx_channels_type ON channels(type);
|
||||
|
||||
-- Channel memberships
|
||||
CREATE TABLE channel_members (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
channel_id UUID REFERENCES channels(id) ON DELETE CASCADE,
|
||||
user_id UUID REFERENCES users(id) ON DELETE CASCADE,
|
||||
role VARCHAR(20) DEFAULT 'member', -- owner, admin, member
|
||||
joined_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
last_read_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
UNIQUE(channel_id, user_id)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_channel_members_channel ON channel_members(channel_id);
|
||||
CREATE INDEX idx_channel_members_user ON channel_members(user_id);
|
||||
|
||||
-- Channel messages
|
||||
CREATE TABLE channel_messages (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
channel_id UUID REFERENCES channels(id) ON DELETE CASCADE,
|
||||
user_id UUID REFERENCES users(id) ON DELETE SET NULL, -- NULL for AI messages
|
||||
content TEXT NOT NULL,
|
||||
mentions JSONB, -- {users: [uuid], models: [name]}
|
||||
thread_id UUID REFERENCES channel_messages(id) ON DELETE CASCADE, -- For threading
|
||||
attachments JSONB, -- Files, images, etc
|
||||
reactions JSONB DEFAULT '{}'::jsonb, -- {emoji: [user_ids]}
|
||||
is_ai_message BOOLEAN DEFAULT false,
|
||||
ai_model VARCHAR(100), -- If AI generated
|
||||
edited_at TIMESTAMP WITH TIME ZONE,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_channel_messages_channel ON channel_messages(channel_id, created_at DESC);
|
||||
CREATE INDEX idx_channel_messages_thread ON channel_messages(thread_id);
|
||||
CREATE INDEX idx_channel_messages_mentions ON channel_messages USING GIN(mentions);
|
||||
|
||||
-- ==========================================
|
||||
-- Feature 3: NOTES & KNOWLEDGE BASES
|
||||
-- ==========================================
|
||||
|
||||
CREATE TABLE notes (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
user_id UUID REFERENCES users(id) ON DELETE CASCADE,
|
||||
title VARCHAR(500) NOT NULL,
|
||||
content TEXT NOT NULL,
|
||||
content_type VARCHAR(20) DEFAULT 'markdown', -- markdown, html, plain
|
||||
folder VARCHAR(100),
|
||||
tags TEXT[],
|
||||
is_pinned BOOLEAN DEFAULT false,
|
||||
is_shared BOOLEAN DEFAULT false,
|
||||
share_token UUID UNIQUE DEFAULT uuid_generate_v4(),
|
||||
parent_note_id UUID REFERENCES notes(id) ON DELETE SET NULL, -- For hierarchies
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_notes_user ON notes(user_id);
|
||||
CREATE INDEX idx_notes_updated ON notes(updated_at DESC);
|
||||
CREATE INDEX idx_notes_tags ON notes USING GIN(tags);
|
||||
CREATE INDEX idx_notes_share_token ON notes(share_token) WHERE is_shared = true;
|
||||
|
||||
-- Knowledge Bases (Collections of documents)
|
||||
CREATE TABLE knowledge_bases (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
user_id UUID REFERENCES users(id) ON DELETE CASCADE,
|
||||
name VARCHAR(200) NOT NULL,
|
||||
description TEXT,
|
||||
embedding_model VARCHAR(100) DEFAULT 'text-embedding-ada-002',
|
||||
settings JSONB DEFAULT '{}'::jsonb, -- chunk_size, overlap, etc
|
||||
is_public BOOLEAN DEFAULT false,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_knowledge_bases_user ON knowledge_bases(user_id);
|
||||
|
||||
-- Documents in knowledge bases
|
||||
CREATE TABLE kb_documents (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
kb_id UUID REFERENCES knowledge_bases(id) ON DELETE CASCADE,
|
||||
filename VARCHAR(500) NOT NULL,
|
||||
content TEXT NOT NULL,
|
||||
content_type VARCHAR(50), -- application/pdf, text/markdown, etc
|
||||
file_size INTEGER,
|
||||
metadata JSONB DEFAULT '{}'::jsonb,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_kb_documents_kb ON kb_documents(kb_id);
|
||||
|
||||
-- Document chunks (for RAG)
|
||||
CREATE TABLE kb_chunks (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
document_id UUID REFERENCES kb_documents(id) ON DELETE CASCADE,
|
||||
kb_id UUID REFERENCES knowledge_bases(id) ON DELETE CASCADE,
|
||||
content TEXT NOT NULL,
|
||||
embedding vector(1536), -- For pgvector similarity search
|
||||
chunk_index INTEGER,
|
||||
metadata JSONB DEFAULT '{}'::jsonb,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_kb_chunks_document ON kb_chunks(document_id);
|
||||
CREATE INDEX idx_kb_chunks_embedding ON kb_chunks USING ivfflat (embedding vector_cosine_ops);
|
||||
|
||||
-- ==========================================
|
||||
-- Feature 4: PLUGIN ORCHESTRATION
|
||||
-- ==========================================
|
||||
|
||||
-- Installed extensions/plugins
|
||||
CREATE TABLE extensions (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
name VARCHAR(100) UNIQUE NOT NULL,
|
||||
version VARCHAR(20) NOT NULL,
|
||||
author VARCHAR(100),
|
||||
description TEXT,
|
||||
runtime VARCHAR(20), -- python, go, node, rust
|
||||
entry_point TEXT NOT NULL,
|
||||
port INTEGER,
|
||||
manifest JSONB NOT NULL, -- Full extension.json
|
||||
is_enabled BOOLEAN DEFAULT true,
|
||||
is_system BOOLEAN DEFAULT false, -- Core extensions
|
||||
install_source VARCHAR(200), -- URL or marketplace ID
|
||||
installed_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_extensions_enabled ON extensions(is_enabled);
|
||||
|
||||
-- Extension tools/functions
|
||||
CREATE TABLE extension_tools (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
extension_id UUID REFERENCES extensions(id) ON DELETE CASCADE,
|
||||
name VARCHAR(100) NOT NULL,
|
||||
description TEXT,
|
||||
parameters_schema JSONB NOT NULL, -- JSON Schema
|
||||
response_schema JSONB,
|
||||
is_enabled BOOLEAN DEFAULT true,
|
||||
UNIQUE(extension_id, name)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_extension_tools_extension ON extension_tools(extension_id);
|
||||
|
||||
-- Tool usage log (for analytics)
|
||||
CREATE TABLE tool_usage_log (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
tool_id UUID REFERENCES extension_tools(id) ON DELETE SET NULL,
|
||||
user_id UUID REFERENCES users(id) ON DELETE SET NULL,
|
||||
chat_id UUID REFERENCES chats(id) ON DELETE SET NULL,
|
||||
channel_id UUID REFERENCES channels(id) ON DELETE SET NULL,
|
||||
input_params JSONB,
|
||||
output_result JSONB,
|
||||
execution_time_ms INTEGER,
|
||||
success BOOLEAN,
|
||||
error_message TEXT,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_tool_usage_tool ON tool_usage_log(tool_id);
|
||||
CREATE INDEX idx_tool_usage_user ON tool_usage_log(user_id);
|
||||
|
||||
-- ==========================================
|
||||
-- Feature 5: WORKFLOWS (Unique Feature!)
|
||||
-- ==========================================
|
||||
|
||||
-- Workflow definitions (DAG of AI operations)
|
||||
CREATE TABLE workflows (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
user_id UUID REFERENCES users(id) ON DELETE CASCADE,
|
||||
name VARCHAR(200) NOT NULL,
|
||||
description TEXT,
|
||||
graph JSONB NOT NULL, -- Node-edge DAG structure
|
||||
input_schema JSONB, -- Expected inputs
|
||||
output_schema JSONB, -- Expected outputs
|
||||
is_public BOOLEAN DEFAULT false,
|
||||
is_template BOOLEAN DEFAULT false,
|
||||
tags TEXT[],
|
||||
usage_count INTEGER DEFAULT 0,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_workflows_user ON workflows(user_id);
|
||||
CREATE INDEX idx_workflows_public ON workflows(is_public) WHERE is_public = true;
|
||||
CREATE INDEX idx_workflows_tags ON workflows USING GIN(tags);
|
||||
|
||||
-- Workflow executions
|
||||
CREATE TABLE workflow_executions (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
workflow_id UUID REFERENCES workflows(id) ON DELETE CASCADE,
|
||||
user_id UUID REFERENCES users(id) ON DELETE SET NULL,
|
||||
input_data JSONB,
|
||||
output_data JSONB,
|
||||
status VARCHAR(20), -- running, completed, failed
|
||||
steps JSONB, -- Execution trace
|
||||
total_cost_usd NUMERIC(10, 6),
|
||||
total_time_ms INTEGER,
|
||||
error_message TEXT,
|
||||
started_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
completed_at TIMESTAMP WITH TIME ZONE
|
||||
);
|
||||
|
||||
CREATE INDEX idx_workflow_executions_workflow ON workflow_executions(workflow_id);
|
||||
CREATE INDEX idx_workflow_executions_user ON workflow_executions(user_id);
|
||||
|
||||
-- ==========================================
|
||||
-- Shared/Utility Tables
|
||||
-- ==========================================
|
||||
|
||||
-- File uploads
|
||||
CREATE TABLE files (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
user_id UUID REFERENCES users(id) ON DELETE CASCADE,
|
||||
filename VARCHAR(500) NOT NULL,
|
||||
content_type VARCHAR(100),
|
||||
file_size INTEGER,
|
||||
storage_path TEXT NOT NULL, -- S3/local path
|
||||
is_public BOOLEAN DEFAULT false,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_files_user ON files(user_id);
|
||||
|
||||
-- Webhooks
|
||||
CREATE TABLE webhooks (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
user_id UUID REFERENCES users(id) ON DELETE CASCADE,
|
||||
name VARCHAR(100),
|
||||
url TEXT NOT NULL,
|
||||
events TEXT[] NOT NULL, -- chat.message, channel.message, etc
|
||||
secret VARCHAR(100),
|
||||
is_active BOOLEAN DEFAULT true,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_webhooks_user ON webhooks(user_id);
|
||||
|
||||
-- Audit log
|
||||
CREATE TABLE audit_log (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
user_id UUID REFERENCES users(id) ON DELETE SET NULL,
|
||||
action VARCHAR(100) NOT NULL,
|
||||
resource_type VARCHAR(50),
|
||||
resource_id UUID,
|
||||
metadata JSONB,
|
||||
ip_address INET,
|
||||
user_agent TEXT,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_audit_log_user ON audit_log(user_id);
|
||||
CREATE INDEX idx_audit_log_created ON audit_log(created_at DESC);
|
||||
|
||||
-- ==========================================
|
||||
-- Functions & Triggers
|
||||
-- ==========================================
|
||||
|
||||
-- Auto-update updated_at timestamps
|
||||
CREATE OR REPLACE FUNCTION update_updated_at()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
NEW.updated_at = NOW();
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE TRIGGER users_updated_at BEFORE UPDATE ON users
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at();
|
||||
CREATE TRIGGER chats_updated_at BEFORE UPDATE ON chats
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at();
|
||||
CREATE TRIGGER channels_updated_at BEFORE UPDATE ON channels
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at();
|
||||
CREATE TRIGGER notes_updated_at BEFORE UPDATE ON notes
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at();
|
||||
CREATE TRIGGER knowledge_bases_updated_at BEFORE UPDATE ON knowledge_bases
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at();
|
||||
CREATE TRIGGER workflows_updated_at BEFORE UPDATE ON workflows
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at();
|
||||
|
||||
-- Increment workflow usage count
|
||||
CREATE OR REPLACE FUNCTION increment_workflow_usage()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
UPDATE workflows SET usage_count = usage_count + 1 WHERE id = NEW.workflow_id;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE TRIGGER workflow_executions_insert AFTER INSERT ON workflow_executions
|
||||
FOR EACH ROW EXECUTE FUNCTION increment_workflow_usage();
|
||||
20
server/database/migrations/002_refresh_tokens.sql
Normal file
20
server/database/migrations/002_refresh_tokens.sql
Normal file
@@ -0,0 +1,20 @@
|
||||
-- ==========================================
|
||||
-- Chat Switchboard - Refresh Tokens
|
||||
-- ==========================================
|
||||
-- Supports JWT refresh token rotation.
|
||||
-- Old tokens are revoked on each refresh.
|
||||
|
||||
CREATE TABLE refresh_tokens (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
token_hash TEXT NOT NULL UNIQUE,
|
||||
expires_at TIMESTAMP WITH TIME ZONE NOT NULL,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
revoked_at TIMESTAMP WITH TIME ZONE
|
||||
);
|
||||
|
||||
CREATE INDEX idx_refresh_tokens_user ON refresh_tokens(user_id);
|
||||
CREATE INDEX idx_refresh_tokens_hash ON refresh_tokens(token_hash) WHERE revoked_at IS NULL;
|
||||
|
||||
-- Cleanup: auto-delete expired tokens older than 30 days
|
||||
-- (run periodically or via pg_cron if available)
|
||||
15
server/database/migrations/003_global_settings.sql
Normal file
15
server/database/migrations/003_global_settings.sql
Normal file
@@ -0,0 +1,15 @@
|
||||
-- Migration 003: Global Settings
|
||||
-- Stores application-wide configuration managed by admins.
|
||||
|
||||
CREATE TABLE IF NOT EXISTS global_settings (
|
||||
key VARCHAR(100) PRIMARY KEY,
|
||||
value JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_by UUID REFERENCES users(id)
|
||||
);
|
||||
|
||||
-- Seed defaults
|
||||
INSERT INTO global_settings (key, value) VALUES
|
||||
('registration', '{"enabled": true}'::jsonb),
|
||||
('site', '{"name": "Chat Switchboard", "tagline": "Multi-Model AI Chat"}'::jsonb)
|
||||
ON CONFLICT (key) DO NOTHING;
|
||||
12
server/database/migrations/004_model_configs.sql
Normal file
12
server/database/migrations/004_model_configs.sql
Normal file
@@ -0,0 +1,12 @@
|
||||
-- Model configurations: admin-curated list of available models
|
||||
CREATE TABLE IF NOT EXISTS model_configs (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
api_config_id UUID REFERENCES api_configs(id) ON DELETE CASCADE,
|
||||
model_id TEXT NOT NULL,
|
||||
display_name TEXT,
|
||||
is_enabled BOOLEAN DEFAULT true,
|
||||
capabilities JSONB DEFAULT '{"tool": false, "thinking": false, "vision": false, "code": false}',
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
UNIQUE(api_config_id, model_id)
|
||||
);
|
||||
60
server/database/migrations/005_provider_capabilities.sql
Normal file
60
server/database/migrations/005_provider_capabilities.sql
Normal file
@@ -0,0 +1,60 @@
|
||||
-- Migration 005: Provider Capabilities & Custom Headers
|
||||
--
|
||||
-- Adds provider-level configuration (custom headers, provider-specific settings)
|
||||
-- and expands model capabilities to include output token limits.
|
||||
-- This eliminates hardcoded max_tokens defaults throughout the system.
|
||||
|
||||
-- ── api_configs: custom headers and global flag ──
|
||||
ALTER TABLE api_configs
|
||||
ADD COLUMN IF NOT EXISTS custom_headers JSONB DEFAULT '{}'::jsonb,
|
||||
ADD COLUMN IF NOT EXISTS provider_settings JSONB DEFAULT '{}'::jsonb,
|
||||
ADD COLUMN IF NOT EXISTS is_global BOOLEAN DEFAULT false;
|
||||
|
||||
COMMENT ON COLUMN api_configs.custom_headers IS 'Extra HTTP headers sent with every request (e.g. OpenRouter HTTP-Referer)';
|
||||
COMMENT ON COLUMN api_configs.provider_settings IS 'Provider-specific params (e.g. Venice web_search, OpenRouter route)';
|
||||
COMMENT ON COLUMN api_configs.is_global IS 'Admin-managed configs visible to all users';
|
||||
|
||||
-- Backfill: existing admin-created configs (user_id IS NULL) are global
|
||||
UPDATE api_configs SET is_global = true WHERE user_id IS NULL;
|
||||
|
||||
-- ── model_configs: richer capabilities ──
|
||||
-- The existing capabilities JSONB gets richer fields.
|
||||
-- No schema change needed (it's JSONB), but let's update existing rows
|
||||
-- that have the old minimal shape to include the new fields.
|
||||
-- New canonical shape:
|
||||
-- {
|
||||
-- "streaming": true,
|
||||
-- "tool_calling": false,
|
||||
-- "vision": false,
|
||||
-- "thinking": false,
|
||||
-- "reasoning": false,
|
||||
-- "code_optimized": false,
|
||||
-- "max_context": 0,
|
||||
-- "max_output_tokens": 0,
|
||||
-- "web_search": false
|
||||
-- }
|
||||
-- max_output_tokens = 0 means "not set, use provider/heuristic default"
|
||||
|
||||
-- Migrate old "tool" key to "tool_calling" for consistency
|
||||
UPDATE model_configs
|
||||
SET capabilities = capabilities - 'tool' || jsonb_build_object('tool_calling', COALESCE(capabilities->>'tool', 'false')::boolean)
|
||||
WHERE capabilities ? 'tool' AND NOT capabilities ? 'tool_calling';
|
||||
|
||||
-- Migrate old "code" key to "code_optimized"
|
||||
UPDATE model_configs
|
||||
SET capabilities = capabilities - 'code' || jsonb_build_object('code_optimized', COALESCE(capabilities->>'code', 'false')::boolean)
|
||||
WHERE capabilities ? 'code' AND NOT capabilities ? 'code_optimized';
|
||||
|
||||
-- ── user_model_preferences ──
|
||||
-- Users can enable/disable models from global providers for personal use.
|
||||
CREATE TABLE IF NOT EXISTS user_model_preferences (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
model_config_id UUID NOT NULL REFERENCES model_configs(id) ON DELETE CASCADE,
|
||||
is_enabled BOOLEAN DEFAULT true,
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
UNIQUE(user_id, model_config_id)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_user_model_prefs_user ON user_model_preferences(user_id);
|
||||
Reference in New Issue
Block a user