Cloudflare 2022年推出了 电子邮件路由 功能,通过这个功能,我们可以直接在 cloudfalre 接收域名邮件,无需再申请其他第三方的域名邮箱服务。
关于该功能的开通已有多篇文章进行介绍,本文就不再赘述。
使用该服务时,可以选择开通 Call all 功能,将所有除自定义地址以外的邮箱,全部进行转发。
转发方式有两种,一种是转发至另一个邮箱,但这种方式有很大弊端。一是该方式仍需使用传统邮箱进行邮件接收,域名邮箱多用于客户服务方面,最好有一种编程的方式可以直接读取邮件。第二点,该方式依托第三方邮箱服务,服务商会对邮箱进行限制,如邮件过滤,登录验证,邮箱容量等,会导致邮件并不能 100% 到达。
第二种方式则使用 Cloudflare Worker 功能,将接收到的电子邮件通过 Worker 处理。结合自己开发的后端服务,将邮件存储到自己的服务器上,这样就可以通过编程的方式来获取邮箱。
Cloudfalre Worker:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| const { PostalMime } = require('postal-mime');
async function streamToArrayBuffer(stream, streamSize) { let result = new Uint8Array(streamSize); let bytesRead = 0; const reader = stream.getReader(); while (true) { const { done, value } = await reader.read(); if (done) { break; } result.set(value, bytesRead); bytesRead += value.length; } return result; }
export default { async email(message, env, ctx) { let from = message.headers.get("from"); let to = message.headers.get("to"); let subject = message.headers.get("subject"); let data = ""; if (message.raw != null) { const rawEmail = await streamToArrayBuffer(message.raw, message.rawSize); const parser = new PostalMime.default(); const parsedEmail = await parser.parse(rawEmail); data = parsedEmail.html; } let response = await fetch("https://example.com/email", { method: "POST", headers: { "Content-type": "application/json; chatset=UTF-8" }, body: JSON.stringify({ "from": from, "to": to, "subject": subject, "data": data }) }); console.log(response.status, response.statusText, await response.text()); } }
|
以上 Worker 在部署时,需要使用 Wrangler CLI.
部署完成后,在 call all 中选择该 Worker,域名所收到的邮箱将会全部转发至 https://example.com/email
from, to 不采用 message.from, message.to 是因为在实践中发现, message.from 有事会设置为代发邮箱的邮件地址,无法看到具体发件人的邮件地址
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| func NewEmail(c *fiber.Ctx) error { email := new(emailBody) c.BodyParser(email) email.CreatedAt = int(time.Now().Unix()) reg, _ := regexp.Compile(`<(.*)>`) fromSubMatch := reg.FindStringSubmatch(email.From) if len(fromSubMatch) != 0 { email.From = fromSubMatch[1] } toSubMatch := reg.FindStringSubmatch(email.To) if len(toSubMatch) != 0 { email.To = toSubMatch[1] }
domainSplit := strings.Split(email.To, "@") if len(domainSplit) == 2 { email.Domain = domainSplit[1] } rows, err := db.Query("INSERT INTO `table` (`from`,`to`,`subject`,`data`,`domain`,`created_at`) VALUES(?,?,?,?,?,?)", email.From, email.To, email.Subject, email.Data, email.Domain, email.CreatedAt) if err != nil { log.Println(err) return c.SendString("ERROR") } defer rows.Close() return c.SendString("OK") }
|
在该代码中,会将转发过来的邮件存储到数据库中,以便日后查询。需要注意的是 from, to 参数均进行正则过滤。是因为 from, to 参数可能含有 发件人,收件人自定义名称,需要进行删除。
对于发件服务,我目前使用 AWS SES 进行发件。该服务可以直接仅配置子域的 mx 记录,完成域名的认证。然后就可以自定义发件人名称向不特定邮箱进行发件。