180 lines
4.9 KiB
JavaScript
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);
|
|
});
|