production-taskbar-client / src / main / utils / nodemailer.js
nodemailer.js
Raw
import { arch } from "os";
import nodemailer from "nodemailer";
import _ from "lodash";

import { getHostname } from "./os";

const host = process.env.EMAIL_HOST;
const port = process.env.EMAIL_PORT;
const fromEmail = process.env.FROM_EMAIL || "taskbar-client@example.com";
const toEmail = process.env.ADMIN_EMAIL;

const retryTimeout = 30 * 60 * 1000; // 30min

const sendedErrors = [];

const transporter = nodemailer.createTransport({
  host,
  port,
  secure: false, // true for 465, false for other ports
  name: getHostname(), // required usage to prevent nodemailer internal call _getHostname() that leads to exception [A system error occurred: uv_os_gethostname returned ENOSYS (function not implemented)] on win7 with electron=^19
});

export async function sendAdmin({ error, versions }) {
  const id = error?.stack?.substring(0, 60);
  const ignoredErrors = [
    "TypeError: Object has been destroyed",
    "Error: spawn C:\\Program Files\\Common Files\\microsoft shared\\ink\\TabTip.exe ENOENT",
  ];

  if (
    !!error &&
    !sendedErrors.includes(id) &&
    !ignoredErrors.includes(error?.toString())
  ) {
    const subject = error?.name || "unknown error";
    const date = new Date(Date.now()).toLocaleDateString("uk");
    const time = new Date(Date.now()).toLocaleTimeString("uk");
    const v = JSON.stringify(versions);
    const text = `Versions: ${v}\nLocal DT: ${date} ${time}\nError: ${error}\n\nStack trace: ${error?.stack}`;

    try {
      const result = await transporter.sendMail({
        from: fromEmail,
        to: toEmail,
        subject: `${getHostname()}(${arch()}): ${subject}`,
        text,
      });
      sendedErrors.push(id);

      setTimeout(() => {
        _.remove(sendedErrors, (i) => i === id);
      }, retryTimeout);

      return result;
    } catch (e) {
      setTimeout(() => sendAdmin({ error, versions }), retryTimeout);
      return 0;
    }
  }
  return 0;
}

export default transporter;