云计算百科
云计算领域专业知识百科平台

MailDev v3 程序化 API 实战指南:在 Node.js 中嵌入 SMTP 测试服务器与 Web 界面

  • 后端
  • 开发工具
  • 测试

【免费下载链接】maildev

:mailbox: SMTP Server + Web Interface for viewing and testing emails during development.

项目地址:
https://gitcode.com/gh_mirrors/ma/maildev

点击查看 免费下载

导读

MailDev v3 提供了完全基于 TypeScript 与现代 async/await 风格的程序化 API,允许开发者将本地 SMTP 开发服务器、Web 邮件查看界面、REST API 与 WebSocket 推送直接嵌入自己的 Node.js 应用、测试框架或工具链中。本文以官方 docs/api.md 为骨架,结合仓库源码逐层展开:从 MailDev 类的构造与启动、内存/磁盘两种存储模型、邮件对象结构与各类操作(查询、附件、删除、中继),到事件监听、独立子包组合、中间件集成与 v2→v3 迁移,最终你将能够把 MailDev 作为一个可编程组件深度集成进自己的项目中。


一、为什么需要程序化 API

MailDev 在开发环境扮演的角色是"本地邮箱":应用把邮件发到本机 SMTP 端口(默认 1025),开发者通过 Web 界面(默认 1080)查看、搜索、下载与删除这些邮件。CLI 方式(maildev 命令)适合独立使用,但遇到以下场景时,程序化 API 是更优解:

  • 自动化测试:为每个并行测试 worker 启动独立实例,通过 smtp: 0 让操作系统分配空闲端口,避免端口冲突;
  • 工具链集成:把 SMTP 服务器嵌入自己的 Express/Fastify 应用或 CI 脚本,统一端口与生命周期管理;
  • 事件驱动:通过 'new'、'delete' 等事件实时响应邮件,例如自动中继关键邮件到真实邮箱;
  • 精细控制:直接操作底层 @maildev/core、@maildev/smtp、@maildev/api 子包,绕过 MailDev 门面类。

从源码结构看,MailDev 类本身是一个门面(facade):构造时将配置合并进默认值,并委托给内部的 Orchestrator 统一创建、启动与停止存储、SMTP、API 三组服务(见 packages/cli/src/index.ts 与 packages/cli/src/server/orchestrator.ts)。

二、快速开始与安装

安装与使用同样简单:

npm install maildev

import { MailDev } from 'maildev'

const maildev = new MailDev({
smtp: 1025,
web: 1080,
})

await maildev.start()

// Access server instances
const servers = maildev.getServers()

// Listen for new emails
servers.smtp.on('new', (email) => {
console.log('Received:', email.subject)
})

// Stop when done
await maildev.stop()

从仓库源码看,start() 内部依次执行:初始化存储(MemoryStorage 或 FileStorage)→ 创建 SMTP 服务器 → 配置中继 → 启动 SMTP → 可选裁剪与恢复磁盘邮件 → 创建并启动 API 服务器(除非 disableWeb),最后把 { storage, smtp, api? } 返回给调用方(见 packages/cli/src/server/orchestrator.ts)。如果服务已经启动,再次调用 start() 会抛出 'Servers are already running' 异常。

三、MailDev 类:构造函数选项详解

MailDev 类提供最简洁的程序化使用方式,构造函数接受一个 PartialMailDevConfig,未提供的字段会与默认配置合并(见 packages/cli/src/config/types.ts)。以下是完整配置选项:

import { MailDev } from 'maildev'

const maildev = new MailDev({
// SMTP Server
smtp: 1025, // SMTP port (default: 1025)
ip: '::', // SMTP bind address (default: '::')

// Web/API Server
web: 1080, // Web UI port (default: 1080)
webIp: '0.0.0.0', // Web bind address (default: '0.0.0.0')
disableWeb: false, // Disable web interface
basePathname: '/', // Base path for web interface

// Storage
mailDirectory: '/tmp/maildev', // Persist emails to disk (optional)
maxEmails: 0, // Keep at most this many emails (0 = unlimited, default)

// Authentication
incomingUser: 'user', // SMTP auth username
incomingPass: 'pass', // SMTP auth password
webUser: 'admin', // Web UI username
webPass: 'admin', // Web UI password

// Relay (outgoing mail)
outgoingHost: 'smtp.example.com',
outgoingPort: 587,
outgoingUser: 'user',
outgoingPass: 'pass',
outgoingSecure: true,
autoRelay: true, // Auto-forward emails

// Logging
verbose: false,
silent: false,
logMailContents: false,

// MCP (Claude integration)
mcp: true, // Enable MCP server at /mcp endpoint
})

