#!/usr/bin/env node
import { FastMailClient } from './src/fastmail-client.js';
import dotenv from 'dotenv';
dotenv.config({ path: '../.env' });
async function migrateAllEmailsFixed() {
console.log('π§ MIGRATING ALL EMAILS TO HIERARCHICAL STRUCTURE (FIXED)\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 migrations - old folders to new hierarchical structure
const migrations = [
{ from: 'Newsletter', to: 'Information/Newsletters' },
{ from: 'screened-newsletters', to: 'Information/Newsletters' },
{ from: 'Updates', to: 'Information/Newsletters' },
{ from: 'Education', to: 'Information' }, // Move to parent since Education sub-label doesn't exist
{ from: 'Banking', to: 'Financial/Banking' },
{ from: 'Transactions', to: 'Financial/Receipts' },
{ from: 'Shopping', to: 'Commerce/Orders' },
{ from: 'Technical', to: 'Professional/GitHub' },
{ from: 'Unsubscribed', to: 'Archive' }
];
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
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(` π― Target ID: ${targetMailbox.id}`);
// Get emails using correct method signature
let allEmailIds = [];
let position = 0;
const batchSize = 100;
while (true) {
console.log(` π₯ Loading emails batch at position ${position}...`);
const emailResult = await client.getEmails(sourceMailbox.id, batchSize, position);
if (!emailResult || !emailResult.emails || emailResult.emails.length === 0) {
break;
}
allEmailIds.push(...emailResult.emails.map(email => email.id));
console.log(` π₯ Loaded ${emailResult.emails.length} emails (total: ${allEmailIds.length})`);
if (emailResult.emails.length < batchSize) {
break; // Last batch
}
position += batchSize;
}
if (allEmailIds.length > 0) {
console.log(` π Moving ${allEmailIds.length} emails...`);
// Move emails in smaller batches
const moveBatchSize = 25;
let moved = 0;
for (let i = 0; i < allEmailIds.length; i += moveBatchSize) {
const batch = allEmailIds.slice(i, i + moveBatchSize);
try {
await client.moveEmailsToMailbox(batch, targetMailbox.id);
moved += batch.length;
console.log(` β
Moved ${moved}/${allEmailIds.length} emails`);
} catch (error) {
console.log(` β οΈ Batch error (continuing): ${error.message}`);
}
// Delay between batches
await new Promise(resolve => setTimeout(resolve, 2000));
}
totalEmailsMigrated += moved;
console.log(` π Successfully migrated ${moved} emails`);
} else {
console.log(` βΉοΈ No emails found in source mailbox`);
}
} catch (error) {
console.log(` β Migration error for ${migration.from}: ${error.message}`);
}
}
// Handle 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 emailResult = await client.getEmails(child.id, 100, 0);
if (emailResult && emailResult.emails && emailResult.emails.length > 0) {
const emailIds = emailResult.emails.map(e => e.id);
await client.moveEmailsToMailbox(emailIds, target.id);
console.log(` β
Moved ${emailIds.length} emails to ${targetPath}`);
totalEmailsMigrated += emailIds.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}`);
if (totalEmailsMigrated > 0) {
console.log('β
Emails successfully organized in hierarchical structure');
console.log('β
Old folders should now be empty and ready for deletion');
} else {
console.log('β οΈ No emails were migrated - check for issues');
}
} catch (error) {
console.log('β Error:', error.message);
console.error(error.stack);
}
}
migrateAllEmailsFixed().catch(console.error);