@kne/fastify-message 1.0.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +254 -0
- package/index.js +38 -0
- package/libs/controllers/main.js +34 -0
- package/libs/models/example._js +13 -0
- package/libs/models/record.js +37 -0
- package/libs/models/template.js +43 -0
- package/libs/services/main.js +137 -0
- package/package.json +60 -0
package/README.md
ADDED
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
|
|
2
|
+
# fastify-message
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
### 描述
|
|
6
|
+
|
|
7
|
+
管理消息发送,支持sms消息,email消息等.
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
### 安装
|
|
11
|
+
|
|
12
|
+
```shell
|
|
13
|
+
npm i --save @kne/fastify-message
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
### 概述
|
|
18
|
+
|
|
19
|
+
### 项目概述
|
|
20
|
+
|
|
21
|
+
fastify-message 是一个基于 Fastify 框架的消息管理插件,提供了模板管理和消息发送功能。目前支持邮件发送,并预留了短信发送的扩展接口。
|
|
22
|
+
|
|
23
|
+
### 主要特性
|
|
24
|
+
|
|
25
|
+
- 支持邮件模板管理
|
|
26
|
+
- 支持模板变量替换
|
|
27
|
+
- 支持 HTML 和纯文本邮件格式
|
|
28
|
+
- 支持邮件发送记录追踪
|
|
29
|
+
- 支持系统级和业务级模板分类
|
|
30
|
+
- 支持模板状态管理(启用/禁用)
|
|
31
|
+
|
|
32
|
+
### 安装
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
npm install fastify-message
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### 基础配置
|
|
39
|
+
|
|
40
|
+
```javascript
|
|
41
|
+
const fastify = require('fastify')();
|
|
42
|
+
|
|
43
|
+
fastify.register(require('fastify-message'), {
|
|
44
|
+
name: 'message', // 插件注册名称
|
|
45
|
+
emailConfig: {
|
|
46
|
+
host: 'smtp.example.com',
|
|
47
|
+
port: 465,
|
|
48
|
+
secure: true,
|
|
49
|
+
user: 'your-email@example.com',
|
|
50
|
+
pass: 'your-password',
|
|
51
|
+
defaultSubject: '系统通知' // 默认邮件主题
|
|
52
|
+
},
|
|
53
|
+
isTest: false // 测试模式下不会真实发送邮件
|
|
54
|
+
});
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### 模板格式
|
|
58
|
+
|
|
59
|
+
模板文件使用 EJS 语法,支持以下特殊注释标记来区分不同部分:
|
|
60
|
+
|
|
61
|
+
```html
|
|
62
|
+
<!-- subject -->
|
|
63
|
+
邮件主题
|
|
64
|
+
|
|
65
|
+
<!-- html -->
|
|
66
|
+
<h1>你好,<%= username %>!</h1>
|
|
67
|
+
<div><%= content %></div>
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### 模板命名规则
|
|
71
|
+
|
|
72
|
+
模板文件命名格式:`code_type_name.ejs`
|
|
73
|
+
|
|
74
|
+
- code: 模板编码
|
|
75
|
+
- type: 模板类型(可选,默认为0)
|
|
76
|
+
- 0: 邮件
|
|
77
|
+
- 1: 短信
|
|
78
|
+
- name: 模板名称(可选,默认使用code)
|
|
79
|
+
|
|
80
|
+
示例:`welcome_0_欢迎邮件.ejs`
|
|
81
|
+
|
|
82
|
+
### 使用示例
|
|
83
|
+
|
|
84
|
+
```javascript
|
|
85
|
+
// 发送邮件
|
|
86
|
+
await fastify.message.services.sendMessage({
|
|
87
|
+
type: 0, // 0: 邮件
|
|
88
|
+
name: 'recipient@example.com', // 收件人
|
|
89
|
+
code: 'welcome', // 模板编码
|
|
90
|
+
props: { // 模板变量
|
|
91
|
+
username: 'John',
|
|
92
|
+
content: 'Welcome to our platform!'
|
|
93
|
+
},
|
|
94
|
+
options: {
|
|
95
|
+
title: '系统通知', // 发件人名称
|
|
96
|
+
attachments: [] // 附件列表
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
// 导入模板文件
|
|
101
|
+
await fastify.message.services.includeTemplate('./templates');
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
### 示例
|
|
106
|
+
|
|
107
|
+
#### 示例代码
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
### API
|
|
112
|
+
|
|
113
|
+
### 服务方法 (Services)
|
|
114
|
+
|
|
115
|
+
#### includeTemplate
|
|
116
|
+
|
|
117
|
+
从指定目录导入模板文件到数据库。
|
|
118
|
+
|
|
119
|
+
| 参数 | 类型 | 必填 | 描述 |
|
|
120
|
+
|-----|--------|----|----------|
|
|
121
|
+
| dir | String | 是 | 模板文件目录路径 |
|
|
122
|
+
|
|
123
|
+
| 返回值 | 类型 | 描述 |
|
|
124
|
+
|-----|---------------|------|
|
|
125
|
+
| - | Promise<void> | 无返回值 |
|
|
126
|
+
|
|
127
|
+
示例:
|
|
128
|
+
|
|
129
|
+
```javascript
|
|
130
|
+
await fastify.message.services.includeTemplate('./templates');
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
#### messageTemplate
|
|
134
|
+
|
|
135
|
+
根据模板编码和参数生成消息内容。
|
|
136
|
+
|
|
137
|
+
| 参数 | 类型 | 必填 | 默认值 | 描述 |
|
|
138
|
+
|-------|--------|----|-----|------------|
|
|
139
|
+
| code | String | 是 | - | 模板编码 |
|
|
140
|
+
| type | Number | 否 | 0 | 模板类型(0:邮件) |
|
|
141
|
+
| level | Number | 否 | 0 | 模板级别 |
|
|
142
|
+
| props | Object | 是 | - | 模板变量 |
|
|
143
|
+
|
|
144
|
+
| 返回值字段 | 类型 | 描述 |
|
|
145
|
+
|-----------------|--------|--------|
|
|
146
|
+
| content.subject | String | 邮件主题 |
|
|
147
|
+
| content.html | String | HTML内容 |
|
|
148
|
+
| content.text | String | 纯文本内容 |
|
|
149
|
+
| props | Object | 模板变量 |
|
|
150
|
+
| code | String | 模板编码 |
|
|
151
|
+
| type | Number | 模板类型 |
|
|
152
|
+
| templateId | Number | 模板ID |
|
|
153
|
+
|
|
154
|
+
示例:
|
|
155
|
+
|
|
156
|
+
```javascript
|
|
157
|
+
const message = await fastify.message.services.messageTemplate({
|
|
158
|
+
code: 'welcome',
|
|
159
|
+
props: {
|
|
160
|
+
username: 'John',
|
|
161
|
+
content: 'Welcome!'
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
#### parseTemplate
|
|
167
|
+
|
|
168
|
+
解析模板文本中的特殊注释标记。
|
|
169
|
+
|
|
170
|
+
| 参数 | 类型 | 必填 | 描述 |
|
|
171
|
+
|------|--------|----|--------|
|
|
172
|
+
| text | String | 是 | 模板文本内容 |
|
|
173
|
+
|
|
174
|
+
| 返回值字段 | 类型 | 描述 |
|
|
175
|
+
|---------|--------|------------------------|
|
|
176
|
+
| subject | String | <!-- subject --> 标记内容 |
|
|
177
|
+
| html | String | <!-- html --> 标记内容 |
|
|
178
|
+
| text | String | <!-- text --> 标记内容(可选) |
|
|
179
|
+
|
|
180
|
+
#### sendMessage
|
|
181
|
+
|
|
182
|
+
发送消息(邮件/短信)。
|
|
183
|
+
|
|
184
|
+
| 参数 | 类型 | 必填 | 默认值 | 描述 |
|
|
185
|
+
|---------|--------|----|-----|---------------|
|
|
186
|
+
| type | Number | 否 | 0 | 消息类型(0:邮件) |
|
|
187
|
+
| name | String | 是 | - | 接收者(邮件地址/手机号) |
|
|
188
|
+
| props | Object | 是 | - | 模板变量 |
|
|
189
|
+
| code | String | 是 | - | 模板编码 |
|
|
190
|
+
| level | Number | 否 | 0 | 模板级别 |
|
|
191
|
+
| client | Object | 否 | - | 客户端配置(覆盖默认配置) |
|
|
192
|
+
| options | Object | 否 | - | 发送选项 |
|
|
193
|
+
|
|
194
|
+
options 参数详情:
|
|
195
|
+
|
|
196
|
+
| 字段 | 类型 | 必填 | 描述 |
|
|
197
|
+
|-------------|--------|----|-------|
|
|
198
|
+
| title | String | 否 | 发件人名称 |
|
|
199
|
+
| subject | String | 否 | 邮件主题 |
|
|
200
|
+
| attachments | Array | 否 | 附件列表 |
|
|
201
|
+
|
|
202
|
+
示例:
|
|
203
|
+
|
|
204
|
+
```javascript
|
|
205
|
+
await fastify.message.services.sendMessage({
|
|
206
|
+
type: 0,
|
|
207
|
+
name: 'user@example.com',
|
|
208
|
+
code: 'welcome',
|
|
209
|
+
props: {
|
|
210
|
+
username: 'John',
|
|
211
|
+
content: 'Welcome!'
|
|
212
|
+
},
|
|
213
|
+
options: {
|
|
214
|
+
title: 'System',
|
|
215
|
+
attachments: [
|
|
216
|
+
{
|
|
217
|
+
filename: 'welcome.pdf',
|
|
218
|
+
path: './welcome.pdf'
|
|
219
|
+
}
|
|
220
|
+
]
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
### 数据模型 (Models)
|
|
226
|
+
|
|
227
|
+
#### Template 模型
|
|
228
|
+
|
|
229
|
+
| 字段 | 类型 | 默认值 | 描述 |
|
|
230
|
+
|-----------|--------|-----|------------|
|
|
231
|
+
| id | Number | - | 主键 |
|
|
232
|
+
| code | String | - | 模板编码 |
|
|
233
|
+
| type | Number | 0 | 模板类型 |
|
|
234
|
+
| name | String | - | 模板名称 |
|
|
235
|
+
| content | String | - | 模板内容 |
|
|
236
|
+
| level | Number | 0 | 模板级别 |
|
|
237
|
+
| status | Number | 0 | 模板状态(0:启用) |
|
|
238
|
+
| createdAt | Date | - | 创建时间 |
|
|
239
|
+
| updatedAt | Date | - | 更新时间 |
|
|
240
|
+
|
|
241
|
+
#### Record 模型
|
|
242
|
+
|
|
243
|
+
| 字段 | 类型 | 默认值 | 描述 |
|
|
244
|
+
|------------|--------|-----|------|
|
|
245
|
+
| id | Number | - | 主键 |
|
|
246
|
+
| type | Number | - | 消息类型 |
|
|
247
|
+
| code | String | - | 模板编码 |
|
|
248
|
+
| templateId | Number | - | 模板ID |
|
|
249
|
+
| props | Object | - | 模板变量 |
|
|
250
|
+
| name | String | - | 接收者 |
|
|
251
|
+
| content | Object | - | 发送内容 |
|
|
252
|
+
| createdAt | Date | - | 创建时间 |
|
|
253
|
+
| updatedAt | Date | - | 更新时间 |
|
|
254
|
+
|
package/index.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
const fp = require('fastify-plugin');
|
|
2
|
+
const path = require('node:path');
|
|
3
|
+
|
|
4
|
+
module.exports = fp(async function (fastify, options) {
|
|
5
|
+
options = Object.assign(
|
|
6
|
+
{},
|
|
7
|
+
{
|
|
8
|
+
name: 'message',
|
|
9
|
+
dbTableNamePrefix: 't_message_',
|
|
10
|
+
getUserModel: () => {
|
|
11
|
+
if (!fastify.account) {
|
|
12
|
+
throw new Error('fastify-account plugin must be registered before fastify-trtc-conference,or set options.getUserModel');
|
|
13
|
+
}
|
|
14
|
+
return fastify.account.models.user;
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
options
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
fastify.register(require('@kne/fastify-namespace'), {
|
|
21
|
+
name: options.name,
|
|
22
|
+
options,
|
|
23
|
+
modules: [
|
|
24
|
+
[
|
|
25
|
+
'models',
|
|
26
|
+
await fastify.sequelize.addModels(path.resolve(__dirname, './libs/models'), {
|
|
27
|
+
prefix: options.dbTableNamePrefix,
|
|
28
|
+
getUserModel: options.getUserModel
|
|
29
|
+
})
|
|
30
|
+
],
|
|
31
|
+
['services', path.resolve(__dirname, './libs/services')]
|
|
32
|
+
]
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
fastify.register(async (fastify, options) => {
|
|
36
|
+
await fastify[options.name].services.includeTemplate(options.templateDir);
|
|
37
|
+
}, options);
|
|
38
|
+
});
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
const fp = require('fastify-plugin');
|
|
2
|
+
|
|
3
|
+
module.exports = fp(async (fastify, options) => {
|
|
4
|
+
const { services } = fastify.project;
|
|
5
|
+
fastify.get(
|
|
6
|
+
`${options.prefix}/welcome`,
|
|
7
|
+
{
|
|
8
|
+
onRequest: [],
|
|
9
|
+
schema: {
|
|
10
|
+
description: '接口说明',
|
|
11
|
+
summary: '接口主题',
|
|
12
|
+
query: {},
|
|
13
|
+
response: {
|
|
14
|
+
200: {
|
|
15
|
+
description: '返回值说明',
|
|
16
|
+
content: {
|
|
17
|
+
'application/json': {
|
|
18
|
+
schema: {
|
|
19
|
+
type: 'object',
|
|
20
|
+
properties: {
|
|
21
|
+
message: { type: 'string', description: '信息' }
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
async request => {
|
|
31
|
+
return { message: 'welcome' };
|
|
32
|
+
}
|
|
33
|
+
);
|
|
34
|
+
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
module.exports = ({DataTypes}) => {
|
|
2
|
+
return {
|
|
3
|
+
name: 'modelName',//此处定义modelName,默认根据文件名转驼峰命名,可以缺省
|
|
4
|
+
model: {
|
|
5
|
+
name: DataTypes.STRING
|
|
6
|
+
}, associate: ({/*这里可以获取models*/}, fastify) => {
|
|
7
|
+
//可以这样获取某个namespace的models fastify.account.models
|
|
8
|
+
}, options: {
|
|
9
|
+
indexed: []
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
module.exports = ({ DataTypes, options }) => {
|
|
2
|
+
return {
|
|
3
|
+
model: {
|
|
4
|
+
name: {
|
|
5
|
+
type: DataTypes.STRING,
|
|
6
|
+
comment: '发送对象'
|
|
7
|
+
},
|
|
8
|
+
type: {
|
|
9
|
+
type: DataTypes.INTEGER,
|
|
10
|
+
defaultValue: 0,
|
|
11
|
+
comment: '发送类型: 0:邮件'
|
|
12
|
+
},
|
|
13
|
+
code: {
|
|
14
|
+
type: DataTypes.INTEGER,
|
|
15
|
+
comment: '消息类型: 1:账号验证码'
|
|
16
|
+
},
|
|
17
|
+
props: {
|
|
18
|
+
type: DataTypes.JSON,
|
|
19
|
+
comment: '消息模版变量'
|
|
20
|
+
},
|
|
21
|
+
content: {
|
|
22
|
+
type: DataTypes.JSONB,
|
|
23
|
+
comment: '消息内容'
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
associate: ({ record, template }) => {
|
|
27
|
+
record.belongsTo(options.getUserModel(), {
|
|
28
|
+
foreignKey: 'userId',
|
|
29
|
+
comment: '发送用户,为空时为系统发送'
|
|
30
|
+
});
|
|
31
|
+
record.belongsTo(template);
|
|
32
|
+
},
|
|
33
|
+
options: {
|
|
34
|
+
comment: '系统消息记录'
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
module.exports = ({ DataTypes, options }) => {
|
|
2
|
+
return {
|
|
3
|
+
model: {
|
|
4
|
+
name: {
|
|
5
|
+
type: DataTypes.STRING,
|
|
6
|
+
comment: '模版名称'
|
|
7
|
+
},
|
|
8
|
+
code: {
|
|
9
|
+
type: DataTypes.STRING,
|
|
10
|
+
comment: '模版编码',
|
|
11
|
+
allowNull: false
|
|
12
|
+
},
|
|
13
|
+
type: {
|
|
14
|
+
type: DataTypes.INTEGER,
|
|
15
|
+
comment: '模版类型: 0:email,1:SMS',
|
|
16
|
+
allowNull: false
|
|
17
|
+
},
|
|
18
|
+
content: {
|
|
19
|
+
type: DataTypes.TEXT,
|
|
20
|
+
comment: '模版内容'
|
|
21
|
+
},
|
|
22
|
+
level: {
|
|
23
|
+
type: DataTypes.INTEGER,
|
|
24
|
+
comment: '模版级别: 0:系统,1:业务',
|
|
25
|
+
defaultValue: 1
|
|
26
|
+
},
|
|
27
|
+
status: {
|
|
28
|
+
type: DataTypes.INTEGER,
|
|
29
|
+
comment: '状态: 0:启用,1:禁用',
|
|
30
|
+
defaultValue: 0
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
associate: ({ template }) => {
|
|
34
|
+
template.belongsTo(options.getUserModel(), {
|
|
35
|
+
foreignKey: 'userId',
|
|
36
|
+
comment: '创建用户,为空时为系统初始化默认创建'
|
|
37
|
+
});
|
|
38
|
+
},
|
|
39
|
+
options: {
|
|
40
|
+
comment: '消息模版'
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
};
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
const fp = require('fastify-plugin');
|
|
2
|
+
const fs = require('fs-extra');
|
|
3
|
+
const path = require('node:path');
|
|
4
|
+
const template = require('lodash/template');
|
|
5
|
+
const merge = require('lodash/merge');
|
|
6
|
+
const nodemailer = require('nodemailer');
|
|
7
|
+
const { convert } = require('html-to-text');
|
|
8
|
+
const pify = require('pify');
|
|
9
|
+
|
|
10
|
+
module.exports = fp(async (fastify, options) => {
|
|
11
|
+
const emailConfig = Object.assign(
|
|
12
|
+
{},
|
|
13
|
+
{
|
|
14
|
+
port: 465,
|
|
15
|
+
secure: true
|
|
16
|
+
},
|
|
17
|
+
options.emailConfig
|
|
18
|
+
);
|
|
19
|
+
const isTest = options.isTest;
|
|
20
|
+
const { models, services } = fastify[options.name];
|
|
21
|
+
const includeTemplate = async dir => {
|
|
22
|
+
if (!(await fs.exists(dir))) {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
const list = await fs.readdir(dir);
|
|
26
|
+
for (const file of list) {
|
|
27
|
+
const filePath = path.join(dir, file);
|
|
28
|
+
const content = fs.readFile(filePath, 'utf8');
|
|
29
|
+
const filename = file.replace(path.extname(file), '');
|
|
30
|
+
const tempArray = filename.split('_');
|
|
31
|
+
const code = tempArray[0],
|
|
32
|
+
type = tempArray[1] || 0,
|
|
33
|
+
name = tempArray[2] || code;
|
|
34
|
+
const codeTemplate = await models.template.findOne({
|
|
35
|
+
where: {
|
|
36
|
+
code,
|
|
37
|
+
type,
|
|
38
|
+
level: 0
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
if (codeTemplate) {
|
|
42
|
+
codeTemplate.content = content;
|
|
43
|
+
codeTemplate.name = name;
|
|
44
|
+
await codeTemplate.save();
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
await models.template.create({
|
|
48
|
+
code,
|
|
49
|
+
type,
|
|
50
|
+
name,
|
|
51
|
+
content,
|
|
52
|
+
level: 0
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const parseTemplate = text => {
|
|
58
|
+
const regex = /<!--\s*([^\n]+?)\s*-->\s*([\s\S]*?)(?=\s*<!--|$)/g;
|
|
59
|
+
let match;
|
|
60
|
+
const result = {};
|
|
61
|
+
|
|
62
|
+
while ((match = regex.exec(text)) !== null) {
|
|
63
|
+
const fieldName = match[1].trim();
|
|
64
|
+
const fieldValue = match[2].trim();
|
|
65
|
+
result[fieldName] = fieldValue.replace(/\n/g, '\\n');
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return result;
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const messageTemplate = async ({ code, type = 0, level = 0, props }) => {
|
|
72
|
+
const codeTemplate = await models.template.findOne({
|
|
73
|
+
where: {
|
|
74
|
+
code,
|
|
75
|
+
type,
|
|
76
|
+
level
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
if (!codeTemplate) {
|
|
80
|
+
throw new Error('template not found');
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const content = parseTemplate(template(codeTemplate.content)(props).split(/<!--(\s*)-->/g));
|
|
84
|
+
|
|
85
|
+
return {
|
|
86
|
+
content,
|
|
87
|
+
props,
|
|
88
|
+
code,
|
|
89
|
+
type,
|
|
90
|
+
templateId: codeTemplate.id
|
|
91
|
+
};
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
const sendMessage = async ({ type = 0, name, props, code, level = 0, client, options }) => {
|
|
95
|
+
if (type === 0) {
|
|
96
|
+
const currentClient = merge(
|
|
97
|
+
{},
|
|
98
|
+
{
|
|
99
|
+
host: emailConfig.host,
|
|
100
|
+
port: emailConfig.port,
|
|
101
|
+
secure: emailConfig.secure,
|
|
102
|
+
auth: {
|
|
103
|
+
user: emailConfig.user,
|
|
104
|
+
pass: emailConfig.pass
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
client
|
|
108
|
+
);
|
|
109
|
+
const smtp = nodemailer.createTransport(currentClient);
|
|
110
|
+
|
|
111
|
+
const { content, type, templateId } = await messageTemplate({ code, type, level, props });
|
|
112
|
+
|
|
113
|
+
const mailOptions = {
|
|
114
|
+
...options,
|
|
115
|
+
from: `"${options.title || emailConfig.user}" <${emailConfig.user}>`,
|
|
116
|
+
to: name,
|
|
117
|
+
subject: content.subject || options.subject || emailConfig.defaultSubject || 'Message reminder',
|
|
118
|
+
html: content.html,
|
|
119
|
+
text: content.text || convert(content.html),
|
|
120
|
+
attachments: options.attachments || []
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
if (isTest) {
|
|
124
|
+
await pify(smtp.sendMail.bind(smtp))(mailOptions);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
await models.record.create({ type, code, templateId, props, name, content: mailOptions });
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
Object.assign(fastify[options.name].services, {
|
|
132
|
+
includeTemplate,
|
|
133
|
+
messageTemplate,
|
|
134
|
+
parseTemplate,
|
|
135
|
+
sendMessage
|
|
136
|
+
});
|
|
137
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@kne/fastify-message",
|
|
3
|
+
"version": "1.0.0-alpha.0",
|
|
4
|
+
"description": "管理消息发送,支持sms消息,email消息等.",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"init": "husky",
|
|
8
|
+
"dev": "nodemon ./index.js",
|
|
9
|
+
"start": "node ./index.js",
|
|
10
|
+
"build:md": "npx @kne/md-doc",
|
|
11
|
+
"start:md": "npx @kne/md-doc --watch",
|
|
12
|
+
"prettier": "prettier --config .prettierrc --write '{libs/**/*,index}.{js,jsx,ts,tsx,json,css,scss}'",
|
|
13
|
+
"lint-staged": "npx lint-staged"
|
|
14
|
+
},
|
|
15
|
+
"lint-staged": {
|
|
16
|
+
"**/*.md": [
|
|
17
|
+
"git add"
|
|
18
|
+
],
|
|
19
|
+
"{libs/**/*,index}.{js,jsx,ts,tsx,json,css,scss}": [
|
|
20
|
+
"prettier --config .prettierrc --write",
|
|
21
|
+
"git add"
|
|
22
|
+
]
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"index.js",
|
|
26
|
+
"libs"
|
|
27
|
+
],
|
|
28
|
+
"repository": {
|
|
29
|
+
"type": "git",
|
|
30
|
+
"url": "git+https://github.com/kne-union/fastify-message.git"
|
|
31
|
+
},
|
|
32
|
+
"keywords": [],
|
|
33
|
+
"author": "linzp",
|
|
34
|
+
"license": "ISC",
|
|
35
|
+
"bugs": {
|
|
36
|
+
"url": "https://github.com/kne-union/fastify-message/issues"
|
|
37
|
+
},
|
|
38
|
+
"homepage": "https://github.com/kne-union/fastify-message#readme",
|
|
39
|
+
"peerDependencies": {
|
|
40
|
+
"@kne/fastify-namespace": "*",
|
|
41
|
+
"@kne/fastify-sequelize": "*",
|
|
42
|
+
"fastify-plugin": ">=5"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@fastify/env": "^4.4.0",
|
|
46
|
+
"@kne/fastify-sequelize": "^2.0.1",
|
|
47
|
+
"fastify": "^5.3.2",
|
|
48
|
+
"fastify-plugin": "^5.0.1",
|
|
49
|
+
"husky": "^9.0.11",
|
|
50
|
+
"nodemon": "^3.1.3",
|
|
51
|
+
"prettier": "^3.2.5"
|
|
52
|
+
},
|
|
53
|
+
"dependencies": {
|
|
54
|
+
"fs-extra": "^11.3.0",
|
|
55
|
+
"html-to-text": "^9.0.5",
|
|
56
|
+
"lodash": "^4.17.21",
|
|
57
|
+
"nodemailer": "^7.0.3",
|
|
58
|
+
"pify": "^6.1.0"
|
|
59
|
+
}
|
|
60
|
+
}
|