blog-backup/scripts/migrate-titles.js

180 lines
4.9 KiB
JavaScript

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