botserver/migrations/mail/6.1.5/up.sql
Rodrigo Rodriguez (Pragmatismo) b103c07248 Fix migration errors and reorganize migration files
- Fixed 'relation session_kb_associations does not exist' error in core consolidated migration.
- Renamed migration directories from timestamp-based to version-based (6.0.x, 6.1.x, 6.2.x).
- Reorganized migrations into dedicated feature folders (products, dashboards, learn, video).
- Updated migration execution order in core/shared/utils.rs.
- Moves legacy migrations to 6.0.x/6.1.x and workflow to 6.2.0.
2026-01-27 13:45:54 -03:00

361 lines
15 KiB
PL/PgSQL

-- Legacy Mail Tables extracted from consolidated
-- Global email signature (applied to all emails from this bot)
CREATE TABLE IF NOT EXISTS global_email_signatures (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
bot_id UUID NOT NULL REFERENCES bots(id) ON DELETE CASCADE,
name VARCHAR(100) NOT NULL DEFAULT 'Default',
content_html TEXT NOT NULL,
content_plain TEXT NOT NULL,
position VARCHAR(20) DEFAULT 'bottom',
is_active BOOLEAN DEFAULT true,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW(),
CONSTRAINT unique_bot_global_signature UNIQUE (bot_id, name),
CONSTRAINT check_signature_position CHECK (position IN ('top', 'bottom'))
);
CREATE INDEX IF NOT EXISTS idx_global_signatures_bot ON global_email_signatures(bot_id) WHERE is_active = true;
-- User email signatures (in addition to global)
CREATE TABLE IF NOT EXISTS email_signatures (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
bot_id UUID REFERENCES bots(id) ON DELETE CASCADE,
name VARCHAR(100) NOT NULL DEFAULT 'Default',
content_html TEXT NOT NULL,
content_plain TEXT NOT NULL,
is_default BOOLEAN DEFAULT false,
is_active BOOLEAN DEFAULT true,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW(),
CONSTRAINT unique_user_signature_name UNIQUE (user_id, bot_id, name)
);
CREATE INDEX IF NOT EXISTS idx_email_signatures_user ON email_signatures(user_id);
CREATE INDEX IF NOT EXISTS idx_email_signatures_default ON email_signatures(user_id, bot_id) WHERE is_default = true;
-- Scheduled emails (send later)
CREATE TABLE IF NOT EXISTS scheduled_emails (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
bot_id UUID NOT NULL REFERENCES bots(id) ON DELETE CASCADE,
to_addresses TEXT NOT NULL,
cc_addresses TEXT,
bcc_addresses TEXT,
subject TEXT NOT NULL,
body_html TEXT NOT NULL,
body_plain TEXT,
attachments_json TEXT DEFAULT '[]',
scheduled_at TIMESTAMPTZ NOT NULL,
sent_at TIMESTAMPTZ,
status VARCHAR(20) DEFAULT 'pending',
retry_count INTEGER DEFAULT 0,
error_message TEXT,
created_at TIMESTAMPTZ DEFAULT NOW(),
CONSTRAINT check_scheduled_status CHECK (status IN ('pending', 'sent', 'failed', 'cancelled'))
);
CREATE INDEX IF NOT EXISTS idx_scheduled_emails_pending ON scheduled_emails(scheduled_at) WHERE status = 'pending';
CREATE INDEX IF NOT EXISTS idx_scheduled_emails_user ON scheduled_emails(user_id, bot_id);
-- Email templates
CREATE TABLE IF NOT EXISTS email_templates (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
bot_id UUID NOT NULL REFERENCES bots(id) ON DELETE CASCADE,
user_id UUID REFERENCES users(id) ON DELETE SET NULL,
name VARCHAR(255) NOT NULL,
description TEXT,
subject_template TEXT NOT NULL,
body_html_template TEXT NOT NULL,
body_plain_template TEXT,
variables_json TEXT DEFAULT '[]',
category VARCHAR(100),
is_shared BOOLEAN DEFAULT false,
usage_count INTEGER DEFAULT 0,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_email_templates_bot ON email_templates(bot_id);
CREATE INDEX IF NOT EXISTS idx_email_templates_category ON email_templates(category);
CREATE INDEX IF NOT EXISTS idx_email_templates_shared ON email_templates(bot_id) WHERE is_shared = true;
-- Auto-responders (Out of Office) - works with Stalwart Sieve
CREATE TABLE IF NOT EXISTS email_auto_responders (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
bot_id UUID NOT NULL REFERENCES bots(id) ON DELETE CASCADE,
responder_type VARCHAR(50) NOT NULL DEFAULT 'out_of_office',
subject TEXT NOT NULL,
body_html TEXT NOT NULL,
body_plain TEXT,
start_date TIMESTAMPTZ,
end_date TIMESTAMPTZ,
send_to_internal_only BOOLEAN DEFAULT false,
exclude_addresses TEXT,
is_active BOOLEAN DEFAULT false,
stalwart_sieve_id VARCHAR(255),
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW(),
CONSTRAINT check_responder_type CHECK (responder_type IN ('out_of_office', 'vacation', 'custom')),
CONSTRAINT unique_user_responder UNIQUE (user_id, bot_id, responder_type)
);
CREATE INDEX IF NOT EXISTS idx_auto_responders_active ON email_auto_responders(user_id, bot_id) WHERE is_active = true;
-- Email rules/filters - synced with Stalwart Sieve
CREATE TABLE IF NOT EXISTS email_rules (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
bot_id UUID NOT NULL REFERENCES bots(id) ON DELETE CASCADE,
name VARCHAR(255) NOT NULL,
priority INTEGER DEFAULT 0,
conditions_json TEXT NOT NULL,
actions_json TEXT NOT NULL,
stop_processing BOOLEAN DEFAULT false,
is_active BOOLEAN DEFAULT true,
stalwart_sieve_id VARCHAR(255),
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_email_rules_user ON email_rules(user_id, bot_id);
CREATE INDEX IF NOT EXISTS idx_email_rules_priority ON email_rules(user_id, bot_id, priority);
-- Email labels/categories
CREATE TABLE IF NOT EXISTS email_labels (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
bot_id UUID NOT NULL REFERENCES bots(id) ON DELETE CASCADE,
name VARCHAR(100) NOT NULL,
color VARCHAR(7) DEFAULT '#3b82f6',
parent_id UUID REFERENCES email_labels(id) ON DELETE CASCADE,
is_system BOOLEAN DEFAULT false,
created_at TIMESTAMPTZ DEFAULT NOW(),
CONSTRAINT unique_user_label UNIQUE (user_id, bot_id, name)
);
CREATE INDEX IF NOT EXISTS idx_email_labels_user ON email_labels(user_id, bot_id);
-- Email-label associations
CREATE TABLE IF NOT EXISTS email_label_assignments (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
email_message_id VARCHAR(255) NOT NULL,
label_id UUID NOT NULL REFERENCES email_labels(id) ON DELETE CASCADE,
assigned_at TIMESTAMPTZ DEFAULT NOW(),
CONSTRAINT unique_email_label UNIQUE (email_message_id, label_id)
);
CREATE INDEX IF NOT EXISTS idx_label_assignments_email ON email_label_assignments(email_message_id);
CREATE INDEX IF NOT EXISTS idx_label_assignments_label ON email_label_assignments(label_id);
-- Distribution lists
CREATE TABLE IF NOT EXISTS distribution_lists (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
bot_id UUID NOT NULL REFERENCES bots(id) ON DELETE CASCADE,
owner_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
name VARCHAR(255) NOT NULL,
email_alias VARCHAR(255),
description TEXT,
members_json TEXT NOT NULL DEFAULT '[]',
is_public BOOLEAN DEFAULT false,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_distribution_lists_bot ON distribution_lists(bot_id);
CREATE INDEX IF NOT EXISTS idx_distribution_lists_owner ON distribution_lists(owner_id);
-- Shared mailboxes - managed via Stalwart
CREATE TABLE IF NOT EXISTS shared_mailboxes (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
bot_id UUID NOT NULL REFERENCES bots(id) ON DELETE CASCADE,
email_address VARCHAR(255) NOT NULL,
display_name VARCHAR(255) NOT NULL,
description TEXT,
settings_json TEXT DEFAULT '{}',
stalwart_account_id VARCHAR(255),
is_active BOOLEAN DEFAULT true,
created_at TIMESTAMPTZ DEFAULT NOW(),
CONSTRAINT unique_shared_mailbox_email UNIQUE (bot_id, email_address)
);
CREATE INDEX IF NOT EXISTS idx_shared_mailboxes_bot ON shared_mailboxes(bot_id);
CREATE TABLE IF NOT EXISTS shared_mailbox_members (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
mailbox_id UUID NOT NULL REFERENCES shared_mailboxes(id) ON DELETE CASCADE,
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
permission_level VARCHAR(20) DEFAULT 'read',
added_at TIMESTAMPTZ DEFAULT NOW(),
CONSTRAINT unique_mailbox_member UNIQUE (mailbox_id, user_id),
CONSTRAINT check_permission CHECK (permission_level IN ('read', 'write', 'admin'))
);
CREATE INDEX IF NOT EXISTS idx_shared_mailbox_members ON shared_mailbox_members(mailbox_id);
CREATE INDEX IF NOT EXISTS idx_shared_mailbox_user ON shared_mailbox_members(user_id);
-- Add user_email_accounts table for storing user email credentials
CREATE TABLE IF NOT EXISTS user_email_accounts (
id uuid DEFAULT gen_random_uuid() NOT NULL,
user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE,
email varchar(255) NOT NULL,
display_name varchar(255) NULL,
imap_server varchar(255) NOT NULL,
imap_port int4 DEFAULT 993 NOT NULL,
smtp_server varchar(255) NOT NULL,
smtp_port int4 DEFAULT 587 NOT NULL,
username varchar(255) NOT NULL,
password_encrypted text NOT NULL,
is_primary bool DEFAULT false NOT NULL,
is_active bool DEFAULT true NOT NULL,
created_at timestamptz DEFAULT now() NOT NULL,
updated_at timestamptz DEFAULT now() NOT NULL,
CONSTRAINT user_email_accounts_pkey PRIMARY KEY (id),
CONSTRAINT user_email_accounts_user_email_key UNIQUE (user_id, email)
);
CREATE INDEX IF NOT EXISTS idx_user_email_accounts_user_id ON user_email_accounts(user_id);
CREATE INDEX IF NOT EXISTS idx_user_email_accounts_active ON user_email_accounts(is_active) WHERE is_active;
-- Add email drafts table
CREATE TABLE IF NOT EXISTS email_drafts (
id uuid DEFAULT gen_random_uuid() NOT NULL,
user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE,
account_id uuid NOT NULL REFERENCES user_email_accounts(id) ON DELETE CASCADE,
to_address text NOT NULL,
cc_address text NULL,
bcc_address text NULL,
subject varchar(500) NULL,
body text NULL,
attachments jsonb DEFAULT '[]'::jsonb NOT NULL,
created_at timestamptz DEFAULT now() NOT NULL,
updated_at timestamptz DEFAULT now() NOT NULL,
CONSTRAINT email_drafts_pkey PRIMARY KEY (id)
);
CREATE INDEX IF NOT EXISTS idx_email_drafts_user_id ON email_drafts(user_id);
CREATE INDEX IF NOT EXISTS idx_email_drafts_account_id ON email_drafts(account_id);
-- Add email folders metadata table (for caching and custom folders)
CREATE TABLE IF NOT EXISTS email_folders (
id uuid DEFAULT gen_random_uuid() NOT NULL,
account_id uuid NOT NULL REFERENCES user_email_accounts(id) ON DELETE CASCADE,
folder_name varchar(255) NOT NULL,
folder_path varchar(500) NOT NULL,
unread_count int4 DEFAULT 0 NOT NULL,
total_count int4 DEFAULT 0 NOT NULL,
last_synced timestamptz NULL,
created_at timestamptz DEFAULT now() NOT NULL,
updated_at timestamptz DEFAULT now() NOT NULL,
CONSTRAINT email_folders_pkey PRIMARY KEY (id),
CONSTRAINT email_folders_account_path_key UNIQUE (account_id, folder_path)
);
CREATE INDEX IF NOT EXISTS idx_email_folders_account_id ON email_folders(account_id);
-- Add sessions table enhancement for storing current email account
-- Check if column exists, if not add it (idempotent)
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_name = 'user_sessions' AND column_name = 'active_email_account_id'
) THEN
ALTER TABLE user_sessions ADD COLUMN active_email_account_id uuid NULL;
ALTER TABLE user_sessions ADD CONSTRAINT user_sessions_email_account_id_fkey
FOREIGN KEY (active_email_account_id) REFERENCES user_email_accounts(id) ON DELETE SET NULL;
END IF;
END $$;
-- Email Read Tracking Table
CREATE TABLE IF NOT EXISTS sent_email_tracking (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tracking_id UUID NOT NULL UNIQUE,
bot_id UUID NOT NULL,
account_id UUID NOT NULL,
from_email VARCHAR(255) NOT NULL,
to_email VARCHAR(255) NOT NULL,
cc TEXT,
bcc TEXT,
subject TEXT NOT NULL,
sent_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
is_read BOOLEAN NOT NULL DEFAULT FALSE,
read_at TIMESTAMPTZ,
read_count INTEGER NOT NULL DEFAULT 0,
first_read_ip VARCHAR(45),
last_read_ip VARCHAR(45),
user_agent TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- Indexes for efficient queries
CREATE INDEX IF NOT EXISTS idx_sent_email_tracking_tracking_id ON sent_email_tracking(tracking_id);
CREATE INDEX IF NOT EXISTS idx_sent_email_tracking_bot_id ON sent_email_tracking(bot_id);
CREATE INDEX IF NOT EXISTS idx_sent_email_tracking_account_id ON sent_email_tracking(account_id);
CREATE INDEX IF NOT EXISTS idx_sent_email_tracking_to_email ON sent_email_tracking(to_email);
CREATE INDEX IF NOT EXISTS idx_sent_email_tracking_sent_at ON sent_email_tracking(sent_at DESC);
CREATE INDEX IF NOT EXISTS idx_sent_email_tracking_is_read ON sent_email_tracking(is_read);
CREATE INDEX IF NOT EXISTS idx_sent_email_tracking_read_status ON sent_email_tracking(bot_id, is_read, sent_at DESC);
-- Trigger to auto-update updated_at for tracking
CREATE OR REPLACE FUNCTION update_sent_email_tracking_updated_at()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at = NOW();
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
DROP TRIGGER IF EXISTS trigger_update_sent_email_tracking_updated_at ON sent_email_tracking;
CREATE TRIGGER trigger_update_sent_email_tracking_updated_at
BEFORE UPDATE ON sent_email_tracking
FOR EACH ROW
EXECUTE FUNCTION update_sent_email_tracking_updated_at();
-- Add comment for documentation
COMMENT ON TABLE sent_email_tracking IS 'Tracks sent emails for read receipt functionality via tracking pixel';
-- Email monitoring table for ON EMAIL triggers
CREATE TABLE IF NOT EXISTS email_monitors (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
bot_id UUID NOT NULL REFERENCES bots(id) ON DELETE CASCADE,
email_address VARCHAR(500) NOT NULL,
script_path VARCHAR(1000) NOT NULL,
is_active BOOLEAN DEFAULT true,
last_check_at TIMESTAMPTZ,
last_uid BIGINT DEFAULT 0,
filter_from VARCHAR(500),
filter_subject VARCHAR(500),
created_at TIMESTAMPTZ DEFAULT NOW() NOT NULL,
updated_at TIMESTAMPTZ DEFAULT NOW() NOT NULL,
CONSTRAINT unique_bot_email UNIQUE (bot_id, email_address)
);
CREATE INDEX IF NOT EXISTS idx_email_monitors_bot_id ON email_monitors(bot_id);
CREATE INDEX IF NOT EXISTS idx_email_monitors_email ON email_monitors(email_address);
CREATE INDEX IF NOT EXISTS idx_email_monitors_active ON email_monitors(is_active) WHERE is_active = true;
-- Email received events log
CREATE TABLE IF NOT EXISTS email_received_events (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
monitor_id UUID NOT NULL REFERENCES email_monitors(id) ON DELETE CASCADE,
message_uid BIGINT NOT NULL,
message_id VARCHAR(500),
from_address VARCHAR(500) NOT NULL,
to_addresses_json TEXT,
subject VARCHAR(1000),
received_at TIMESTAMPTZ,
has_attachments BOOLEAN DEFAULT false,
content_preview TEXT,
processed BOOLEAN DEFAULT false,
processed_at TIMESTAMPTZ,
error_message TEXT,
created_at TIMESTAMPTZ DEFAULT NOW() NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_email_events_monitor ON email_received_events(monitor_id);
CREATE INDEX IF NOT EXISTS idx_email_events_received ON email_received_events(received_at DESC);
CREATE INDEX IF NOT EXISTS idx_email_events_processed ON email_received_events(processed) WHERE processed = false;