dify icon indicating copy to clipboard operation
dify copied to clipboard

fix: use smtp to send email

Open AnoyiX opened this issue 9 months ago • 4 comments

Description

邀请用户,发送注册邮件时,邮件一直无法发出,经过本地代码测试排查,使用 smtplib 发送邮件,会卡主,同样的 SMTP 配置,使用 aiosmtplib 能正常发送。SMTP 配置的是飞书邮箱。

另外,在部署时,dify-api 在 MODE 为 api 模式时,配置以下参数是无效的,故删除:

      # Mail configuration, support: resend, smtp
      MAIL_TYPE: ''
      # default send from email address, if not specified
      MAIL_DEFAULT_SEND_FROM: 'YOUR EMAIL FROM (eg: no-reply <[email protected]>)'
      SMTP_SERVER: ''
      SMTP_PORT: 587
      SMTP_USERNAME: ''
      SMTP_PASSWORD: ''
      SMTP_USE_TLS: 'true'
      # the api-key for resend (https://resend.com)
      RESEND_API_KEY: ''
      RESEND_API_URL: https://api.resend.com

Type of Change

  • [x] Bug fix (non-breaking change which fixes an issue)
  • [ ] New feature (non-breaking change which adds functionality)
  • [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • [ ] This change requires a documentation update, included: Dify Document
  • [ ] Improvement, including but not limited to code refactoring, performance optimization, and UI/UX improvement
  • [ ] Dependency upgrade

How Has This Been Tested?

使用 aiosmtplib 发送邮件的测试代码:

import asyncio
from email.message import EmailMessage

import aiosmtplib

message = EmailMessage()
message["From"] = "root@localhost"
message["To"] = "[email protected]"
message["Subject"] = "Hello World!"
message.set_content("Sent via aiosmtplib")

asyncio.run(aiosmtplib.send(message, hostname="127.0.0.1", port=25))

Suggested Checklist:

  • [ ] I have performed a self-review of my own code
  • [ ] I have commented my code, particularly in hard-to-understand areas
  • [ ] My changes generate no new warnings
  • [ ] I ran dev/reformat(backend) and cd web && npx lint-staged(frontend) to appease the lint gods
  • [ ] optional I have made corresponding changes to the documentation
  • [ ] optional I have added tests that prove my fix is effective or that my feature works
  • [ ] optional New and existing unit tests pass locally with my changes

AnoyiX avatar May 13 '24 10:05 AnoyiX

aiosmtplib is the asynchronous version of smtplib, but Dify's current flask/celery doesn't support asynchronous. Have you investigated the reason for this stuck?

takatost avatar May 13 '24 11:05 takatost

aiosmtplib is the asynchronous version of smtplib, but Dify's current flask/celery doesn't support asynchronous. Have you investigated the reason for this stuck?

已经知道问题所在了,如果 SMTP 服务器使用 SSL,使用 smtplib.SMTP() 初始化的时候会卡主,需要使用 smtplib.SMTP_SSL() 初始化。

AnoyiX avatar May 14 '24 01:05 AnoyiX

I was able to reproduce this issue with an Implicit TLS server on port 465. Changing the SMTP initialization to the following code resolved the problem:

if self._use_tls:
    smtp = smtplib.SMTP_SSL(self.server, self.port, timeout=10)
else:
    smtp = smtplib.SMTP(self.server, self.port, timeout=10)

However, this might cause issues for Explicit TLS servers using port 587. To address this, we may need an additional flag to distinguish between these scenarios. Since custom ports can be used, it's not always clear whether the connection should use STARTTLS or Implicit TLS.

ref rfc8314

chazzhou avatar May 15 '24 03:05 chazzhou

@takatost Do you have any thoughts on how to distinguish between Explicit and Implicit TLS? Implementing current fixes may cause issues for Explicit TLS servers, as they require STARTTLS implementation. However, Explicit TLS is deprecated and potentially insecure, so Dify might consider not supporting it at all.

chazzhou avatar May 20 '24 16:05 chazzhou

@AnoyiX @chazzhou @rainchen Another question should be added. The URL address for splicing the login invitation email of users must also be configured in the worker environment under services.worker.environment.CONSOLE_WEB_URL in docker-compose.yaml file, as users cannot normally open the invitation link.

drummerglen avatar Jun 13 '24 08:06 drummerglen

@AnoyiX @chazzhou @rainchen Another question should be added. The URL address for splicing the login invitation email of users must also be configured in the worker environment under services.worker.environment.CONSOLE_WEB_URL in docker-compose.yaml file, as users cannot normally open the invitation link.

@drummerglen The CONSOLE_WEB_URL is already under the suggested env variables in the docker-compose.yaml for worker. Perhaps you can make a PR to the documents repo about configuring emails?

chazzhou avatar Jun 13 '24 13:06 chazzhou