diff --git a/lib/reports/full-archive-export.ts b/lib/reports/full-archive-export.ts index 1e6d73f0..046931e7 100644 --- a/lib/reports/full-archive-export.ts +++ b/lib/reports/full-archive-export.ts @@ -994,6 +994,7 @@ export const ARCHIVE_EXCLUDED_TABLES: Record = { graph_transaction_counterparties: 'derived AI context graph, regenerable', idempotency_keys: 'infrastructure', inbox_rate_counters: 'infrastructure', + mail_connections: 'mailbox OAuth grants (live refresh tokens), not portable', mcp_tasks: 'MCP task handles: transient tool-call state with a 1-hour TTL', metered_events: 'billing telemetry', notification_log: 'notification dedup log', diff --git a/supabase/migrations/20260807090000_mail_connections.sql b/supabase/migrations/20260807090000_mail_connections.sql new file mode 100644 index 00000000..b4af5ced --- /dev/null +++ b/supabase/migrations/20260807090000_mail_connections.sql @@ -0,0 +1,46 @@ +-- pg-test: skip (reconciliation only: this DDL was already applied to prod +-- out-of-band on 2026-08-07 and recovered byte-identical from +-- supabase_migrations.schema_migrations; the file exists so Supabase +-- branching stops failing on the orphaned version and will never re-run. +-- The mail-hunt feature branch that owns this schema must ship the real +-- pg-real coverage for the trigger and RLS.) +CREATE TABLE IF NOT EXISTS public.mail_connections ( + id uuid PRIMARY KEY DEFAULT gen_random_uuid(), + company_id uuid NOT NULL REFERENCES public.companies(id) ON DELETE CASCADE, + provider text NOT NULL CHECK (provider IN ('gmail', 'microsoft')), + email_address text NOT NULL, + connected_by uuid REFERENCES auth.users(id) ON DELETE SET NULL, + encrypted_refresh_token text NOT NULL, + encrypted_access_token text, + access_token_expires_at timestamptz, + scopes text[] NOT NULL DEFAULT '{}', + scope_label text, + backfill_from date, + backfill_completed_at timestamptz, + status text NOT NULL DEFAULT 'active' + CHECK (status IN ('active', 'needs_reconsent', 'revoked')), + last_error_code text, + last_error_at timestamptz, + last_searched_at timestamptz, + created_at timestamptz NOT NULL DEFAULT now(), + updated_at timestamptz NOT NULL DEFAULT now() +); + +CREATE UNIQUE INDEX IF NOT EXISTS idx_mail_connections_identity + ON public.mail_connections (company_id, provider, email_address); + +CREATE INDEX IF NOT EXISTS idx_mail_connections_company_active + ON public.mail_connections (company_id) + WHERE status = 'active'; + +ALTER TABLE public.mail_connections ENABLE ROW LEVEL SECURITY; + +DROP TRIGGER IF EXISTS mail_connections_updated_at ON public.mail_connections; +CREATE TRIGGER mail_connections_updated_at + BEFORE UPDATE ON public.mail_connections + FOR EACH ROW EXECUTE FUNCTION public.update_updated_at_column(); + +COMMENT ON TABLE public.mail_connections IS + 'Read-only mailbox grants for receipt hunting. Service-role only: rows carry live refresh tokens.'; + +NOTIFY pgrst, 'reload schema'; diff --git a/supabase/migrations/20260807090100_inbox_mail_hunt_source.sql b/supabase/migrations/20260807090100_inbox_mail_hunt_source.sql new file mode 100644 index 00000000..aa958c43 --- /dev/null +++ b/supabase/migrations/20260807090100_inbox_mail_hunt_source.sql @@ -0,0 +1,28 @@ +ALTER TABLE public.invoice_inbox_items + DROP CONSTRAINT IF EXISTS invoice_inbox_items_source_check; + +ALTER TABLE public.invoice_inbox_items + ADD CONSTRAINT invoice_inbox_items_source_check + CHECK (source IN ('email', 'upload', 'whatsapp', 'mail_hunt')) NOT VALID; + +ALTER TABLE public.invoice_inbox_items + VALIDATE CONSTRAINT invoice_inbox_items_source_check; + +ALTER TABLE public.document_attachments + DROP CONSTRAINT IF EXISTS document_attachments_upload_source_check; + +ALTER TABLE public.document_attachments + ADD CONSTRAINT document_attachments_upload_source_check + CHECK (upload_source IN ( + 'camera', 'file_upload', 'email', 'e_invoice', 'scan', 'api', 'system', + 'whatsapp', 'mail_hunt' + )) NOT VALID; + +ALTER TABLE public.document_attachments + VALIDATE CONSTRAINT document_attachments_upload_source_check; + +CREATE UNIQUE INDEX IF NOT EXISTS idx_invoice_inbox_mail_message_unique + ON public.invoice_inbox_items (company_id, ((channel_context->>'mail_message_id'))) + WHERE source = 'mail_hunt'; + +NOTIFY pgrst, 'reload schema';