/** * Rollback Script: Notebooks → Tags * * This script rolls back the notebooks migration by: * 1. Deleting all notebooks * 2. Removing notebookId from labels (set to empty string) * 3. Removing notebookId from notes (set to null) * * WARNING: This will revert ALL notebook-related changes! * Make sure you have a database backup before running this. * * Usage: * npx tsx scripts/rollback-notebooks.ts * * Confirm by adding --confirm flag: * npx tsx scripts/rollback-notebooks.ts --confirm */ import { prisma } from '../keep-notes/lib/prisma' interface RollbackStats { notebooksDeleted: number labelsUpdated: number notesUpdated: number errors: string[] } /** * Main rollback function */ async function rollbackNotebooks(confirm = false): Promise { console.log('⚠️ NOTEBOOKS ROLLBACK\n') if (!confirm) { console.log('❌ ERROR: Rollback requires confirmation.') console.log('\nTo rollback, run with --confirm flag:') console.log(' npx tsx scripts/rollback-notebooks.ts --confirm') console.log('\n⚠️ This will:') console.log(' - Delete ALL notebooks') console.log(' - Remove notebookId from all labels') console.log(' - Remove notebookId from all notes') console.log(' - IRREVERSIBLY delete notebook associations\n') process.exit(1) } console.log('🔄 Starting rollback...\n') const stats: RollbackStats = { notebooksDeleted: 0, labelsUpdated: 0, notesUpdated: 0, errors: [], } try { // Step 1: Get count before rollback console.log('📊 Counting items to rollback...') const notebookCount = await prisma.notebook.count() const labelCount = await prisma.label.count({ where: { notebookId: { not: '', }, }, }) const noteCount = await prisma.note.count({ where: { notebookId: { not: null, }, }, }) console.log(` Notebooks to delete: ${notebookCount}`) console.log(` Labels to update: ${labelCount}`) console.log(` Notes to update: ${noteCount}\n`) if (notebookCount === 0 && labelCount === 0 && noteCount === 0) { console.log('ℹ️ Nothing to rollback. System is already in pre-migration state.\n') return stats } // Step 2: Update labels (remove notebookId) if (labelCount > 0) { console.log('🏷️ Updating labels...') const labelResult = await prisma.label.updateMany({ where: { notebookId: { not: '', }, }, data: { notebookId: '', }, }) stats.labelsUpdated = labelResult.count console.log(` ✅ Updated ${stats.labelsUpdated} label(s)`) } // Step 3: Update notes (remove notebookId) if (noteCount > 0) { console.log('📝 Updating notes...') const noteResult = await prisma.note.updateMany({ where: { notebookId: { not: null, }, }, data: { notebookId: null, }, }) stats.notesUpdated = noteResult.count console.log(` ✅ Updated ${stats.notesUpdated} note(s)`) } // Step 4: Delete notebooks (cascade deletes labels, but notes are already updated) if (notebookCount > 0) { console.log('📁 Deleting notebooks...') const notebookResult = await prisma.notebook.deleteMany({}) stats.notebooksDeleted = notebookResult.count console.log(` ✅ Deleted ${stats.notebooksDeleted} notebook(s)`) } // Summary console.log('\n' + '='.repeat(60)) console.log('✅ Rollback complete!\n') console.log('📊 Summary:') console.log(` Notebooks deleted: ${stats.notebooksDeleted}`) console.log(` Labels updated: ${stats.labelsUpdated}`) console.log(` Notes updated: ${stats.notesUpdated}`) if (stats.errors.length > 0) { console.log(`\n⚠️ Errors: ${stats.errors.length}`) stats.errors.forEach((error) => console.log(` ❌ ${error}`)) } console.log('\n' + '='.repeat(60)) console.log('\n✨ Rollback successful!') console.log('\n📌 System is now back to the flat tags model:') console.log(' - All notebooks removed') console.log(' - All labels detached from notebooks') console.log(' - All notes back in "Notes générales" (Inbox)\n') return stats } catch (error) { console.error('\n❌ Rollback failed:', error) throw error } finally { await prisma.$disconnect() } } // Run rollback const args = process.argv.slice(2) const isConfirmed = args.includes('--confirm') rollbackNotebooks(isConfirmed) .then(() => { console.log('✨ All done!') process.exit(0) }) .catch((error) => { console.error('\n💥 Fatal error:', error) process.exit(1) })