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>
138 lines
4.5 KiB
JavaScript
138 lines
4.5 KiB
JavaScript
#!/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);
|
||
});
|