mirror of https://github.com/kortix-ai/suna.git
245 lines
8.7 KiB
PL/PgSQL
245 lines
8.7 KiB
PL/PgSQL
-- Safe Templates Config Structure Migration for Production
|
|
-- This migration safely updates agent_templates to use the unified config structure
|
|
-- with proper existence checks for production environments
|
|
|
|
BEGIN;
|
|
|
|
-- Function to check if column exists
|
|
CREATE OR REPLACE FUNCTION column_exists(p_table_name text, p_column_name text)
|
|
RETURNS boolean AS $$
|
|
BEGIN
|
|
RETURN EXISTS (
|
|
SELECT 1
|
|
FROM information_schema.columns
|
|
WHERE table_schema = 'public'
|
|
AND table_name = p_table_name
|
|
AND column_name = p_column_name
|
|
);
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
-- Function to check if constraint exists
|
|
CREATE OR REPLACE FUNCTION constraint_exists(p_table_name text, p_constraint_name text)
|
|
RETURNS boolean AS $$
|
|
BEGIN
|
|
RETURN EXISTS (
|
|
SELECT 1
|
|
FROM information_schema.table_constraints
|
|
WHERE table_schema = 'public'
|
|
AND table_name = p_table_name
|
|
AND constraint_name = p_constraint_name
|
|
);
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
-- Backup existing templates if not already done
|
|
DO $$
|
|
BEGIN
|
|
IF NOT EXISTS (SELECT FROM information_schema.tables WHERE table_name = 'agent_templates_backup') THEN
|
|
CREATE TABLE agent_templates_backup AS SELECT * FROM agent_templates;
|
|
END IF;
|
|
END
|
|
$$;
|
|
|
|
-- Add config column if it doesn't exist
|
|
DO $$
|
|
BEGIN
|
|
IF NOT column_exists('agent_templates', 'config') THEN
|
|
ALTER TABLE agent_templates ADD COLUMN config JSONB DEFAULT '{}'::jsonb;
|
|
END IF;
|
|
END
|
|
$$;
|
|
|
|
-- Migrate data from old structure to new config structure (only if old columns exist)
|
|
DO $$
|
|
BEGIN
|
|
-- Only migrate if we have old columns and config is empty
|
|
IF column_exists('agent_templates', 'system_prompt') AND
|
|
column_exists('agent_templates', 'agentpress_tools') THEN
|
|
|
|
UPDATE agent_templates
|
|
SET config = jsonb_build_object(
|
|
'system_prompt', COALESCE(system_prompt, ''),
|
|
'tools', jsonb_build_object(
|
|
'agentpress', COALESCE(agentpress_tools, '{}'::jsonb),
|
|
'mcp', '[]'::jsonb,
|
|
'custom_mcp', COALESCE(
|
|
CASE
|
|
WHEN column_exists('agent_templates', 'mcp_requirements')
|
|
THEN mcp_requirements
|
|
ELSE '[]'::jsonb
|
|
END,
|
|
'[]'::jsonb
|
|
)
|
|
),
|
|
'metadata', jsonb_build_object(
|
|
'avatar', avatar,
|
|
'avatar_color', avatar_color,
|
|
'template_metadata', COALESCE(metadata, '{}'::jsonb)
|
|
)
|
|
)
|
|
WHERE config = '{}'::jsonb OR config IS NULL;
|
|
END IF;
|
|
END
|
|
$$;
|
|
|
|
-- Drop old columns if they exist
|
|
DO $$
|
|
BEGIN
|
|
IF column_exists('agent_templates', 'system_prompt') THEN
|
|
ALTER TABLE agent_templates DROP COLUMN system_prompt;
|
|
END IF;
|
|
|
|
IF column_exists('agent_templates', 'mcp_requirements') THEN
|
|
ALTER TABLE agent_templates DROP COLUMN mcp_requirements;
|
|
END IF;
|
|
|
|
IF column_exists('agent_templates', 'agentpress_tools') THEN
|
|
ALTER TABLE agent_templates DROP COLUMN agentpress_tools;
|
|
END IF;
|
|
END
|
|
$$;
|
|
|
|
-- Add constraints if they don't exist
|
|
DO $$
|
|
BEGIN
|
|
IF NOT constraint_exists('agent_templates', 'agent_templates_config_structure_check') THEN
|
|
ALTER TABLE agent_templates ADD CONSTRAINT agent_templates_config_structure_check
|
|
CHECK (
|
|
config ? 'system_prompt' AND
|
|
config ? 'tools' AND
|
|
config ? 'metadata'
|
|
);
|
|
END IF;
|
|
|
|
IF NOT constraint_exists('agent_templates', 'agent_templates_tools_structure_check') THEN
|
|
ALTER TABLE agent_templates ADD CONSTRAINT agent_templates_tools_structure_check
|
|
CHECK (
|
|
config->'tools' ? 'agentpress' AND
|
|
config->'tools' ? 'mcp' AND
|
|
config->'tools' ? 'custom_mcp'
|
|
);
|
|
END IF;
|
|
END
|
|
$$;
|
|
|
|
-- Create indexes if they don't exist
|
|
CREATE INDEX IF NOT EXISTS idx_agent_templates_creator_id ON agent_templates(creator_id);
|
|
CREATE INDEX IF NOT EXISTS idx_agent_templates_is_public ON agent_templates(is_public);
|
|
CREATE INDEX IF NOT EXISTS idx_agent_templates_marketplace_published_at ON agent_templates(marketplace_published_at);
|
|
CREATE INDEX IF NOT EXISTS idx_agent_templates_download_count ON agent_templates(download_count);
|
|
CREATE INDEX IF NOT EXISTS idx_agent_templates_tags ON agent_templates USING gin(tags);
|
|
CREATE INDEX IF NOT EXISTS idx_agent_templates_created_at ON agent_templates(created_at);
|
|
|
|
-- Add config-specific indexes
|
|
CREATE INDEX IF NOT EXISTS idx_agent_templates_config_tools ON agent_templates USING gin((config->'tools'));
|
|
CREATE INDEX IF NOT EXISTS idx_agent_templates_config_agentpress ON agent_templates USING gin((config->'tools'->'agentpress'));
|
|
|
|
-- Add sanitization function for template creation
|
|
CREATE OR REPLACE FUNCTION sanitize_config_for_template(input_config JSONB)
|
|
RETURNS JSONB AS $$
|
|
DECLARE
|
|
sanitized_config JSONB;
|
|
custom_mcp_array JSONB;
|
|
custom_mcp_item JSONB;
|
|
sanitized_mcp JSONB;
|
|
result_array JSONB := '[]'::jsonb;
|
|
BEGIN
|
|
-- Start with the basic structure
|
|
sanitized_config := jsonb_build_object(
|
|
'system_prompt', COALESCE(input_config->>'system_prompt', ''),
|
|
'tools', jsonb_build_object(
|
|
'agentpress', COALESCE(input_config->'tools'->'agentpress', '{}'::jsonb),
|
|
'mcp', COALESCE(input_config->'tools'->'mcp', '[]'::jsonb),
|
|
'custom_mcp', '[]'::jsonb
|
|
),
|
|
'metadata', jsonb_build_object(
|
|
'avatar', input_config->'metadata'->>'avatar',
|
|
'avatar_color', input_config->'metadata'->>'avatar_color'
|
|
)
|
|
);
|
|
|
|
-- Get custom_mcp array safely
|
|
custom_mcp_array := COALESCE(input_config->'tools'->'custom_mcp', '[]'::jsonb);
|
|
|
|
-- Process each custom MCP item
|
|
FOR custom_mcp_item IN SELECT jsonb_array_elements(custom_mcp_array)
|
|
LOOP
|
|
-- Create sanitized MCP item
|
|
sanitized_mcp := jsonb_build_object(
|
|
'name', custom_mcp_item->>'name',
|
|
'type', custom_mcp_item->>'type',
|
|
'display_name', COALESCE(custom_mcp_item->>'display_name', custom_mcp_item->>'name'),
|
|
'enabledTools', COALESCE(custom_mcp_item->'enabledTools', '[]'::jsonb)
|
|
);
|
|
|
|
-- Add config based on type
|
|
IF custom_mcp_item->>'type' = 'pipedream' THEN
|
|
-- For pipedream, keep URL but remove profile_id from headers
|
|
sanitized_mcp := jsonb_set(
|
|
sanitized_mcp,
|
|
'{config}',
|
|
jsonb_build_object(
|
|
'url', custom_mcp_item->'config'->>'url',
|
|
'headers', COALESCE(custom_mcp_item->'config'->'headers', '{}'::jsonb) - 'profile_id'
|
|
)
|
|
);
|
|
ELSE
|
|
-- For other types (like http with secure URLs), remove all config
|
|
sanitized_mcp := jsonb_set(sanitized_mcp, '{config}', '{}'::jsonb);
|
|
END IF;
|
|
|
|
-- Add to result array
|
|
result_array := result_array || sanitized_mcp;
|
|
END LOOP;
|
|
|
|
-- Update sanitized config with cleaned custom_mcps
|
|
sanitized_config := jsonb_set(
|
|
sanitized_config,
|
|
'{tools,custom_mcp}',
|
|
result_array
|
|
);
|
|
|
|
RETURN sanitized_config;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
-- Create function to increment download count
|
|
CREATE OR REPLACE FUNCTION increment_template_download_count(template_id_param UUID)
|
|
RETURNS void AS $$
|
|
BEGIN
|
|
UPDATE agent_templates
|
|
SET download_count = download_count + 1,
|
|
updated_at = NOW()
|
|
WHERE template_id = template_id_param;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
-- Enable RLS if not already enabled
|
|
ALTER TABLE agent_templates ENABLE ROW LEVEL SECURITY;
|
|
|
|
-- Drop and recreate RLS policies to ensure they're current
|
|
DROP POLICY IF EXISTS "Users can view public templates or their own templates" ON agent_templates;
|
|
CREATE POLICY "Users can view public templates or their own templates" ON agent_templates
|
|
FOR SELECT USING (
|
|
is_public = true OR
|
|
creator_id = (auth.jwt() ->> 'sub')::uuid
|
|
);
|
|
|
|
DROP POLICY IF EXISTS "Users can create their own templates" ON agent_templates;
|
|
CREATE POLICY "Users can create their own templates" ON agent_templates
|
|
FOR INSERT WITH CHECK (creator_id = (auth.jwt() ->> 'sub')::uuid);
|
|
|
|
DROP POLICY IF EXISTS "Users can update their own templates" ON agent_templates;
|
|
CREATE POLICY "Users can update their own templates" ON agent_templates
|
|
FOR UPDATE USING (creator_id = (auth.jwt() ->> 'sub')::uuid);
|
|
|
|
DROP POLICY IF EXISTS "Users can delete their own templates" ON agent_templates;
|
|
CREATE POLICY "Users can delete their own templates" ON agent_templates
|
|
FOR DELETE USING (creator_id = (auth.jwt() ->> 'sub')::uuid);
|
|
|
|
-- Clean up helper functions
|
|
DROP FUNCTION IF EXISTS column_exists(text, text);
|
|
DROP FUNCTION IF EXISTS constraint_exists(text, text);
|
|
|
|
COMMIT; |