Files
descrybe/apps/api/sql/schema/012_integrations_email.sql
T

68 lines
2.5 KiB
SQL
Raw Normal View History

-- +goose Up
-- Tenant email provider + unsubscribes + send log (Marketing suite).
-- Secrets: AES-GCM ciphertext (enc:v1:...) of JSON {api_key,smtp_*}; never log plaintext.
-- Prefer APP_ENCRYPTION_KEY for DeriveKey.
CREATE TABLE email_providers (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
company_id UUID NOT NULL REFERENCES companies(id) ON DELETE CASCADE,
provider_type TEXT NOT NULL DEFAULT 'smtp'
CHECK (provider_type IN ('smtp', 'resend', 'sendgrid')),
from_email TEXT NOT NULL DEFAULT '',
from_name TEXT NOT NULL DEFAULT '',
secrets_enc TEXT NOT NULL DEFAULT '',
config JSONB NOT NULL DEFAULT '{}'::jsonb,
status TEXT NOT NULL DEFAULT 'unverified'
CHECK (status IN ('unverified', 'verified', 'error')),
verified_at TIMESTAMPTZ,
last_error TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
UNIQUE (company_id)
);
CREATE INDEX email_providers_company_status_idx ON email_providers (company_id, status);
CREATE TABLE email_unsubscribes (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
company_id UUID NOT NULL REFERENCES companies(id) ON DELETE CASCADE,
email TEXT NOT NULL,
email_hash TEXT NOT NULL,
token TEXT NOT NULL UNIQUE,
reason TEXT,
unsubscribed_at TIMESTAMPTZ NOT NULL DEFAULT now(),
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
UNIQUE (company_id, email_hash)
);
CREATE INDEX email_unsubscribes_company_idx ON email_unsubscribes (company_id);
CREATE TABLE email_sends (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
company_id UUID NOT NULL REFERENCES companies(id) ON DELETE CASCADE,
campaign_id UUID REFERENCES email_campaigns(id) ON DELETE SET NULL,
version_id UUID REFERENCES email_campaign_versions(id) ON DELETE SET NULL,
recipient_email TEXT NOT NULL,
recipient_hash TEXT NOT NULL,
kind TEXT NOT NULL DEFAULT 'blast'
CHECK (kind IN ('test', 'blast')),
status TEXT NOT NULL DEFAULT 'queued'
CHECK (status IN ('queued', 'sent', 'failed', 'skipped', 'unsubscribed')),
provider_message_id TEXT,
error TEXT,
sent_at TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX email_sends_company_created_idx ON email_sends (company_id, created_at DESC);
CREATE INDEX email_sends_campaign_idx ON email_sends (campaign_id);
CREATE UNIQUE INDEX email_sends_blast_idempotent_idx
ON email_sends (campaign_id, recipient_hash)
WHERE kind = 'blast' AND status IN ('queued', 'sent');
-- +goose Down
DROP TABLE IF EXISTS email_sends;
DROP TABLE IF EXISTS email_unsubscribes;
DROP TABLE IF EXISTS email_providers;