UNPKG

gmail-to-exchange365

Version:

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

155 lines (133 loc) 4.3 kB
import { Client } from "@microsoft/microsoft-graph-client"; import { EmailMessage } from "./types"; interface MSToken { access_token: string; refresh_token?: string; expires_in?: number; } export function getGraphClient(msToken: MSToken) { return Client.init({ authProvider: (done) => { done(null, msToken.access_token); } }); } export async function uploadEmail( client: Client, email: EmailMessage, folderId: string = "Inbox" ): Promise<void> { try { const eml = buildMime(email); // Use the MIME message endpoint await client .api(`/me/mailFolders/${folderId}/messages`) .header("Content-Type", "message/rfc822") .post(eml); } catch (error: any) { // If MIME upload fails, try creating message via Graph API if (error.statusCode === 400 || error.statusCode === 415) { await uploadEmailAsGraphMessage(client, email, folderId); } else { throw error; } } } async function uploadEmailAsGraphMessage( client: Client, email: EmailMessage, folderId: string ): Promise<void> { const message: any = { subject: email.subject, from: { emailAddress: { address: email.from } }, toRecipients: email.to.map(addr => ({ emailAddress: { address: addr } })), body: { contentType: email.htmlBody ? "html" : "text", content: email.htmlBody || email.textBody || "" }, receivedDateTime: email.date, sentDateTime: email.date }; if (email.cc && email.cc.length > 0) { message.ccRecipients = email.cc.map(addr => ({ emailAddress: { address: addr } })); } if (email.bcc && email.bcc.length > 0) { message.bccRecipients = email.bcc.map(addr => ({ emailAddress: { address: addr } })); } const createdMessage = await client .api(`/me/mailFolders/${folderId}/messages`) .post(message); // Upload attachments separately if (email.attachments && email.attachments.length > 0) { for (const attachment of email.attachments) { await uploadAttachment(client, createdMessage.id, attachment); } } } async function uploadAttachment( client: Client, messageId: string, attachment: { filename: string; mimeType: string; data: Buffer } ): Promise<void> { try { await client .api(`/me/messages/${messageId}/attachments`) .post({ "@odata.type": "#microsoft.graph.fileAttachment", name: attachment.filename, contentType: attachment.mimeType, contentBytes: attachment.data.toString("base64") }); } catch (error: any) { console.error(`Error uploading attachment ${attachment.filename}:`, error.message); } } export function buildMime(email: EmailMessage): Buffer { let mime = ""; // Headers mime += `From: ${email.from}\r\n`; mime += `To: ${email.to.join(", ")}\r\n`; if (email.cc && email.cc.length > 0) { mime += `Cc: ${email.cc.join(", ")}\r\n`; } if (email.bcc && email.bcc.length > 0) { mime += `Bcc: ${email.bcc.join(", ")}\r\n`; } mime += `Subject: ${email.subject}\r\n`; mime += `Date: ${email.date}\r\n`; mime += `MIME-Version: 1.0\r\n`; const boundary = "----=_Part_" + Date.now(); mime += `Content-Type: multipart/mixed; boundary="${boundary}"\r\n\r\n`; // Body mime += `--${boundary}\r\n`; if (email.htmlBody) { mime += `Content-Type: text/html; charset=utf-8\r\n`; mime += `Content-Transfer-Encoding: 7bit\r\n\r\n`; mime += `${email.htmlBody}\r\n\r\n`; } else if (email.textBody) { mime += `Content-Type: text/plain; charset=utf-8\r\n`; mime += `Content-Transfer-Encoding: 7bit\r\n\r\n`; mime += `${email.textBody}\r\n\r\n`; } // Attachments for (const attachment of email.attachments || []) { mime += `--${boundary}\r\n`; mime += `Content-Type: ${attachment.mimeType}\r\n`; mime += `Content-Disposition: attachment; filename="${attachment.filename}"\r\n`; mime += `Content-Transfer-Encoding: base64\r\n\r\n`; mime += attachment.data.toString("base64") + "\r\n\r\n"; } mime += `--${boundary}--\r\n`; return Buffer.from(mime, "utf-8"); }