-- Mail plugin initial migration: creates all mail tables CREATE TABLE IF NOT EXISTS mail_accounts ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id UUID NOT NULL, email_address VARCHAR(255) NOT NULL, display_name VARCHAR(255) NOT NULL DEFAULT '', imap_host VARCHAR(255) NOT NULL, imap_port INTEGER NOT NULL DEFAULT 993, imap_ssl BOOLEAN NOT NULL DEFAULT TRUE, smtp_host VARCHAR(255) NOT NULL, smtp_port INTEGER NOT NULL DEFAULT 587, smtp_tls BOOLEAN NOT NULL DEFAULT TRUE, username VARCHAR(255) NOT NULL, encrypted_password TEXT NOT NULL, is_shared BOOLEAN NOT NULL DEFAULT FALSE, is_active BOOLEAN NOT NULL DEFAULT TRUE, tenant_id UUID NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now() ); CREATE INDEX IF NOT EXISTS ix_mail_accounts_user ON mail_accounts(user_id); CREATE INDEX IF NOT EXISTS ix_mail_accounts_tenant ON mail_accounts(tenant_id); CREATE TABLE IF NOT EXISTS mail_folders ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), account_id UUID NOT NULL REFERENCES mail_accounts(id) ON DELETE CASCADE, name VARCHAR(255) NOT NULL, imap_name VARCHAR(255) NOT NULL, parent_id UUID REFERENCES mail_folders(id) ON DELETE CASCADE, is_standard BOOLEAN NOT NULL DEFAULT FALSE, unread_count INTEGER NOT NULL DEFAULT 0, total_count INTEGER NOT NULL DEFAULT 0, tenant_id UUID NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now() ); CREATE INDEX IF NOT EXISTS ix_mail_folders_account ON mail_folders(account_id); CREATE INDEX IF NOT EXISTS ix_mail_folders_tenant ON mail_folders(tenant_id); CREATE TABLE IF NOT EXISTS mails ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), account_id UUID NOT NULL REFERENCES mail_accounts(id) ON DELETE CASCADE, folder_id UUID NOT NULL REFERENCES mail_folders(id) ON DELETE CASCADE, message_id VARCHAR(512) NOT NULL, thread_id VARCHAR(512) NOT NULL DEFAULT '', in_reply_to VARCHAR(512), references_header TEXT, subject VARCHAR(512) NOT NULL DEFAULT '', from_address VARCHAR(255) NOT NULL, to_addresses TEXT NOT NULL DEFAULT '', cc_addresses TEXT NOT NULL DEFAULT '', bcc_addresses TEXT NOT NULL DEFAULT '', body_text TEXT NOT NULL DEFAULT '', body_html TEXT NOT NULL DEFAULT '', body_html_sanitized TEXT NOT NULL DEFAULT '', is_seen BOOLEAN NOT NULL DEFAULT FALSE, is_flagged BOOLEAN NOT NULL DEFAULT FALSE, is_draft BOOLEAN NOT NULL DEFAULT FALSE, is_answered BOOLEAN NOT NULL DEFAULT FALSE, is_forwarded BOOLEAN NOT NULL DEFAULT FALSE, has_attachments BOOLEAN NOT NULL DEFAULT FALSE, size_bytes INTEGER NOT NULL DEFAULT 0, received_at TIMESTAMPTZ, sent_at TIMESTAMPTZ, contact_id UUID, company_id UUID, tenant_id UUID NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now() ); CREATE INDEX IF NOT EXISTS ix_mails_folder ON mails(folder_id); CREATE INDEX IF NOT EXISTS ix_mails_account ON mails(account_id); CREATE INDEX IF NOT EXISTS ix_mails_tenant ON mails(tenant_id); CREATE INDEX IF NOT EXISTS ix_mails_thread ON mails(thread_id); CREATE TABLE IF NOT EXISTS mail_attachments ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), mail_id UUID NOT NULL REFERENCES mails(id) ON DELETE CASCADE, filename VARCHAR(255) NOT NULL, mime_type VARCHAR(255) NOT NULL DEFAULT 'application/octet-stream', size_bytes INTEGER NOT NULL DEFAULT 0, storage_path VARCHAR(1024) NOT NULL, dms_file_id UUID, content_id VARCHAR(255), tenant_id UUID NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now() ); CREATE INDEX IF NOT EXISTS ix_mail_attachments_mail ON mail_attachments(mail_id); CREATE INDEX IF NOT EXISTS ix_mail_attachments_tenant ON mail_attachments(tenant_id); CREATE TABLE IF NOT EXISTS mail_labels ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), name VARCHAR(100) NOT NULL, color VARCHAR(20) NOT NULL DEFAULT '#808080', user_id UUID NOT NULL, tenant_id UUID NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now() ); CREATE INDEX IF NOT EXISTS ix_mail_labels_tenant ON mail_labels(tenant_id); CREATE TABLE IF NOT EXISTS mail_label_assignments ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), mail_id UUID NOT NULL REFERENCES mails(id) ON DELETE CASCADE, label_id UUID NOT NULL REFERENCES mail_labels(id) ON DELETE CASCADE, tenant_id UUID NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), CONSTRAINT uq_mail_label_assignment UNIQUE (mail_id, label_id) ); CREATE INDEX IF NOT EXISTS ix_mail_label_assign_mail ON mail_label_assignments(mail_id); CREATE INDEX IF NOT EXISTS ix_mail_label_assign_label ON mail_label_assignments(label_id); CREATE TABLE IF NOT EXISTS mail_rules ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), account_id UUID REFERENCES mail_accounts(id) ON DELETE CASCADE, name VARCHAR(255) NOT NULL, priority INTEGER NOT NULL DEFAULT 0, is_active BOOLEAN NOT NULL DEFAULT TRUE, conditions TEXT NOT NULL DEFAULT '{}', actions TEXT NOT NULL DEFAULT '{}', tenant_id UUID NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now() ); CREATE INDEX IF NOT EXISTS ix_mail_rules_account ON mail_rules(account_id); CREATE INDEX IF NOT EXISTS ix_mail_rules_tenant ON mail_rules(tenant_id); CREATE TABLE IF NOT EXISTS mail_templates ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), name VARCHAR(255) NOT NULL, subject VARCHAR(512) NOT NULL DEFAULT '', body_html TEXT NOT NULL DEFAULT '', user_id UUID NOT NULL, tenant_id UUID NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now() ); CREATE INDEX IF NOT EXISTS ix_mail_templates_tenant ON mail_templates(tenant_id); CREATE TABLE IF NOT EXISTS mail_signatures ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id UUID NOT NULL, account_id UUID REFERENCES mail_accounts(id) ON DELETE CASCADE, name VARCHAR(255) NOT NULL, body_html TEXT NOT NULL DEFAULT '', is_default BOOLEAN NOT NULL DEFAULT FALSE, tenant_id UUID NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now() ); CREATE INDEX IF NOT EXISTS ix_mail_signatures_tenant ON mail_signatures(tenant_id); CREATE TABLE IF NOT EXISTS vacation_sent_log ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), account_id UUID NOT NULL REFERENCES mail_accounts(id) ON DELETE CASCADE, sender_address VARCHAR(255) NOT NULL, sent_at TIMESTAMPTZ NOT NULL, tenant_id UUID NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now() ); CREATE INDEX IF NOT EXISTS ix_vacation_sent_log_account ON vacation_sent_log(account_id); CREATE INDEX IF NOT EXISTS ix_vacation_sent_log_tenant ON vacation_sent_log(tenant_id); CREATE TABLE IF NOT EXISTS mail_seen_by ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), mail_id UUID NOT NULL REFERENCES mails(id) ON DELETE CASCADE, user_id UUID NOT NULL, seen_at TIMESTAMPTZ NOT NULL, tenant_id UUID NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), CONSTRAINT uq_mail_seen_by UNIQUE (mail_id, user_id) ); CREATE INDEX IF NOT EXISTS ix_mail_seen_by_mail ON mail_seen_by(mail_id); CREATE TABLE IF NOT EXISTS mail_account_delegates ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), account_id UUID NOT NULL REFERENCES mail_accounts(id) ON DELETE CASCADE, delegate_user_id UUID NOT NULL, access_level VARCHAR(20) NOT NULL DEFAULT 'read', tenant_id UUID NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), CONSTRAINT uq_mail_delegate UNIQUE (account_id, delegate_user_id) ); CREATE INDEX IF NOT EXISTS ix_mail_delegates_account ON mail_account_delegates(account_id); CREATE TABLE IF NOT EXISTS mail_account_send_permissions ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), account_id UUID NOT NULL REFERENCES mail_accounts(id) ON DELETE CASCADE, user_id UUID NOT NULL, tenant_id UUID NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), CONSTRAINT uq_mail_send_perm UNIQUE (account_id, user_id) ); CREATE INDEX IF NOT EXISTS ix_mail_send_perms_account ON mail_account_send_permissions(account_id); CREATE TABLE IF NOT EXISTS pgp_keys ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id UUID NOT NULL, key_id VARCHAR(255) NOT NULL, encrypted_private_key TEXT NOT NULL, public_key_armored TEXT NOT NULL, tenant_id UUID NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now() ); CREATE INDEX IF NOT EXISTS ix_pgp_keys_user ON pgp_keys(user_id); CREATE TABLE IF NOT EXISTS contact_pgp_keys ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), contact_id UUID NOT NULL, public_key_armored TEXT NOT NULL, key_id VARCHAR(255) NOT NULL DEFAULT '', tenant_id UUID NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now() ); CREATE INDEX IF NOT EXISTS ix_contact_pgp_keys_contact ON contact_pgp_keys(contact_id);