deckhearth/scripts/add-collection-slugs.js

124 lines
4 KiB
JavaScript
Raw Normal View History

#!/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 };