下面对几个关键选项做源码级解读:

配置项默认值作用与源码依据
smtp 1025 SMTP 监听端口。注意 SMTP 服务器源码中使用 ?? 而非 \\|\\| 判断默认值,因此显式传入 smtp: 0 会被当作"由操作系统分配空闲端口"而不是回退默认值(见 packages/smtp/src/server.ts)
ip '::' SMTP 绑定地址,默认监听所有 IPv6/IPv4 地址
web 1080 Web/API 服务器端口
webIp '0.0.0.0' Web 服务器绑定地址
disableWeb false 为 true 时不创建 API/UI 服务器,适用于纯 SMTP 场景
mailDirectory 不设置 设置后使用 FileStorage 持久化邮件;不设置则用内存存储。见下方"存储模型"小节
maxEmails 0 邮件数量上限,0 表示不限量(历史默认行为)。设置为正数后,超出上限时最旧的邮件连同磁盘 .eml 文件与附件一并删除
incomingUser / incomingPass 不设置 SMTP 认证凭据,两者同时设置才启用(见 packages/cli/src/server/orchestrator.ts)
webUser / webPass 不设置 Web 界面 HTTP Basic Auth 凭据;/api/healthz 健康检查端点始终免认证(见 packages/api/src/server.ts)
outgoingHost / outgoingPort 不设置 / 25 出站中继服务器地址。注意 outgoingPort 缺省时在 orchestrator 中回退为 25(见 packages/cli/src/server/orchestrator.ts)
outgoingSecure 不设置 中继连接是否使用 TLS/SSL
autoRelay 不设置 自动转发所有收件邮件;传入字符串(如 'catch-all@example.com')则把全部邮件转发到该固定收件人
verbose / silent false 控制日志输出
mcp false 启用 MCP(Model Context Protocol)服务器,挂载于 /mcp 端点,供 Claude 等 Agent 读取与操作邮件

构造背后的机制

MailDev 构造函数在源码中做三件事:用默认配置合并用户配置、根据 verbose/silent 创建日志器、创建 Orchestrator(见 packages/cli/src/index.ts)。Orchestrator 的 createStorage() 根据是否设置 mailDirectory 决定实例化 FileStorage 还是 MemoryStorage,两者都接收 maxEmails 作为上限(见 packages/cli/src/server/orchestrator.ts)。

四、核心方法:start / stop / isRunning / getServers

// 启动所有服务器(异步)
const servers = await maildev.start()
// servers.smtp – SMTP server instance
// servers.storage – Storage instance
// servers.api – API server instance (if not disabled)

// 优雅停止所有服务器(异步)
await maildev.stop()

// 检查服务器是否运行
if (maildev.isRunning()) {
console.log('MailDev is running')
}

// 获取服务器实例
const servers = maildev.getServers()

几个值得注意的细节:

  • stop() 的关闭顺序:源码中按照"API → SMTP → 存储"逆序停止。API 关闭时会先关闭所有 MCP 会话与 WebSocket 连接再关闭 Fastify;存储则调用 close()(见 packages/cli/src/server/orchestrator.ts)。
  • getServers() 的返回时机:只有在 running 且存储、SMTP 都已创建时才返回实例对象,否则返回 null(见 packages/cli/src/server/orchestrator.ts)。
  • Servers 结构:{ storage, smtp, api? },api 在 disableWeb 时为可选(见 packages/cli/src/server/orchestrator.ts)。

五、临时端口(Ephemeral Ports):并行测试的关键技巧

并行测试运行器(如 Jest、Vitest 的多 worker)最大的痛点之一是端口冲突。MailDev 支持传 smtp: 0(或 web: 0)让操作系统分配空闲端口,再读回实际绑定的端口——这是官方推荐的在并行测试中"每个 worker 一个 MailDev"的可靠方式,无需手工分配端口:

const maildev = new MailDev({ smtp: 0, disableWeb: true })
const { smtp } = await maildev.start()

const { host, port } = smtp.getAddress()
console.log(`SMTP listening on ${host}:${port}`)

// Point your app's mail transport at `port` — e.g. nodemailer:
// nodemailer.createTransport({ host, port, secure: false })

