搞定Ghost的邮箱问题
背景
最近刚开始经营ghost博客, 感觉ghost可真好用呀, ui又漂亮, 功能又实用, 性能也很不赖的样子. 而且还是基于我最熟悉的node构建的, 比wordpress啥的平添了几份亲切感. 强烈安利 https://ghost.org/
问题
但, 有一个邮箱的问题, 部署上之后就一直没搞定, 很头痛.
我 ysslang.com
用的是腾讯的邮箱服务. 毕竟是国内的大厂, 企业邮箱还是蛮香的. 可以设置几个服务账号用来统一提供服务, 我设置了一个 no-reply@ysslang.com
用来给服务器发发邮件, 挺不错的.
但按照 https://ghost.org/docs/config/#mail 文档配置了几个环境变量后, 能连通, 但一尝试发送邮件就会报错
[2022-09-26 16:34:00] ERROR Failed to send email. Reason: Mail command failed: 501 mail from address must be same as authorization user.
Failed to send email. Reason: Mail command failed: 501 mail from address must be same as authorization user.
"Please see https://ghost.org/docs/config/#mail for instructions on configuring email."
Error ID:
068188f0-3db9-11ed-bcca-dbe6a2908088
Error Code:
EENVELOPE
----------------------------------------
Error: Mail command failed: 501 mail from address must be same as authorization user
at createMailError (/var/lib/ghost/versions/5.14.2/core/server/services/mail/GhostMailer.js:71:12)
at SMTPConnection._formatError (/var/lib/ghost/versions/5.14.2/node_modules/nodemailer/lib/smtp-connection/index.js:787:19)
at SMTPConnection._actionMAIL (/var/lib/ghost/versions/5.14.2/node_modules/nodemailer/lib/smtp-connection/index.js:1569:34)
at SMTPConnection.<anonymous> (/var/lib/ghost/versions/5.14.2/node_modules/nodemailer/lib/smtp-connection/index.js:1044:18)
at SMTPConnection._processResponse (/var/lib/ghost/versions/5.14.2/node_modules/nodemailer/lib/smtp-connection/index.js:950:20)
at SMTPConnection._onData (/var/lib/ghost/versions/5.14.2/node_modules/nodemailer/lib/smtp-connection/index.js:752:14)
at TLSSocket.SMTPConnection._onSocketData (/var/lib/ghost/versions/5.14.2/node_modules/nodemailer/lib/smtp-connection/index.js:191:44)
at TLSSocket.emit (node:events:513:28)
at addChunk (node:internal/streams/readable:315:12)
at readableAddChunk (node:internal/streams/readable:289:9)
at TLSSocket.Readable.push (node:internal/streams/readable:228:10)
at TLSWrap.onStreamRead (node:internal/stream_base_commons:190:23)
[2022-09-26 16:34:00] INFO "POST /members/api/send-magic-link/" 400 728ms
很奇怪
排查
NodeMailer
后来研究了一番文档和源码, 发现Ghost底层用的是NodeMailer的邮件发送服务, 于是又去翻了翻NodeMailer的配置文档和源码, 发现可以给options里传递debug和logger参数来启动debug日志输出, 于是又给ghost容器多加了两条参数 mail__options__debug: 'true'
和 mail__options__logger: 'true'
, 重新尝试发送邮件后看到了更详细的报错.
[2022-09-26 16:33:59] DEBUG Sending mail using SMTP/6.7.8[client:6.7.8]
[2022-09-26 16:33:59] DEBUG [HVxeyzRDfjw] Resolved smtp.exmail.qq.com as 183.2.143.59 [cache miss]
[2022-09-26 16:33:59] INFO [HVxeyzRDfjw] Secure connection established to 183.2.143.59:465
[2022-09-26 16:34:00] DEBUG [HVxeyzRDfjw] S: 220 smtp.qq.com Esmtp QQ QMail Server
[2022-09-26 16:34:00] DEBUG [HVxeyzRDfjw] C: EHLO [127.0.0.1]
[2022-09-26 16:34:00] DEBUG [HVxeyzRDfjw] S: 250-smtp.qq.com
[2022-09-26 16:34:00] DEBUG [HVxeyzRDfjw] S: 250-PIPELINING
[2022-09-26 16:34:00] DEBUG [HVxeyzRDfjw] S: 250-SIZE 73400320
[2022-09-26 16:34:00] DEBUG [HVxeyzRDfjw] S: 250-AUTH LOGIN PLAIN
[2022-09-26 16:34:00] DEBUG [HVxeyzRDfjw] S: 250-AUTH=LOGIN
[2022-09-26 16:34:00] DEBUG [HVxeyzRDfjw] S: 250-MAILCOMPRESS
[2022-09-26 16:34:00] DEBUG [HVxeyzRDfjw] S: 250 8BITMIME
[2022-09-26 16:34:00] DEBUG [HVxeyzRDfjw] SMTP handshake finished
[2022-09-26 16:34:00] DEBUG [HVxeyzRDfjw] C: AUTH PLAIN ===================================
[2022-09-26 16:34:00] DEBUG [HVxeyzRDfjw] S: 235 Authentication successful
[2022-09-26 16:34:00] INFO [HVxeyzRDfjw] User "no-reply@ysslang.com" authenticated
[2022-09-26 16:34:00] INFO Sending message <ae522939-40de-48dd-e21d-4da0e813b14a@ysslang.com> to <yuansanshilang@gmail.com>
[2022-09-26 16:34:00] DEBUG [HVxeyzRDfjw] C: MAIL FROM:<noreply@ysslang.com>
[2022-09-26 16:34:00] DEBUG [HVxeyzRDfjw] S: 501 mail from address must be same as authorization user
原来是mail__from的地址变成了 noreply@ysslang.com
, 跟我的 no-reply@ysslang.com
不一致.
然而, 根据NodeMailer的文档, mail.from的信息, 是作为邮件的内容 envelope 来提供的, 并不是配置项, 于是只能继续研究 Ghost.
Mail__from
后来研究了一番源码和配置, 始终没能搞定. 谷歌了一下报错, 没想到竟然找到了一样的问题和issue . 根据提示, 我在Ghost的设置里尝试配置服务邮箱为 no-reply@ysslang.com
, 没想到还是失败了, 看日志, 它好像还是会尝试发送邮件后才会配置成功, 而这个邮件发送, 还是会用原来的错误邮箱.
源码调试
没办法, 只能再重新调试源码尝试理解逻辑.
根据 源码安装的文档 准备了一个vscode下的开发环境, yarn setup
后到package.json下找到debug的命令, 运行debug, 然后抓包到发邮箱的请求
curl 'http://localhost:2368/members/api/send-magic-link/' \
-H 'content-type: application/json' \
--data-raw '{"name":"","email":"yuansanshilang@gmail.com","requestSrc":"portal"}'
跟踪了两下后, 找到了原来地址是从这里生成的
getMembersSupportAddress() {
const supportAddress = this.settingsCache.get('members_support_address') || 'noreply';
// Any fromAddress without domain uses site domain, like default setting `noreply`
if (supportAddress.indexOf('@') < 0) {
return `${supportAddress}@${this.getDefaultEmailDomain()}`;
}
return supportAddress;
}
读取了一个 members_support_address
的配置项作为地址来发送邮件, 如果没读到, 就会生成 noreply@域名
来发送.
这才想起来, 我前段页面上改不成功, 直接改后台配置库不就好了嘛, 反正我是外迁的.
解决方案
于是连到外接的mysql里, 找到setting表, 根据key找到了 members_support_address
记录.
原值是 noreply
, 修改为 no-reply@ysslang.com
后保存.
重启ghost, 访问页面, 可以发邮件了!
Member discussion