Skip to main content
Glama
migrate-all-emails.jsβ€’8.31 kB
#!/usr/bin/env node import { FastMailClient } from './src/fastmail-client.js'; import dotenv from 'dotenv'; dotenv.config({ path: '../.env' }); async function migrateAllEmails() { console.log('πŸ“§ MIGRATING ALL EMAILS TO HIERARCHICAL STRUCTURE\n'); const client = new FastMailClient( process.env.FASTMAIL_API_TOKEN, 'clark@clarkeverson.com', 'clark@clarkeverson.com', 'clarkeverson.com', 'https://api.fastmail.com/jmap/session' ); try { await client.authenticate(); console.log('βœ… Authenticated\n'); const mailboxes = await client.getMailboxes(); console.log(`πŸ“‹ Found ${mailboxes.length} mailboxes\n`); // Define all migrations - old folders to new hierarchical structure const migrations = [ // Information migrations { from: 'News', to: 'Information/News', reason: 'News content' }, { from: 'Newsletter', to: 'Information/Newsletters', reason: 'Newsletter content' }, { from: 'screened-newsletters', to: 'Information/Newsletters', reason: 'Screened newsletters' }, { from: 'Updates', to: 'Information/Newsletters', reason: 'Update notifications' }, { from: 'Education', to: 'Information/Education', reason: 'Educational content' }, // Financial migrations { from: 'Banking', to: 'Financial/Banking', reason: 'Banking notifications' }, { from: 'Transactions', to: 'Financial/Receipts', reason: 'Transaction receipts' }, // Commerce migrations { from: 'Shopping', to: 'Commerce/Orders', reason: 'Shopping orders' }, // Professional migrations { from: 'Technical', to: 'Professional/GitHub', reason: 'Technical content' }, // Cleanup migrations { from: 'Unsubscribed', to: 'Archive', reason: 'Archive unsubscribed emails' } ]; console.log('πŸ“§ STARTING EMAIL MIGRATIONS:'); console.log('='.repeat(60)); let totalEmailsMigrated = 0; for (const migration of migrations) { try { // Find source mailbox const sourceMailbox = mailboxes.find(mb => mb.name === migration.from); if (!sourceMailbox) { console.log(`⚠️ Source '${migration.from}' not found`); continue; } if (sourceMailbox.totalEmails === 0) { console.log(`βœ… ${migration.from} β†’ ${migration.to} (already empty)`); continue; } // Find target mailbox (handle hierarchical paths) let targetMailbox; if (migration.to.includes('/')) { const [parentName, childName] = migration.to.split('/'); const parent = mailboxes.find(mb => mb.name === parentName); if (parent) { targetMailbox = mailboxes.find(mb => mb.parentId === parent.id && mb.name === childName ); } } else { targetMailbox = mailboxes.find(mb => mb.name === migration.to); } if (!targetMailbox) { console.log(`❌ Target '${migration.to}' not found for ${migration.from}`); continue; } console.log(`\nπŸ“§ Migrating: ${migration.from} β†’ ${migration.to}`); console.log(` πŸ“Š Emails to move: ${sourceMailbox.totalEmails}`); console.log(` πŸ“ Reason: ${migration.reason}`); // Get all emails from source mailbox in batches let allEmailIds = []; let position = 0; const batchSize = 100; while (true) { const emailBatch = await client.getEmails({ filter: { inMailbox: sourceMailbox.id }, properties: ['id'], position: position, limit: batchSize }); if (!emailBatch || emailBatch.length === 0) { break; } allEmailIds.push(...emailBatch.map(email => email.id)); position += batchSize; console.log(` πŸ“₯ Loaded ${allEmailIds.length} email IDs...`); if (emailBatch.length < batchSize) { break; // Last batch } } if (allEmailIds.length > 0) { console.log(` πŸ”„ Moving ${allEmailIds.length} emails...`); // Move emails in smaller batches to avoid timeouts const moveBatchSize = 50; let moved = 0; for (let i = 0; i < allEmailIds.length; i += moveBatchSize) { const batch = allEmailIds.slice(i, i + moveBatchSize); await client.moveEmailsToMailbox(batch, targetMailbox.id); moved += batch.length; console.log(` βœ… Moved ${moved}/${allEmailIds.length} emails`); // Small delay between batches await new Promise(resolve => setTimeout(resolve, 1000)); } totalEmailsMigrated += allEmailIds.length; console.log(` πŸŽ‰ Successfully migrated all ${allEmailIds.length} emails`); } else { console.log(` ℹ️ No emails found to move`); } } catch (error) { console.log(` ❌ Migration error: ${error.message}`); } } // Special handling for Transactions sub-folders console.log('\nπŸ“¦ Handling Transactions sub-folders...'); const transactionsParent = mailboxes.find(mb => mb.name === 'Transactions'); if (transactionsParent) { const transactionChildren = mailboxes.filter(mb => mb.parentId === transactionsParent.id); for (const child of transactionChildren) { if (child.totalEmails > 0) { console.log(`\nπŸ“§ Moving from Transactions/${child.name} (${child.totalEmails} emails)`); const targetPath = child.name === 'Orders' ? 'Commerce/Orders' : 'Financial/Receipts'; const [parentName, childName] = targetPath.split('/'); const parent = mailboxes.find(mb => mb.name === parentName); const target = parent ? mailboxes.find(mb => mb.parentId === parent.id && mb.name === childName ) : null; if (target) { try { const emails = await client.getEmails({ filter: { inMailbox: child.id }, properties: ['id'] }); if (emails && emails.length > 0) { await client.moveEmailsToMailbox(emails.map(e => e.id), target.id); console.log(` βœ… Moved ${emails.length} emails to ${targetPath}`); totalEmailsMigrated += emails.length; } } catch (error) { console.log(` ❌ Error moving from Transactions/${child.name}: ${error.message}`); } } } } } console.log('\nπŸŽ‰ ALL EMAIL MIGRATIONS COMPLETE!'); console.log('='.repeat(60)); console.log(`πŸ“Š Total emails migrated: ${totalEmailsMigrated}`); console.log('βœ… All emails now organized in hierarchical structure'); console.log('βœ… Old folders should now be empty and ready for deletion'); // Show final email counts in hierarchical structure console.log('\nπŸ“‚ FINAL HIERARCHICAL STRUCTURE EMAIL COUNTS:'); console.log('='.repeat(60)); const parents = ['Information', 'Financial', 'Commerce', 'Professional', 'Personal']; for (const parentName of parents) { const parent = mailboxes.find(mb => mb.name === parentName); if (parent) { // Refresh mailbox data to get updated counts const updatedMailboxes = await client.getMailboxes(); const updatedParent = updatedMailboxes.find(mb => mb.id === parent.id); console.log(`πŸ“‚ ${parentName} (${updatedParent.totalEmails} emails)`); const children = updatedMailboxes.filter(mb => mb.parentId === parent.id); children.forEach(child => { console.log(` └── ${child.name} (${child.totalEmails} emails)`); }); } } console.log('\nβœ… Ready for final cleanup and sieve rule updates!'); } catch (error) { console.log('❌ Error:', error.message); console.error(error.stack); } } migrateAllEmails().catch(console.error);

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/gr3enarr0w/fastmail-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server