SMTP 与 API 服务器都暴露了绑定地址访问器:

  • smtp.getAddress() → { host, port }(以及 smtp.getPort())
  • servers.api?.getAddress() → { host, port } | null(以及 api.getPort())

API 服务器的访问器在开始监听之前返回 null。

const maildev = new MailDev({ smtp: 0, web: 0 })
const { smtp, api } = await maildev.start()

const smtpPort = smtp.getPort() // OS-assigned SMTP port
const webPort = api?.getPort() // OS-assigned web/API port

原理:SMTP 服务器在 start() 成功后通过 this.smtp!.server.address() 读取操作系统实际绑定的端口并写回 this.port(见 packages/smtp/src/server.ts);API 服务器同理,在 listen() 中记录 boundAddress(见 packages/api/src/server.ts)。所以 smtp: 0 报告的是真实端口而非 0。

另外 SMTP 服务器源码对 port: 0 使用 ?? 回退默认值的处理,保证了"0 是有效值"语义正确(见 packages/smtp/src/server.ts)。

六、与邮件交互:事件、查询、分页与持久化

6.1 监听新邮件

MailDev 运行后,可以通过 SMTP 服务器实例访问邮件:

const maildev = new MailDev()
const { smtp } = await maildev.start()

smtp.on('new', (email) => {
console.log('New email received!')
console.log('From:', email.from[0].address)
console.log('To:', email.to.map(t => t.address).join(', '))
console.log('Subject:', email.subject)
console.log('Text:', email.text)
console.log('HTML:', email.html)
})

注意:email.html 经过 sanitizeHtml 净化处理(见 packages/smtp/src/server.ts),避免在 Web 界面中渲染恶意脚本。

6.2 限制保留的邮件数量

MailDev 默认保留所有邮件(maxEmails: 0)。设置正数 maxEmails 后,只保留最新的 N 封,新邮件到达时丢弃最旧的。当邮件持久化到磁盘时,.eml 文件和附件会随邮件一并删除,邮件目录同样保持有界:

const maildev = new MailDev({
mailDirectory: '/var/mail/maildev',
maxEmails: 5000,
})

性能提醒:maxEmails: 0(默认)会无限增长。内存使用与邮件目录都会持续膨胀:按典型邮件大小估算,10000 封邮件约占用 150 MB 堆内存,且磁盘文件永远不会被移除——长时运行或高吞吐场景务必设置正数上限。

实现细节:SMTP 服务器在构造时向存储注册 onEvicted 回调,一旦存储因上限淘汰邮件,就会同步删除磁盘上的 .eml 与附件目录(见 packages/smtp/src/server.ts)。启动时若配置了 maxEmails > 0,orchestrator 还会调用 pruneMailDir 预先清理历史上遗留的超限 .eml 文件(见 packages/cli/src/server/orchestrator.ts)。

6.3 列出大型收件箱:优先使用 storage.list()

smtp.getAllEmails() 会把每封邮件(包括正文)完整物化到内存。列表场景应改用 storage.list()——它返回一页邮件及分页所需的计数,工作量与页大小成正比,而不是与整个存储规模成正比:

const { items, total, unread } = await servers.storage.list({
skip: 0,
limit: 50,
search: 'welcome', // optional: subject, participants and body text
sort: 'desc', // 'desc' (default) is newest first
})

要丢弃邮件正文与头信息(REST /api/email/summary 端点与 Web UI 正是这样做的),把结果页映射到 toSummary:

import { toSummary } from '@maildev/core'

const summaries = items.map(toSummary)

原理:EmailSummary 是刻意设计为"轻量投影"的类型,不包含 html、text、headers 这三个体积大户——每封邮件在列表中只占几百字节,而非几十 KB,这正是 Web UI 在大收件箱下仍保持响应性的关键(见 packages/core/src/types/email.ts)。API 服务端在 /api/email/summary 路由中正是调用 storage.list() 后逐条 toSummary 返回(见 packages/api/src/server.ts)。

6.4 邮件 CRUD 操作

// 获取全部邮件
const emails = await smtp.getAllEmails()
console.log(`Total emails: ${emails.length}`)

// 获取单封邮件
const email = await smtp.getEmail('email-id')
console.log(email.subject)

// 获取原始邮件(EML 格式),可写流落盘
const stream = await smtp.getRawEmail('email-id')
stream.pipe(fs.createWriteStream('email.eml'))

// 删除单封邮件
await smtp.deleteEmail('email-id')

// 删除全部邮件
await smtp.deleteAllEmails()

