deckhearth/scripts/add-collaboration-features.js
Randall Stillwell 1cc2e28423 Migrate Deck Hearth off Vercel/Neon to homelab Dokploy stack.
Replace @vercel/postgres, Blob, and Upstash with lib/sql.js, MinIO object
storage, and CT 102 Redis rate limits. Add Dockerfile for Dokploy deploy,
homelab runbooks, Neon data-copy helper, and point CI smoke/visual at the
homelab URL instead of Vercel previews.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-15 09:32:13 -05:00

115 lines
4.2 KiB
JavaScript
Executable file
Raw Permalink Blame History

#!/usr/bin/env node
import { config } from 'dotenv';
import { sql } from '../lib/sql.js';
// 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('<27><> 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();