ultisuite-backend/migrations/000002_mail.up.sql

195 lines
7.7 KiB
PL/PgSQL

CREATE TABLE mail_accounts (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
name TEXT NOT NULL DEFAULT '',
email TEXT NOT NULL,
provider TEXT NOT NULL DEFAULT 'imap',
imap_host TEXT NOT NULL DEFAULT '',
imap_port INT NOT NULL DEFAULT 993,
imap_tls BOOLEAN NOT NULL DEFAULT true,
smtp_host TEXT NOT NULL DEFAULT '',
smtp_port INT NOT NULL DEFAULT 587,
smtp_tls BOOLEAN NOT NULL DEFAULT true,
credentials BYTEA,
last_sync_at TIMESTAMPTZ,
sync_state JSONB NOT NULL DEFAULT '{}',
is_active BOOLEAN NOT NULL DEFAULT true,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_mail_accounts_user ON mail_accounts(user_id);
CREATE TABLE mail_identities (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
account_id UUID NOT NULL REFERENCES mail_accounts(id) ON DELETE CASCADE,
email TEXT NOT NULL,
name TEXT NOT NULL DEFAULT '',
is_default BOOLEAN NOT NULL DEFAULT false,
signature_html TEXT NOT NULL DEFAULT '',
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_mail_identities_account ON mail_identities(account_id);
CREATE TABLE mail_folders (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
account_id UUID NOT NULL REFERENCES mail_accounts(id) ON DELETE CASCADE,
name TEXT NOT NULL,
remote_name TEXT NOT NULL,
folder_type TEXT NOT NULL DEFAULT 'custom',
uidvalidity BIGINT NOT NULL DEFAULT 0,
highest_modseq BIGINT NOT NULL DEFAULT 0,
message_count INT NOT NULL DEFAULT 0,
unread_count INT NOT NULL DEFAULT 0,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
UNIQUE(account_id, remote_name)
);
CREATE INDEX idx_mail_folders_account ON mail_folders(account_id);
CREATE TABLE messages (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
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 TEXT NOT NULL DEFAULT '',
thread_id UUID,
uid BIGINT NOT NULL DEFAULT 0,
subject TEXT NOT NULL DEFAULT '',
from_addr JSONB NOT NULL DEFAULT '[]',
to_addrs JSONB NOT NULL DEFAULT '[]',
cc_addrs JSONB NOT NULL DEFAULT '[]',
bcc_addrs JSONB NOT NULL DEFAULT '[]',
reply_to JSONB NOT NULL DEFAULT '[]',
date TIMESTAMPTZ NOT NULL DEFAULT NOW(),
snippet TEXT NOT NULL DEFAULT '',
body_text TEXT NOT NULL DEFAULT '',
body_html TEXT NOT NULL DEFAULT '',
flags TEXT[] NOT NULL DEFAULT '{}',
labels TEXT[] NOT NULL DEFAULT '{}',
has_attachments BOOLEAN NOT NULL DEFAULT false,
raw_size INT NOT NULL DEFAULT 0,
in_reply_to TEXT NOT NULL DEFAULT '',
references_header TEXT[] NOT NULL DEFAULT '{}',
search_vector tsvector,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_messages_account_folder ON messages(account_id, folder_id);
CREATE INDEX idx_messages_thread ON messages(thread_id);
CREATE INDEX idx_messages_date ON messages(date DESC);
CREATE INDEX idx_messages_message_id ON messages(message_id);
CREATE INDEX idx_messages_flags ON messages USING GIN(flags);
CREATE INDEX idx_messages_labels ON messages USING GIN(labels);
CREATE INDEX idx_messages_search ON messages USING GIN(search_vector);
CREATE UNIQUE INDEX idx_messages_uid ON messages(folder_id, uid);
-- Auto-update search_vector on insert/update
CREATE FUNCTION messages_search_vector_update() RETURNS trigger AS $$
BEGIN
NEW.search_vector :=
setweight(to_tsvector('simple', COALESCE(NEW.subject, '')), 'A') ||
setweight(to_tsvector('simple', COALESCE(NEW.from_addr::text, '')), 'B') ||
setweight(to_tsvector('simple', COALESCE(NEW.to_addrs::text, '')), 'B') ||
setweight(to_tsvector('simple', COALESCE(NEW.body_text, '')), 'C');
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER messages_search_trigger
BEFORE INSERT OR UPDATE OF subject, from_addr, to_addrs, body_text
ON messages
FOR EACH ROW
EXECUTE FUNCTION messages_search_vector_update();
CREATE TABLE attachments (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
message_id UUID NOT NULL REFERENCES messages(id) ON DELETE CASCADE,
filename TEXT NOT NULL DEFAULT '',
content_type TEXT NOT NULL DEFAULT 'application/octet-stream',
size BIGINT NOT NULL DEFAULT 0,
s3_bucket TEXT NOT NULL DEFAULT 'mail-attachments',
s3_key TEXT NOT NULL DEFAULT '',
content_id TEXT NOT NULL DEFAULT '',
is_inline BOOLEAN NOT NULL DEFAULT false,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_attachments_message ON attachments(message_id);
CREATE TABLE mail_rules (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
account_id UUID REFERENCES mail_accounts(id) ON DELETE CASCADE,
name TEXT NOT NULL DEFAULT '',
priority INT NOT NULL DEFAULT 0,
is_active BOOLEAN NOT NULL DEFAULT true,
conditions JSONB NOT NULL DEFAULT '[]',
actions JSONB NOT NULL DEFAULT '[]',
llm_config JSONB,
match_count BIGINT NOT NULL DEFAULT 0,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_mail_rules_user ON mail_rules(user_id);
CREATE INDEX idx_mail_rules_priority ON mail_rules(user_id, priority);
CREATE TABLE webhook_templates (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
name TEXT NOT NULL,
url TEXT NOT NULL,
method TEXT NOT NULL DEFAULT 'POST',
headers JSONB NOT NULL DEFAULT '{}',
body_template TEXT NOT NULL DEFAULT '',
is_active BOOLEAN NOT NULL DEFAULT true,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_webhook_templates_user ON webhook_templates(user_id);
CREATE TABLE webhook_logs (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
template_id UUID NOT NULL REFERENCES webhook_templates(id) ON DELETE CASCADE,
message_id UUID REFERENCES messages(id) ON DELETE SET NULL,
status_code INT,
response_body TEXT NOT NULL DEFAULT '',
error TEXT NOT NULL DEFAULT '',
duration_ms INT NOT NULL DEFAULT 0,
executed_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_webhook_logs_template ON webhook_logs(template_id);
CREATE INDEX idx_webhook_logs_executed ON webhook_logs(executed_at DESC);
CREATE TABLE outbox (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
account_id UUID NOT NULL REFERENCES mail_accounts(id) ON DELETE CASCADE,
identity_id UUID REFERENCES mail_identities(id) ON DELETE SET NULL,
to_addrs JSONB NOT NULL DEFAULT '[]',
cc_addrs JSONB NOT NULL DEFAULT '[]',
bcc_addrs JSONB NOT NULL DEFAULT '[]',
subject TEXT NOT NULL DEFAULT '',
body_text TEXT NOT NULL DEFAULT '',
body_html TEXT NOT NULL DEFAULT '',
in_reply_to TEXT NOT NULL DEFAULT '',
references_header TEXT[] NOT NULL DEFAULT '{}',
attachments JSONB NOT NULL DEFAULT '[]',
scheduled_at TIMESTAMPTZ,
sent_at TIMESTAMPTZ,
status TEXT NOT NULL DEFAULT 'draft',
error TEXT NOT NULL DEFAULT '',
retry_count INT NOT NULL DEFAULT 0,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_outbox_user ON outbox(user_id);
CREATE INDEX idx_outbox_status ON outbox(status) WHERE status IN ('queued', 'sending');
CREATE INDEX idx_outbox_scheduled ON outbox(scheduled_at) WHERE scheduled_at IS NOT NULL AND status = 'queued';