// 全部标记为已读
const count = await smtp.markAllRead()
console.log(`Marked ${count} emails as read`)

实现细节:

  • getRawEmail 直接以流形式返回 mailDir/<id>.eml 文件(见 packages/smtp/src/server.ts);
  • deleteEmail 会先删除磁盘文件再删除存储记录,成功时发出 'delete' 事件(见 packages/smtp/src/server.ts);
  • deleteAllEmails 会清空整个邮件目录(含附件),并发出 { id: 'all' } 的 'delete' 事件(见 packages/smtp/src/server.ts);
  • markAllRead 返回被标记的邮件数量,底层调用存储的 markAllRead()。

6.5 附件操作

const email = await smtp.getEmail('email-id')

for (const attachment of email.attachments) {
console.log(`Attachment: ${attachment.filename}`)
console.log(`Type: ${attachment.contentType}`)
console.log(`Size: ${attachment.size} bytes`)
}

// 获取附件内容
const { contentType, stream } = await smtp.getEmailAttachment('email-id', 'filename.pdf')
stream.pipe(fs.createWriteStream('filename.pdf'))

实现细节:附件以"生成文件名"(generatedFileName,基于 MD5 哈希,见 packages/core/src/types/email.ts)保存为 mailDir/<id>/ 目录下的文件,getEmailAttachment 按 generatedFileName 匹配并返回可读流(见 packages/smtp/src/server.ts)。采用哈希文件名而非原始文件名,是为了避免路径穿越等安全问题。

七、邮件对象结构

Email 对象是 MailDev 的核心数据结构(见 packages/core/src/types/email.ts),定义如下:

interface Email {
id: string
time: Date
read: boolean
subject: string
source: string
size: number
sizeHuman: string
from: Address[]
to: Address[]
cc?: Address[]
bcc?: Address[]
calculatedBcc?: Address[]
date?: Date
html?: string
text?: string
headers: Record<string, string | string[]>
inReplyTo?: string
priority?: 'high' | 'normal' | 'low'
attachments: Attachment[]
envelope: Envelope
relayedAt?: Date // when the email was last successfully relayed
relayedTo?: string[] // recipients delivered to on the last successful relay
}

interface Address {
address: string
name?: string
}

interface Envelope {
from: EnvelopeAddress
to: EnvelopeAddress[]
host?: string
remoteAddress?: string
}

interface EnvelopeAddress extends Address {
args?: boolean | Record<string, unknown>
}

interface Attachment {
filename: string
generatedFileName: string
contentType: string
contentDisposition: 'inline' | 'attachment'
contentId?: string
size?: number
transferred?: boolean
}

字段说明:

  • id:8 位字母数字唯一标识,由 makeId() 生成,同时也是磁盘 .eml 文件名;
  • source:.eml 文件在磁盘上的绝对路径;
  • calculatedBcc:由 SMTP 信封收件人减去头部 To/Cc 推导出的密送收件人(由 calculateBcc 计算,见 packages/core/src/helpers/bcc.ts);
  • relayedAt / relayedTo:记录最近一次成功中继的时间与收件人,仅在启用出站中继时存在;
  • attachments:仅元数据(文件名、MIME 类型、CID 等),附件二进制内容通过 getEmailAttachment() 以流获取。

八、中继(转发)邮件

MailDev 可以把收到的邮件转发到真实 SMTP 服务器。

8.1 手动中继

const maildev = new MailDev({
outgoingHost: 'smtp.gmail.com',
outgoingPort: 587,
outgoingUser: 'you@gmail.com',
outgoingPass: 'app-password',
outgoingSecure: true,
})

const { smtp } = await maildev.start()

// 手动中继指定邮件
smtp.on('new', async (email) => {
if (email.to.some(t => t.address === 'important@example.com')) {
await smtp.relayEmail(email.id)
console.log('Email relayed!')
}
})

成功中继(手动或自动)后,邮件记录 relayedAt 与 relayedTo,可用于判断消息是否以及在哪里被投递:

const email = await smtp.getEmail('email-id')
if (email.relayedAt) {
console.log(`Relayed at ${email.relayedAt} to ${email.relayedTo?.join(', ')}`)
}

持久化差异:使用 FileStorage 时该状态会持久化到磁盘并在下次启动时恢复;内存存储下它只在进程生命周期内有效。中继成功后 relayEmail() 会把 relayedAt 与 relayedTo 写回存储(见 packages/smtp/src/server.ts)。

8.2 自动中继

