-- +goose Up -- Tenant AI / BYOK providers (OpenAI-compatible). Secrets: AES-GCM enc:v1:; never return plaintext. -- Analytics field (coordinate): ai_provider_mode on jobs/products = -- 'internal' | 'popular:' | 'custom' CREATE TABLE ai_providers ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), company_id UUID NOT NULL REFERENCES companies(id) ON DELETE CASCADE, mode TEXT NOT NULL DEFAULT 'internal' CHECK (mode IN ('internal', 'popular', 'custom')), popular_name TEXT NOT NULL DEFAULT '', base_url TEXT NOT NULL DEFAULT '', model TEXT NOT NULL DEFAULT '', api_key_enc TEXT NOT NULL DEFAULT '', api_key_last4 TEXT NOT NULL DEFAULT '', is_enabled BOOLEAN NOT NULL DEFAULT false, last_test_at TIMESTAMPTZ, last_test_status TEXT, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), UNIQUE (company_id) ); CREATE INDEX ai_providers_company_enabled_idx ON ai_providers (company_id, is_enabled); ALTER TABLE processing_jobs ADD COLUMN IF NOT EXISTS ai_provider_mode TEXT NOT NULL DEFAULT 'internal'; ALTER TABLE processed_products ADD COLUMN IF NOT EXISTS ai_provider_mode TEXT NOT NULL DEFAULT 'internal'; CREATE INDEX IF NOT EXISTS processed_products_ai_provider_mode_idx ON processed_products (ai_provider_mode); CREATE INDEX IF NOT EXISTS processed_products_company_ai_provider_mode_idx ON processed_products (company_id, ai_provider_mode); CREATE INDEX IF NOT EXISTS processing_jobs_ai_provider_mode_idx ON processing_jobs (ai_provider_mode); -- +goose Down DROP INDEX IF EXISTS processing_jobs_ai_provider_mode_idx; DROP INDEX IF EXISTS processed_products_company_ai_provider_mode_idx; DROP INDEX IF EXISTS processed_products_ai_provider_mode_idx; ALTER TABLE processed_products DROP COLUMN IF EXISTS ai_provider_mode; ALTER TABLE processing_jobs DROP COLUMN IF EXISTS ai_provider_mode; DROP TABLE IF EXISTS ai_providers;