免费创建私有电子邮件 API
由 Mux 赞助的 DEV 全球展示挑战赛:展示你的项目!
本文介绍了如何免费创建您自己的安全电子邮件 API,并解释了所有代码。
这个项目的缘起是我想要打造加载速度极快的网站,让全球用户几乎瞬间就能访问(< 30毫秒)。问题在于:
- 我的每个网站都有一个服务器,用于在联系表格填写完毕后发送电子邮件。
- 服务器速度慢,而且位于地球上的一个(或两个)地方。
- 复制服务器管理起来既麻烦又昂贵。
我解决这些问题的策略是:
- 只需保留前端并将其上传到 CDN:该网站已预先构建完成,完全使用 HTML/CSS/JS,现在即可在全球范围内访问。无需再维护服务器。本文不涉及此主题。
- 搭建一个独立的邮件API服务器:无论何时,只要有人从一个或多个网站填写了联系表单,它都会请求邮件API,然后发送邮件🎉 这就是我们接下来要讨论的内容 :D
👋 了解电子邮件 API
邮件 API 已在 GitHub 上开源,使用 NodeJS 编写:https://github.com/steevepay/email-api。代码非常简洁,只有一个 API 端点POST /send。以下是发送邮件的详细流程:
- 从网站或服务器向电子邮件 API 发出 HTTP 请求。
- 每个 HTTP 请求都会经过验证:如果域名来源属于 CORS 白名单,或者在
Authorization标头中提供了访问密钥,则接受请求;否则,返回 401 状态代码。 - 电子邮件 API 连接到 SMTP 服务器(您选择的服务器),然后发送电子邮件。
获取您的 SMTP 凭据
邮件 API 会连接到 SMTP 服务器来发送邮件,因此您需要 SMTP 凭据。许多 SMTP 服务器都是免费的,请选择您喜欢的服务:
- Brevo:每天最多发送 300 封电子邮件
- Smtp2go:每月发送 1,000 封电子邮件
- Mailjet:每月发送 6000 封电子邮件
- Google SMTP:如果您订阅了 Google Workspace,则每月可获得一定数量的电子邮件发送配额。
- OVHCloud:当您购买域名和主机方案时,OVH 会为您提供一个或多个电子邮件地址的免费 SMTP 服务器。
- Proton Mail:如果您订阅了 Proton,即可访问 SMTP 凭据。
- 这份清单并不完整,请自行查阅相关资料……
电子邮件 API 设置
在继续操作之前,请确保已安装:Git、NodeJS,以及(可选)Docker。
- 首先克隆以下代码库:
git clone https://github.com/steevepay/email-api.git
- 进入
email-api目录:
cd email-api
- 创建一个包含以下配置的 .env 文件,并添加您的 SMTP 凭据:
# SERVER
DOMAIN="mailer.domain.com"
PORT=3000
# SECURITY WHITELIST (OPTIONAL): multiple domains are accepted if it is seperated with a coma, such as `website1.org,website2.org`
WHITELIST="domain.org"
# SECURITY ACCESS KEY (OPTIONAL)
AUTHORIZATION_KEY=""
# SMTP CONFIG
SMTP_HOST=""
SMTP_PORT=""
SMTP_FROM=""
SMTP_PASS=""
- 使用以下命令安装 NPM 包
npm install - 启动服务器:
# With NPM Command Line:
npm run start
# With Docker Compose:
docker-compose up
要发送电子邮件,请向端点发出 HTTP 请求POST /send,您必须以 JSON 格式提供以下正文:
{
"from": "",
"to": "",
"subject": "",
"text": "",
"html": "",
}
这from是邮件发送者,这to是邮件接收者。邮件内容必须以 HTML 和纯文本格式提供。
如果一切顺利,响应将返回OK,状态码为200。
如果出现错误,它将返回以下响应:
- 401:未授权,源域名可能不在 CORS 白名单中,或者您未提供访问密钥。
- 404:您请求的页面不正确。
- 405:HTTP 请求正文不是 JSON 格式,或者格式错误,或者缺少属性。
- 500:服务器出现问题,请检查日志,可能是您的 SMTP 凭据不正确,或者电子邮件未能送达。
堆栈和代码分解
启动 API 只需要三个文件:
index.js
mailer.js
.env
在index.js文件中,环境变量被加载,然后我们可以看到由Express提供支持的 API 路由。以下是/send请求:
app.use(express.json());
app.options('/send', cors(corsOptionsDelegate)) // enable pre-flight request for POST request
app.post(
"/send",
cors(corsOptionsDelegate),
body("from").optional().isEmail().normalizeEmail(),
body("to").optional().isEmail().normalizeEmail(),
body("cc").optional().isEmail().normalizeEmail(),
body("subject")
.optional()
.not()
.isEmpty()
.trim()
.escape()
.isLength({ min: 2 }),
body("html").optional().not().isEmpty().trim().escape().isLength({ min: 2 }),
body("text").optional().not().isEmpty().trim().escape().isLength({ min: 2 }),
(req, res, next) => {
// Finds the validation errors in this request and wraps them in an object with handy functions
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
return next();
},
mailer.sendEmail
);
请注意,每个主体参数都使用 Node 模块express-validator进行验证。
CORS npm 模块作为中间件加载,并在每个 HTTP 请求之前检查请求的源域是否在白名单中:
// Configure CORS
var whitelist = process.env?.WHITELIST && process.env?.WHITELIST !== '' ? process.env.WHITELIST?.split(',') : [];
if (whitelist.length > 0) {
console.log("CORS Whitelist: ", whitelist)
}
var corsOptionsDelegate = function (req, callback) {
if (whitelist.indexOf(req.header('Origin')) === -1 && req.header('authorization') !== process.env.AUTHORIZATION_KEY) {
return callback(new Error("Not allowed by CORS"));
}
return callback(null, true);
}
中间件也接受访问密钥,并且必须通过标头提供Authorization。
如果请求配置正确,mailer.sendEmail则会执行最后一个中间件来发送电子邮件。该函数mailer.sendEmail位于mailer.js文件中:
const nodemailer = require('nodemailer')
sendEmail = function (req, res) {
const _cc = [];
if (req.body.cc) {
_cc.push(req.body.cc);
}
if (process.env.SMTP_CC) {
_cc.push(process.env.SMTP_CC);
}
if (req.body.from && req.body.to && req.body.subject && req.body.text && req.body.html) {
if (req.body?.cc) {
_cc.push(req.body.cc);
}
if (process.env.SMTP_CC) {
_cc.push(process.env.SMTP_CC);
}
return deliver({
from: req.body.from,
to: req.body.to,
cc: _cc,
subject: `${req.body.subject}`,
text: req.body.text,
html: req.body.html
}, res);
} else {
return res.sendStatus(405); // Method Not Allowed
}
}
function deliver(message, res) {
const transporter = nodemailer.createTransport({
host: process.env.SMTP_HOST,
port: process.env.SMTP_PORT,
secure: true, // true for 465, false for other ports
auth: {
user: process.env.SMTP_FROM,
pass: process.env.SMTP_PASS
},
logger: true
})
transporter.sendMail(message, (error, info) => {
if (error) {
// eslint-disable-next-line no-console
console.error("🔴 Send Email Error:", error.toString());
res.sendStatus(500)
} else {
res.sendStatus(200)
}
})
}
module.exports = {
sendEmail
}
Nodemailer模块通过函数连接到 SMTP 服务器nodemailer.createTransport,然后发送邮件transporter.sendMail。函数开始时,会检查邮件的每个参数。如果缺少任何参数,则返回状态码405 。
结论
现在多个网站可以请求同一个 API:该服务是共享的,维护成本低廉且易于维护。
需要考虑的主要缺点:
- 您只能从一个地址发送电子邮件,即与您的 SMTP 凭据关联的地址。
- 要从新网站发送电子邮件,必须将网站域名添加到 CORS 白名单中,这需要重启才能加载新的环境变量。
- API 仅支持单个访问密钥,如果要更新密钥,必须编辑文件
.env并重启服务器。
感谢阅读!干杯🍻
文章来源:https://dev.to/steeve/create-a-private-email-api-for-free-353m