#!/usr/bin/env node import dotenv from 'dotenv'; import { neon } from '@neondatabase/serverless'; import { generateUniqueSlug } from '../lib/slug-utils.js'; dotenv.config({ path: '.env.local' }); async function addCollectionSlugs() { const sql = neon(process.env.POSTGRES_URL); try { console.log('šŸ”— Adding slug support to collections...'); // Step 1: Add slug column to collections table console.log('šŸ“ Adding slug column...'); await sql` ALTER TABLE collections ADD COLUMN IF NOT EXISTS slug VARCHAR(100) UNIQUE `; console.log('āœ… Added slug column'); // Step 2: Get all existing collections console.log('šŸ” Fetching existing collections...'); const collections = await sql` SELECT id, name, slug FROM collections ORDER BY created_at ASC `; console.log(`šŸ“Š Found ${collections.length} collections`); if (collections.length === 0) { console.log('āœ… No collections to migrate'); return; } // Step 3: Generate unique slugs for all collections console.log('šŸ·ļø Generating unique slugs...'); const existingSlugs = []; const updates = []; for (const collection of collections) { // Skip if collection already has a slug if (collection.slug) { existingSlugs.push(collection.slug); console.log(`ā­ļø Skipping "${collection.name}" - already has slug: ${collection.slug}`); continue; } // Generate unique slug const uniqueSlug = await generateUniqueSlug(collection.name, existingSlugs); existingSlugs.push(uniqueSlug); updates.push({ id: collection.id, name: collection.name, slug: uniqueSlug }); console.log(`šŸŽÆ "${collection.name}" → "${uniqueSlug}"`); } // Step 4: Update collections with their new slugs if (updates.length > 0) { console.log(`šŸ“ Updating ${updates.length} collections with slugs...`); for (const update of updates) { await sql` UPDATE collections SET slug = ${update.slug} WHERE id = ${update.id} `; console.log(`āœ… Updated: ${update.name} → ${update.slug}`); } } // Step 5: Add index for performance console.log('šŸš€ Adding slug index for performance...'); try { await sql`CREATE UNIQUE INDEX IF NOT EXISTS idx_collections_slug ON collections(slug)`; console.log('āœ… Added slug index'); } catch (error) { if (!error.message.includes('already exists')) { throw error; } console.log('āœ… Slug index already exists'); } // Step 6: Add constraint to ensure slugs are not null for new collections console.log('šŸ”’ Adding slug constraints...'); try { await sql`ALTER TABLE collections ADD CONSTRAINT check_slug_format CHECK (slug ~ '^[a-z0-9]([a-z0-9-]*[a-z0-9])?$' AND length(slug) <= 50)`; console.log('āœ… Added slug format constraint'); } catch (error) { if (!error.message.includes('already exists')) { console.warn('āš ļø Could not add slug constraint:', error.message); } else { console.log('āœ… Slug constraint already exists'); } } console.log('\nšŸŽ‰ Collection slug migration completed successfully!'); console.log('\nšŸ“‹ Summary:'); console.log(` šŸ“Š Total collections: ${collections.length}`); console.log(` šŸ†• New slugs created: ${updates.length}`); console.log(` ā­ļø Already had slugs: ${collections.length - updates.length}`); if (updates.length > 0) { console.log('\nšŸ”— New URL structure:'); console.log(' Old: /collection/123'); console.log(' New: /collection/modern-masters-2021'); } } catch (error) { console.error('āŒ Failed to add collection slugs:', error.message); console.error('Full error:', error); process.exit(1); } } if (import.meta.url === `file://${process.argv[1]}`) { addCollectionSlugs(); } export { addCollectionSlugs };