#!/usr/bin/env node import { config } from 'dotenv'; import { sql } from '@vercel/postgres'; // Load environment variables config({ path: '.env.local' }); async function addCollaborationFeatures() { try { console.log('šŸ”§ Adding collaboration features to database...\n'); // Add visibility and collaboration fields to collections table console.log('šŸ“‹ Updating collections table...'); await sql` ALTER TABLE collections ADD COLUMN IF NOT EXISTS visibility VARCHAR(20) DEFAULT 'private', ADD COLUMN IF NOT EXISTS tcg VARCHAR(50) DEFAULT 'MTG', ADD COLUMN IF NOT EXISTS tags TEXT `; console.log('āœ… Updated collections table'); // Create collection_permissions table console.log('�� Creating collection_permissions table...'); await sql` CREATE TABLE IF NOT EXISTS collection_permissions ( id SERIAL PRIMARY KEY, collection_id INTEGER REFERENCES collections(id) ON DELETE CASCADE, user_id INTEGER REFERENCES users(id) ON DELETE CASCADE, role VARCHAR(20) NOT NULL CHECK (role IN ('owner', 'editor', 'viewer')), status VARCHAR(20) DEFAULT 'active' CHECK (status IN ('active', 'pending', 'declined')), invite_token VARCHAR(255) UNIQUE, invited_by INTEGER REFERENCES users(id), created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, UNIQUE(collection_id, user_id) ) `; console.log('āœ… Created collection_permissions table'); // Create collection_activity table for audit trail console.log('šŸ“Š Creating collection_activity table...'); await sql` CREATE TABLE IF NOT EXISTS collection_activity ( id SERIAL PRIMARY KEY, collection_id INTEGER REFERENCES collections(id) ON DELETE CASCADE, user_id INTEGER REFERENCES users(id) ON DELETE SET NULL, action VARCHAR(50) NOT NULL, details JSONB, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) `; console.log('āœ… Created collection_activity table'); // Add is_pending field to users table for invited users console.log('šŸ‘¤ Updating users table...'); await sql` ALTER TABLE users ADD COLUMN IF NOT EXISTS is_pending BOOLEAN DEFAULT false `; console.log('āœ… Updated users table'); // Create indexes for better performance console.log('⚔ Creating indexes...'); await sql` CREATE INDEX IF NOT EXISTS idx_collection_permissions_collection_id ON collection_permissions(collection_id) `; await sql` CREATE INDEX IF NOT EXISTS idx_collection_permissions_user_id ON collection_permissions(user_id) `; await sql` CREATE INDEX IF NOT EXISTS idx_collection_activity_collection_id ON collection_activity(collection_id) `; await sql` CREATE INDEX IF NOT EXISTS idx_collections_visibility ON collections(visibility) `; console.log('āœ… Created indexes'); // Migrate existing collections to have owner permissions console.log('šŸ”„ Migrating existing collections...'); const existingCollections = await sql` SELECT c.id, c.user_id FROM collections c LEFT JOIN collection_permissions cp ON c.id = cp.collection_id AND c.user_id = cp.user_id WHERE cp.id IS NULL `; for (const collection of existingCollections.rows) { await sql` INSERT INTO collection_permissions (collection_id, user_id, role, status) VALUES (${collection.id}, ${collection.user_id}, 'owner', 'active') ON CONFLICT (collection_id, user_id) DO NOTHING `; } console.log(`āœ… Migrated ${existingCollections.rows.length} existing collections`); console.log('\nšŸŽ‰ Collaboration features added successfully!'); console.log('\nšŸ“‹ New Features:'); console.log(' • Collection visibility (private, invite-only, public)'); console.log(' • User permissions (owner, editor, viewer)'); console.log(' • Invitation system with email notifications'); console.log(' • Activity logging for audit trails'); console.log(' • Pending user support for email invitations'); } catch (error) { console.error('āŒ Migration failed:', error.message); process.exit(1); } } addCollaborationFeatures();