195 lines
7.7 KiB
PL/PgSQL
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';
|