Skip to content

Commit

Permalink
Merge pull request #480 from evershopcommerce/dev
Browse files Browse the repository at this point in the history
Dev
  • Loading branch information
treoden authored Mar 16, 2024
2 parents d71d5a7 + 55672a0 commit df8c048
Show file tree
Hide file tree
Showing 11 changed files with 2,100 additions and 106 deletions.
1,486 changes: 1,381 additions & 105 deletions package-lock.json

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ export default function ProductForm({ product, action }) {
qty={response.data.item.qty}
count={response.data.count}
cartUrl="/cart"
toastId={toastId + "-" + Math.random().toString(36).slice(2)}
toastId={`${toastId }-${ Math.random().toString(36).slice(2)}`}
/>,
{ closeButton: false }
)
Expand Down
117 changes: 117 additions & 0 deletions packages/resend/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
# Resend extension for EverShop

A Resend extension for EverShop. This extension is used to send email to customers.

> **Note**: This extension requires EverShop version 1.0.0 or higher.
## Installation

### Step 1: Install the extension package

```javascript
npm install @evershop/resend
```
### Step 2: Register the extension by adding it to the config file

```javascript
// config/default.json
{
...,
"system": [
...,
"extensions": [
...,
{
"name": "resend",
"resolve": "node_modules/@evershop/resend",
"enabled": true,
"priority": 10
}
]
]
}
```

### Step 3: Configure the extension

#### 1. Add your Resend API key to the `.env` file

```env
RESEND_API_KEY=your_api_key
```

#### 2. Add the following configuration to the `config/config.json` file

```javascript
// config/config.json
{
...,
"resend": {
"from": "Customer Service <Your email>",
"events": {
"order_placed": {
"subject": "Order Confirmation",
"enabled": true,
"templatePath": "config/emails/order_confirmation.html" // The path to your email template. Starting from the root of your project
},
"reset_password": {
"subject": "Reset Password",
"enabled": true,
"templatePath": "config/emails/reset_password.html" // The path to your email template. Starting from the root of your project
},
"customer_registered": {
"subject": "Welcome to EverShop",
"enabled": true,
"templatePath": "config/emails/welcome.html" // The path to your email template. Starting from the root of your project
}
}
}
}
```

This extension now supports 3 events:
1. order_placed: This event is fired when a customer places an order. Send an email to confirm the order.
2. reset_password: This event is fired when a customer requests to reset password. Send an email with a link to reset password.
3. customer_registered: This event is fired when a customer registers an account. Send a customer welcome email.

## Email templates

You can customize the email templates by creating your own HTML files. You can keep the HTML files anywhere in your project. Just make sure to provide the correct path in the `config/config.json` file.

For example if you store your email templates in the `config/emails` directory, you can provide the path as `config/emails/order_confirmation.html`.

```javascript
// The folder structure
config
└── emails
├── order_confirmation.html
├── reset_password.html
└── welcome.html

// The config file
{
...,
"resend": {
"from": "Customer Service <Your email>",
"events": {
"order_placed": {
"subject": "Order Confirmation",
"enabled": true,
"templatePath": "config/emails/order_confirmation.html"
},
"reset_password": {
"subject": "Reset Password",
"enabled": true,
"templatePath": "config/emails/reset_password.html"
},
"customer_registered": {
"subject": "Welcome to EverShop",
"enabled": true,
"templatePath": "config/emails/welcome.html"
}
}
}
}
```

> **Note**: You can check the example email templates [here](https://github.com/evershopcommerce/evershop/tree/main/packages/resend/email_template_examples).
86 changes: 86 additions & 0 deletions packages/resend/api/resetPassword/[resetPassword]sendMail.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
const path = require('path');
const fs = require('fs').promises;
const {
INTERNAL_SERVER_ERROR
} = require('@evershop/evershop/src/lib/util/httpStatus');
const { buildUrl } = require('@evershop/evershop/src/lib/router/buildUrl');
const { debug } = require('@evershop/evershop/src/lib/log/debuger');
const {
getContextValue
} = require('@evershop/evershop/src/modules/graphql/services/contextHelper');
const { getConfig } = require('@evershop/evershop/src/lib/util/getConfig');
const { Resend } = require('resend');
const Handlebars = require('handlebars');
const { getEnv } = require('@evershop/evershop/src/lib/util/getEnv');
const { getValue } = require('@evershop/evershop/src/lib/util/registry');

// eslint-disable-next-line no-unused-vars
module.exports = async (request, response, delegate, next) => {
try {
const {
$body: { email, token }
} = response;

// Check if the API key is set
const apiKey = getEnv('RESEND_API_KEY', '');
const from = getConfig('resend.from', '');

if (!apiKey || !from) {
return;
}
const resend = new Resend(apiKey);
const resetPassword = getConfig('resend.events.reset_password', {});

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

// Generate the url to reset password page
const url = buildUrl('updatePasswordPage');
// Add the token to the url
const resetPasswordUrl = `${getContextValue(
request,
'homeUrl'
)}${url}?token=${token}`;

// Build the email data
const emailDataFinal = await getValue(
'resend_reset_password_email_data',
{
resetPasswordUrl
},
{}
);

// Send email to customer
const msg = {
name: 'Reset Password',
to: email,
subject: resetPassword.subject || 'Reset Password',
from
};

// Read the template if it's set
if (resetPassword.templatePath) {
// So we need to get the full path to the file
const filePath = path.join(process.cwd(), resetPassword.templatePath);
const templateContent = await fs.readFile(filePath, 'utf8');
msg.html = Handlebars.compile(templateContent)(emailDataFinal);
} else {
msg.text = `This is your reset password link: ${resetPasswordUrl}`;
}

await resend.emails.send(msg);
next();
} catch (e) {
debug('critical', e);
response.status(INTERNAL_SERVER_ERROR);
response.json({
error: {
status: INTERNAL_SERVER_ERROR,
message: e.message
}
});
}
};
48 changes: 48 additions & 0 deletions packages/resend/bootstrap.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
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 resendConfig = {
from: 'Customer Service <[email protected]>',
events: {
order_placed: {
subject: 'Order Confirmation',
enabled: true,
templatePath: undefined // This is the path to the email template. Starting from the root of the project.
},
reset_password: {
subject: 'Reset Password',
enabled: true,
templatePath: undefined // This is the path to the email template. Starting from the root of the project.
},
customer_registered: {
subject: 'Welcome to Evershop',
enabled: true,
templatePath: undefined // This is the path to the email template. Starting from the root of the project.
}
}
};
config.util.setModuleDefaults('resend', resendConfig);
// Add a processor to proceed the email data before sending
addProcessor('resend_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;
});
};
Loading

0 comments on commit df8c048

Please sign in to comment.