Skip to content
This repository has been archived by the owner on Apr 3, 2023. It is now read-only.

Commit

Permalink
refactor: make more params configurable for SMTP connector (#21)
Browse files Browse the repository at this point in the history
  • Loading branch information
darcyYe authored Oct 19, 2022
1 parent e68eaa0 commit 7d1e5dd
Show file tree
Hide file tree
Showing 6 changed files with 288 additions and 47 deletions.
66 changes: 50 additions & 16 deletions packages/connector-smtp/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,10 @@ By following the post, your connector JSON should be like this:
{
"host": "smtp.gmail.com",
"port": 587, // your SMTP port
"username": "<your-gmail-address>",
"password": "<password-to-previous-gmail-address>",
"auth": {
"user": "<your-gmail-address>",
"pass": "<password-to-previous-gmail-address>",
},
"fromEmail": "<your-gmail-address>",
"templates": [
{
Expand All @@ -79,8 +81,10 @@ After going through the guide, your connector JSON should look like this:
{
"host": "smtp.sendgrid.net",
"port": 587, // your SMTP port
"username": "apiKey",
"password": "<api-key-with-at-least-mail-permission>",
"auth": {
"user": "apiKey",
"pass": "<api-key-with-at-least-mail-permission>",
},
"fromEmail": "<email-address-of-a-verified-sender>",
"templates": [
{
Expand Down Expand Up @@ -109,8 +113,10 @@ After going through the guide, your connector JSON should look like this:
{
"host": "<SMTP-service-address>",
"port": 1234, // your SMTP port
"username": "<email-address-of-chosen-sender-address>",
"password": "<api-key-with-at-least-mail-permission>",
"auth": {
"user": "<email-address-of-chosen-sender-address>",
"pass": "<api-key-with-at-least-mail-permission>",
},
"fromEmail": "<email-address-of-a-verified-sender-should-be-the-same-as-`username`>",
"templates": [
{
Expand Down Expand Up @@ -141,8 +147,6 @@ That's it. Don't forget to [Enable connector in sign-in experience](https://docs
|-----------|------------|
| host | string |
| port | string |
| username | string |
| password | string |
| fromEmail | string |
| templates | Template[] |

Expand All @@ -153,12 +157,25 @@ That's it. Don't forget to [Enable connector in sign-in experience](https://docs
| usageType | enum string | 'Register' \| 'SignIn' \| 'Test' |
| contentType | enum string | 'text/plain' \| 'text/html' |

**Username and password Auth Options**

| Name | Type | Enum values |
|------|------------------------|-------------|
| user | string | N/A |
| pass | string | N/A |
| type | enum string (OPTIONAL) | 'login' |

You can also configure [OAuth2 Auth Options](https://nodemailer.com/smtp/oauth2/) and other advanced configurations. See [here](https://nodemailer.com/smtp/) for more details.

We gave an example config with all configurable parameters in the text box to help you to set up own configuration. (You are responsible to the configuration, some values are for demonstration purpose and may not fit your use case.)

## References

- [Gmail - Send email from a printer, scanner, or app](https://support.google.com/a/answer/176600)
- [SendGrid - Integrating with the SMTP API](https://docs.sendgrid.com/for-developers/sending-email/integrating-with-the-smtp-api)
- [Aliyun Direct Mail - Send emails using SMTP](https://www.alibabacloud.com/help/en/directmail/latest/send-emails-using-smtp)
- [Aliyun Direct Mail - SMTP Reference](https://www.alibabacloud.com/help/en/directmail/latest/smtp-reference)
- [Nodemailer - SMTP Transport](https://nodemailer.com/smtp/)

# SMTP 连接器

Expand Down Expand Up @@ -189,8 +206,10 @@ SMTP 是一个所有邮件服务提供商通用的传输协议。
{
"host": "smtp.gmail.com",
"port": 587, // your SMTP port
"username": "<your-gmail-address>",
"password": "<password-to-previous-gmail-address>",
"auth": {
"user": "<your-gmail-address>",
"pass": "<password-to-previous-gmail-address>",
},
"fromEmail": "<your-gmail-address>",
"templates": [
{
Expand All @@ -217,8 +236,10 @@ SMTP 是一个所有邮件服务提供商通用的传输协议。
{
"host": "smtp.sendgrid.net",
"port": 587, // your SMTP port
"username": "apiKey",
"password": "<api-key-with-at-least-mail-permission>",
"auth": {
"user": "apiKey",
"pass": "<api-key-with-at-least-mail-permission>",
},
"fromEmail": "<email-address-of-a-verified-sender>",
"templates": [
{
Expand Down Expand Up @@ -247,8 +268,10 @@ SMTP 是一个所有邮件服务提供商通用的传输协议。
{
"host": "<SMTP-service-address>",
"port": 1234, // your SMTP port
"username": "<email-address-of-chosen-sender-address>",
"password": "<api-key-with-at-least-mail-permission>",
"auth": {
"user": "<email-address-of-chosen-sender-address>",
"pass": "<api-key-with-at-least-mail-permission>",
},
"fromEmail": "<email-address-of-a-verified-sender-should-be-the-same-as-`username`>",
"templates": [
{
Expand Down Expand Up @@ -279,8 +302,6 @@ SMTP 是一个所有邮件服务提供商通用的传输协议。
|-----------|------------|
| host | string |
| port | string |
| username | string |
| password | string |
| fromEmail | string |
| templates | Template[] |

Expand All @@ -291,9 +312,22 @@ SMTP 是一个所有邮件服务提供商通用的传输协议。
| usageType | enum string | 'Register' \| 'SignIn' \| 'Test' |
| contentType | enum string | 'text/plain' \| 'text/html' |

**用户名密码的授权配置**

| 模板属性 | 类型 | 枚举值 |
|----------|------------------------|---------|
| user | string | N/A |
| pass | string | N/A |
| type | enum string (OPTIONAL) | 'login' |

你也可以使用 [OAuth2 授权配置](https://nodemailer.com/smtp/oauth2/) 和其他高级的 SMTP 配置。点按 [这里](https://nodemailer.com/smtp/) 了解更多.

我们在文本输入框预填的配置样例里预填了所有可配置的参数,以便你可以用来参考并建立自己的配置。(你需要对自己的配置负责,样例配置中展示的一些值是为了示意,可能并不适用你的用户场景。)

## 参考

- [Gmail - 从打印机、扫描仪或应用发送电子邮件](https://support.google.com/a/answer/176600?hl=zh-Hans)
- [SendGrid - Integrating with the SMTP API](https://docs.sendgrid.com/for-developers/sending-email/integrating-with-the-smtp-api)
- [阿里云邮件推送 - 使用 SMTP 发送邮件](https://www.alibabacloud.com/help/zh/directmail/latest/send-emails-using-smtp)
- [阿里云邮件推送 - SMTP 参考](https://www.alibabacloud.com/help/zh/directmail/latest/smtp-reference)
- [Nodemailer - SMTP Transport](https://nodemailer.com/smtp/)
25 changes: 22 additions & 3 deletions packages/connector-smtp/docs/config-template.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
{
"host": "<test.smtp.host>",
"port": 80,
"password": "<password>",
"username": "<username>",
"auth": {
"pass": "<password>",
"user": "<username>"
},
"fromEmail": "<[email protected]>",
"templates": [
{
Expand All @@ -29,5 +31,22 @@
"subject": "Logto Forgot Password with SMTP",
"usageType": "ForgotPassword"
}
]
],
"secure": true,
"tls": {
"rejectUnauthorized":false
},
"servername": "<my-server>",
"ignoreTLS": false,
"requireTLS": true,
"name": "<my-host-name>",
"localAddress": "<local-address>",
"connectionTimeout": 120000,
"greetingTimeout": 30000,
"socketTimeout": 600000,
"dnsTimeout": 30000,
"logger": true,
"debug": true,
"disableFileAccess": false,
"disableUrlAccess": false
}
68 changes: 67 additions & 1 deletion packages/connector-smtp/src/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
import createConnector from '.';
import { mockedConfig } from './mock';
import {
mockedConfig,
mockedOauth2AuthWithKey,
mockedOauth2AuthWithToken,
mockedTlsOptionsWithTls,
mockedTlsOptionsWithoutTls,
mockedConnectionOptionsValid,
mockedConnectionOptionsInvalid,
mockedDebuggingOptions,
mockedSecurityOptions,
} from './mock';
import { smtpConfigGuard } from './types';

const getConfig = jest.fn().mockResolvedValue(mockedConfig);

Expand All @@ -8,3 +19,58 @@ describe('SMTP connector', () => {
await expect(createConnector({ getConfig })).resolves.not.toThrow();
});
});

describe('Test config guard', () => {
it('basic config', () => {
const result = smtpConfigGuard.safeParse(mockedConfig);
expect(result.success && result.data).toMatchObject(expect.objectContaining(mockedConfig));
});

it('config with oauth2 auth (private key needed)', () => {
const testConfig = { ...mockedConfig, auth: mockedOauth2AuthWithKey };
const result = smtpConfigGuard.safeParse(testConfig);
expect(result.success && result.data).toMatchObject(expect.objectContaining(testConfig));
});

it('config with oauth2 auth (token needed)', () => {
const testConfig = { ...mockedConfig, auth: mockedOauth2AuthWithToken };
const result = smtpConfigGuard.safeParse(testConfig);
expect(result.success && result.data).toMatchObject(expect.objectContaining(testConfig));
});

it('config with tls options (with additional `tls` configuration)', () => {
const testConfig = { ...mockedConfig, ...mockedTlsOptionsWithTls };
const result = smtpConfigGuard.safeParse(testConfig);
expect(result.success && result.data).toMatchObject(
expect.objectContaining(mockedTlsOptionsWithTls)
);
});

it('config with tls options (without additional `tls` configuration)', () => {
const testConfig = { ...mockedConfig, ...mockedTlsOptionsWithoutTls };
const result = smtpConfigGuard.safeParse(testConfig);
expect(result.success && result.data).toMatchObject(expect.objectContaining(mockedConfig));
});

it('config with VALID connection options', () => {
const testConfig = { ...mockedConfig, ...mockedConnectionOptionsValid };
const result = smtpConfigGuard.safeParse(testConfig);
expect(result.success && result.data).toMatchObject(expect.objectContaining(testConfig));
});

it('config with INVALID connection options', () => {
const testConfig = { ...mockedConfig, ...mockedConnectionOptionsInvalid };
const result = smtpConfigGuard.safeParse(testConfig);
expect(result.success && result.data).toMatchObject(expect.objectContaining(mockedConfig));
});

it('config with debugging and security options', () => {
const testConfig = {
...mockedConfig,
...mockedDebuggingOptions,
...mockedSecurityOptions,
};
const result = smtpConfigGuard.safeParse(testConfig);
expect(result.success && result.data).toMatchObject(expect.objectContaining(testConfig));
});
});
21 changes: 4 additions & 17 deletions packages/connector-smtp/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,7 @@ const sendMessage =
const { to, type, payload } = data;
const config = inputConfig ?? (await getConfig(defaultMetadata.id));
validateConfig<SmtpConfig>(config, smtpConfigGuard);
const { host, port, username, password, fromEmail, replyTo, templates } = config;
const template = templates.find((template) => template.usageType === type);
const template = config.templates.find((template) => template.usageType === type);

assert(
template,
Expand All @@ -32,19 +31,7 @@ const sendMessage =
)
);

const configOptions: SMTPTransport.Options = {
host,
port,
auth: {
user: username,
pass: password,
},
// Set `secure` to be false and `requireTLS` to be true to make sure `nodemailer` calls STARTTLS, which is wildly adopted in email servers.
secure: false,
requireTLS: true,
// Enable `logger` to help debugging.
logger: true,
};
const configOptions: SMTPTransport.Options = config;

const transporter = nodemailer.createTransport(configOptions);

Expand All @@ -57,8 +44,8 @@ const sendMessage =

const mailOptions = {
to,
from: fromEmail,
replyTo,
from: config.fromEmail,
replyTo: config.replyTo,
subject: template.subject,
...contentsObject,
};
Expand Down
49 changes: 47 additions & 2 deletions packages/connector-smtp/src/mock.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
export const mockedConfig = {
host: '<test.smtp.host>',
port: 80,
password: '<password>',
username: '<username>',
auth: { pass: '<password>', user: '<username>' },
fromEmail: '<[email protected]>',
templates: [
{
Expand All @@ -13,3 +12,49 @@ export const mockedConfig = {
},
],
};

export const mockedOauth2AuthWithToken = {
user: '<[email protected]>',
type: 'oauth2',
clientId: '<client-id>',
clientSecret: '<client-secret>',
accessToken: '<access-token>',
};

export const mockedOauth2AuthWithKey = {
user: '<[email protected]>',
serviceClient: '<service-client>',
privateKey: '<private-key>',
};

export const mockedTlsOptionsWithoutTls = {
servername: '<servername>',
ignoreTLS: false,
requireTLS: true,
};

export const mockedTlsOptionsWithTls = {
tls: { rejectUnauthorized: true },
servername: '<servername>',
ignoreTLS: false,
requireTLS: true,
};

export const mockedConnectionOptionsValid = {
localAddress: '<local-address>',
name: '<name>',
};

export const mockedConnectionOptionsInvalid = {
name: '<name>',
};

export const mockedDebuggingOptions = {
logger: true,
debug: false,
};

export const mockedSecurityOptions = {
disableFileAccess: true,
disableUrlAccess: false,
};
Loading

0 comments on commit 7d1e5dd

Please sign in to comment.