evershopcommerce / evershop

🛍️ NodeJS E-commerce Platform
https://evershop.io/
GNU General Public License v3.0
4.5k stars 1.24k forks source link

[BUG] extension to send email with nodemailer #571

Closed Antoninnnn closed 4 months ago

Antoninnnn commented 5 months ago

Describe the bug No action was performed when i created a user, place an order or reset password.

To Reproduce I created an extension similarly like the resend packages you provided. I have tested that my SMTP server worked well. I am not sure how the sendOrderConfirmationEmailsendOrderConfirmationEmail or the reset password function was triggered. Could you give some clues about the problem?

Here is how i write the sendWelcomeEmail.js:

const path = require('path');
const fs = require('fs').promises;
const { pool } = require('@evershop/evershop/src/lib/postgres/connection');
const { getConfig } = require('@evershop/evershop/src/lib/util/getConfig');
const nodemailer = require('nodemailer')
const Handlebars = require('handlebars');
const { select } = require('@evershop/postgres-query-builder');
const { error } = require('@evershop/evershop/src/lib/log/logger');
const { getEnv } = require('@evershop/evershop/src/lib/util/getEnv');
const { getValue } = require('@evershop/evershop/src/lib/util/registry');

module.exports = async function sendOrderConfirmationEmail(data) {
  try {
    // Check if the API key is set
    const apiKey = getEnv('NODEMAILER_API_KEY', '');
    const from = getConfig('nodemailor.from', '');

    if (!apiKey || !from) {
      return;
    }
    // const resend = new Resend(apiKey);

    let transporter = nodemailer.createTransport({
      host: 'smtp.qq.com',
      port: 465,
      auth: {
          user: 'XXXX@qq.com',
          pass: apiKey
      }
    });

    const customerRegistered = getConfig(
      'nodemailer.events.customer_registered',
      {}
    );

    // Check if the we need to send the email on order placed event
    if (customerRegistered.enabled !== true) {
      return;
    }

    // Build the email data
    const customerId = data.customer_id;
    const customer = await select()
      .from('customer')
      .where('customer_id', '=', customerId)
      .load(pool);

    if (!customer) {
      return;
    }

    // Remove the password
    delete customer.password;

    const emailDataFinal = await getValue(
      'nodemailer_customer_welcome_email_data',
      customer,
      {}
    );
    // Send the email
    const msg = {
      to: emailDataFinal.email,
      subject: customerRegistered.subject || `Welcome`,
      from
    };

    // Read the template if it's set
    if (customerRegistered.templatePath) {
      // So we need to get the full path to the file
      const filePath = path.join(
        process.cwd(),
        customerRegistered.templatePath
      );
      const templateContent = await fs.readFile(filePath, 'utf8');
      msg.html = Handlebars.compile(templateContent)(emailDataFinal);
    } else {
      msg.text = `Hello ${emailDataFinal.full_name}. Welcome to our store!`;
    }

    await transporter.sendMail(msg);
  } catch (e) {
    error(e);
  }
};

5b547a38f69ac6e158d1e6463d21f2a

Background (please complete the following information):

Additional context Add any other context about the problem here.

treoden commented 5 months ago

Hi, Can you share your json configuration file?

Antoninnnn commented 4 months ago

Hi, Can you share your json configuration file?

This is the way i define in the bootstrap.js

const { getConfig } = require('@evershop/evershop/src/lib/util/getConfig');
const { addProcessor } = require('@evershop/evershop/src/lib/util/registry');
const config = require('config');

module.exports = () => {
  const nodemailerConfig = {
    "from": "Customer Service <sixdy2024@qq.com>",
    "events": {
        "order_placed": {
          "subject": "Order Confirmation",
          "enabled": true,
          "templatePath": undefined 
        },
        "reset_password": {
          "subject": "Reset Password",
          "enabled": true,
          "templatePath": undefined 
        },
        "customer_registered": {
          "subject": "Welcome",
          "enabled": true,
          "templatePath": undefined 
        }
      }
  };
  config.util.setModuleDefaults('nodemailer', nodemailerConfig);
  // Add a processor to proceed the email data before sending
  addProcessor('nodemailer_order_confirmation_email_data', (order) => {
    // Convert the order.created_at to a human readable date
    const locale = getConfig('shop.language', 'en');
    const options = { year: 'numeric', month: 'long', day: 'numeric' };

    // eslint-disable-next-line no-param-reassign
    order.created_at = new Date(order.created_at).toLocaleDateString(
      locale,
      options
    );

    // Add the order total text including the currency
    // eslint-disable-next-line no-param-reassign
    order.grand_total_text = Number(order.grand_total).toLocaleString(locale, {
      style: 'currency',
      currency: order.currency
    });

    return order;
  });
};

And i did not introduce the html template yet.

Antoninnnn commented 4 months ago

OOOOOOOH no!!! I made a mistake in the const from = getConfig('nodemailor.from', '');

It should be const from = getConfig('nodemailer.from', '');

Thank you so much

Antoninnnn commented 4 months ago

By the way, I am also curious about the trigger mechanism of these functions, could you explain more?

treoden commented 4 months ago

What function @Antoninnnn ?

Antoninnnn commented 4 months ago

The functions sendOrderConfirmationEmailsendOrderConfirmationEmail.

Antoninnnn commented 4 months ago

I notice it in your documentation events and subscribers .