const maildev = new MailDev({
outgoingHost: 'smtp.example.com',
outgoingPort: 587,
outgoingUser: 'user',
outgoingPass: 'pass',
autoRelay: true, // 自动中继所有邮件
})

8.3 自动中继到指定地址

const maildev = new MailDev({
outgoingHost: 'smtp.example.com',
outgoingPort: 587,
outgoingUser: 'user',
outgoingPass: 'pass',
autoRelay: 'catch-all@example.com', // 覆盖收件人
})

autoRelay 传入字符串时,orchestrator 会把它作为 defaultRecipient 注入中继配置——即所有邮件改写为发给该固定地址(见 packages/cli/src/server/orchestrator.ts)。

九、事件系统

SMTP 服务器对外发出以下事件(事件类型定义见 packages/smtp/src/types.ts):

'new' — 收到新邮件

smtp.on('new', (email: Email) => {
console.log('New email:', email.subject)
})

'delete' — 邮件被删除

smtp.on('delete', (data: { id: string }) => {
console.log('Deleted email:', data.id)
})

'error' — 服务器错误

smtp.on('error', (error: Error) => {
console.error('SMTP error:', error.message)
})

'close' — 服务器关闭

smtp.on('close', () => {
console.log('SMTP server closed')
})

实现细节:'new' 事件在邮件成功保存到存储后发出;'delete' 在删除成功后发出;'close' 由 stop() 触发(见 packages/smtp/src/server.ts)。WebSocket 层正是通过订阅这两个事件把更新实时推送给 Web UI 的所有标签页(见 packages/api/src/server.ts)。

十、高级用法

10.1 直接使用独立子包

追求更精细控制时,可以绕过 MailDev 门面类,直接组合底层包:

import { MemoryStorage } from '@maildev/core'
import { createSMTPServer } from '@maildev/smtp'
import { createAPIServer } from '@maildev/api'

// 创建存储
const storage = new MemoryStorage()
await storage.initialize()

// 创建 SMTP 服务器
const smtp = createSMTPServer({
port: 1025,
host: '::',
storage,
mailDir: '/tmp/maildev',
})

await smtp.start()

// 创建 API 服务器
const api = createAPIServer({
port: 1080,
storage,
smtp,
mcp: { enabled: true },
})

await api.start()

说明:子包边界清晰——@maildev/core 提供存储与类型(Storage、Email 等),@maildev/smtp 提供 createSMTPServer 与中继能力,@maildev/api 提供基于 Fastify 的 REST API、WebSocket 与 MCP 端点。APIServer 还暴露 registerPlugins() 方法,可先注册插件与路由、再 listen(),便于在注册 UI 前后插入自定义逻辑(见 packages/api/src/server.ts)。

10.2 使用 FileStorage 持久化

import { FileStorage } from '@maildev/core'

const storage = new FileStorage({
mailDirectory: '/var/mail/maildev',
maxEmails: 1000, // optional: cap the store (0/omitted = unlimited)
})
await storage.initialize()

当 maxEmails 被超出时,最旧的邮件被丢弃且其文件被删除。若要清理你自己在邮件旁边写入的其他数据,可注册 evict 回调——save() 会等待它完成,因此回调 resolve 后邮件才算完全消失:

storage.onEvicted(async (email) => {
await removeMyIndexEntry(email.id)
})

实现细节:SMTP 服务器正是通过 onEvicted 挂钩来删除磁盘 .eml 与附件目录的(见 packages/smtp/src/server.ts),这一机制同样可以服务于你的自定义清理逻辑。

10.3 自定义日志器

import { MailDev, createLogger } from 'maildev'

const logger = createLogger({
verbose: true,
silent: false,
})

// The MailDev class uses the logger internally
const maildev = new MailDev({
verbose: true,
})

MailDev 构造时根据 verbose/silent 创建内部 Logger(见 packages/cli/src/index.ts),日志器提供 log / info / warn / error / debug 等级别(见 packages/cli/src/utils/logger.ts)。

10.4 中间件集成(代理 / 子路径挂载)

MailDev 可以挂在反向代理之后,或通过 basePathname 运行在现有 Express/Fastify 应用的子路径下:

const maildev = new MailDev({
basePathname: '/maildev',
web: 3001,
})

await maildev.start()
// MailDev UI now available at http://localhost:3001/maildev

然后把请求代理到 MailDev:

import express from 'express'
import { createProxyMiddleware } from 'http-proxy-middleware'

const app = express()

