bookwiz.io / supabase / migrations / 006_ai_operations.sql
006_ai_operations.sql
Raw
-- AI operations tracking and revert functionality
-- Depends on: 002_001_books_file_system.sql, 003_000_chat_tables.sql

-- Add AI operations tracking for revert functionality
CREATE TABLE ai_operations (
    id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
    book_id UUID REFERENCES books(id) ON DELETE CASCADE NOT NULL,
    user_id UUID REFERENCES auth.users(id) ON DELETE CASCADE,
    
    -- Operation details
    operation_type TEXT NOT NULL CHECK (operation_type IN ('start', 'completed', 'reverted', 'failed')),
    description TEXT NOT NULL,
    
    -- Timestamps
    created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
    completed_at TIMESTAMP WITH TIME ZONE,
    
    -- Additional metadata (JSON)
    metadata JSONB DEFAULT '{}'::jsonb
);

-- Optional: File snapshots table for more granular revert (Phase 2)
CREATE TABLE file_snapshots (
    id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
    ai_operation_id UUID REFERENCES ai_operations(id) ON DELETE CASCADE NOT NULL,
    file_id UUID REFERENCES file_system_items(id) ON DELETE CASCADE,
    
    -- Snapshot data
    snapshot_data JSONB NOT NULL, -- Complete file state
    snapshot_type TEXT NOT NULL CHECK (snapshot_type IN ('before', 'after')),
    
    -- Timestamp
    created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);

-- Indexes for performance
CREATE INDEX idx_ai_operations_book_id ON ai_operations(book_id);
CREATE INDEX idx_ai_operations_user_id ON ai_operations(user_id);
CREATE INDEX idx_ai_operations_created_at ON ai_operations(created_at DESC);
CREATE INDEX idx_ai_operations_type ON ai_operations(operation_type);

CREATE INDEX idx_file_snapshots_operation_id ON file_snapshots(ai_operation_id);
CREATE INDEX idx_file_snapshots_file_id ON file_snapshots(file_id);
CREATE INDEX idx_file_snapshots_type ON file_snapshots(snapshot_type);

-- Row Level Security
ALTER TABLE ai_operations ENABLE ROW LEVEL SECURITY;
ALTER TABLE file_snapshots ENABLE ROW LEVEL SECURITY;

-- AI Operations policies
CREATE POLICY "Users can view their own AI operations" ON ai_operations
    FOR SELECT USING (
        -- Allow if user_id matches auth.uid()
        auth.uid() = user_id OR
        -- Allow if user_id is null but book belongs to authenticated user
        (user_id IS NULL AND
         EXISTS (
             SELECT 1 FROM books 
             WHERE books.id = ai_operations.book_id 
             AND books.user_id = auth.uid()
         ))
    );

CREATE POLICY "Users can create AI operations for their books" ON ai_operations
    FOR INSERT WITH CHECK (
        -- Allow if user_id matches auth.uid() and book belongs to user
        (auth.uid() = user_id AND
         EXISTS (
             SELECT 1 FROM books 
             WHERE books.id = ai_operations.book_id 
             AND books.user_id = auth.uid()
         )) OR
        -- Allow if user_id is null but book belongs to authenticated user
        (user_id IS NULL AND
         EXISTS (
             SELECT 1 FROM books 
             WHERE books.id = ai_operations.book_id 
             AND books.user_id = auth.uid()
         ))
    );

CREATE POLICY "Users can update their own AI operations" ON ai_operations
    FOR UPDATE USING (
        -- Allow if user_id matches auth.uid()
        auth.uid() = user_id OR
        -- Allow if user_id is null but book belongs to authenticated user
        (user_id IS NULL AND
         EXISTS (
             SELECT 1 FROM books 
             WHERE books.id = ai_operations.book_id 
             AND books.user_id = auth.uid()
         ))
    );

-- File Snapshots policies  
CREATE POLICY "Users can view snapshots for their operations" ON file_snapshots
    FOR SELECT USING (
        EXISTS (
            SELECT 1 FROM ai_operations 
            WHERE ai_operations.id = file_snapshots.ai_operation_id 
            AND (
                ai_operations.user_id = auth.uid() OR
                (ai_operations.user_id IS NULL AND
                 EXISTS (
                     SELECT 1 FROM books 
                     WHERE books.id = ai_operations.book_id 
                     AND books.user_id = auth.uid()
                 ))
            )
        )
    );

CREATE POLICY "Users can create snapshots for their operations" ON file_snapshots
    FOR INSERT WITH CHECK (
        EXISTS (
            SELECT 1 FROM ai_operations 
            WHERE ai_operations.id = file_snapshots.ai_operation_id 
            AND (
                ai_operations.user_id = auth.uid() OR
                (ai_operations.user_id IS NULL AND
                 EXISTS (
                     SELECT 1 FROM books 
                     WHERE books.id = ai_operations.book_id 
                     AND books.user_id = auth.uid()
                 ))
            )
        )
    );

-- Function to clean up old AI operations (optional - run periodically)
CREATE OR REPLACE FUNCTION cleanup_old_ai_operations()
RETURNS INTEGER AS $$
DECLARE
    deleted_count INTEGER;
BEGIN
    -- Delete AI operations older than 30 days
    DELETE FROM ai_operations 
    WHERE created_at < NOW() - INTERVAL '30 days';
    
    GET DIAGNOSTICS deleted_count = ROW_COUNT;
    
    RETURN deleted_count;
END;
$$ LANGUAGE plpgsql;

-- Add metadata to existing file_system_items for tracking AI changes
ALTER TABLE file_system_items 
ADD COLUMN IF NOT EXISTS last_ai_operation_id UUID REFERENCES ai_operations(id) ON DELETE SET NULL;