#!/usr/bin/env node /** * Migration Script: Standardize Blog Titles * * This script standardizes all Daily Digest titles to the format: * "## Daily Digest - Monday, February 24, 2026" * * Run: node migrate-titles.js [--dry-run] */ require('dotenv').config({ path: require('path').join(__dirname, '..', '.env.local') }); const { createClient } = require('@supabase/supabase-js'); const SUPABASE_URL = process.env.NEXT_PUBLIC_SUPABASE_URL || 'https://qnatchrjlpehiijwtreh.supabase.co'; const SUPABASE_SERVICE_KEY = process.env.SUPABASE_SERVICE_ROLE_KEY || process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY; if (!SUPABASE_SERVICE_KEY) { console.error('Error: SUPABASE_SERVICE_ROLE_KEY or NEXT_PUBLIC_SUPABASE_ANON_KEY environment variable required'); process.exit(1); } const supabase = createClient(SUPABASE_URL, SUPABASE_SERVICE_KEY); /** * Get day name from date string (YYYY-MM-DD) */ function getDayName(dateStr) { const days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']; const date = new Date(dateStr + 'T12:00:00'); // Use noon to avoid timezone issues return days[date.getDay()]; } /** * Format date for title (e.g., "February 24, 2026") */ function formatDateForTitle(dateStr) { const months = [ 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December' ]; const [year, month, day] = dateStr.split('-'); const monthName = months[parseInt(month, 10) - 1]; return `${monthName} ${parseInt(day, 10)}, ${year}`; } /** * Generate standardized title */ function generateStandardTitle(dateStr) { const dayName = getDayName(dateStr); const datePart = formatDateForTitle(dateStr); return `## Daily Digest - ${dayName}, ${datePart}`; } /** * Extract current title from content */ function extractCurrentTitle(content) { const lines = content.split('\n'); const titleLine = lines.find(l => l.startsWith('# ') || l.startsWith('## ')); return titleLine || null; } /** * Replace title in content */ function replaceTitle(content, newTitle) { const lines = content.split('\n'); const titleIndex = lines.findIndex(l => l.startsWith('# ') || l.startsWith('## ')); if (titleIndex === -1) { // No title found, prepend new title return newTitle + '\n\n' + content; } // Replace existing title line lines[titleIndex] = newTitle; return lines.join('\n'); } /** * Process all messages and update titles */ async function migrateTitles(dryRun = false) { console.log('šŸ”„ Fetching all blog messages...\n'); const { data: messages, error } = await supabase .from('blog_messages') .select('id, date, content') .order('date', { ascending: false }); if (error) { console.error('Error fetching messages:', error); process.exit(1); } console.log(`Found ${messages.length} messages\n`); const updates = []; const skipped = []; for (const message of messages) { const currentTitle = extractCurrentTitle(message.content); const newTitle = generateStandardTitle(message.date); // Check if update is needed if (currentTitle === newTitle) { skipped.push({ id: message.id, date: message.date, title: currentTitle, reason: 'Already in standard format' }); continue; } updates.push({ id: message.id, date: message.date, oldTitle: currentTitle, newTitle: newTitle }); if (!dryRun) { const newContent = replaceTitle(message.content, newTitle); const { error: updateError } = await supabase .from('blog_messages') .update({ content: newContent }) .eq('id', message.id); if (updateError) { console.error(`āŒ Error updating message ${message.id}:`, updateError); } else { console.log(`āœ… Updated: ${message.date}`); console.log(` Old: ${currentTitle}`); console.log(` New: ${newTitle}\n`); } } } // Summary console.log('\n' + '='.repeat(60)); console.log('MIGRATION SUMMARY'); console.log('='.repeat(60)); console.log(`Total messages: ${messages.length}`); console.log(`Updates needed: ${updates.length}`); console.log(`Already correct: ${skipped.length}`); if (dryRun) { console.log('\nšŸ“‹ DRY RUN - No changes made\n'); if (updates.length > 0) { console.log('Proposed changes:'); updates.forEach(u => { console.log(`\n${u.date}:`); console.log(` From: ${u.oldTitle}`); console.log(` To: ${u.newTitle}`); }); } } else { console.log(`\nāœ… Migration complete!`); } return { updates, skipped }; } // Main execution const dryRun = process.argv.includes('--dry-run'); if (dryRun) { console.log('šŸ” DRY RUN MODE - No changes will be made\n'); } migrateTitles(dryRun) .then(() => process.exit(0)) .catch(err => { console.error('Migration failed:', err); process.exit(1); });