mirror of
https://github.com/Dvorinka/Containr.git
synced 2026-06-03 20:12:58 +00:00
feat: initial implementation of container management platform
This commit is contained in:
@@ -0,0 +1,142 @@
|
||||
-- Initial schema for Containr platform
|
||||
-- This migration creates the core tables for users, projects, services, and deployments
|
||||
|
||||
-- Users table
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
email VARCHAR(255) UNIQUE NOT NULL,
|
||||
password_hash VARCHAR(255) NOT NULL,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
avatar_url VARCHAR(500),
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Projects table
|
||||
CREATE TABLE IF NOT EXISTS projects (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
name VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
owner_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Environments table
|
||||
CREATE TABLE IF NOT EXISTS environments (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
name VARCHAR(50) NOT NULL, -- 'production', 'preview', 'development'
|
||||
project_id UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
UNIQUE(name, project_id)
|
||||
);
|
||||
|
||||
-- Services table
|
||||
CREATE TABLE IF NOT EXISTS services (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
name VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
project_id UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
|
||||
environment_id UUID NOT NULL REFERENCES environments(id) ON DELETE CASCADE,
|
||||
service_type VARCHAR(50) NOT NULL, -- 'web', 'worker', 'database', 'cron'
|
||||
source_type VARCHAR(50) NOT NULL, -- 'github', 'dockerfile', 'image', 'template'
|
||||
source_url VARCHAR(500),
|
||||
image_name VARCHAR(500),
|
||||
build_command TEXT,
|
||||
start_command TEXT,
|
||||
cpu_limit INTEGER,
|
||||
memory_limit INTEGER, -- in MB
|
||||
public_url VARCHAR(500),
|
||||
health_check_url VARCHAR(500),
|
||||
status VARCHAR(50) DEFAULT 'created', -- 'created', 'building', 'running', 'stopped', 'failed'
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Environment variables table
|
||||
CREATE TABLE IF NOT EXISTS environment_variables (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
service_id UUID NOT NULL REFERENCES services(id) ON DELETE CASCADE,
|
||||
key VARCHAR(255) NOT NULL,
|
||||
value TEXT,
|
||||
is_secret BOOLEAN DEFAULT false,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
UNIQUE(service_id, key)
|
||||
);
|
||||
|
||||
-- Deployments table
|
||||
CREATE TABLE IF NOT EXISTS deployments (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
service_id UUID NOT NULL REFERENCES services(id) ON DELETE CASCADE,
|
||||
version VARCHAR(100) NOT NULL,
|
||||
commit_hash VARCHAR(100),
|
||||
image_digest VARCHAR(500),
|
||||
status VARCHAR(50) DEFAULT 'created', -- 'created', 'building', 'deploying', 'running', 'failed', 'rolled_back'
|
||||
build_log TEXT,
|
||||
deployment_log TEXT,
|
||||
started_at TIMESTAMP WITH TIME ZONE,
|
||||
completed_at TIMESTAMP WITH TIME ZONE,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Service dependencies table (for service-to-service relationships)
|
||||
CREATE TABLE IF NOT EXISTS service_dependencies (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
service_id UUID NOT NULL REFERENCES services(id) ON DELETE CASCADE,
|
||||
depends_on_service_id UUID NOT NULL REFERENCES services(id) ON DELETE CASCADE,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
UNIQUE(service_id, depends_on_service_id),
|
||||
CHECK (service_id != depends_on_service_id)
|
||||
);
|
||||
|
||||
-- Project members table (for collaboration)
|
||||
CREATE TABLE IF NOT EXISTS project_members (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
project_id UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
|
||||
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
role VARCHAR(50) NOT NULL DEFAULT 'developer', -- 'owner', 'admin', 'developer', 'viewer'
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
UNIQUE(project_id, user_id)
|
||||
);
|
||||
|
||||
-- Indexes for better performance
|
||||
CREATE INDEX IF NOT EXISTS idx_users_email ON users(email);
|
||||
CREATE INDEX IF NOT EXISTS idx_projects_owner_id ON projects(owner_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_services_project_id ON services(project_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_services_environment_id ON services(environment_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_deployments_service_id ON deployments(service_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_deployments_status ON deployments(status);
|
||||
CREATE INDEX IF NOT EXISTS idx_environment_variables_service_id ON environment_variables(service_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_project_members_project_id ON project_members(project_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_project_members_user_id ON project_members(user_id);
|
||||
|
||||
-- Update timestamp trigger function
|
||||
CREATE OR REPLACE FUNCTION update_updated_at_column()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
NEW.updated_at = NOW();
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ language 'plpgsql';
|
||||
|
||||
-- Add update triggers to tables with updated_at columns
|
||||
CREATE TRIGGER update_users_updated_at BEFORE UPDATE ON users
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
|
||||
CREATE TRIGGER update_projects_updated_at BEFORE UPDATE ON projects
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
|
||||
CREATE TRIGGER update_environments_updated_at BEFORE UPDATE ON environments
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
|
||||
CREATE TRIGGER update_services_updated_at BEFORE UPDATE ON services
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
|
||||
CREATE TRIGGER update_environment_variables_updated_at BEFORE UPDATE ON environment_variables
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
|
||||
CREATE TRIGGER update_deployments_updated_at BEFORE UPDATE ON deployments
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
@@ -0,0 +1,123 @@
|
||||
-- Git integration schema for Containr platform
|
||||
-- This migration adds tables for Git providers, repositories, and webhooks
|
||||
|
||||
-- Git providers table (GitHub, GitLab, Bitbucket accounts)
|
||||
CREATE TABLE IF NOT EXISTS git_providers (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
name VARCHAR(50) NOT NULL, -- 'github', 'gitlab', 'bitbucket'
|
||||
display_name VARCHAR(255) NOT NULL, -- User-friendly name for the account
|
||||
api_url VARCHAR(500) NOT NULL,
|
||||
webhook_url VARCHAR(500) NOT NULL,
|
||||
access_token TEXT NOT NULL, -- Encrypted in production
|
||||
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
UNIQUE(name, user_id)
|
||||
);
|
||||
|
||||
-- Git repositories table (connected repositories)
|
||||
CREATE TABLE IF NOT EXISTS git_repositories (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
provider_id UUID NOT NULL REFERENCES git_providers(id) ON DELETE CASCADE,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
full_name VARCHAR(500) NOT NULL, -- e.g., "owner/repo-name"
|
||||
description TEXT,
|
||||
clone_url VARCHAR(500) NOT NULL,
|
||||
webhook_url VARCHAR(500), -- Webhook URL on the Git provider
|
||||
default_branch VARCHAR(100) DEFAULT 'main',
|
||||
is_private BOOLEAN DEFAULT false,
|
||||
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
UNIQUE(provider_id, full_name)
|
||||
);
|
||||
|
||||
-- Git webhooks table (webhook configurations)
|
||||
CREATE TABLE IF NOT EXISTS git_webhooks (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
repo_id UUID NOT NULL REFERENCES git_repositories(id) ON DELETE CASCADE,
|
||||
provider_id UUID NOT NULL REFERENCES git_providers(id) ON DELETE CASCADE,
|
||||
events TEXT NOT NULL, -- JSON array of webhook events
|
||||
webhook_secret TEXT NOT NULL, -- Secret for validating webhook payloads
|
||||
remote_webhook_id VARCHAR(255), -- Webhook ID on the Git provider
|
||||
active BOOLEAN DEFAULT true,
|
||||
branch_filter VARCHAR(100), -- Optional branch filter for deployments
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
UNIQUE(repo_id, provider_id)
|
||||
);
|
||||
|
||||
-- Git branches table (for tracking branch-specific deployments)
|
||||
CREATE TABLE IF NOT EXISTS git_branches (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
repo_id UUID NOT NULL REFERENCES git_repositories(id) ON DELETE CASCADE,
|
||||
name VARCHAR(255) NOT NULL, -- Branch name
|
||||
last_commit_hash VARCHAR(100),
|
||||
last_commit_message TEXT,
|
||||
last_commit_author VARCHAR(255),
|
||||
last_commit_date TIMESTAMP WITH TIME ZONE,
|
||||
is_protected BOOLEAN DEFAULT false,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
UNIQUE(repo_id, name)
|
||||
);
|
||||
|
||||
-- Git deployment triggers table (links webhooks to services)
|
||||
CREATE TABLE IF NOT EXISTS git_deployment_triggers (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
webhook_id UUID NOT NULL REFERENCES git_webhooks(id) ON DELETE CASCADE,
|
||||
service_id UUID NOT NULL REFERENCES services(id) ON DELETE CASCADE,
|
||||
branch VARCHAR(255) NOT NULL, -- Branch to trigger deployment for
|
||||
environment VARCHAR(50) NOT NULL, -- Target environment
|
||||
auto_deploy BOOLEAN DEFAULT false, -- Whether to auto-deploy on push
|
||||
build_command TEXT,
|
||||
start_command TEXT,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
UNIQUE(webhook_id, service_id, branch)
|
||||
);
|
||||
|
||||
-- Indexes for better performance
|
||||
CREATE INDEX IF NOT EXISTS idx_git_providers_user_id ON git_providers(user_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_git_providers_name ON git_providers(name);
|
||||
CREATE INDEX IF NOT EXISTS idx_git_repositories_provider_id ON git_repositories(provider_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_git_repositories_user_id ON git_repositories(user_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_git_repositories_full_name ON git_repositories(full_name);
|
||||
CREATE INDEX IF NOT EXISTS idx_git_webhooks_repo_id ON git_webhooks(repo_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_git_webhooks_provider_id ON git_webhooks(provider_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_git_webhooks_active ON git_webhooks(active);
|
||||
CREATE INDEX IF NOT EXISTS idx_git_branches_repo_id ON git_branches(repo_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_git_branches_name ON git_branches(name);
|
||||
CREATE INDEX IF NOT EXISTS idx_git_deployment_triggers_webhook_id ON git_deployment_triggers(webhook_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_git_deployment_triggers_service_id ON git_deployment_triggers(service_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_git_deployment_triggers_branch ON git_deployment_triggers(branch);
|
||||
|
||||
-- Add update triggers to new tables
|
||||
CREATE TRIGGER update_git_providers_updated_at BEFORE UPDATE ON git_providers
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
|
||||
CREATE TRIGGER update_git_repositories_updated_at BEFORE UPDATE ON git_repositories
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
|
||||
CREATE TRIGGER update_git_webhooks_updated_at BEFORE UPDATE ON git_webhooks
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
|
||||
CREATE TRIGGER update_git_branches_updated_at BEFORE UPDATE ON git_branches
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
|
||||
CREATE TRIGGER update_git_deployment_triggers_updated_at BEFORE UPDATE ON git_deployment_triggers
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
|
||||
-- Add foreign key constraint for project_members table if not exists
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM information_schema.table_constraints
|
||||
WHERE constraint_name = 'project_members_user_id_fkey'
|
||||
) THEN
|
||||
ALTER TABLE project_members
|
||||
ADD CONSTRAINT project_members_user_id_fkey
|
||||
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;
|
||||
END IF;
|
||||
END
|
||||
$$;
|
||||
@@ -0,0 +1,178 @@
|
||||
-- Agent system migration for Phase 3: Node Agent System
|
||||
|
||||
-- Create node_agents table
|
||||
CREATE TABLE IF NOT EXISTS node_agents (
|
||||
id VARCHAR(255) PRIMARY KEY,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
hostname VARCHAR(255) NOT NULL,
|
||||
ip_address VARCHAR(45) NOT NULL,
|
||||
port INTEGER NOT NULL,
|
||||
status VARCHAR(50) DEFAULT 'offline',
|
||||
version VARCHAR(50),
|
||||
capabilities JSONB,
|
||||
resources JSONB,
|
||||
last_heartbeat TIMESTAMP WITH TIME ZONE,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
metadata JSONB
|
||||
);
|
||||
|
||||
-- Create container_instances table
|
||||
CREATE TABLE IF NOT EXISTS container_instances (
|
||||
id VARCHAR(255) PRIMARY KEY,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
image VARCHAR(255) NOT NULL,
|
||||
project_id VARCHAR(255) NOT NULL,
|
||||
service_id VARCHAR(255) NOT NULL,
|
||||
node_agent_id VARCHAR(255) NOT NULL,
|
||||
status JSONB,
|
||||
resources JSONB,
|
||||
ports JSONB,
|
||||
environment JSONB,
|
||||
volumes JSONB,
|
||||
networks JSONB,
|
||||
restart_policy JSONB,
|
||||
health_check JSONB,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
started_at TIMESTAMP WITH TIME ZONE,
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
FOREIGN KEY (node_agent_id) REFERENCES node_agents(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
-- Create agent_commands table
|
||||
CREATE TABLE IF NOT EXISTS agent_commands (
|
||||
id VARCHAR(255) PRIMARY KEY,
|
||||
type VARCHAR(100) NOT NULL,
|
||||
node_agent_id VARCHAR(255) NOT NULL,
|
||||
container_id VARCHAR(255),
|
||||
payload JSONB,
|
||||
status VARCHAR(50) DEFAULT 'pending',
|
||||
result TEXT,
|
||||
error TEXT,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
completed_at TIMESTAMP WITH TIME ZONE,
|
||||
FOREIGN KEY (node_agent_id) REFERENCES node_agents(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (container_id) REFERENCES container_instances(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
-- Create node_clusters table
|
||||
CREATE TABLE IF NOT EXISTS node_clusters (
|
||||
id VARCHAR(255) PRIMARY KEY,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
status VARCHAR(50) DEFAULT 'active',
|
||||
total_resources JSONB,
|
||||
used_resources JSONB,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Create cluster_agents table to link clusters and agents
|
||||
CREATE TABLE IF NOT EXISTS cluster_agents (
|
||||
cluster_id VARCHAR(255) NOT NULL,
|
||||
agent_id VARCHAR(255) NOT NULL,
|
||||
added_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
PRIMARY KEY (cluster_id, agent_id),
|
||||
FOREIGN KEY (cluster_id) REFERENCES node_clusters(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (agent_id) REFERENCES node_agents(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
-- Create scheduling_rules table
|
||||
CREATE TABLE IF NOT EXISTS scheduling_rules (
|
||||
id VARCHAR(255) PRIMARY KEY,
|
||||
cluster_id VARCHAR(255) NOT NULL,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
type VARCHAR(50) NOT NULL,
|
||||
selector JSONB,
|
||||
weight INTEGER DEFAULT 1,
|
||||
enabled BOOLEAN DEFAULT true,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
FOREIGN KEY (cluster_id) REFERENCES node_clusters(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
-- Create container_metrics table for monitoring
|
||||
CREATE TABLE IF NOT EXISTS container_metrics (
|
||||
id SERIAL PRIMARY KEY,
|
||||
container_id VARCHAR(255) NOT NULL,
|
||||
timestamp TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
cpu_usage DECIMAL(5,2),
|
||||
cpu_usage_percent DECIMAL(5,2),
|
||||
memory_usage BIGINT,
|
||||
memory_usage_percent DECIMAL(5,2),
|
||||
memory_limit BIGINT,
|
||||
network_rx_bytes BIGINT,
|
||||
network_tx_bytes BIGINT,
|
||||
network_rx_packets BIGINT,
|
||||
network_tx_packets BIGINT,
|
||||
block_read_bytes BIGINT,
|
||||
block_write_bytes BIGINT,
|
||||
pids_current INTEGER,
|
||||
pids_limit INTEGER,
|
||||
FOREIGN KEY (container_id) REFERENCES container_instances(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
-- Create indexes for performance
|
||||
CREATE INDEX IF NOT EXISTS idx_node_agents_status ON node_agents(status);
|
||||
CREATE INDEX IF NOT EXISTS idx_node_agents_hostname ON node_agents(hostname);
|
||||
CREATE INDEX IF NOT EXISTS idx_node_agents_ip_address ON node_agents(ip_address);
|
||||
CREATE INDEX IF NOT EXISTS idx_node_agents_last_heartbeat ON node_agents(last_heartbeat);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_container_instances_project_id ON container_instances(project_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_container_instances_service_id ON container_instances(service_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_container_instances_node_agent_id ON container_instances(node_agent_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_container_instances_status ON container_instances USING GIN(status);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_agent_commands_node_agent_id ON agent_commands(node_agent_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_agent_commands_status ON agent_commands(status);
|
||||
CREATE INDEX IF NOT EXISTS idx_agent_commands_type ON agent_commands(type);
|
||||
CREATE INDEX IF NOT EXISTS idx_agent_commands_created_at ON agent_commands(created_at);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_container_metrics_container_id ON container_metrics(container_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_container_metrics_timestamp ON container_metrics(timestamp);
|
||||
|
||||
-- Create updated_at trigger function
|
||||
CREATE OR REPLACE FUNCTION update_updated_at_column()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
NEW.updated_at = NOW();
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ language 'plpgsql';
|
||||
|
||||
-- Create triggers for updated_at
|
||||
CREATE TRIGGER update_node_agents_updated_at BEFORE UPDATE ON node_agents
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
|
||||
CREATE TRIGGER update_container_instances_updated_at BEFORE UPDATE ON container_instances
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
|
||||
CREATE TRIGGER update_agent_commands_updated_at BEFORE UPDATE ON agent_commands
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
|
||||
CREATE TRIGGER update_node_clusters_updated_at BEFORE UPDATE ON node_clusters
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
|
||||
CREATE TRIGGER update_scheduling_rules_updated_at BEFORE UPDATE ON scheduling_rules
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
|
||||
-- Insert default cluster
|
||||
INSERT INTO node_clusters (id, name, description, status, total_resources, used_resources)
|
||||
VALUES (
|
||||
'default-cluster',
|
||||
'Default Cluster',
|
||||
'Default cluster for all node agents',
|
||||
'active',
|
||||
'{"cpu": {"cores": 0, "allocation": 0, "usage": 0}, "memory": {"total": 0, "allocated": 0, "used": 0, "available": 0}, "storage": {"total": 0, "allocated": 0, "used": 0, "available": 0}, "network": {"interfaces": [], "bandwidth": {"inbound": 0, "outbound": 0}}}',
|
||||
'{"cpu": {"cores": 0, "allocation": 0, "usage": 0}, "memory": {"total": 0, "allocated": 0, "used": 0, "available": 0}, "storage": {"total": 0, "allocated": 0, "used": 0, "available": 0}, "network": {"interfaces": [], "bandwidth": {"inbound": 0, "outbound": 0}}}'
|
||||
) ON CONFLICT (id) DO NOTHING;
|
||||
|
||||
-- Add comment to tables
|
||||
COMMENT ON TABLE node_agents IS 'Container orchestration agents that manage containers on nodes';
|
||||
COMMENT ON TABLE container_instances IS 'Container instances running on node agents';
|
||||
COMMENT ON TABLE agent_commands IS 'Commands sent to node agents for execution';
|
||||
COMMENT ON TABLE node_clusters IS 'Clusters of node agents for resource pooling';
|
||||
COMMENT ON TABLE cluster_agents IS 'Many-to-many relationship between clusters and agents';
|
||||
COMMENT ON TABLE scheduling_rules IS 'Rules for scheduling containers on agents';
|
||||
COMMENT ON TABLE container_metrics IS 'Metrics collected from running containers';
|
||||
@@ -0,0 +1,333 @@
|
||||
-- Metrics Schema Migration
|
||||
-- This migration creates tables for storing system and service metrics
|
||||
|
||||
-- Enable TimescaleDB extension for time-series data (optional)
|
||||
CREATE EXTENSION IF NOT EXISTS timescaledb CASCADE;
|
||||
|
||||
-- Node metrics table
|
||||
CREATE TABLE IF NOT EXISTS node_metrics (
|
||||
node_id VARCHAR(255) NOT NULL,
|
||||
timestamp TIMESTAMPTZ NOT NULL,
|
||||
cpu_usage DECIMAL(5,2),
|
||||
cpu_cores DECIMAL(10,2),
|
||||
load_avg_1 DECIMAL(5,2),
|
||||
load_avg_5 DECIMAL(5,2),
|
||||
load_avg_15 DECIMAL(5,2),
|
||||
memory_total BIGINT,
|
||||
memory_used BIGINT,
|
||||
memory_available BIGINT,
|
||||
memory_usage_percent DECIMAL(5,2),
|
||||
storage_total BIGINT,
|
||||
storage_used BIGINT,
|
||||
storage_available BIGINT,
|
||||
storage_usage_percent DECIMAL(5,2),
|
||||
network_bytes_in BIGINT,
|
||||
network_bytes_out BIGINT,
|
||||
network_packets_in BIGINT,
|
||||
network_packets_out BIGINT,
|
||||
network_connections_in INTEGER,
|
||||
network_connections_out INTEGER,
|
||||
network_errors_in BIGINT,
|
||||
network_errors_out BIGINT,
|
||||
uptime INTERVAL,
|
||||
processes INTEGER,
|
||||
os VARCHAR(50),
|
||||
kernel VARCHAR(50),
|
||||
architecture VARCHAR(20),
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
PRIMARY KEY (node_id, timestamp)
|
||||
);
|
||||
|
||||
-- Create index for time-series queries
|
||||
CREATE INDEX IF NOT EXISTS idx_node_metrics_timestamp ON node_metrics (timestamp DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_node_metrics_node_timestamp ON node_metrics (node_id, timestamp DESC);
|
||||
|
||||
-- Container metrics table
|
||||
CREATE TABLE IF NOT EXISTS container_metrics (
|
||||
node_id VARCHAR(255) NOT NULL,
|
||||
timestamp TIMESTAMPTZ NOT NULL,
|
||||
container_id VARCHAR(255) NOT NULL,
|
||||
name VARCHAR(255),
|
||||
state VARCHAR(50),
|
||||
cpu DECIMAL(5,2),
|
||||
memory BIGINT,
|
||||
network_bytes_in BIGINT,
|
||||
network_bytes_out BIGINT,
|
||||
network_packets_in BIGINT,
|
||||
network_packets_out BIGINT,
|
||||
start_time TIMESTAMPTZ,
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
PRIMARY KEY (node_id, timestamp, container_id),
|
||||
FOREIGN KEY (node_id, timestamp) REFERENCES node_metrics (node_id, timestamp) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
-- Service metrics table
|
||||
CREATE TABLE IF NOT EXISTS service_metrics (
|
||||
service_id VARCHAR(255) NOT NULL,
|
||||
service_name VARCHAR(255) NOT NULL,
|
||||
project_id VARCHAR(255) NOT NULL,
|
||||
timestamp TIMESTAMPTZ NOT NULL,
|
||||
requests_total BIGINT DEFAULT 0,
|
||||
requests_success BIGINT DEFAULT 0,
|
||||
requests_errors BIGINT DEFAULT 0,
|
||||
requests_avg_latency DECIMAL(10,3),
|
||||
requests_p95_latency DECIMAL(10,3),
|
||||
requests_p99_latency DECIMAL(10,3),
|
||||
requests_throughput DECIMAL(10,3),
|
||||
errors_total BIGINT DEFAULT 0,
|
||||
errors_rate DECIMAL(5,4),
|
||||
performance_response_time DECIMAL(10,3),
|
||||
performance_throughput DECIMAL(10,3),
|
||||
performance_concurrency BIGINT,
|
||||
performance_saturation DECIMAL(5,2),
|
||||
performance_utilization DECIMAL(5,2),
|
||||
resource_cpu_usage DECIMAL(5,2),
|
||||
resource_memory_usage BIGINT,
|
||||
resource_storage_usage BIGINT,
|
||||
resource_network_usage BIGINT,
|
||||
resource_score DECIMAL(5,2),
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
PRIMARY KEY (service_id, timestamp)
|
||||
);
|
||||
|
||||
-- Create indexes for service metrics
|
||||
CREATE INDEX IF NOT EXISTS idx_service_metrics_timestamp ON service_metrics (timestamp DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_service_metrics_service_timestamp ON service_metrics (service_id, timestamp DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_service_metrics_project_timestamp ON service_metrics (project_id, timestamp DESC);
|
||||
|
||||
-- Instance metrics table
|
||||
CREATE TABLE IF NOT EXISTS instance_metrics (
|
||||
service_id VARCHAR(255) NOT NULL,
|
||||
timestamp TIMESTAMPTZ NOT NULL,
|
||||
instance_id VARCHAR(255) NOT NULL,
|
||||
node_id VARCHAR(255),
|
||||
status VARCHAR(50),
|
||||
cpu DECIMAL(5,2),
|
||||
memory BIGINT,
|
||||
network_bytes_in BIGINT,
|
||||
network_bytes_out BIGINT,
|
||||
network_packets_in BIGINT,
|
||||
network_packets_out BIGINT,
|
||||
network_connections_in INTEGER,
|
||||
network_connections_out INTEGER,
|
||||
network_errors_in BIGINT,
|
||||
network_errors_out BIGINT,
|
||||
start_time TIMESTAMPTZ,
|
||||
last_seen TIMESTAMPTZ,
|
||||
health_status VARCHAR(20),
|
||||
health_last_check TIMESTAMPTZ,
|
||||
health_check_count INTEGER DEFAULT 0,
|
||||
health_failure_count INTEGER DEFAULT 0,
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
PRIMARY KEY (service_id, timestamp, instance_id),
|
||||
FOREIGN KEY (service_id, timestamp) REFERENCES service_metrics (service_id, timestamp) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
-- Service discovery table
|
||||
CREATE TABLE IF NOT EXISTS service_discovery (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
service_id VARCHAR(255) NOT NULL,
|
||||
service_name VARCHAR(255) NOT NULL,
|
||||
project_id VARCHAR(255) NOT NULL,
|
||||
instance_id VARCHAR(255) NOT NULL,
|
||||
node_id VARCHAR(255),
|
||||
ip_address INET NOT NULL,
|
||||
port INTEGER,
|
||||
status VARCHAR(50) DEFAULT 'unknown',
|
||||
health_status VARCHAR(20) DEFAULT 'unknown',
|
||||
labels JSONB DEFAULT '{}',
|
||||
metadata JSONB DEFAULT '{}',
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
last_seen TIMESTAMPTZ DEFAULT NOW(),
|
||||
UNIQUE(service_id, instance_id)
|
||||
);
|
||||
|
||||
-- Create indexes for service discovery
|
||||
CREATE INDEX IF NOT EXISTS idx_service_discovery_service ON service_discovery (service_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_service_discovery_project ON service_discovery (project_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_service_discovery_name ON service_discovery (service_name);
|
||||
CREATE INDEX IF NOT EXISTS idx_service_discovery_status ON service_discovery (status);
|
||||
CREATE INDEX IF NOT EXISTS idx_service_discovery_ip ON service_discovery (ip_address);
|
||||
CREATE INDEX IF NOT EXISTS idx_service_discovery_labels ON service_discovery USING GIN (labels);
|
||||
|
||||
-- DNS records table
|
||||
CREATE TABLE IF NOT EXISTS dns_records (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
name VARCHAR(255) NOT NULL,
|
||||
type VARCHAR(10) NOT NULL, -- A, SRV, CNAME, etc.
|
||||
ttl INTEGER DEFAULT 300,
|
||||
records JSONB NOT NULL, -- Array of records
|
||||
priority INTEGER,
|
||||
weight INTEGER,
|
||||
port INTEGER,
|
||||
service_id VARCHAR(255),
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Create indexes for DNS records
|
||||
CREATE INDEX IF NOT EXISTS idx_dns_records_name ON dns_records (name);
|
||||
CREATE INDEX IF NOT EXISTS idx_dns_records_type ON dns_records (type);
|
||||
CREATE INDEX IF NOT EXISTS idx_dns_records_service ON dns_records (service_id);
|
||||
|
||||
-- Metrics aggregation rules table
|
||||
CREATE TABLE IF NOT EXISTS metrics_aggregation_rules (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
name VARCHAR(255) NOT NULL UNIQUE,
|
||||
metric_type VARCHAR(50) NOT NULL, -- node, service, container
|
||||
aggregation_function VARCHAR(50) NOT NULL, -- avg, sum, min, max, count
|
||||
interval INTERVAL NOT NULL, -- 1m, 5m, 1h, etc.
|
||||
retention_period INTERVAL DEFAULT '30 days',
|
||||
fields JSONB NOT NULL, -- Which fields to aggregate
|
||||
filters JSONB DEFAULT '{}', -- Optional filters
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Create indexes for aggregation rules
|
||||
CREATE INDEX IF NOT EXISTS idx_metrics_aggregation_rules_type ON metrics_aggregation_rules (metric_type);
|
||||
|
||||
-- Alert rules table
|
||||
CREATE TABLE IF NOT EXISTS alert_rules (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
name VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
metric_type VARCHAR(50) NOT NULL,
|
||||
metric_field VARCHAR(100) NOT NULL,
|
||||
condition VARCHAR(20) NOT NULL, -- gt, lt, eq, gte, lte
|
||||
threshold DECIMAL(15,4) NOT NULL,
|
||||
duration INTERVAL DEFAULT '5 minutes',
|
||||
severity VARCHAR(20) DEFAULT 'warning', -- critical, warning, info
|
||||
enabled BOOLEAN DEFAULT true,
|
||||
filters JSONB DEFAULT '{}',
|
||||
notification_channels JSONB DEFAULT '[]',
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Create indexes for alert rules
|
||||
CREATE INDEX IF NOT EXISTS idx_alert_rules_type ON alert_rules (metric_type);
|
||||
CREATE INDEX IF NOT EXISTS idx_alert_rules_enabled ON alert_rules (enabled);
|
||||
|
||||
-- Alert incidents table
|
||||
CREATE TABLE IF NOT EXISTS alert_incidents (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
rule_id UUID NOT NULL REFERENCES alert_rules (id) ON DELETE CASCADE,
|
||||
metric_type VARCHAR(50) NOT NULL,
|
||||
metric_field VARCHAR(100) NOT NULL,
|
||||
current_value DECIMAL(15,4) NOT NULL,
|
||||
threshold DECIMAL(15,4) NOT NULL,
|
||||
severity VARCHAR(20) NOT NULL,
|
||||
status VARCHAR(20) DEFAULT 'firing', -- firing, resolved
|
||||
started_at TIMESTAMPTZ NOT NULL,
|
||||
resolved_at TIMESTAMPTZ,
|
||||
duration INTERVAL,
|
||||
description TEXT,
|
||||
metadata JSONB DEFAULT '{}',
|
||||
created_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Create indexes for alert incidents
|
||||
CREATE INDEX IF NOT EXISTS idx_alert_incidents_rule ON alert_incidents (rule_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_alert_incidents_status ON alert_incidents (status);
|
||||
CREATE INDEX IF NOT EXISTS idx_alert_incidents_started ON alert_incidents (started_at DESC);
|
||||
|
||||
-- Create TimescaleDB hypertables if TimescaleDB is available
|
||||
DO $$
|
||||
BEGIN
|
||||
-- Only create hypertables if TimescaleDB extension is available
|
||||
IF EXISTS (SELECT 1 FROM pg_extension WHERE extname = 'timescaledb') THEN
|
||||
PERFORM create_hypertable('node_metrics', 'timestamp', chunk_time_interval => INTERVAL '1 hour');
|
||||
PERFORM create_hypertable('service_metrics', 'timestamp', chunk_time_interval => INTERVAL '1 hour');
|
||||
PERFORM create_hypertable('container_metrics', 'timestamp', chunk_time_interval => INTERVAL '1 hour');
|
||||
PERFORM create_hypertable('instance_metrics', 'timestamp', chunk_time_interval => INTERVAL '1 hour');
|
||||
|
||||
-- Create compression policies for older data
|
||||
PERFORM add_compression_policy('node_metrics', INTERVAL '7 days');
|
||||
PERFORM add_compression_policy('service_metrics', INTERVAL '7 days');
|
||||
PERFORM add_compression_policy('container_metrics', INTERVAL '7 days');
|
||||
PERFORM add_compression_policy('instance_metrics', INTERVAL '7 days');
|
||||
|
||||
-- Create retention policies
|
||||
PERFORM add_retention_policy('node_metrics', INTERVAL '90 days');
|
||||
PERFORM add_retention_policy('service_metrics', INTERVAL '90 days');
|
||||
PERFORM add_retention_policy('container_metrics', INTERVAL '90 days');
|
||||
PERFORM add_retention_policy('instance_metrics', INTERVAL '90 days');
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
-- Create updated_at trigger function
|
||||
CREATE OR REPLACE FUNCTION update_updated_at_column()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
NEW.updated_at = NOW();
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ language 'plpgsql';
|
||||
|
||||
-- Create triggers for updated_at columns
|
||||
CREATE TRIGGER update_service_discovery_updated_at BEFORE UPDATE ON service_discovery FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
CREATE TRIGGER update_dns_records_updated_at BEFORE UPDATE ON dns_records FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
CREATE TRIGGER update_metrics_aggregation_rules_updated_at BEFORE UPDATE ON metrics_aggregation_rules FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
CREATE TRIGGER update_alert_rules_updated_at BEFORE UPDATE ON alert_rules FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
|
||||
-- Insert default aggregation rules
|
||||
INSERT INTO metrics_aggregation_rules (name, metric_type, aggregation_function, interval, fields) VALUES
|
||||
('node_cpu_1m', 'node', 'avg', INTERVAL '1 minute', '{"cpu_usage": true, "memory_usage_percent": true}'),
|
||||
('node_cpu_5m', 'node', 'avg', INTERVAL '5 minutes', '{"cpu_usage": true, "memory_usage_percent": true}'),
|
||||
('node_cpu_1h', 'node', 'avg', INTERVAL '1 hour', '{"cpu_usage": true, "memory_usage_percent": true, "storage_usage_percent": true}'),
|
||||
('service_requests_1m', 'service', 'sum', INTERVAL '1 minute', '{"requests_total": true, "requests_success": true, "requests_errors": true}'),
|
||||
('service_requests_5m', 'service', 'sum', INTERVAL '5 minutes', '{"requests_total": true, "requests_success": true, "requests_errors": true}'),
|
||||
('service_performance_5m', 'service', 'avg', INTERVAL '5 minutes', '{"requests_avg_latency": true, "requests_p95_latency": true, "requests_throughput": true}')
|
||||
ON CONFLICT (name) DO NOTHING;
|
||||
|
||||
-- Insert default alert rules
|
||||
INSERT INTO alert_rules (name, description, metric_type, metric_field, condition, threshold, severity) VALUES
|
||||
('High CPU Usage', 'Node CPU usage is above 80%', 'node', 'cpu_usage', 'gt', 80.0, 'warning'),
|
||||
('Critical CPU Usage', 'Node CPU usage is above 95%', 'node', 'cpu_usage', 'gt', 95.0, 'critical'),
|
||||
('High Memory Usage', 'Node memory usage is above 85%', 'node', 'memory_usage_percent', 'gt', 85.0, 'warning'),
|
||||
('Critical Memory Usage', 'Node memory usage is above 95%', 'node', 'memory_usage_percent', 'gt', 95.0, 'critical'),
|
||||
('High Error Rate', 'Service error rate is above 10%', 'service', 'errors_rate', 'gt', 0.10, 'warning'),
|
||||
('Critical Error Rate', 'Service error rate is above 25%', 'service', 'errors_rate', 'gt', 0.25, 'critical'),
|
||||
('High Latency', 'Service P95 latency is above 1000ms', 'service', 'requests_p95_latency', 'gt', 1000.0, 'warning'),
|
||||
('Critical Latency', 'Service P95 latency is above 5000ms', 'service', 'requests_p95_latency', 'gt', 5000.0, 'critical')
|
||||
ON CONFLICT (name) DO NOTHING;
|
||||
|
||||
-- Create views for common queries
|
||||
CREATE OR REPLACE VIEW node_metrics_summary AS
|
||||
SELECT
|
||||
node_id,
|
||||
timestamp,
|
||||
cpu_usage,
|
||||
memory_usage_percent,
|
||||
storage_usage_percent,
|
||||
network_bytes_in + network_bytes_out as total_network_bytes,
|
||||
load_avg_1,
|
||||
uptime
|
||||
FROM node_metrics
|
||||
ORDER BY timestamp DESC;
|
||||
|
||||
CREATE OR REPLACE VIEW service_metrics_summary AS
|
||||
SELECT
|
||||
service_id,
|
||||
service_name,
|
||||
project_id,
|
||||
timestamp,
|
||||
requests_total,
|
||||
requests_success,
|
||||
requests_errors,
|
||||
CASE WHEN requests_total > 0 THEN (requests_errors::DECIMAL / requests_total) ELSE 0 END as error_rate,
|
||||
requests_avg_latency,
|
||||
requests_p95_latency,
|
||||
requests_throughput,
|
||||
resource_cpu_usage,
|
||||
resource_memory_usage
|
||||
FROM service_metrics
|
||||
ORDER BY timestamp DESC;
|
||||
|
||||
-- Grant permissions (adjust as needed for your setup)
|
||||
-- GRANT SELECT, INSERT, UPDATE ON ALL TABLES IN SCHEMA public TO containr_app;
|
||||
-- GRANT USAGE ON ALL SEQUENCES IN SCHEMA public TO containr_app;
|
||||
|
||||
COMMIT;
|
||||
@@ -0,0 +1,255 @@
|
||||
-- Database Services Migration
|
||||
-- This migration creates tables for managed database services
|
||||
|
||||
-- Database Services table
|
||||
CREATE TABLE IF NOT EXISTS database_services (
|
||||
id VARCHAR(255) PRIMARY KEY,
|
||||
user_id VARCHAR(255) NOT NULL,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
type VARCHAR(50) NOT NULL CHECK (type IN ('postgresql', 'redis', 'mysql')),
|
||||
status VARCHAR(50) NOT NULL DEFAULT 'building' CHECK (status IN ('running', 'stopped', 'building', 'error')),
|
||||
version VARCHAR(50) NOT NULL,
|
||||
plan VARCHAR(50) NOT NULL CHECK (plan IN ('hobby', 'starter', 'standard', 'business')),
|
||||
region VARCHAR(50) NOT NULL,
|
||||
connection_url TEXT,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
|
||||
CONSTRAINT fk_database_user FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
-- Database Backups table
|
||||
CREATE TABLE IF NOT EXISTS database_backups (
|
||||
id VARCHAR(255) PRIMARY KEY,
|
||||
database_id VARCHAR(255) NOT NULL,
|
||||
size VARCHAR(50) NOT NULL,
|
||||
status VARCHAR(50) NOT NULL DEFAULT 'in_progress' CHECK (status IN ('completed', 'failed', 'in_progress')),
|
||||
backup_path TEXT,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
completed_at TIMESTAMP WITH TIME ZONE,
|
||||
|
||||
CONSTRAINT fk_backup_database FOREIGN KEY (database_id) REFERENCES database_services(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
-- Database Settings table
|
||||
CREATE TABLE IF NOT EXISTS database_settings (
|
||||
database_id VARCHAR(255) PRIMARY KEY,
|
||||
max_connections INTEGER DEFAULT 100,
|
||||
timeout INTEGER DEFAULT 30,
|
||||
ssl_enabled BOOLEAN DEFAULT true,
|
||||
logging_enabled BOOLEAN DEFAULT true,
|
||||
retention_days INTEGER DEFAULT 30,
|
||||
backup_enabled BOOLEAN DEFAULT true,
|
||||
next_backup_time TIMESTAMP WITH TIME ZONE,
|
||||
last_backup_time TIMESTAMP WITH TIME ZONE,
|
||||
|
||||
CONSTRAINT fk_settings_database FOREIGN KEY (database_id) REFERENCES database_services(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
-- Database Metrics table for storing historical metrics
|
||||
CREATE TABLE IF NOT EXISTS database_metrics (
|
||||
id SERIAL PRIMARY KEY,
|
||||
database_id VARCHAR(255) NOT NULL,
|
||||
cpu_usage DECIMAL(5,2),
|
||||
memory_usage DECIMAL(5,2),
|
||||
storage_usage DECIMAL(5,2),
|
||||
active_connections INTEGER,
|
||||
read_iops INTEGER,
|
||||
write_iops INTEGER,
|
||||
network_in_mbps DECIMAL(8,2),
|
||||
network_out_mbps DECIMAL(8,2),
|
||||
recorded_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
|
||||
CONSTRAINT fk_metrics_database FOREIGN KEY (database_id) REFERENCES database_services(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
-- Create indexes for better performance
|
||||
CREATE INDEX IF NOT EXISTS idx_database_services_user_id ON database_services(user_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_database_services_type ON database_services(type);
|
||||
CREATE INDEX IF NOT EXISTS idx_database_services_status ON database_services(status);
|
||||
CREATE INDEX IF NOT EXISTS idx_database_services_created_at ON database_services(created_at);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_database_backups_database_id ON database_backups(database_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_database_backups_created_at ON database_backups(created_at);
|
||||
CREATE INDEX IF NOT EXISTS idx_database_backups_status ON database_backups(status);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_database_metrics_database_id ON database_metrics(database_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_database_metrics_recorded_at ON database_metrics(recorded_at);
|
||||
|
||||
-- Create trigger 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';
|
||||
|
||||
CREATE TRIGGER update_database_services_updated_at
|
||||
BEFORE UPDATE ON database_services
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION update_updated_at_column();
|
||||
|
||||
-- Insert default settings for existing databases (if any)
|
||||
INSERT INTO database_settings (database_id)
|
||||
SELECT id FROM database_services
|
||||
WHERE id NOT IN (SELECT database_id FROM database_settings);
|
||||
|
||||
-- Create view for database statistics
|
||||
CREATE OR REPLACE VIEW database_stats AS
|
||||
SELECT
|
||||
ds.id,
|
||||
ds.name,
|
||||
ds.type,
|
||||
ds.status,
|
||||
ds.plan,
|
||||
ds.region,
|
||||
ds.created_at,
|
||||
ds.updated_at,
|
||||
COUNT(db.id) as backup_count,
|
||||
MAX(db.created_at) as last_backup_time,
|
||||
dm.cpu_usage as latest_cpu,
|
||||
dm.memory_usage as latest_memory,
|
||||
dm.storage_usage as latest_storage,
|
||||
dm.active_connections as latest_connections,
|
||||
dm.recorded_at as metrics_updated_at
|
||||
FROM database_services ds
|
||||
LEFT JOIN database_backups db ON ds.id = db.database_id AND db.status = 'completed'
|
||||
LEFT JOIN database_metrics dm ON ds.id = dm.database_id
|
||||
LEFT JOIN LATERAL (
|
||||
SELECT cpu_usage, memory_usage, storage_usage, active_connections, recorded_at
|
||||
FROM database_metrics
|
||||
WHERE database_id = ds.id
|
||||
ORDER BY recorded_at DESC
|
||||
LIMIT 1
|
||||
) dm ON true
|
||||
GROUP BY ds.id, ds.name, ds.type, ds.status, ds.plan, ds.region, ds.created_at, ds.updated_at,
|
||||
dm.cpu_usage, dm.memory_usage, dm.storage_usage, dm.active_connections, dm.recorded_at;
|
||||
|
||||
-- Add RLS (Row Level Security) policies
|
||||
ALTER TABLE database_services ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE database_backups ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE database_settings ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE database_metrics ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
-- Policy for database services - users can only see their own databases
|
||||
CREATE POLICY "Users can view their own database services" ON database_services
|
||||
FOR SELECT USING (user_id = current_setting('app.current_user_id', true)::VARCHAR);
|
||||
|
||||
CREATE POLICY "Users can insert their own database services" ON database_services
|
||||
FOR INSERT WITH CHECK (user_id = current_setting('app.current_user_id', true)::VARCHAR);
|
||||
|
||||
CREATE POLICY "Users can update their own database services" ON database_services
|
||||
FOR UPDATE USING (user_id = current_setting('app.current_user_id', true)::VARCHAR);
|
||||
|
||||
CREATE POLICY "Users can delete their own database services" ON database_services
|
||||
FOR DELETE USING (user_id = current_setting('app.current_user_id', true)::VARCHAR);
|
||||
|
||||
-- Policies for backups (inherited from database services)
|
||||
CREATE POLICY "Users can view backups of their own databases" ON database_backups
|
||||
FOR SELECT USING (
|
||||
database_id IN (
|
||||
SELECT id FROM database_services
|
||||
WHERE user_id = current_setting('app.current_user_id', true)::VARCHAR
|
||||
)
|
||||
);
|
||||
|
||||
CREATE POLICY "Users can insert backups for their own databases" ON database_backups
|
||||
FOR INSERT WITH CHECK (
|
||||
database_id IN (
|
||||
SELECT id FROM database_services
|
||||
WHERE user_id = current_setting('app.current_user_id', true)::VARCHAR
|
||||
)
|
||||
);
|
||||
|
||||
-- Policies for settings (inherited from database services)
|
||||
CREATE POLICY "Users can view settings of their own databases" ON database_settings
|
||||
FOR SELECT USING (
|
||||
database_id IN (
|
||||
SELECT id FROM database_services
|
||||
WHERE user_id = current_setting('app.current_user_id', true)::VARCHAR
|
||||
)
|
||||
);
|
||||
|
||||
CREATE POLICY "Users can update settings of their own databases" ON database_settings
|
||||
FOR UPDATE USING (
|
||||
database_id IN (
|
||||
SELECT id FROM database_services
|
||||
WHERE user_id = current_setting('app.current_user_id', true)::VARCHAR
|
||||
)
|
||||
);
|
||||
|
||||
-- Policies for metrics (inherited from database services)
|
||||
CREATE POLICY "Users can view metrics of their own databases" ON database_metrics
|
||||
FOR SELECT USING (
|
||||
database_id IN (
|
||||
SELECT id FROM database_services
|
||||
WHERE user_id = current_setting('app.current_user_id', true)::VARCHAR
|
||||
)
|
||||
);
|
||||
|
||||
CREATE POLICY "Users can insert metrics for their own databases" ON database_metrics
|
||||
FOR INSERT WITH CHECK (
|
||||
database_id IN (
|
||||
SELECT id FROM database_services
|
||||
WHERE user_id = current_setting('app.current_user_id', true)::VARCHAR
|
||||
)
|
||||
);
|
||||
|
||||
-- Grant permissions
|
||||
GRANT SELECT, INSERT, UPDATE, DELETE ON database_services TO authenticated_users;
|
||||
GRANT SELECT, INSERT, UPDATE ON database_backups TO authenticated_users;
|
||||
GRANT SELECT, UPDATE ON database_settings TO authenticated_users;
|
||||
GRANT SELECT, INSERT ON database_metrics TO authenticated_users;
|
||||
GRANT SELECT ON database_stats TO authenticated_users;
|
||||
|
||||
-- Create function to clean up old metrics (older than 30 days)
|
||||
CREATE OR REPLACE FUNCTION cleanup_old_metrics()
|
||||
RETURNS void AS $$
|
||||
BEGIN
|
||||
DELETE FROM database_metrics
|
||||
WHERE recorded_at < NOW() - INTERVAL '30 days';
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
-- Create function to schedule next backup
|
||||
CREATE OR REPLACE FUNCTION schedule_next_backup(database_id_param VARCHAR(255))
|
||||
RETURNS void AS $$
|
||||
BEGIN
|
||||
UPDATE database_settings
|
||||
SET next_backup_time = NOW() + INTERVAL '24 hours'
|
||||
WHERE database_id = database_id_param;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
-- Create function to update backup status and schedule next backup
|
||||
CREATE OR REPLACE FUNCTION complete_backup(backup_id_param VARCHAR(255, success_param BOOLEAN))
|
||||
RETURNS void AS $$
|
||||
DECLARE
|
||||
db_id VARCHAR(255);
|
||||
BEGIN
|
||||
-- Get database_id from backup
|
||||
SELECT database_id INTO db_id FROM database_backups WHERE id = backup_id_param;
|
||||
|
||||
IF db_id IS NOT NULL THEN
|
||||
-- Update backup completion time if successful
|
||||
IF success_param THEN
|
||||
UPDATE database_backups
|
||||
SET status = 'completed', completed_at = NOW()
|
||||
WHERE id = backup_id_param;
|
||||
|
||||
-- Update last_backup_time in settings
|
||||
UPDATE database_settings
|
||||
SET last_backup_time = NOW()
|
||||
WHERE database_id = db_id;
|
||||
|
||||
-- Schedule next backup
|
||||
PERFORM schedule_next_backup(db_id);
|
||||
ELSE
|
||||
UPDATE database_backups
|
||||
SET status = 'failed'
|
||||
WHERE id = backup_id_param;
|
||||
END IF;
|
||||
END IF;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
@@ -0,0 +1,54 @@
|
||||
-- Add preview environments table
|
||||
CREATE TABLE IF NOT EXISTS preview_environments (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
project_id UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
|
||||
service_id UUID NOT NULL REFERENCES services(id) ON DELETE CASCADE,
|
||||
branch_name VARCHAR(255) NOT NULL,
|
||||
pr_number INTEGER, -- Optional: Pull request number if applicable
|
||||
environment VARCHAR(255) NOT NULL UNIQUE, -- e.g., preview-feature-branch-20240101-120000
|
||||
status VARCHAR(50) NOT NULL DEFAULT 'building' CHECK (status IN ('building', 'running', 'failed', 'stopped', 'expired')),
|
||||
url TEXT, -- Preview environment URL
|
||||
expires_at TIMESTAMP WITH TIME ZONE NOT NULL,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Create indexes for better performance
|
||||
CREATE INDEX IF NOT EXISTS idx_preview_environments_project_id ON preview_environments(project_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_preview_environments_service_id ON preview_environments(service_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_preview_environments_branch_name ON preview_environments(branch_name);
|
||||
CREATE INDEX IF NOT EXISTS idx_preview_environments_status ON preview_environments(status);
|
||||
CREATE INDEX IF NOT EXISTS idx_preview_environments_expires_at ON preview_environments(expires_at);
|
||||
|
||||
-- Add trigger to update updated_at timestamp
|
||||
CREATE OR REPLACE FUNCTION update_preview_environments_updated_at()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
NEW.updated_at = NOW();
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ language 'plpgsql';
|
||||
|
||||
CREATE TRIGGER preview_environments_updated_at
|
||||
BEFORE UPDATE ON preview_environments
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION update_preview_environments_updated_at();
|
||||
|
||||
-- Add unique constraint to prevent duplicate preview environments for same service and branch
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_preview_environments_unique_active
|
||||
ON preview_environments(service_id, branch_name)
|
||||
WHERE status NOT IN ('expired', 'stopped');
|
||||
|
||||
-- Add comments for documentation
|
||||
COMMENT ON TABLE preview_environments IS 'Preview environments for branch-based deployments';
|
||||
COMMENT ON COLUMN preview_environments.id IS 'Unique identifier for the preview environment';
|
||||
COMMENT ON COLUMN preview_environments.project_id IS 'Reference to the project';
|
||||
COMMENT ON COLUMN preview_environments.service_id IS 'Reference to the service';
|
||||
COMMENT ON COLUMN preview_environments.branch_name IS 'Git branch name';
|
||||
COMMENT ON COLUMN preview_environments.pr_number IS 'Pull request number (optional)';
|
||||
COMMENT ON COLUMN preview_environments.environment IS 'Environment name (e.g., preview-feature-branch-20240101-120000)';
|
||||
COMMENT ON COLUMN preview_environments.status IS 'Current status of the preview environment';
|
||||
COMMENT ON COLUMN preview_environments.url IS 'URL where the preview environment is accessible';
|
||||
COMMENT ON COLUMN preview_environments.expires_at IS 'When the preview environment expires';
|
||||
COMMENT ON COLUMN preview_environments.created_at IS 'When the preview environment was created';
|
||||
COMMENT ON COLUMN preview_environments.updated_at IS 'When the preview environment was last updated';
|
||||
@@ -0,0 +1,224 @@
|
||||
-- Security Features Migration
|
||||
-- This migration adds tables for security scanning, compliance, and audit logging
|
||||
|
||||
-- Security scans table
|
||||
CREATE TABLE IF NOT EXISTS security_scans (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
project_id UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
|
||||
service_id UUID REFERENCES services(id) ON DELETE CASCADE,
|
||||
scan_type VARCHAR(50) NOT NULL CHECK (scan_type IN ('dependency', 'configuration', 'comprehensive')),
|
||||
status VARCHAR(50) NOT NULL DEFAULT 'running' CHECK (status IN ('running', 'completed', 'failed')),
|
||||
started_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
||||
completed_at TIMESTAMP WITH TIME ZONE,
|
||||
summary JSONB DEFAULT '{}',
|
||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Vulnerabilities table
|
||||
CREATE TABLE IF NOT EXISTS vulnerabilities (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
type VARCHAR(50) NOT NULL CHECK (type IN ('dependency', 'configuration', 'code')),
|
||||
severity VARCHAR(20) NOT NULL CHECK (severity IN ('critical', 'high', 'medium', 'low')),
|
||||
title VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
service_id UUID REFERENCES services(id) ON DELETE CASCADE,
|
||||
project_id UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
|
||||
status VARCHAR(50) NOT NULL DEFAULT 'open' CHECK (status IN ('open', 'resolved', 'ignored')),
|
||||
found_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
||||
resolved_at TIMESTAMP WITH TIME ZONE,
|
||||
metadata JSONB DEFAULT '{}',
|
||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Compliance frameworks table
|
||||
CREATE TABLE IF NOT EXISTS compliance_frameworks (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
name VARCHAR(100) NOT NULL UNIQUE,
|
||||
description TEXT,
|
||||
version VARCHAR(20) NOT NULL,
|
||||
enabled BOOLEAN NOT NULL DEFAULT true,
|
||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Compliance controls table
|
||||
CREATE TABLE IF NOT EXISTS compliance_controls (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
framework_id UUID NOT NULL REFERENCES compliance_frameworks(id) ON DELETE CASCADE,
|
||||
code VARCHAR(50) NOT NULL,
|
||||
title VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
category VARCHAR(100),
|
||||
requirement TEXT,
|
||||
test_procedure TEXT,
|
||||
status VARCHAR(50) NOT NULL DEFAULT 'pending' CHECK (status IN ('compliant', 'non_compliant', 'not_applicable', 'pending')),
|
||||
last_assessed TIMESTAMP WITH TIME ZONE,
|
||||
evidence TEXT,
|
||||
metadata JSONB DEFAULT '{}',
|
||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
||||
UNIQUE(framework_id, code)
|
||||
);
|
||||
|
||||
-- Compliance reports table
|
||||
CREATE TABLE IF NOT EXISTS compliance_reports (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
project_id UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
|
||||
framework_id UUID NOT NULL REFERENCES compliance_frameworks(id) ON DELETE CASCADE,
|
||||
assessment_date TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
||||
assessor VARCHAR(255),
|
||||
overall_status VARCHAR(50) NOT NULL CHECK (overall_status IN ('compliant', 'partially_compliant', 'non_compliant', 'in_progress')),
|
||||
score INTEGER NOT NULL DEFAULT 0 CHECK (score >= 0 AND score <= 100),
|
||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Compliance risks table
|
||||
CREATE TABLE IF NOT EXISTS compliance_risks (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
report_id UUID NOT NULL REFERENCES compliance_reports(id) ON DELETE CASCADE,
|
||||
control_id UUID NOT NULL REFERENCES compliance_controls(id) ON DELETE CASCADE,
|
||||
title VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
impact VARCHAR(20) NOT NULL CHECK (impact IN ('high', 'medium', 'low')),
|
||||
likelihood VARCHAR(20) NOT NULL CHECK (likelihood IN ('high', 'medium', 'low')),
|
||||
mitigation TEXT,
|
||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Audit logs table
|
||||
CREATE TABLE IF NOT EXISTS audit_logs (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
timestamp TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
||||
user_id UUID REFERENCES users(id) ON DELETE SET NULL,
|
||||
action VARCHAR(100) NOT NULL,
|
||||
resource VARCHAR(100) NOT NULL,
|
||||
resource_id UUID,
|
||||
details JSONB DEFAULT '{}',
|
||||
ip_address INET,
|
||||
user_agent TEXT,
|
||||
success BOOLEAN NOT NULL DEFAULT true,
|
||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Data retention policies table
|
||||
CREATE TABLE IF NOT EXISTS data_retention_policies (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
name VARCHAR(100) NOT NULL UNIQUE,
|
||||
data_type VARCHAR(100) NOT NULL,
|
||||
retention_period INTERVAL NOT NULL,
|
||||
action VARCHAR(50) NOT NULL CHECK (action IN ('delete', 'anonymize', 'archive')),
|
||||
enabled BOOLEAN NOT NULL DEFAULT true,
|
||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Anonymized data table
|
||||
CREATE TABLE IF NOT EXISTS anonymized_data (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
original_id UUID NOT NULL,
|
||||
anonymized_id VARCHAR(255) NOT NULL UNIQUE,
|
||||
data_type VARCHAR(100) NOT NULL,
|
||||
anonymized_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
||||
retained_data TEXT, -- Encrypted non-sensitive data
|
||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Indexes for performance
|
||||
CREATE INDEX IF NOT EXISTS idx_security_scans_project_id ON security_scans(project_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_security_scans_status ON security_scans(status);
|
||||
CREATE INDEX IF NOT EXISTS idx_security_scans_started_at ON security_scans(started_at);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_vulnerabilities_project_id ON vulnerabilities(project_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_vulnerabilities_service_id ON vulnerabilities(service_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_vulnerabilities_severity ON vulnerabilities(severity);
|
||||
CREATE INDEX IF NOT EXISTS idx_vulnerabilities_status ON vulnerabilities(status);
|
||||
CREATE INDEX IF NOT EXISTS idx_vulnerabilities_found_at ON vulnerabilities(found_at);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_compliance_controls_framework_id ON compliance_controls(framework_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_compliance_controls_status ON compliance_controls(status);
|
||||
CREATE INDEX IF NOT EXISTS idx_compliance_controls_last_assessed ON compliance_controls(last_assessed);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_compliance_reports_project_id ON compliance_reports(project_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_compliance_reports_framework_id ON compliance_reports(framework_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_compliance_reports_assessment_date ON compliance_reports(assessment_date);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_compliance_risks_report_id ON compliance_risks(report_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_compliance_risks_control_id ON compliance_risks(control_id);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_audit_logs_timestamp ON audit_logs(timestamp);
|
||||
CREATE INDEX IF NOT EXISTS idx_audit_logs_user_id ON audit_logs(user_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_audit_logs_action ON audit_logs(action);
|
||||
CREATE INDEX IF NOT EXISTS idx_audit_logs_resource ON audit_logs(resource);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_anonymized_data_original_id ON anonymized_data(original_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_anonymized_data_anonymized_id ON anonymized_data(anonymized_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_anonymized_data_data_type ON anonymized_data(data_type);
|
||||
|
||||
-- Insert default data retention policies
|
||||
INSERT INTO data_retention_policies (name, data_type, retention_period, action, enabled) VALUES
|
||||
('User Data Retention', 'user_data', INTERVAL '2 years', 'anonymize', true),
|
||||
('Analytics Data Retention', 'analytics_data', INTERVAL '6 months', 'delete', true),
|
||||
('Log Data Retention', 'log_data', INTERVAL '90 days', 'delete', true),
|
||||
('Deleted User Data', 'deleted_user_data', INTERVAL '30 days', 'delete', true),
|
||||
('Audit Log Retention', 'audit_log', INTERVAL '1 year', 'archive', true)
|
||||
ON CONFLICT (name) DO NOTHING;
|
||||
|
||||
-- Insert default GDPR framework
|
||||
INSERT INTO compliance_frameworks (id, name, description, version, enabled) VALUES
|
||||
('gen_random_uuid()', 'GDPR', 'General Data Protection Regulation compliance framework', '1.0', true)
|
||||
ON CONFLICT (name) DO UPDATE SET version = '1.0', enabled = true;
|
||||
|
||||
-- Update updated_at trigger function
|
||||
CREATE OR REPLACE FUNCTION update_updated_at_column()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
NEW.updated_at = NOW();
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ language 'plpgsql';
|
||||
|
||||
-- Create triggers for updated_at
|
||||
CREATE TRIGGER update_security_scans_updated_at BEFORE UPDATE ON security_scans FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
CREATE TRIGGER update_vulnerabilities_updated_at BEFORE UPDATE ON vulnerabilities FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
CREATE TRIGGER update_compliance_frameworks_updated_at BEFORE UPDATE ON compliance_frameworks FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
CREATE TRIGGER update_compliance_controls_updated_at BEFORE UPDATE ON compliance_controls FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
CREATE TRIGGER update_compliance_reports_updated_at BEFORE UPDATE ON compliance_reports FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
CREATE TRIGGER update_compliance_risks_updated_at BEFORE UPDATE ON compliance_risks FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
CREATE TRIGGER update_data_retention_policies_updated_at BEFORE UPDATE ON data_retention_policies FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
|
||||
-- Row Level Security (RLS) for audit logs
|
||||
ALTER TABLE audit_logs ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
-- Policy for audit logs - users can only see their own audit logs
|
||||
CREATE POLICY audit_logs_user_policy ON audit_logs
|
||||
FOR SELECT USING (user_id = current_setting('app.current_user_id')::UUID);
|
||||
|
||||
-- Policy for audit logs - no direct inserts (only through application)
|
||||
CREATE POLICY audit_logs_insert_policy ON audit_logs
|
||||
FOR INSERT WITH CHECK (false);
|
||||
|
||||
-- Policy for audit logs - no direct updates (audit logs are immutable)
|
||||
CREATE POLICY audit_logs_update_policy ON audit_logs
|
||||
FOR UPDATE WITH CHECK (false);
|
||||
|
||||
-- Row Level Security for anonymized data
|
||||
ALTER TABLE anonymized_data ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
-- Policy for anonymized data - restricted access
|
||||
CREATE POLICY anonymized_data_policy ON anonymized_data
|
||||
FOR SELECT USING (current_setting('app.is_admin', true)::BOOLEAN);
|
||||
|
||||
-- Add comments for documentation
|
||||
COMMENT ON TABLE security_scans IS 'Security scan records for vulnerability assessment';
|
||||
COMMENT ON TABLE vulnerabilities IS 'Security vulnerabilities found during scans';
|
||||
COMMENT ON TABLE compliance_frameworks IS 'Compliance frameworks like GDPR, SOC2, etc.';
|
||||
COMMENT ON TABLE compliance_controls IS 'Individual controls within compliance frameworks';
|
||||
COMMENT ON TABLE compliance_reports IS 'Compliance assessment reports';
|
||||
COMMENT ON TABLE compliance_risks IS 'Risk assessments for compliance gaps';
|
||||
COMMENT ON TABLE audit_logs IS 'Security audit trail for all sensitive operations';
|
||||
COMMENT ON TABLE data_retention_policies IS 'Data retention and deletion policies';
|
||||
COMMENT ON TABLE anonymized_data IS 'Anonymized user data for privacy compliance';
|
||||
@@ -0,0 +1,81 @@
|
||||
-- Performance Optimization Migration
|
||||
-- This migration adds additional indexes and optimizations for better query performance
|
||||
|
||||
-- Composite indexes for common query patterns
|
||||
CREATE INDEX IF NOT EXISTS idx_projects_owner_updated ON projects(owner_id, updated_at DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_services_project_env ON services(project_id, environment_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_services_status_project ON services(status, project_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_deployments_service_status_created ON deployments(service_id, status, created_at DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_deployments_service_created ON deployments(service_id, created_at DESC);
|
||||
|
||||
-- Partial indexes for better performance on filtered queries
|
||||
CREATE INDEX IF NOT EXISTS idx_active_deployments ON deployments(service_id, created_at DESC)
|
||||
WHERE status IN ('running', 'deploying');
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_running_services ON services(project_id, updated_at DESC)
|
||||
WHERE status = 'running';
|
||||
|
||||
-- Environment variables optimization
|
||||
CREATE INDEX IF NOT EXISTS idx_env_vars_service_key ON environment_variables(service_id, key);
|
||||
|
||||
-- Service dependencies optimization
|
||||
CREATE INDEX IF NOT EXISTS idx_service_deps_service ON service_dependencies(service_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_service_deps_depends_on ON service_dependencies(depends_on_service_id);
|
||||
|
||||
-- Project members optimization for role-based queries
|
||||
CREATE INDEX IF NOT EXISTS idx_project_members_role ON project_members(project_id, role);
|
||||
|
||||
-- Add table statistics for better query planning
|
||||
ANALYZE projects;
|
||||
ANALYZE services;
|
||||
ANALYZE deployments;
|
||||
ANALYZE environment_variables;
|
||||
ANALYZE service_dependencies;
|
||||
ANALYZE project_members;
|
||||
ANALYZE users;
|
||||
ANALYZE environments;
|
||||
|
||||
-- Create a view for project statistics to optimize dashboard queries
|
||||
CREATE OR REPLACE VIEW project_stats AS
|
||||
SELECT
|
||||
p.id,
|
||||
p.name,
|
||||
p.description,
|
||||
p.owner_id,
|
||||
p.created_at,
|
||||
p.updated_at,
|
||||
COUNT(DISTINCT s.id) as service_count,
|
||||
COUNT(DISTINCT d.id) as deployment_count,
|
||||
COUNT(DISTINCT CASE WHEN s.status = 'running' THEN s.id END) as running_services,
|
||||
MAX(d.created_at) as last_deployment
|
||||
FROM projects p
|
||||
LEFT JOIN services s ON p.id = s.project_id
|
||||
LEFT JOIN deployments d ON s.id = d.service_id
|
||||
GROUP BY p.id, p.name, p.description, p.owner_id, p.created_at, p.updated_at;
|
||||
|
||||
-- Create index on the view for better performance
|
||||
CREATE INDEX IF NOT EXISTS idx_project_stats_id ON project_stats(id);
|
||||
|
||||
-- Function to get project statistics efficiently
|
||||
CREATE OR REPLACE FUNCTION get_project_stats(project_uuid UUID)
|
||||
RETURNS TABLE(
|
||||
service_count BIGINT,
|
||||
deployment_count BIGINT,
|
||||
running_services BIGINT,
|
||||
last_deployment TIMESTAMP WITH TIME ZONE
|
||||
) AS $$
|
||||
BEGIN
|
||||
RETURN QUERY
|
||||
SELECT
|
||||
COUNT(DISTINCT s.id),
|
||||
COUNT(DISTINCT d.id),
|
||||
COUNT(DISTINCT CASE WHEN s.status = 'running' THEN s.id END),
|
||||
MAX(d.created_at)
|
||||
FROM services s
|
||||
LEFT JOIN deployments d ON s.id = d.service_id
|
||||
WHERE s.project_id = project_uuid;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
-- Add comment for documentation
|
||||
COMMENT ON MIGRATION IS 'Performance optimization with additional indexes and statistics views';
|
||||
Reference in New Issue
Block a user