-- ===================================================== -- WhatsApp Nucleo - Database Schema -- ===================================================== -- Extension for UUID generation CREATE EXTENSION IF NOT EXISTS "pgcrypto"; -- ===================================================== -- INSTANCES: WhatsApp connection instances -- ===================================================== CREATE TABLE instances ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), name VARCHAR(100) NOT NULL, phone_number VARCHAR(20), status VARCHAR(20) DEFAULT 'disconnected' CHECK (status IN ('disconnected', 'connecting', 'connected', 'qr_ready', 'pairing')), qr_code TEXT, pairing_code VARCHAR(10), last_connected_at TIMESTAMPTZ, created_by VARCHAR(100) NOT NULL, created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW(), CONSTRAINT unique_phone UNIQUE (phone_number) ); -- ===================================================== -- AUTH_KEYS: Baileys credentials per instance -- ===================================================== CREATE TABLE auth_keys ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), instance_id UUID NOT NULL REFERENCES instances(id) ON DELETE CASCADE, key_type VARCHAR(50) NOT NULL, key_id VARCHAR(100), key_data JSONB NOT NULL, created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW(), CONSTRAINT unique_key UNIQUE (instance_id, key_type, key_id) ); -- ===================================================== -- CONTACTS: WhatsApp contacts -- ===================================================== CREATE TABLE contacts ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), instance_id UUID NOT NULL REFERENCES instances(id) ON DELETE CASCADE, jid VARCHAR(100) NOT NULL, name VARCHAR(255), push_name VARCHAR(255), phone_number VARCHAR(20), profile_picture_url TEXT, is_group BOOLEAN DEFAULT FALSE, created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW(), CONSTRAINT unique_contact UNIQUE (instance_id, jid) ); -- ===================================================== -- CHATS: Conversations -- ===================================================== CREATE TABLE chats ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), instance_id UUID NOT NULL REFERENCES instances(id) ON DELETE CASCADE, jid VARCHAR(100) NOT NULL, name VARCHAR(255), is_group BOOLEAN DEFAULT FALSE, is_archived BOOLEAN DEFAULT FALSE, is_pinned BOOLEAN DEFAULT FALSE, unread_count INTEGER DEFAULT 0, last_message_id UUID, last_message_at TIMESTAMPTZ, created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW(), CONSTRAINT unique_chat UNIQUE (instance_id, jid) ); -- ===================================================== -- MESSAGES: All messages -- ===================================================== CREATE TABLE messages ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), instance_id UUID NOT NULL REFERENCES instances(id) ON DELETE CASCADE, chat_id UUID NOT NULL REFERENCES chats(id) ON DELETE CASCADE, message_id VARCHAR(100) NOT NULL, from_jid VARCHAR(100) NOT NULL, to_jid VARCHAR(100), from_me BOOLEAN DEFAULT FALSE, message_type VARCHAR(50) NOT NULL, content TEXT, caption TEXT, media_url TEXT, media_mimetype VARCHAR(100), media_filename VARCHAR(255), quoted_message_id VARCHAR(100), timestamp TIMESTAMPTZ NOT NULL, status VARCHAR(20) DEFAULT 'sent' CHECK (status IN ('pending', 'sent', 'delivered', 'read', 'failed')), raw_message JSONB, created_at TIMESTAMPTZ DEFAULT NOW(), CONSTRAINT unique_message UNIQUE (instance_id, message_id) ); -- ===================================================== -- WEBHOOKS: Webhook configurations -- ===================================================== CREATE TABLE webhooks ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), instance_id UUID REFERENCES instances(id) ON DELETE CASCADE, name VARCHAR(100) NOT NULL, url TEXT NOT NULL, secret VARCHAR(255), events TEXT[] NOT NULL DEFAULT '{}', is_active BOOLEAN DEFAULT TRUE, headers JSONB DEFAULT '{}', retry_count INTEGER DEFAULT 3, timeout_ms INTEGER DEFAULT 5000, created_by VARCHAR(100) NOT NULL, created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW() ); -- ===================================================== -- WEBHOOK_LOGS: Delivery logs -- ===================================================== CREATE TABLE webhook_logs ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), webhook_id UUID NOT NULL REFERENCES webhooks(id) ON DELETE CASCADE, event_type VARCHAR(50) NOT NULL, payload JSONB NOT NULL, response_status INTEGER, response_body TEXT, error_message TEXT, attempt INTEGER DEFAULT 1, delivered_at TIMESTAMPTZ, created_at TIMESTAMPTZ DEFAULT NOW() ); -- ===================================================== -- API_KEYS: External API access keys -- ===================================================== CREATE TABLE api_keys ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), name VARCHAR(100) NOT NULL, key_hash VARCHAR(255) NOT NULL, key_prefix VARCHAR(10) NOT NULL, instance_id UUID REFERENCES instances(id) ON DELETE CASCADE, permissions TEXT[] NOT NULL DEFAULT '{}', is_active BOOLEAN DEFAULT TRUE, last_used_at TIMESTAMPTZ, expires_at TIMESTAMPTZ, created_by VARCHAR(100) NOT NULL, created_at TIMESTAMPTZ DEFAULT NOW() ); -- ===================================================== -- INDEXES for performance -- ===================================================== CREATE INDEX idx_auth_keys_instance ON auth_keys(instance_id); CREATE INDEX idx_contacts_instance ON contacts(instance_id); CREATE INDEX idx_chats_instance ON chats(instance_id); CREATE INDEX idx_chats_last_message ON chats(instance_id, last_message_at DESC); CREATE INDEX idx_messages_instance_chat ON messages(instance_id, chat_id); CREATE INDEX idx_messages_timestamp ON messages(timestamp DESC); CREATE INDEX idx_messages_chat_timestamp ON messages(chat_id, timestamp DESC); CREATE INDEX idx_webhooks_instance ON webhooks(instance_id); CREATE INDEX idx_webhooks_active ON webhooks(is_active) WHERE is_active = TRUE; CREATE INDEX idx_webhook_logs_webhook ON webhook_logs(webhook_id, created_at DESC); CREATE INDEX idx_api_keys_prefix ON api_keys(key_prefix) WHERE is_active = TRUE; -- ===================================================== -- TRIGGERS for updated_at -- ===================================================== CREATE OR REPLACE FUNCTION update_updated_at_column() RETURNS TRIGGER AS $$ BEGIN NEW.updated_at = NOW(); RETURN NEW; END; $$ language 'plpgsql'; CREATE TRIGGER update_instances_updated_at BEFORE UPDATE ON instances FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); CREATE TRIGGER update_auth_keys_updated_at BEFORE UPDATE ON auth_keys FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); CREATE TRIGGER update_contacts_updated_at BEFORE UPDATE ON contacts FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); CREATE TRIGGER update_chats_updated_at BEFORE UPDATE ON chats FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); CREATE TRIGGER update_webhooks_updated_at BEFORE UPDATE ON webhooks FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();