#!/usr/bin/env node /** * Database Cleanup CLI Tool * Usage: node cleanup-cli.js [command] [options] */ const databaseCleanupService = require('./src/services/databaseCleanupService'); const logger = require('./src/utils/logger'); const commands = { stats: 'Show database statistics', preview: 'Preview what cleanup would do (dry run)', cleanup: 'Run material cleanup', 'remove-irrelevant': 'Remove materials marked as irrelevant', help: 'Show this help message' }; async function showStats() { try { const stats = await databaseCleanupService.getMaterialCategoryStats(); console.log('\n๐Ÿ“Š DATABASE STATISTICS'); console.log('====================='); console.log(`Total materials: ${stats.totalMaterials}`); console.log(`Problem materials: ${stats.problemMaterials}`); console.log(`Cleanup recommended: ${stats.cleanupRecommended ? 'YES' : 'NO'}`); console.log('\n๐Ÿ“‹ CATEGORY BREAKDOWN:'); stats.categoryBreakdown.forEach(cat => { console.log(` ${cat.category}: ${cat.count} items (${cat.with_subcategory} with subcategory)`); }); if (stats.cleanupRecommended) { console.log('\n๐Ÿ’ก Recommendation: Run "node cleanup-cli.js preview" to see what can be cleaned up'); } } catch (error) { console.error('โŒ Error getting stats:', error.message); process.exit(1); } } async function showPreview(limit = 10) { try { console.log(`\n๐Ÿ” CLEANUP PREVIEW (limit: ${limit})`); console.log('====================================='); const result = await databaseCleanupService.previewCleanup(limit); if (result.processed === 0) { console.log('โœ… No materials need cleanup!'); return; } console.log(`Materials that would be processed: ${result.processed}`); console.log(`Materials that would be updated: ${result.updated}`); if (result.details && result.details.length > 0) { console.log('\n๐Ÿ“ CHANGES THAT WOULD BE MADE:'); result.details.forEach(detail => { const action = detail.isRelevant ? 'UPDATE' : 'DELETE'; console.log(` [${action}] ID ${detail.id}: ${detail.oldCategory}/${detail.oldSubcategory} โ†’ ${detail.newCategory}/${detail.newSubcategory}`); }); } if (result.errors.length > 0) { console.log('\nโš ๏ธ ERRORS:'); result.errors.forEach(error => { console.log(` ${error.materialName}: ${error.error}`); }); } console.log('\n๐Ÿ’ก To apply changes, run: node cleanup-cli.js cleanup'); } catch (error) { console.error('โŒ Error in preview:', error.message); process.exit(1); } } async function runCleanup(limit = 50, onlyInvalid = true) { try { console.log(`\n๐Ÿงน RUNNING CLEANUP (limit: ${limit}, onlyInvalid: ${onlyInvalid})`); console.log('==============================================='); const result = await databaseCleanupService.cleanupMaterials({ limit, onlyInvalid, dryRun: false }); console.log(`โœ… Cleanup completed!`); console.log(`Materials processed: ${result.processed}`); console.log(`Materials updated: ${result.updated}`); console.log(`Errors: ${result.errors.length}`); if (result.details && result.details.length > 0) { console.log('\n๐Ÿ“ CHANGES MADE:'); result.details.forEach(detail => { const action = detail.isRelevant ? 'UPDATED' : 'MARKED FOR DELETION'; console.log(` [${action}] ID ${detail.id}: ${detail.oldCategory}/${detail.oldSubcategory} โ†’ ${detail.newCategory}/${detail.newSubcategory}`); }); } if (result.errors.length > 0) { console.log('\nโš ๏ธ ERRORS:'); result.errors.forEach(error => { console.log(` ${error.materialName}: ${error.error}`); }); } console.log('\n๐Ÿ’ก Run "node cleanup-cli.js remove-irrelevant" to remove materials marked for deletion'); } catch (error) { console.error('โŒ Error in cleanup:', error.message); process.exit(1); } } async function removeIrrelevant(confirm = false) { try { console.log('\n๐Ÿ—‘๏ธ REMOVING IRRELEVANT MATERIALS'); console.log('================================='); const result = await databaseCleanupService.removeIrrelevantMaterials(confirm); if (result.action === 'preview') { console.log(`Found ${result.materialsToDelete.length} materials marked for deletion:`); result.materialsToDelete.forEach(material => { console.log(` - ID ${material.id}: ${material.name}`); }); if (result.materialsToDelete.length > 0) { console.log('\n๐Ÿ’ก To confirm deletion, run: node cleanup-cli.js remove-irrelevant --confirm'); } } else { console.log(`โœ… Deleted ${result.deletedCount} irrelevant materials`); } } catch (error) { console.error('โŒ Error removing irrelevant materials:', error.message); process.exit(1); } } function showHelp() { console.log('\n๐Ÿ› ๏ธ DATABASE CLEANUP CLI TOOL'); console.log('============================'); console.log('\nUsage: node cleanup-cli.js [command] [options]'); console.log('\nCommands:'); Object.entries(commands).forEach(([cmd, desc]) => { console.log(` ${cmd.padEnd(20)} ${desc}`); }); console.log('\nExamples:'); console.log(' node cleanup-cli.js stats'); console.log(' node cleanup-cli.js preview --limit 20'); console.log(' node cleanup-cli.js cleanup --limit 100 --all'); console.log(' node cleanup-cli.js remove-irrelevant --confirm'); console.log('\nOptions:'); console.log(' --limit N Process maximum N materials (default: 50 for cleanup, 10 for preview)'); console.log(' --all Process all materials, not just invalid ones'); console.log(' --confirm Confirm destructive operations'); } async function main() { const args = process.argv.slice(2); const command = args[0] || 'help'; // Parse options const options = { limit: null, all: args.includes('--all'), confirm: args.includes('--confirm') }; const limitIndex = args.indexOf('--limit'); if (limitIndex !== -1 && args[limitIndex + 1]) { options.limit = parseInt(args[limitIndex + 1]); } console.log('๐Ÿš€ Starting Database Cleanup Tool...'); try { switch (command) { case 'stats': await showStats(); break; case 'preview': await showPreview(options.limit || 10); break; case 'cleanup': await runCleanup( options.limit || 50, !options.all // onlyInvalid = !all ); break; case 'remove-irrelevant': await removeIrrelevant(options.confirm); break; case 'help': default: showHelp(); break; } console.log('\nโœจ Done!'); process.exit(0); } catch (error) { console.error('\n๐Ÿ’ฅ Fatal error:', error.message); process.exit(1); } } // Run if called directly if (require.main === module) { main(); } module.exports = { main };