-- ============================================ -- SCRIPT RÁPIDO PARA SUPABASE DASHBOARD -- ============================================ -- Execute este SQL no SQL Editor do Supabase Dashboard -- URL: https://yuanqfswhberkoevtmfr.supabase.co -- Dashboard > SQL Editor > New Query > Cole este código > Run -- ============================================ -- 1. CRIAR TABELAS -- ============================================ -- Conversas entre médicos e pacientes CREATE TABLE IF NOT EXISTS conversations ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), doctor_id UUID NOT NULL REFERENCES doctors(id) ON DELETE CASCADE, patient_id UUID NOT NULL REFERENCES patients(id) ON DELETE CASCADE, created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW(), UNIQUE(doctor_id, patient_id) ); CREATE INDEX IF NOT EXISTS idx_conversations_doctor ON conversations(doctor_id); CREATE INDEX IF NOT EXISTS idx_conversations_patient ON conversations(patient_id); -- Mensagens CREATE TABLE IF NOT EXISTS messages ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), conversation_id UUID NOT NULL REFERENCES conversations(id) ON DELETE CASCADE, sender_id UUID NOT NULL, sender_type VARCHAR(10) NOT NULL CHECK (sender_type IN ('doctor', 'patient')), content TEXT, message_type VARCHAR(20) NOT NULL DEFAULT 'text' CHECK (message_type IN ('text', 'file', 'image', 'video', 'audio', 'report', 'appointment')), read_at TIMESTAMPTZ, created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW(), attachment_name VARCHAR(255), attachment_url TEXT, attachment_type VARCHAR(100), attachment_size INTEGER, metadata JSONB DEFAULT '{}'::jsonb ); CREATE INDEX IF NOT EXISTS idx_messages_conversation ON messages(conversation_id); CREATE INDEX IF NOT EXISTS idx_messages_sender ON messages(sender_id); CREATE INDEX IF NOT EXISTS idx_messages_created_at ON messages(created_at DESC); CREATE INDEX IF NOT EXISTS idx_messages_read_at ON messages(read_at); -- Status online/offline CREATE TABLE IF NOT EXISTS user_presence ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id UUID NOT NULL, user_type VARCHAR(10) NOT NULL CHECK (user_type IN ('doctor', 'patient', 'admin', 'secretaria')), online BOOLEAN DEFAULT false, last_seen TIMESTAMPTZ DEFAULT NOW(), metadata JSONB DEFAULT '{}'::jsonb, UNIQUE(user_id, user_type) ); CREATE INDEX IF NOT EXISTS idx_user_presence_user ON user_presence(user_id, user_type); CREATE INDEX IF NOT EXISTS idx_user_presence_online ON user_presence(online); -- Anexos CREATE TABLE IF NOT EXISTS message_attachments ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), message_id UUID NOT NULL REFERENCES messages(id) ON DELETE CASCADE, file_name VARCHAR(255) NOT NULL, file_url TEXT NOT NULL, file_type VARCHAR(100) NOT NULL, file_size INTEGER NOT NULL, mime_type VARCHAR(100), thumbnail_url TEXT, uploaded_by UUID NOT NULL, created_at TIMESTAMPTZ DEFAULT NOW(), metadata JSONB DEFAULT '{}'::jsonb ); CREATE INDEX IF NOT EXISTS idx_attachments_message ON message_attachments(message_id); CREATE INDEX IF NOT EXISTS idx_attachments_uploader ON message_attachments(uploaded_by); -- Notificações CREATE TABLE IF NOT EXISTS message_notifications ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id UUID NOT NULL, user_type VARCHAR(10) NOT NULL, message_id UUID NOT NULL REFERENCES messages(id) ON DELETE CASCADE, read BOOLEAN DEFAULT false, sent_at TIMESTAMPTZ DEFAULT NOW(), read_at TIMESTAMPTZ ); CREATE INDEX IF NOT EXISTS idx_notifications_user ON message_notifications(user_id, user_type); CREATE INDEX IF NOT EXISTS idx_notifications_read ON message_notifications(read); -- ============================================ -- 2. CRIAR TRIGGERS -- ============================================ CREATE OR REPLACE FUNCTION update_conversation_timestamp() RETURNS TRIGGER AS $$ BEGIN UPDATE conversations SET updated_at = NOW() WHERE id = NEW.conversation_id; RETURN NEW; END; $$ LANGUAGE plpgsql; DROP TRIGGER IF EXISTS update_conversation_on_message ON messages; CREATE TRIGGER update_conversation_on_message AFTER INSERT ON messages FOR EACH ROW EXECUTE FUNCTION update_conversation_timestamp(); CREATE OR REPLACE FUNCTION update_last_seen() RETURNS TRIGGER AS $$ BEGIN NEW.last_seen = NOW(); RETURN NEW; END; $$ LANGUAGE plpgsql; DROP TRIGGER IF EXISTS update_user_presence_last_seen ON user_presence; CREATE TRIGGER update_user_presence_last_seen BEFORE UPDATE ON user_presence FOR EACH ROW EXECUTE FUNCTION update_last_seen(); -- ============================================ -- 3. CRIAR FUNÇÕES -- ============================================ CREATE OR REPLACE FUNCTION get_user_conversations( p_user_id UUID, p_user_type VARCHAR ) RETURNS TABLE ( conversation_id UUID, other_user_id UUID, other_user_name TEXT, other_user_specialty TEXT, other_user_avatar TEXT, last_message TEXT, last_message_time TIMESTAMPTZ, unread_count BIGINT, is_online BOOLEAN ) AS $$ BEGIN IF p_user_type = 'patient' THEN RETURN QUERY SELECT c.id as conversation_id, d.id as other_user_id, d.full_name as other_user_name, d.specialty as other_user_specialty, NULL::TEXT as other_user_avatar, (SELECT content FROM messages WHERE conversation_id = c.id ORDER BY created_at DESC LIMIT 1) as last_message, (SELECT created_at FROM messages WHERE conversation_id = c.id ORDER BY created_at DESC LIMIT 1) as last_message_time, (SELECT COUNT(*) FROM messages m WHERE m.conversation_id = c.id AND m.sender_type = 'doctor' AND m.read_at IS NULL) as unread_count, COALESCE((SELECT online FROM user_presence WHERE user_id = d.id AND user_type = 'doctor'), false) as is_online FROM conversations c JOIN doctors d ON c.doctor_id = d.id WHERE c.patient_id = p_user_id ORDER BY last_message_time DESC NULLS LAST; ELSE RETURN QUERY SELECT c.id as conversation_id, p.id as other_user_id, p.full_name as other_user_name, NULL::TEXT as other_user_specialty, NULL::TEXT as other_user_avatar, (SELECT content FROM messages WHERE conversation_id = c.id ORDER BY created_at DESC LIMIT 1) as last_message, (SELECT created_at FROM messages WHERE conversation_id = c.id ORDER BY created_at DESC LIMIT 1) as last_message_time, (SELECT COUNT(*) FROM messages m WHERE m.conversation_id = c.id AND m.sender_type = 'patient' AND m.read_at IS NULL) as unread_count, COALESCE((SELECT online FROM user_presence WHERE user_id = p.id AND user_type = 'patient'), false) as is_online FROM conversations c JOIN patients p ON c.patient_id = p.id WHERE c.doctor_id = p_user_id ORDER BY last_message_time DESC NULLS LAST; END IF; END; $$ LANGUAGE plpgsql; CREATE OR REPLACE FUNCTION get_or_create_conversation( p_doctor_id UUID, p_patient_id UUID ) RETURNS UUID AS $$ DECLARE v_conversation_id UUID; BEGIN SELECT id INTO v_conversation_id FROM conversations WHERE doctor_id = p_doctor_id AND patient_id = p_patient_id; IF v_conversation_id IS NULL THEN INSERT INTO conversations (doctor_id, patient_id) VALUES (p_doctor_id, p_patient_id) RETURNING id INTO v_conversation_id; END IF; RETURN v_conversation_id; END; $$ LANGUAGE plpgsql; CREATE OR REPLACE FUNCTION mark_messages_as_read( p_conversation_id UUID, p_user_id UUID, p_user_type VARCHAR ) RETURNS INTEGER AS $$ DECLARE v_updated_count INTEGER; BEGIN UPDATE messages SET read_at = NOW() WHERE conversation_id = p_conversation_id AND sender_id != p_user_id AND sender_type != p_user_type AND read_at IS NULL; GET DIAGNOSTICS v_updated_count = ROW_COUNT; RETURN v_updated_count; END; $$ LANGUAGE plpgsql; -- ============================================ -- 4. HABILITAR RLS -- ============================================ ALTER TABLE conversations ENABLE ROW LEVEL SECURITY; ALTER TABLE messages ENABLE ROW LEVEL SECURITY; ALTER TABLE user_presence ENABLE ROW LEVEL SECURITY; ALTER TABLE message_attachments ENABLE ROW LEVEL SECURITY; ALTER TABLE message_notifications ENABLE ROW LEVEL SECURITY; -- ============================================ -- 5. CRIAR POLÍTICAS RLS -- ============================================ -- Políticas para conversations DROP POLICY IF EXISTS "Users can view their own conversations" ON conversations; CREATE POLICY "Users can view their own conversations" ON conversations FOR SELECT USING ( doctor_id IN (SELECT id FROM doctors WHERE user_id = auth.uid()) OR patient_id IN (SELECT id FROM patients WHERE user_id = auth.uid()) ); DROP POLICY IF EXISTS "Users can create conversations" ON conversations; CREATE POLICY "Users can create conversations" ON conversations FOR INSERT WITH CHECK ( doctor_id IN (SELECT id FROM doctors WHERE user_id = auth.uid()) OR patient_id IN (SELECT id FROM patients WHERE user_id = auth.uid()) ); -- Políticas para messages DROP POLICY IF EXISTS "Users can view messages in their conversations" ON messages; CREATE POLICY "Users can view messages in their conversations" ON messages FOR SELECT USING ( conversation_id IN ( SELECT id FROM conversations WHERE doctor_id IN (SELECT id FROM doctors WHERE user_id = auth.uid()) OR patient_id IN (SELECT id FROM patients WHERE user_id = auth.uid()) ) ); DROP POLICY IF EXISTS "Users can send messages in their conversations" ON messages; CREATE POLICY "Users can send messages in their conversations" ON messages FOR INSERT WITH CHECK ( conversation_id IN ( SELECT id FROM conversations WHERE doctor_id IN (SELECT id FROM doctors WHERE user_id = auth.uid()) OR patient_id IN (SELECT id FROM patients WHERE user_id = auth.uid()) ) ); DROP POLICY IF EXISTS "Users can update their own messages" ON messages; CREATE POLICY "Users can update their own messages" ON messages FOR UPDATE USING ( sender_id = auth.uid() OR conversation_id IN ( SELECT id FROM conversations WHERE doctor_id IN (SELECT id FROM doctors WHERE user_id = auth.uid()) OR patient_id IN (SELECT id FROM patients WHERE user_id = auth.uid()) ) ); -- Políticas para user_presence DROP POLICY IF EXISTS "Everyone can view presence" ON user_presence; CREATE POLICY "Everyone can view presence" ON user_presence FOR SELECT USING (true); DROP POLICY IF EXISTS "Users can update their own presence" ON user_presence; CREATE POLICY "Users can update their own presence" ON user_presence FOR ALL USING (user_id = auth.uid()); -- Políticas para attachments DROP POLICY IF EXISTS "Users can view attachments in their conversations" ON message_attachments; CREATE POLICY "Users can view attachments in their conversations" ON message_attachments FOR SELECT USING ( message_id IN ( SELECT m.id FROM messages m JOIN conversations c ON m.conversation_id = c.id WHERE c.doctor_id IN (SELECT id FROM doctors WHERE user_id = auth.uid()) OR c.patient_id IN (SELECT id FROM patients WHERE user_id = auth.uid()) ) ); DROP POLICY IF EXISTS "Users can upload attachments" ON message_attachments; CREATE POLICY "Users can upload attachments" ON message_attachments FOR INSERT WITH CHECK (uploaded_by = auth.uid()); -- ============================================ -- 6. HABILITAR REALTIME -- ============================================ ALTER PUBLICATION supabase_realtime ADD TABLE conversations; ALTER PUBLICATION supabase_realtime ADD TABLE messages; ALTER PUBLICATION supabase_realtime ADD TABLE user_presence; ALTER PUBLICATION supabase_realtime ADD TABLE message_attachments; -- ============================================ -- ✅ CONCLUÍDO! -- ============================================ -- Próximos passos: -- 1. Criar bucket 'message-files' no Storage -- 2. Configurar políticas de storage (ver SETUP_STORAGE.md) -- 3. Testar o sistema de mensagens