UNPKG

gmail-to-exchange365

Version:

Complete Gmail to Exchange 365 migration tool with UI - Migrate emails, attachments, and folders seamlessly

144 lines (123 loc) 4.37 kB
import { google } from "googleapis"; import { EmailMessage } from "./types"; import { simpleParser, ParsedMail } from "mailparser"; interface GmailToken { access_token: string; refresh_token?: string; expiry_date?: number; } export async function fetchAllEmails(gToken: GmailToken): Promise<EmailMessage[]> { const oauth = new google.auth.OAuth2(); oauth.setCredentials(gToken); const gmail = google.gmail({ version: "v1", auth: oauth }); const messages: EmailMessage[] = []; let nextPageToken: string | undefined; do { try { const res = await gmail.users.messages.list({ userId: "me", maxResults: 500, pageToken: nextPageToken }); const messageList = res.data.messages || []; // Fetch messages in batches const batchSize = 10; for (let i = 0; i < messageList.length; i += batchSize) { const batch = messageList.slice(i, i + batchSize); const promises = batch.map(msg => fetchEmailDetails(gmail, msg.id!)); const batchResults = await Promise.all(promises); messages.push(...batchResults.filter(m => m !== null) as EmailMessage[]); } nextPageToken = res.data.nextPageToken || undefined; } catch (error: any) { console.error("Error fetching emails:", error.message); if (error.code === 429) { // Rate limit - wait and retry await new Promise(resolve => setTimeout(resolve, 60000)); continue; } break; } } while (nextPageToken); return messages; } async function fetchEmailDetails(gmail: any, messageId: string): Promise<EmailMessage | null> { try { const raw = await gmail.users.messages.get({ userId: "me", id: messageId, format: "raw" }); if (!raw.data.raw) { return null; } const rawStr = Buffer.from(raw.data.raw, "base64").toString("utf-8"); const parsed = await simpleParser(rawStr); const attachments = []; if (parsed.attachments) { for (const attachment of parsed.attachments) { attachments.push({ filename: attachment.filename || "attachment", mimeType: attachment.contentType || "application/octet-stream", data: attachment.content instanceof Buffer ? attachment.content : Buffer.from(attachment.content as any) }); } } // Helper function to extract addresses const extractAddresses = (addr: any): string[] => { if (!addr) return []; if (Array.isArray(addr)) { return addr.flatMap(a => extractAddresses(a)); } if (addr.value) { if (Array.isArray(addr.value)) { return addr.value.map((v: any) => v.address || v); } return [addr.value.address || addr.value]; } if (addr.address) { return [addr.address]; } return []; }; // Extract from address let fromAddress = ""; if (parsed.from?.text) { fromAddress = parsed.from.text; } else if (parsed.from) { const fromAddrs = extractAddresses(parsed.from); fromAddress = fromAddrs[0] || ""; } return { id: messageId, threadId: raw.data.threadId, from: fromAddress, to: extractAddresses(parsed.to), cc: extractAddresses(parsed.cc), bcc: extractAddresses(parsed.bcc), subject: parsed.subject || "", date: parsed.date?.toISOString() || new Date().toISOString(), htmlBody: parsed.html || undefined, textBody: parsed.text || undefined, attachments: attachments, labels: raw.data.labelIds || [] }; } catch (error: any) { console.error(`Error fetching email ${messageId}:`, error.message); return null; } } export async function fetchGmailLabels(gToken: GmailToken): Promise<string[]> { const oauth = new google.auth.OAuth2(); oauth.setCredentials(gToken); const gmail = google.gmail({ version: "v1", auth: oauth }); try { const res = await gmail.users.labels.list({ userId: "me" }); return (res.data.labels || []).map(label => label.name || "").filter(Boolean); } catch (error: any) { console.error("Error fetching labels:", error.message); return []; } }