@befly-addon/admin 1.1.7 → 1.1.10
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/apis/email/send.ts +27 -4
- package/libs/emailHelper.ts +137 -0
- package/package.json +4 -3
- package/plugins/email.ts +2 -169
package/apis/email/send.ts
CHANGED
|
@@ -25,10 +25,10 @@ export default {
|
|
|
25
25
|
return befly.tool.No('邮件插件未加载,请检查配置');
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
28
|
+
const startTime = Date.now();
|
|
29
|
+
|
|
30
|
+
// 发送邮件
|
|
31
|
+
const result = await (befly as any).addon_admin_email.send({
|
|
32
32
|
to: ctx.body.to,
|
|
33
33
|
subject: ctx.body.subject,
|
|
34
34
|
html: ctx.body.isHtml ? ctx.body.content : undefined,
|
|
@@ -37,6 +37,29 @@ export default {
|
|
|
37
37
|
bcc: ctx.body.bcc || undefined
|
|
38
38
|
});
|
|
39
39
|
|
|
40
|
+
// 记录邮件发送日志
|
|
41
|
+
try {
|
|
42
|
+
await befly.db.insData({
|
|
43
|
+
table: 'addon_admin_email_log',
|
|
44
|
+
data: {
|
|
45
|
+
adminId: ctx.user?.id || 0,
|
|
46
|
+
username: ctx.user?.username || '',
|
|
47
|
+
nickname: ctx.user?.nickname || '',
|
|
48
|
+
toEmail: ctx.body.to,
|
|
49
|
+
subject: ctx.body.subject,
|
|
50
|
+
content: ctx.body.content,
|
|
51
|
+
ccEmail: ctx.body.cc || '',
|
|
52
|
+
bccEmail: ctx.body.bcc || '',
|
|
53
|
+
sendTime: startTime,
|
|
54
|
+
sendResult: result.success ? 1 : 0,
|
|
55
|
+
messageId: result.messageId || '',
|
|
56
|
+
failReason: result.error || ''
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
} catch (logError: any) {
|
|
60
|
+
befly.logger.error({ err: logError }, '记录邮件日志失败');
|
|
61
|
+
}
|
|
62
|
+
|
|
40
63
|
if (result.success) {
|
|
41
64
|
return befly.tool.Yes('发送成功', { messageId: result.messageId });
|
|
42
65
|
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 邮件助手类
|
|
3
|
+
* 提供邮件发送功能,支持 SMTP 配置
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import nodemailer from 'nodemailer';
|
|
7
|
+
|
|
8
|
+
import type { Transporter } from 'nodemailer';
|
|
9
|
+
import type { BeflyContext } from 'befly/types/befly.js';
|
|
10
|
+
|
|
11
|
+
/** 邮件配置 */
|
|
12
|
+
export interface EmailConfig {
|
|
13
|
+
/** SMTP 服务器地址 */
|
|
14
|
+
host: string;
|
|
15
|
+
/** SMTP 端口 */
|
|
16
|
+
port: number;
|
|
17
|
+
/** 是否使用 SSL */
|
|
18
|
+
secure: boolean;
|
|
19
|
+
/** 发件人邮箱 */
|
|
20
|
+
user: string;
|
|
21
|
+
/** 邮箱密码或授权码 */
|
|
22
|
+
pass: string;
|
|
23
|
+
/** 发件人名称 */
|
|
24
|
+
fromName?: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** 发送邮件参数 */
|
|
28
|
+
export interface SendEmailOptions {
|
|
29
|
+
/** 收件人邮箱 */
|
|
30
|
+
to: string;
|
|
31
|
+
/** 邮件主题 */
|
|
32
|
+
subject: string;
|
|
33
|
+
/** 纯文本内容 */
|
|
34
|
+
text?: string;
|
|
35
|
+
/** HTML 内容 */
|
|
36
|
+
html?: string;
|
|
37
|
+
/** 抄送 */
|
|
38
|
+
cc?: string;
|
|
39
|
+
/** 密送 */
|
|
40
|
+
bcc?: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** 发送结果 */
|
|
44
|
+
export interface SendEmailResult {
|
|
45
|
+
success: boolean;
|
|
46
|
+
messageId?: string;
|
|
47
|
+
error?: string;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* 邮件助手类
|
|
52
|
+
*/
|
|
53
|
+
export class EmailHelper {
|
|
54
|
+
private config: EmailConfig;
|
|
55
|
+
private transporter: Transporter | null = null;
|
|
56
|
+
private befly: BeflyContext;
|
|
57
|
+
|
|
58
|
+
constructor(befly: BeflyContext, config: EmailConfig) {
|
|
59
|
+
this.befly = befly;
|
|
60
|
+
this.config = config;
|
|
61
|
+
|
|
62
|
+
// 如果配置了邮箱,则创建 transporter
|
|
63
|
+
if (this.config.user && this.config.pass) {
|
|
64
|
+
this.transporter = nodemailer.createTransport({
|
|
65
|
+
host: this.config.host,
|
|
66
|
+
port: this.config.port,
|
|
67
|
+
secure: this.config.secure,
|
|
68
|
+
auth: {
|
|
69
|
+
user: this.config.user,
|
|
70
|
+
pass: this.config.pass
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* 发送邮件
|
|
78
|
+
*/
|
|
79
|
+
async send(options: SendEmailOptions): Promise<SendEmailResult> {
|
|
80
|
+
if (!this.transporter) {
|
|
81
|
+
return {
|
|
82
|
+
success: false,
|
|
83
|
+
error: '邮件服务未配置,请检查 SMTP 配置'
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const fromAddress = this.config.fromName ? `"${this.config.fromName}" <${this.config.user}>` : this.config.user;
|
|
88
|
+
|
|
89
|
+
try {
|
|
90
|
+
const info = await this.transporter.sendMail({
|
|
91
|
+
from: fromAddress,
|
|
92
|
+
to: options.to,
|
|
93
|
+
subject: options.subject,
|
|
94
|
+
text: options.text,
|
|
95
|
+
html: options.html,
|
|
96
|
+
cc: options.cc,
|
|
97
|
+
bcc: options.bcc
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
return {
|
|
101
|
+
success: true,
|
|
102
|
+
messageId: info.messageId
|
|
103
|
+
};
|
|
104
|
+
} catch (error: any) {
|
|
105
|
+
return {
|
|
106
|
+
success: false,
|
|
107
|
+
error: error.message || '发送失败'
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* 验证 SMTP 连接
|
|
114
|
+
*/
|
|
115
|
+
async verify(): Promise<boolean> {
|
|
116
|
+
if (!this.transporter) {
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
try {
|
|
121
|
+
await this.transporter.verify();
|
|
122
|
+
return true;
|
|
123
|
+
} catch {
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* 获取当前配置(隐藏密码)
|
|
130
|
+
*/
|
|
131
|
+
getConfig(): Omit<EmailConfig, 'pass'> & { pass: string } {
|
|
132
|
+
return {
|
|
133
|
+
...this.config,
|
|
134
|
+
pass: this.config.pass ? '******' : ''
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@befly-addon/admin",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.10",
|
|
4
4
|
"title": "管理后台",
|
|
5
5
|
"description": "Befly - 管理后台功能组件",
|
|
6
6
|
"type": "module",
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
},
|
|
22
22
|
"files": [
|
|
23
23
|
"apis",
|
|
24
|
+
"libs",
|
|
24
25
|
"plugins",
|
|
25
26
|
"styles",
|
|
26
27
|
"tables",
|
|
@@ -43,9 +44,9 @@
|
|
|
43
44
|
"url": "https://github.com/chenbimo/befly.git",
|
|
44
45
|
"directory": "packages/addon-admin"
|
|
45
46
|
},
|
|
46
|
-
"gitHead": "
|
|
47
|
+
"gitHead": "9b5f1999ae83f1f0842f763845ec9d3e80e01f19",
|
|
47
48
|
"dependencies": {
|
|
48
|
-
"befly": "3.9.
|
|
49
|
+
"befly": "^3.9.23",
|
|
49
50
|
"befly-shared": "^1.2.3",
|
|
50
51
|
"nodemailer": "^7.0.11",
|
|
51
52
|
"ua-parser-js": "^2.0.7"
|
package/plugins/email.ts
CHANGED
|
@@ -3,57 +3,12 @@
|
|
|
3
3
|
* 提供邮件发送功能,支持 SMTP 配置
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import
|
|
6
|
+
import { EmailHelper } from '../libs/emailHelper.js';
|
|
7
7
|
|
|
8
|
-
import type {
|
|
8
|
+
import type { EmailConfig } from '../libs/emailHelper.js';
|
|
9
9
|
import type { Plugin } from 'befly/types/plugin.js';
|
|
10
10
|
import type { BeflyContext } from 'befly/types/befly.js';
|
|
11
11
|
|
|
12
|
-
/** 邮件配置 */
|
|
13
|
-
export interface EmailConfig {
|
|
14
|
-
/** SMTP 服务器地址 */
|
|
15
|
-
host: string;
|
|
16
|
-
/** SMTP 端口 */
|
|
17
|
-
port: number;
|
|
18
|
-
/** 是否使用 SSL */
|
|
19
|
-
secure: boolean;
|
|
20
|
-
/** 发件人邮箱 */
|
|
21
|
-
user: string;
|
|
22
|
-
/** 邮箱密码或授权码 */
|
|
23
|
-
pass: string;
|
|
24
|
-
/** 发件人名称 */
|
|
25
|
-
fromName?: string;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/** 发送邮件参数 */
|
|
29
|
-
export interface SendEmailOptions {
|
|
30
|
-
/** 收件人邮箱 */
|
|
31
|
-
to: string;
|
|
32
|
-
/** 邮件主题 */
|
|
33
|
-
subject: string;
|
|
34
|
-
/** 纯文本内容 */
|
|
35
|
-
text?: string;
|
|
36
|
-
/** HTML 内容 */
|
|
37
|
-
html?: string;
|
|
38
|
-
/** 抄送 */
|
|
39
|
-
cc?: string;
|
|
40
|
-
/** 密送 */
|
|
41
|
-
bcc?: string;
|
|
42
|
-
/** 发送人ID */
|
|
43
|
-
adminId?: number;
|
|
44
|
-
/** 发送人账号 */
|
|
45
|
-
username?: string;
|
|
46
|
-
/** 发送人昵称 */
|
|
47
|
-
nickname?: string;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/** 发送结果 */
|
|
51
|
-
export interface SendEmailResult {
|
|
52
|
-
success: boolean;
|
|
53
|
-
messageId?: string;
|
|
54
|
-
error?: string;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
12
|
/** 默认配置 */
|
|
58
13
|
const defaultConfig: EmailConfig = {
|
|
59
14
|
host: 'smtp.qq.com',
|
|
@@ -64,128 +19,6 @@ const defaultConfig: EmailConfig = {
|
|
|
64
19
|
fromName: 'Befly System'
|
|
65
20
|
};
|
|
66
21
|
|
|
67
|
-
/**
|
|
68
|
-
* 邮件助手类
|
|
69
|
-
*/
|
|
70
|
-
class EmailHelper {
|
|
71
|
-
private config: EmailConfig;
|
|
72
|
-
private transporter: Transporter | null = null;
|
|
73
|
-
private befly: BeflyContext;
|
|
74
|
-
|
|
75
|
-
constructor(befly: BeflyContext, config: EmailConfig) {
|
|
76
|
-
this.befly = befly;
|
|
77
|
-
this.config = config;
|
|
78
|
-
|
|
79
|
-
// 如果配置了邮箱,则创建 transporter
|
|
80
|
-
if (this.config.user && this.config.pass) {
|
|
81
|
-
this.transporter = nodemailer.createTransport({
|
|
82
|
-
host: this.config.host,
|
|
83
|
-
port: this.config.port,
|
|
84
|
-
secure: this.config.secure,
|
|
85
|
-
auth: {
|
|
86
|
-
user: this.config.user,
|
|
87
|
-
pass: this.config.pass
|
|
88
|
-
}
|
|
89
|
-
});
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* 发送邮件
|
|
95
|
-
*/
|
|
96
|
-
async send(options: SendEmailOptions): Promise<SendEmailResult> {
|
|
97
|
-
if (!this.transporter) {
|
|
98
|
-
return {
|
|
99
|
-
success: false,
|
|
100
|
-
error: '邮件服务未配置,请检查 SMTP 配置'
|
|
101
|
-
};
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
const fromAddress = this.config.fromName ? `"${this.config.fromName}" <${this.config.user}>` : this.config.user;
|
|
105
|
-
|
|
106
|
-
try {
|
|
107
|
-
const info = await this.transporter.sendMail({
|
|
108
|
-
from: fromAddress,
|
|
109
|
-
to: options.to,
|
|
110
|
-
subject: options.subject,
|
|
111
|
-
text: options.text,
|
|
112
|
-
html: options.html,
|
|
113
|
-
cc: options.cc,
|
|
114
|
-
bcc: options.bcc
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
return {
|
|
118
|
-
success: true,
|
|
119
|
-
messageId: info.messageId
|
|
120
|
-
};
|
|
121
|
-
} catch (error: any) {
|
|
122
|
-
return {
|
|
123
|
-
success: false,
|
|
124
|
-
error: error.message || '发送失败'
|
|
125
|
-
};
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
/**
|
|
130
|
-
* 发送邮件并记录日志
|
|
131
|
-
*/
|
|
132
|
-
async sendAndLog(options: SendEmailOptions): Promise<SendEmailResult> {
|
|
133
|
-
const startTime = Date.now();
|
|
134
|
-
const result = await this.send(options);
|
|
135
|
-
|
|
136
|
-
// 记录邮件发送日志
|
|
137
|
-
try {
|
|
138
|
-
await this.befly.db.insData({
|
|
139
|
-
table: 'addon_admin_email_log',
|
|
140
|
-
data: {
|
|
141
|
-
adminId: options.adminId || 0,
|
|
142
|
-
username: options.username || '',
|
|
143
|
-
nickname: options.nickname || '',
|
|
144
|
-
toEmail: options.to,
|
|
145
|
-
subject: options.subject,
|
|
146
|
-
content: options.html || options.text || '',
|
|
147
|
-
ccEmail: options.cc || '',
|
|
148
|
-
bccEmail: options.bcc || '',
|
|
149
|
-
sendTime: startTime,
|
|
150
|
-
sendResult: result.success ? 1 : 0,
|
|
151
|
-
messageId: result.messageId || '',
|
|
152
|
-
failReason: result.error || ''
|
|
153
|
-
}
|
|
154
|
-
});
|
|
155
|
-
} catch (logError: any) {
|
|
156
|
-
this.befly.logger.error({ err: logError }, '记录邮件日志失败');
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
return result;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
/**
|
|
163
|
-
* 验证 SMTP 连接
|
|
164
|
-
*/
|
|
165
|
-
async verify(): Promise<boolean> {
|
|
166
|
-
if (!this.transporter) {
|
|
167
|
-
return false;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
try {
|
|
171
|
-
await this.transporter.verify();
|
|
172
|
-
return true;
|
|
173
|
-
} catch {
|
|
174
|
-
return false;
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
/**
|
|
179
|
-
* 获取当前配置(隐藏密码)
|
|
180
|
-
*/
|
|
181
|
-
getConfig(): Omit<EmailConfig, 'pass'> & { pass: string } {
|
|
182
|
-
return {
|
|
183
|
-
...this.config,
|
|
184
|
-
pass: this.config.pass ? '******' : ''
|
|
185
|
-
};
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
|
|
189
22
|
/**
|
|
190
23
|
* 邮件插件
|
|
191
24
|
*/
|