tractatus/scripts/migrate-public-to-visibility.js
TheFlow 2af47035ac refactor: remove website code and fix critical startup crashes (Phase 8)
CRITICAL FIX: Server would CRASH ON STARTUP (multiple import errors)

REMOVED (2 scripts):
1. scripts/framework-watchdog.js
   - Monitored .claude/session-state.json (OUR Claude Code setup)
   - Monitored .claude/token-checkpoints.json (OUR file structure)
   - Implementers won't have our .claude/ directory

2. scripts/init-db.js
   - Created website collections: blog_posts, media_inquiries, case_submissions
   - Created website collections: resources, moderation_queue, users, citations
   - Created website collections: translations, koha_donations
   - Next steps referenced deleted scripts (npm run seed:admin)

REWRITTEN (2 files):

src/models/index.js (29 lines → 27 lines)
- REMOVED imports: Document, BlogPost, MediaInquiry, CaseSubmission, Resource
- REMOVED imports: ModerationQueue, User (all deleted in Phase 2)
- KEPT imports: AuditLog, DeliberationSession, GovernanceLog, GovernanceRule
- KEPT imports: Precedent, Project, SessionState, VariableValue, VerificationLog
- Result: Only framework models exported

src/server.js (284 lines → 163 lines, 43% reduction)
- REMOVED: Imports to deleted middleware (csrf-protection, response-sanitization)
- REMOVED: Stripe webhook handling (/api/koha/webhook)
- REMOVED: Static file caching (for deleted public/ directory)
- REMOVED: Static file serving (public/ deleted in Phase 6)
- REMOVED: CSRF token endpoint
- REMOVED: Website homepage with "auth, documents, blog, admin" references
- REMOVED: Instruction sync (scripts/sync-instructions-to-db.js reference)
- REMOVED: Hardcoded log path (${process.env.HOME}/var/log/tractatus/...)
- REMOVED: Website-specific security middleware
- KEPT: Security headers, rate limiting, CORS, body parsers
- KEPT: API routes, governance services, MongoDB connections
- RESULT: Clean framework-only server

RESULT: Repository can now start without crashes, all imports resolve

🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-21 22:17:02 +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);
});