-- Migration 000053: Create missing model tables and add missing columns -- These tables exist as Go models but were never created by SQL migrations -- or GORM AutoMigrate (which was not called in the Bootstrap path). -- ============ conversation_labels ============ CREATE TABLE IF NOT EXISTS conversation_labels ( id BIGSERIAL PRIMARY KEY, conversation_id BIGINT NOT NULL, tag_id BIGINT NOT NULL, account_id BIGINT NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE INDEX IF NOT EXISTS idx_conv_labels_conv_id ON conversation_labels (conversation_id); CREATE INDEX IF NOT EXISTS idx_conv_labels_tag_id ON conversation_labels (tag_id); CREATE INDEX IF NOT EXISTS idx_conv_labels_account_id ON conversation_labels (account_id); CREATE UNIQUE INDEX IF NOT EXISTS idx_conv_label_tag ON conversation_labels (conversation_id, tag_id); -- ============ contact_labels (may already exist from migration) ============ CREATE TABLE IF NOT EXISTS contact_labels ( contact_id BIGINT NOT NULL, tag_id BIGINT NOT NULL, account_id BIGINT NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), PRIMARY KEY (contact_id, tag_id) ); CREATE INDEX IF NOT EXISTS idx_contact_labels_contact_id ON contact_labels (contact_id); CREATE INDEX IF NOT EXISTS idx_contact_labels_tag_id ON contact_labels (tag_id); CREATE INDEX IF NOT EXISTS idx_contact_labels_account_id ON contact_labels (account_id); -- ============ tags (may already exist) ============ CREATE TABLE IF NOT EXISTS tags ( id BIGSERIAL PRIMARY KEY, account_id BIGINT NOT NULL, name VARCHAR(255) NOT NULL, color VARCHAR(50) NOT NULL DEFAULT '#1f93ff', description TEXT, show_on_sidebar BOOLEAN, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), deleted_at TIMESTAMPTZ ); CREATE UNIQUE INDEX IF NOT EXISTS idx_tag_account_name ON tags (account_id, name) WHERE deleted_at IS NULL; CREATE INDEX IF NOT EXISTS idx_tags_deleted_at ON tags (deleted_at); -- ============ agent_bots ============ CREATE TABLE IF NOT EXISTS agent_bots ( id BIGSERIAL PRIMARY KEY, account_id BIGINT, name VARCHAR(255) NOT NULL, description VARCHAR(512), avatar_url VARCHAR(512), outgoing_url VARCHAR(1024), bot_type VARCHAR(50) DEFAULT 'webhook', secret VARCHAR(128), access_token VARCHAR(128), config JSONB, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE INDEX IF NOT EXISTS idx_agent_bots_account_id ON agent_bots (account_id); CREATE UNIQUE INDEX IF NOT EXISTS idx_agent_bots_secret ON agent_bots (secret) WHERE secret != ''; CREATE UNIQUE INDEX IF NOT EXISTS idx_agent_bots_access_token ON agent_bots (access_token) WHERE access_token != ''; -- ============ agent_bot_inboxes ============ CREATE TABLE IF NOT EXISTS agent_bot_inboxes ( id BIGSERIAL PRIMARY KEY, agent_bot_id BIGINT NOT NULL, inbox_id BIGINT NOT NULL, account_id BIGINT, status INTEGER DEFAULT 0, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE INDEX IF NOT EXISTS idx_agent_bot_inboxes_agent_bot_id ON agent_bot_inboxes (agent_bot_id); CREATE INDEX IF NOT EXISTS idx_agent_bot_inboxes_inbox_id ON agent_bot_inboxes (inbox_id); CREATE INDEX IF NOT EXISTS idx_agent_bot_inboxes_account_id ON agent_bot_inboxes (account_id); -- ============ agent_bot_presence_events ============ CREATE TABLE IF NOT EXISTS agent_bot_presence_events ( id BIGSERIAL PRIMARY KEY, agent_bot_id BIGINT NOT NULL, inbox_id BIGINT NOT NULL, account_id BIGINT NOT NULL, presence_type VARCHAR(100) NOT NULL, metadata JSONB, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE INDEX IF NOT EXISTS idx_agent_bot_presence_events_agent_bot_id ON agent_bot_presence_events (agent_bot_id); CREATE INDEX IF NOT EXISTS idx_agent_bot_presence_events_inbox_id ON agent_bot_presence_events (inbox_id); CREATE INDEX IF NOT EXISTS idx_agent_bot_presence_events_account_id ON agent_bot_presence_events (account_id); -- ============ custom_attribute_definitions ============ CREATE TABLE IF NOT EXISTS custom_attribute_definitions ( id BIGSERIAL PRIMARY KEY, account_id BIGINT NOT NULL, attribute_name VARCHAR(255) NOT NULL, attribute_display_name VARCHAR(255) NOT NULL, attribute_type VARCHAR(50) NOT NULL DEFAULT 'text', attribute_model VARCHAR(50) NOT NULL, default_value JSONB, attribute_values JSONB, regex_pattern VARCHAR(255), regex_cue VARCHAR(255), description TEXT, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), deleted_at TIMESTAMPTZ ); CREATE INDEX IF NOT EXISTS idx_custom_attr_defs_account_id ON custom_attribute_definitions (account_id); CREATE UNIQUE INDEX IF NOT EXISTS idx_attr_name_account_model ON custom_attribute_definitions (attribute_name, account_id, attribute_model) WHERE deleted_at IS NULL; CREATE INDEX IF NOT EXISTS idx_custom_attr_defs_deleted_at ON custom_attribute_definitions (deleted_at); -- ============ custom_filters ============ CREATE TABLE IF NOT EXISTS custom_filters ( id BIGSERIAL PRIMARY KEY, account_id BIGINT NOT NULL, name VARCHAR(255) NOT NULL, filter_type VARCHAR(50) NOT NULL, query JSONB NOT NULL, created_by_id BIGINT NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), deleted_at TIMESTAMPTZ ); CREATE INDEX IF NOT EXISTS idx_custom_filters_account_id ON custom_filters (account_id); CREATE INDEX IF NOT EXISTS idx_custom_filters_filter_type ON custom_filters (filter_type); CREATE INDEX IF NOT EXISTS idx_custom_filters_created_by_id ON custom_filters (created_by_id); CREATE INDEX IF NOT EXISTS idx_custom_filters_deleted_at ON custom_filters (deleted_at); -- ============ conversation_participants ============ CREATE TABLE IF NOT EXISTS conversation_participants ( id BIGSERIAL PRIMARY KEY, account_id BIGINT NOT NULL, conversation_id BIGINT NOT NULL, user_id BIGINT NOT NULL, role VARCHAR(50) DEFAULT 'participant', created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), deleted_at TIMESTAMPTZ ); CREATE INDEX IF NOT EXISTS idx_conv_participants_account_id ON conversation_participants (account_id); CREATE INDEX IF NOT EXISTS idx_conv_participants_conversation_id ON conversation_participants (conversation_id); CREATE INDEX IF NOT EXISTS idx_conv_participants_user_id ON conversation_participants (user_id); CREATE UNIQUE INDEX IF NOT EXISTS idx_conv_participants_user_conv ON conversation_participants (user_id, conversation_id) WHERE deleted_at IS NULL; CREATE INDEX IF NOT EXISTS idx_conv_participants_deleted_at ON conversation_participants (deleted_at); -- ============ notes ============ CREATE TABLE IF NOT EXISTS notes ( id BIGSERIAL PRIMARY KEY, content TEXT NOT NULL, account_id BIGINT NOT NULL, contact_id BIGINT NOT NULL, user_id BIGINT, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), deleted_at TIMESTAMPTZ ); CREATE INDEX IF NOT EXISTS idx_notes_account_id ON notes (account_id); CREATE INDEX IF NOT EXISTS idx_notes_contact_id ON notes (contact_id); CREATE INDEX IF NOT EXISTS idx_notes_user_id ON notes (user_id); CREATE INDEX IF NOT EXISTS idx_notes_deleted_at ON notes (deleted_at); -- ============ contact_notes ============ CREATE TABLE IF NOT EXISTS contact_notes ( id BIGSERIAL PRIMARY KEY, contact_id BIGINT NOT NULL, user_id BIGINT NOT NULL, content TEXT, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE INDEX IF NOT EXISTS idx_contact_notes_contact_id ON contact_notes (contact_id); -- ============ company_notes (may already exist) ============ CREATE TABLE IF NOT EXISTS company_notes ( id BIGSERIAL PRIMARY KEY, company_id BIGINT NOT NULL, user_id BIGINT NOT NULL, content TEXT, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE INDEX IF NOT EXISTS idx_company_notes_company_id ON company_notes (company_id); -- ============ csat_templates ============ CREATE TABLE IF NOT EXISTS csat_templates ( id BIGSERIAL PRIMARY KEY, inbox_id BIGINT NOT NULL, message TEXT, status VARCHAR(50) DEFAULT 'pending', created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), deleted_at TIMESTAMPTZ ); CREATE INDEX IF NOT EXISTS idx_csat_templates_inbox_id ON csat_templates (inbox_id); CREATE INDEX IF NOT EXISTS idx_csat_templates_deleted_at ON csat_templates (deleted_at); -- ============ delivery_statuses ============ CREATE TABLE IF NOT EXISTS delivery_statuses ( id BIGSERIAL PRIMARY KEY, message_id BIGINT NOT NULL, inbox_id BIGINT NOT NULL, contact_id BIGINT NOT NULL, status VARCHAR(50) NOT NULL DEFAULT 'sent', delivered_at TIMESTAMPTZ, read_at TIMESTAMPTZ, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), deleted_at TIMESTAMPTZ ); CREATE INDEX IF NOT EXISTS idx_delivery_statuses_message_id ON delivery_statuses (message_id); CREATE INDEX IF NOT EXISTS idx_delivery_statuses_inbox_id ON delivery_statuses (inbox_id); CREATE INDEX IF NOT EXISTS idx_delivery_statuses_contact_id ON delivery_statuses (contact_id); CREATE INDEX IF NOT EXISTS idx_delivery_statuses_deleted_at ON delivery_statuses (deleted_at); -- ============ draft_messages ============ CREATE TABLE IF NOT EXISTS draft_messages ( id BIGSERIAL PRIMARY KEY, conversation_id BIGINT NOT NULL, user_id BIGINT NOT NULL, content TEXT, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), deleted_at TIMESTAMPTZ ); CREATE INDEX IF NOT EXISTS idx_draft_messages_conversation_id ON draft_messages (conversation_id); CREATE INDEX IF NOT EXISTS idx_draft_messages_user_id ON draft_messages (user_id); CREATE INDEX IF NOT EXISTS idx_draft_messages_deleted_at ON draft_messages (deleted_at); -- ============ email_channel_migrations ============ CREATE TABLE IF NOT EXISTS email_channel_migrations ( id BIGSERIAL PRIMARY KEY, account_id BIGINT NOT NULL, inbox_id BIGINT NOT NULL, target_inbox_id BIGINT NOT NULL, migration_status VARCHAR(50) NOT NULL DEFAULT 'pending', created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), deleted_at TIMESTAMPTZ ); CREATE INDEX IF NOT EXISTS idx_email_channel_migrations_account_id ON email_channel_migrations (account_id); CREATE INDEX IF NOT EXISTS idx_email_channel_migrations_deleted_at ON email_channel_migrations (deleted_at); -- ============ inbox_limits ============ CREATE TABLE IF NOT EXISTS inbox_limits ( id BIGSERIAL PRIMARY KEY, inbox_id BIGINT NOT NULL, type VARCHAR(100) NOT NULL, value INTEGER NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), deleted_at TIMESTAMPTZ ); CREATE INDEX IF NOT EXISTS idx_inbox_limits_inbox_id ON inbox_limits (inbox_id); CREATE INDEX IF NOT EXISTS idx_inbox_limits_deleted_at ON inbox_limits (deleted_at); -- ============ notification_subscriptions ============ CREATE TABLE IF NOT EXISTS notification_subscriptions ( id BIGSERIAL PRIMARY KEY, identifier TEXT NOT NULL, subscription_attributes JSONB NOT NULL, subscription_type INTEGER NOT NULL, user_id BIGINT NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE UNIQUE INDEX IF NOT EXISTS idx_notification_subscriptions_on_identifier ON notification_subscriptions (identifier); CREATE INDEX IF NOT EXISTS idx_notification_subscriptions_on_user_id ON notification_subscriptions (user_id); -- ============ sso_sessions ============ CREATE TABLE IF NOT EXISTS sso_sessions ( id BIGSERIAL PRIMARY KEY, user_id BIGINT NOT NULL, session_id VARCHAR(255) NOT NULL, provider VARCHAR(50) NOT NULL, idp_entity_id VARCHAR(512), name_id VARCHAR(512), account_id BIGINT NOT NULL, role VARCHAR(50), access_token VARCHAR(512), refresh_token VARCHAR(512), created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), expires_at TIMESTAMPTZ NOT NULL, last_seen_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), ended_at TIMESTAMPTZ, status VARCHAR(20) NOT NULL DEFAULT 'active', deleted_at TIMESTAMPTZ ); CREATE INDEX IF NOT EXISTS idx_sso_sessions_user_id ON sso_sessions (user_id); CREATE UNIQUE INDEX IF NOT EXISTS idx_sso_sessions_session_id ON sso_sessions (session_id); CREATE INDEX IF NOT EXISTS idx_sso_sessions_account_id ON sso_sessions (account_id); CREATE INDEX IF NOT EXISTS idx_sso_sessions_expires_at ON sso_sessions (expires_at); CREATE INDEX IF NOT EXISTS idx_sso_sessions_deleted_at ON sso_sessions (deleted_at); -- ============ whatsapp_calls ============ CREATE TABLE IF NOT EXISTS whatsapp_calls ( id BIGSERIAL PRIMARY KEY, inbox_id BIGINT NOT NULL, conversation_id BIGINT NOT NULL, call_id VARCHAR(255) NOT NULL, call_status VARCHAR(50) NOT NULL, duration INTEGER, caller_number VARCHAR(50), created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE INDEX IF NOT EXISTS idx_whatsapp_calls_inbox_id ON whatsapp_calls (inbox_id); CREATE INDEX IF NOT EXISTS idx_whatsapp_calls_conversation_id ON whatsapp_calls (conversation_id); CREATE UNIQUE INDEX IF NOT EXISTS idx_whatsapp_calls_call_id ON whatsapp_calls (call_id); CREATE INDEX IF NOT EXISTS idx_whatsapp_calls_call_status ON whatsapp_calls (call_status); CREATE INDEX IF NOT EXISTS idx_whatsapp_calls_created_at ON whatsapp_calls (created_at); -- ============ assignment_policies (may already exist from AutoMigrate) ============ CREATE TABLE IF NOT EXISTS assignment_policies ( id BIGSERIAL PRIMARY KEY, account_id BIGINT NOT NULL, inbox_id BIGINT, policy_type VARCHAR(100) NOT NULL DEFAULT 'round_robin', enabled BOOLEAN NOT NULL DEFAULT true, config JSONB, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), deleted_at TIMESTAMPTZ ); CREATE INDEX IF NOT EXISTS idx_assignment_policies_account_id ON assignment_policies (account_id); CREATE INDEX IF NOT EXISTS idx_assignment_policies_inbox_id ON assignment_policies (inbox_id); CREATE INDEX IF NOT EXISTS idx_assignment_policies_deleted_at ON assignment_policies (deleted_at); -- ============ assignment_policies_v2 ============ CREATE TABLE IF NOT EXISTS assignment_policies_v2 ( id BIGSERIAL PRIMARY KEY, account_id BIGINT NOT NULL, inbox_id BIGINT, policy_type VARCHAR(100) NOT NULL DEFAULT 'round_robin', enabled BOOLEAN NOT NULL DEFAULT true, config JSONB, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), deleted_at TIMESTAMPTZ ); CREATE INDEX IF NOT EXISTS idx_assignment_policies_v2_account_id ON assignment_policies_v2 (account_id); CREATE INDEX IF NOT EXISTS idx_assignment_policies_v2_inbox_id ON assignment_policies_v2 (inbox_id); CREATE INDEX IF NOT EXISTS idx_assignment_policies_v2_deleted_at ON assignment_policies_v2 (deleted_at); -- ============ automation_actions ============ CREATE TABLE IF NOT EXISTS automation_actions ( id BIGSERIAL PRIMARY KEY, account_id BIGINT NOT NULL, action_type VARCHAR(100) NOT NULL, config JSONB, executed_at TIMESTAMPTZ, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), deleted_at TIMESTAMPTZ ); CREATE INDEX IF NOT EXISTS idx_automation_actions_account_id ON automation_actions (account_id); CREATE INDEX IF NOT EXISTS idx_automation_actions_deleted_at ON automation_actions (deleted_at); -- ============ banners ============ CREATE TABLE IF NOT EXISTS banners ( id BIGSERIAL PRIMARY KEY, title VARCHAR(255), content TEXT, banner_type VARCHAR(50), status VARCHAR(50) DEFAULT 'active', starts_at TIMESTAMPTZ, ends_at TIMESTAMPTZ, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), deleted_at TIMESTAMPTZ ); CREATE INDEX IF NOT EXISTS idx_banners_status ON banners (status); CREATE INDEX IF NOT EXISTS idx_banners_deleted_at ON banners (deleted_at); -- ============ captain_assistant_inboxes ============ CREATE TABLE IF NOT EXISTS captain_assistant_inboxes ( id BIGSERIAL PRIMARY KEY, captain_assistant_id BIGINT NOT NULL, inbox_id BIGINT NOT NULL, account_id BIGINT NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), deleted_at TIMESTAMPTZ ); CREATE INDEX IF NOT EXISTS idx_captain_assistant_inboxes_assistant_id ON captain_assistant_inboxes (captain_assistant_id); CREATE INDEX IF NOT EXISTS idx_captain_assistant_inboxes_inbox_id ON captain_assistant_inboxes (inbox_id); CREATE INDEX IF NOT EXISTS idx_captain_assistant_inboxes_account_id ON captain_assistant_inboxes (account_id); CREATE INDEX IF NOT EXISTS idx_captain_assistant_inboxes_deleted_at ON captain_assistant_inboxes (deleted_at); -- ============ contactables ============ CREATE TABLE IF NOT EXISTS contactables ( id BIGSERIAL PRIMARY KEY, account_id BIGINT NOT NULL, contactable_type VARCHAR(50) NOT NULL, contactable_id BIGINT NOT NULL, email VARCHAR(255), phone VARCHAR(50), created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), deleted_at TIMESTAMPTZ ); CREATE INDEX IF NOT EXISTS idx_contactables_account_id ON contactables (account_id); CREATE INDEX IF NOT EXISTS idx_contactables_contactable_type_id ON contactables (contactable_type, contactable_id); CREATE INDEX IF NOT EXISTS idx_contactables_deleted_at ON contactables (deleted_at); -- ============ contact_exports ============ CREATE TABLE IF NOT EXISTS contact_exports ( id BIGSERIAL PRIMARY KEY, account_id BIGINT NOT NULL, user_id BIGINT NOT NULL, status VARCHAR(50) DEFAULT 'pending', file_url TEXT, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), deleted_at TIMESTAMPTZ ); CREATE INDEX IF NOT EXISTS idx_contact_exports_account_id ON contact_exports (account_id); CREATE INDEX IF NOT EXISTS idx_contact_exports_user_id ON contact_exports (user_id); CREATE INDEX IF NOT EXISTS idx_contact_exports_deleted_at ON contact_exports (deleted_at); -- ============ data_imports ============ CREATE TABLE IF NOT EXISTS data_imports ( id BIGSERIAL PRIMARY KEY, account_id BIGINT NOT NULL, user_id BIGINT NOT NULL, status VARCHAR(50) DEFAULT 'pending', file_url TEXT, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), deleted_at TIMESTAMPTZ ); CREATE INDEX IF NOT EXISTS idx_data_imports_account_id ON data_imports (account_id); CREATE INDEX IF NOT EXISTS idx_data_imports_user_id ON data_imports (user_id); CREATE INDEX IF NOT EXISTS idx_data_imports_deleted_at ON data_imports (deleted_at); -- ============ direct_uploads ============ CREATE TABLE IF NOT EXISTS direct_uploads ( id BIGSERIAL PRIMARY KEY, account_id BIGINT NOT NULL, file_name VARCHAR(255), file_url TEXT, file_size BIGINT, content_type VARCHAR(100), created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), deleted_at TIMESTAMPTZ ); CREATE INDEX IF NOT EXISTS idx_direct_uploads_account_id ON direct_uploads (account_id); CREATE INDEX IF NOT EXISTS idx_direct_uploads_deleted_at ON direct_uploads (deleted_at); -- ============ email_templates ============ CREATE TABLE IF NOT EXISTS email_templates ( id BIGSERIAL PRIMARY KEY, account_id BIGINT NOT NULL, name VARCHAR(255) NOT NULL, subject VARCHAR(512), body TEXT, template_type VARCHAR(50), created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), deleted_at TIMESTAMPTZ ); CREATE INDEX IF NOT EXISTS idx_email_templates_account_id ON email_templates (account_id); CREATE INDEX IF NOT EXISTS idx_email_templates_deleted_at ON email_templates (deleted_at); -- ============ message_reactions ============ CREATE TABLE IF NOT EXISTS message_reactions ( id BIGSERIAL PRIMARY KEY, message_id BIGINT NOT NULL, account_id BIGINT NOT NULL, user_id BIGINT NOT NULL, reaction_type VARCHAR(50) NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), deleted_at TIMESTAMPTZ ); CREATE INDEX IF NOT EXISTS idx_message_reactions_message_id ON message_reactions (message_id); CREATE INDEX IF NOT EXISTS idx_message_reactions_account_id ON message_reactions (account_id); CREATE INDEX IF NOT EXISTS idx_message_reactions_user_id ON message_reactions (user_id); CREATE INDEX IF NOT EXISTS idx_message_reactions_deleted_at ON message_reactions (deleted_at); -- ============ reports ============ CREATE TABLE IF NOT EXISTS reports ( id BIGSERIAL PRIMARY KEY, account_id BIGINT NOT NULL, report_type VARCHAR(100) NOT NULL, config JSONB, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), deleted_at TIMESTAMPTZ ); CREATE INDEX IF NOT EXISTS idx_reports_account_id ON reports (account_id); CREATE INDEX IF NOT EXISTS idx_reports_report_type ON reports (report_type); CREATE INDEX IF NOT EXISTS idx_reports_deleted_at ON reports (deleted_at); -- ============ saml_idp_configs ============ CREATE TABLE IF NOT EXISTS saml_idp_configs ( id BIGSERIAL PRIMARY KEY, account_id BIGINT NOT NULL, entity_id VARCHAR(512), sso_url VARCHAR(512), slo_url VARCHAR(512), x509_cert TEXT, name_id_format VARCHAR(100), created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), deleted_at TIMESTAMPTZ ); CREATE INDEX IF NOT EXISTS idx_saml_idp_configs_account_id ON saml_idp_configs (account_id); CREATE INDEX IF NOT EXISTS idx_saml_idp_configs_deleted_at ON saml_idp_configs (deleted_at); -- ============ pre_chat_forms ============ CREATE TABLE IF NOT EXISTS pre_chat_forms ( id BIGSERIAL PRIMARY KEY, inbox_id BIGINT NOT NULL, enabled BOOLEAN DEFAULT false, form_config JSONB, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), deleted_at TIMESTAMPTZ ); CREATE INDEX IF NOT EXISTS idx_pre_chat_forms_inbox_id ON pre_chat_forms (inbox_id); CREATE INDEX IF NOT EXISTS idx_pre_chat_forms_deleted_at ON pre_chat_forms (deleted_at); -- ============ widget_file_uploads ============ CREATE TABLE IF NOT EXISTS widget_file_uploads ( id BIGSERIAL PRIMARY KEY, inbox_id BIGINT NOT NULL, enabled BOOLEAN DEFAULT true, max_file_size INTEGER, allowed_types JSONB, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), deleted_at TIMESTAMPTZ ); CREATE INDEX IF NOT EXISTS idx_widget_file_uploads_inbox_id ON widget_file_uploads (inbox_id); CREATE INDEX IF NOT EXISTS idx_widget_file_uploads_deleted_at ON widget_file_uploads (deleted_at); -- ============ widget_offline_messages ============ CREATE TABLE IF NOT EXISTS widget_offline_messages ( id BIGSERIAL PRIMARY KEY, inbox_id BIGINT NOT NULL, email VARCHAR(255), message TEXT, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), deleted_at TIMESTAMPTZ ); CREATE INDEX IF NOT EXISTS idx_widget_offline_messages_inbox_id ON widget_offline_messages (inbox_id); CREATE INDEX IF NOT EXISTS idx_widget_offline_messages_deleted_at ON widget_offline_messages (deleted_at); -- ============ widget_theme_configs ============ CREATE TABLE IF NOT EXISTS widget_theme_configs ( id BIGSERIAL PRIMARY KEY, inbox_id BIGINT NOT NULL, theme_config JSONB, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), deleted_at TIMESTAMPTZ ); CREATE INDEX IF NOT EXISTS idx_widget_theme_configs_inbox_id ON widget_theme_configs (inbox_id); CREATE INDEX IF NOT EXISTS idx_widget_theme_configs_deleted_at ON widget_theme_configs (deleted_at); -- ============ widget_tests ============ CREATE TABLE IF NOT EXISTS widget_tests ( id BIGSERIAL PRIMARY KEY, inbox_id BIGINT NOT NULL, test_config JSONB, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), deleted_at TIMESTAMPTZ ); CREATE INDEX IF NOT EXISTS idx_widget_tests_inbox_id ON widget_tests (inbox_id); CREATE INDEX IF NOT EXISTS idx_widget_tests_deleted_at ON widget_tests (deleted_at); -- ============ working_hours ============ CREATE TABLE IF NOT EXISTS working_hours ( id BIGSERIAL PRIMARY KEY, inbox_id BIGINT NOT NULL, day_of_week INTEGER NOT NULL, start_time VARCHAR(10), end_time VARCHAR(10), enabled BOOLEAN DEFAULT true, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), deleted_at TIMESTAMPTZ ); CREATE INDEX IF NOT EXISTS idx_working_hours_inbox_id ON working_hours (inbox_id); CREATE INDEX IF NOT EXISTS idx_working_hours_deleted_at ON working_hours (deleted_at); -- ============ account_ldap_settings ============ CREATE TABLE IF NOT EXISTS account_ldap_settings ( id BIGSERIAL PRIMARY KEY, account_id BIGINT NOT NULL, enabled BOOLEAN DEFAULT false, host VARCHAR(255), port INTEGER, base_dn VARCHAR(255), bind_dn VARCHAR(255), bind_password VARCHAR(255), created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), deleted_at TIMESTAMPTZ ); CREATE INDEX IF NOT EXISTS idx_account_ldap_settings_account_id ON account_ldap_settings (account_id); CREATE INDEX IF NOT EXISTS idx_account_ldap_settings_deleted_at ON account_ldap_settings (deleted_at); -- ============ account_oidc_settings ============ CREATE TABLE IF NOT EXISTS account_oidc_settings ( id BIGSERIAL PRIMARY KEY, account_id BIGINT NOT NULL, enabled BOOLEAN DEFAULT false, issuer VARCHAR(512), client_id VARCHAR(255), client_secret VARCHAR(255), created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), deleted_at TIMESTAMPTZ ); CREATE INDEX IF NOT EXISTS idx_account_oidc_settings_account_id ON account_oidc_settings (account_id); CREATE INDEX IF NOT EXISTS idx_account_oidc_settings_deleted_at ON account_oidc_settings (deleted_at); -- ============ account_saml_settings ============ CREATE TABLE IF NOT EXISTS account_saml_settings ( id BIGSERIAL PRIMARY KEY, account_id BIGINT NOT NULL, enabled BOOLEAN DEFAULT false, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), deleted_at TIMESTAMPTZ ); CREATE INDEX IF NOT EXISTS idx_account_saml_settings_account_id ON account_saml_settings (account_id); CREATE INDEX IF NOT EXISTS idx_account_saml_settings_deleted_at ON account_saml_settings (deleted_at); -- ============ push_tokens ============ CREATE TABLE IF NOT EXISTS push_tokens ( id BIGSERIAL PRIMARY KEY, user_id BIGINT NOT NULL, token TEXT NOT NULL, platform VARCHAR(50), created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), deleted_at TIMESTAMPTZ ); CREATE INDEX IF NOT EXISTS idx_push_tokens_user_id ON push_tokens (user_id); CREATE INDEX IF NOT EXISTS idx_push_tokens_deleted_at ON push_tokens (deleted_at); -- ============ Add snoozed_until column to notifications ============ ALTER TABLE notifications ADD COLUMN IF NOT EXISTS snoozed_until TIMESTAMPTZ; -- ============ Add missing columns to channels table ============ ALTER TABLE channels ADD COLUMN IF NOT EXISTS type VARCHAR(100);