AeThex-OS/supabase/migrations/001_messaging_system.sql
MrPiglr b3c308b2c8 Add functional marketplace modules, bottom nav bar, root terminal, arcade games
- ModuleManager: Central tracking for installed marketplace modules
- DataAnalyzerWidget: Real-time CPU/RAM/Battery/Storage widget (unlocked by Data Analyzer module)
- BottomNavBar: Navigation bar for Projects/Chat/Marketplace/Settings
- RootShell: Real root command execution utility
- TerminalActivity: Full root shell with neofetch, sysinfo, real Linux commands
- Terminal Pro module: Adds aliases (ll, la, h), command history
- ArcadeActivity + SnakeGame: Pixel Arcade module unlocks retro games
- fade_in/fade_out animations for smooth transitions
2026-02-18 22:03:50 -07:00

202 lines
8 KiB
PL/PgSQL

-- Migration 001: Messaging System
-- Creates tables for conversations, messages, participants, reactions
-- ============================================================================
-- CONVERSATIONS
-- ============================================================================
CREATE TABLE IF NOT EXISTS conversations (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
type VARCHAR(20) NOT NULL CHECK (type IN ('direct', 'group', 'channel')),
title VARCHAR(200),
description TEXT,
avatar_url VARCHAR(500),
created_by UUID REFERENCES auth.users(id),
is_archived BOOLEAN DEFAULT false,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_conversations_type ON conversations(type);
CREATE INDEX IF NOT EXISTS idx_conversations_creator ON conversations(created_by);
CREATE INDEX IF NOT EXISTS idx_conversations_updated ON conversations(updated_at DESC);
-- ============================================================================
-- CONVERSATION PARTICIPANTS
-- ============================================================================
CREATE TABLE IF NOT EXISTS conversation_participants (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
conversation_id UUID NOT NULL REFERENCES conversations(id) ON DELETE CASCADE,
user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
role VARCHAR(20) DEFAULT 'member' CHECK (role IN ('admin', 'moderator', 'member')),
joined_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
last_read_at TIMESTAMP WITH TIME ZONE,
notification_settings JSONB DEFAULT '{"enabled": true, "mentions_only": false}'::jsonb,
UNIQUE(conversation_id, user_id)
);
CREATE INDEX IF NOT EXISTS idx_participants_conversation ON conversation_participants(conversation_id);
CREATE INDEX IF NOT EXISTS idx_participants_user ON conversation_participants(user_id);
-- ============================================================================
-- MESSAGES
-- ============================================================================
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 REFERENCES auth.users(id),
content TEXT NOT NULL,
content_type VARCHAR(20) DEFAULT 'text' CHECK (content_type IN ('text', 'image', 'video', 'audio', 'file', 'code')),
metadata JSONB,
reply_to_id UUID REFERENCES messages(id) ON DELETE SET NULL,
edited_at TIMESTAMP WITH TIME ZONE,
deleted_at TIMESTAMP WITH TIME ZONE,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_messages_conversation ON messages(conversation_id, created_at DESC);
CREATE INDEX IF NOT EXISTS idx_messages_sender ON messages(sender_id);
CREATE INDEX IF NOT EXISTS idx_messages_reply_to ON messages(reply_to_id);
CREATE INDEX IF NOT EXISTS idx_messages_created ON messages(created_at DESC);
-- ============================================================================
-- MESSAGE REACTIONS
-- ============================================================================
CREATE TABLE IF NOT EXISTS message_reactions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
message_id UUID NOT NULL REFERENCES messages(id) ON DELETE CASCADE,
user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
emoji VARCHAR(20) NOT NULL,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
UNIQUE(message_id, user_id, emoji)
);
CREATE INDEX IF NOT EXISTS idx_reactions_message ON message_reactions(message_id);
CREATE INDEX IF NOT EXISTS idx_reactions_user ON message_reactions(user_id);
-- ============================================================================
-- MESSAGE ATTACHMENTS
-- ============================================================================
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(500) NOT NULL,
file_url VARCHAR(1000) NOT NULL,
file_size INTEGER,
file_type VARCHAR(100),
uploaded_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_attachments_message ON message_attachments(message_id);
-- ============================================================================
-- FUNCTIONS
-- ============================================================================
-- Function to update conversation updated_at timestamp when messages are added
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;
-- Trigger to update conversation timestamp on new message
DROP TRIGGER IF EXISTS trigger_update_conversation_timestamp ON messages;
CREATE TRIGGER trigger_update_conversation_timestamp
AFTER INSERT ON messages
FOR EACH ROW
EXECUTE FUNCTION update_conversation_timestamp();
-- Function to get or create a direct conversation between two users
CREATE OR REPLACE FUNCTION get_or_create_direct_conversation(user1_id UUID, user2_id UUID)
RETURNS UUID AS $$
DECLARE
conv_id UUID;
BEGIN
-- Check if direct conversation exists between these two users
SELECT c.id INTO conv_id
FROM conversations c
WHERE c.type = 'direct'
AND EXISTS (SELECT 1 FROM conversation_participants WHERE conversation_id = c.id AND user_id = user1_id)
AND EXISTS (SELECT 1 FROM conversation_participants WHERE conversation_id = c.id AND user_id = user2_id)
AND (SELECT COUNT(*) FROM conversation_participants WHERE conversation_id = c.id) = 2
LIMIT 1;
-- If not found, create new conversation
IF conv_id IS NULL THEN
INSERT INTO conversations (type, created_by)
VALUES ('direct', user1_id)
RETURNING id INTO conv_id;
-- Add both participants
INSERT INTO conversation_participants (conversation_id, user_id, role)
VALUES (conv_id, user1_id, 'member'), (conv_id, user2_id, 'member');
END IF;
RETURN conv_id;
END;
$$ LANGUAGE plpgsql;
-- ============================================================================
-- ROW LEVEL SECURITY
-- ============================================================================
ALTER TABLE conversations ENABLE ROW LEVEL SECURITY;
ALTER TABLE conversation_participants ENABLE ROW LEVEL SECURITY;
ALTER TABLE messages ENABLE ROW LEVEL SECURITY;
ALTER TABLE message_reactions ENABLE ROW LEVEL SECURITY;
ALTER TABLE message_attachments ENABLE ROW LEVEL SECURITY;
-- Conversations: Users can see conversations they're participants in
CREATE POLICY "Users can view their conversations" ON conversations
FOR SELECT USING (
EXISTS (
SELECT 1 FROM conversation_participants
WHERE conversation_id = id AND user_id = auth.uid()
)
);
CREATE POLICY "Users can create conversations" ON conversations
FOR INSERT WITH CHECK (created_by = auth.uid());
-- Participants: Users can see participants in their conversations
CREATE POLICY "Users can view participants in their conversations" ON conversation_participants
FOR SELECT USING (
EXISTS (
SELECT 1 FROM conversation_participants cp
WHERE cp.conversation_id = conversation_participants.conversation_id
AND cp.user_id = auth.uid()
)
);
-- Messages: Users can see messages in conversations they're in
CREATE POLICY "Users can view messages in their conversations" ON messages
FOR SELECT USING (
EXISTS (
SELECT 1 FROM conversation_participants
WHERE conversation_id = messages.conversation_id AND user_id = auth.uid()
)
);
CREATE POLICY "Users can send messages to their conversations" ON messages
FOR INSERT WITH CHECK (
sender_id = auth.uid() AND
EXISTS (
SELECT 1 FROM conversation_participants
WHERE conversation_id = messages.conversation_id AND user_id = auth.uid()
)
);
CREATE POLICY "Users can edit their own messages" ON messages
FOR UPDATE USING (sender_id = auth.uid());
CREATE POLICY "Users can delete their own messages" ON messages
FOR DELETE USING (sender_id = auth.uid());