app.use('/maildev', createProxyMiddleware({
target: 'http://localhost:3001',
ws: true,
}))

app.listen(3000)
// MailDev accessible at http://localhost:3000/maildev

实现细节:orchestrator 会归一化 basePathname——根路径 / 被规范为空字符串、尾部斜杠被去除,再传给 API 服务器(见 packages/cli/src/server/orchestrator.ts)。所有 REST 路由、Socket.io 路径(<basePath>/socket.io)与 MCP 端点(<basePath>/mcp)都会自动加上该前缀(见 packages/api/src/server.ts)。ws: true 确保 WebSocket 也能穿透代理,让多标签页实时刷新正常工作。

十一、TypeScript 支持

MailDev v3 完全用 TypeScript 编写,导出全部类型:

import type {
MailDevConfig,
Email,
EmailSummary,
Address,
Attachment,
Storage,
ListOptions,
ListResult,
} from 'maildev'

import type {
SMTPServer,
SMTPServerOptions,
RelayConfig,
} from '@maildev/smtp'

import type {
APIServer,
APIServerOptions,
} from '@maildev/api'

此外 @maildev/api 还导出了 AuthConfig、CorsConfig、EmailQuery、ConfigResponse、SummaryResponse、DeleteResponse、ErrorResponse 等 API 相关类型,并重导出核心类型(见 packages/api/src/index.ts 与 packages/api/src/types.ts)。例如 SummaryQuery 明确了 /api/email/summary 端点接受的 skip、limit、search、sort、unread 参数(见 packages/api/src/types.ts)。

十二、从 v2 迁移到 v3

关键变化

  • Promise 化 API:所有方法改为 async/await;
  • 无回调:用 Promise 替代所有回调模式;
  • 包结构调整:核心功能拆分为 @maildev/core、@maildev/smtp、@maildev/api(以及 UI 相关的 @maildev/ui/server);
  • TypeScript:内置完整类型定义;
  • 事件不变:事件名与载荷保持向后兼容。
  • 迁移示例

    v2(回调风格):

    const MailDev = require('maildev')

    const maildev = new MailDev()

    maildev.listen(function(err) {
    if (err) throw err
    console.log('MailDev running')
    })

    maildev.on('new', function(email) {
    console.log('New email:', email.subject)
    })

    maildev.getAllEmail(function(err, emails) {
    console.log('Total:', emails.length)
    })

    v3(async/await 风格):

    import { MailDev } from 'maildev'

    const maildev = new MailDev()

    const { smtp } = await maildev.start()
    console.log('MailDev running')

    smtp.on('new', (email) => {
    console.log('New email:', email.subject)
    })

    const emails = await smtp.getAllEmails()
    console.log('Total:', emails.length)

    注意 v3 中收件接口从 maildev.getAllEmail(cb) 变为 smtp.getAllEmails(),事件监听对象从 maildev 变更为 start() 返回的 smtp 实例——这正是 v3 把"服务器"与"门面"解耦的直接体现。

    总结

    MailDev v3 的程序化 API 把"本地 SMTP 收件 + Web 查看 + REST/WebSocket + MCP"整合为一个可编程的 TypeScript 组件。本文覆盖了从 MailDev 门面类的全部配置项、生命周期方法,到临时端口、分页查询、附件流式读取、邮件中继、事件驱动、子包级组合与 v2 迁移的完整链路。如果你正在搭建自动化邮件测试、开发调试工具或需要把邮件收件能力嵌入 Node.js 服务,这些 API 即开即用。

    如需查看更多细节,可继续阅读仓库内的 docs/rest.md(REST 端点)、docs/mcp.md(MCP 集成)与 docs/https.md(HTTPS 配置),以及各子包的 README(packages/core/README.md、packages/smtp/README.md、packages/api/README.md)。

    赞

    分享

    • 后端
    • 开发工具
    • 测试

    【免费下载链接】maildev

    :mailbox: SMTP Server + Web Interface for viewing and testing emails during development.

    项目地址:
    https://gitcode.com/gh_mirrors/ma/maildev

    点击查看 免费下载

    上一篇:
    免费音频转换神器fre:ac完整使用指南:从入门到精通

    下一篇:
    OPC-UA客户端工具:工业自动化数据交互的终极解决方案

    创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

    赞(0)
    未经允许不得转载:网硕互联帮助中心 » MailDev v3 程序化 API 实战指南:在 Node.js 中嵌入 SMTP 测试服务器与 Web 界面
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!