tractatus/scripts/migrate-public-to-visibility.js
TheFlow 2298d36bed fix(submissions): restructure Economist package and fix article display
- Create Economist SubmissionTracking package correctly:
  * mainArticle = full blog post content
  * coverLetter = 216-word SIR— letter
  * Links to blog post via blogPostId
- Archive 'Letter to The Economist' from blog posts (it's the cover letter)
- Fix date display on article cards (use published_at)
- Target publication already displaying via blue badge

Database changes:
- Make blogPostId optional in SubmissionTracking model
- Economist package ID: 68fa85ae49d4900e7f2ecd83
- Le Monde package ID: 68fa2abd2e6acd5691932150

Next: Enhanced modal with tabs, validation, export

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-24 08:47:42 +13:00

138 lines
4.5 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env node
/**
* Migrate legacy 'public' field to modern 'visibility' field
*
* SECURITY: Safe migration with dry-run support
* - Migrates public: true → visibility: 'public'
* - Migrates public: false → visibility: 'internal'
* - Preserves documents that already have visibility set
* - Removes the deprecated 'public' field after migration
*/
const { getCollection } = require('../src/utils/db.util');
async function migrate(dryRun = false) {
try {
const collection = await getCollection('documents');
// Find documents with public field but no visibility
const docsWithPublicOnly = await collection.find({
public: { $exists: true },
visibility: { $exists: false }
}).toArray();
// Find documents with both fields (inconsistent state)
const docsWithBoth = await collection.find({
public: { $exists: true },
visibility: { $exists: true }
}).toArray();
console.log('\n📊 Migration Analysis:');
console.log(` Documents with only 'public' field: ${docsWithPublicOnly.length}`);
console.log(` Documents with both fields: ${docsWithBoth.length}`);
console.log(` Total to migrate: ${docsWithPublicOnly.length + docsWithBoth.length}`);
if (docsWithPublicOnly.length === 0 && docsWithBoth.length === 0) {
console.log('\n✅ No documents need migration. All documents already use visibility field.');
return;
}
if (dryRun) {
console.log('\n🔍 DRY RUN - No changes will be made\n');
// Show what would be migrated
if (docsWithPublicOnly.length > 0) {
console.log('Documents with only public field:');
docsWithPublicOnly.forEach(doc => {
const newVisibility = doc.public ? 'public' : 'internal';
console.log(` - ${doc.title} (${doc.slug})`);
console.log(` public: ${doc.public} → visibility: '${newVisibility}'`);
});
}
if (docsWithBoth.length > 0) {
console.log('\nDocuments with both fields (will remove public):');
docsWithBoth.forEach(doc => {
console.log(` - ${doc.title} (${doc.slug})`);
console.log(` current: public=${doc.public}, visibility='${doc.visibility}'`);
console.log(` action: Keep visibility='${doc.visibility}', remove public field`);
});
}
console.log('\n💡 Run with --execute to perform migration');
return;
}
// Perform actual migration
console.log('\n🔄 Performing migration...\n');
let migratedCount = 0;
// Migrate documents with only public field
for (const doc of docsWithPublicOnly) {
const visibility = doc.public ? 'public' : 'internal';
await collection.updateOne(
{ _id: doc._id },
{
$set: { visibility },
$unset: { public: "" }
}
);
console.log(`${doc.title}: public=${doc.public} → visibility='${visibility}'`);
migratedCount++;
}
// Clean up documents with both fields (keep visibility, remove public)
for (const doc of docsWithBoth) {
await collection.updateOne(
{ _id: doc._id },
{ $unset: { public: "" } }
);
console.log(`${doc.title}: Removed public field, kept visibility='${doc.visibility}'`);
migratedCount++;
}
console.log(`\n✅ Migration complete! ${migratedCount} documents updated.`);
// Verify results
const remainingWithPublic = await collection.countDocuments({ public: { $exists: true } });
const totalWithVisibility = await collection.countDocuments({ visibility: { $exists: true } });
console.log('\n📊 Post-migration verification:');
console.log(` Documents with 'public' field: ${remainingWithPublic}`);
console.log(` Documents with 'visibility' field: ${totalWithVisibility}`);
if (remainingWithPublic > 0) {
console.warn('\n⚠ Warning: Some documents still have the public field. Review manually.');
} else {
console.log('\n✅ All documents successfully migrated to visibility field!');
}
} catch (error) {
console.error('❌ Migration failed:', error);
throw error;
}
}
// CLI interface
const args = process.argv.slice(2);
const dryRun = !args.includes('--execute');
if (dryRun) {
console.log('🔍 Running in DRY RUN mode (no changes will be made)');
console.log(' Use --execute flag to perform actual migration\n');
}
migrate(dryRun)
.then(() => {
console.log('\n✨ Script complete');
process.exit(0);
})
.catch((error) => {
console.error('\n💥 Script failed:', error);
process.exit(1);
});