chore: update SMTP module and tests; remove unused assets
This commit is contained in:
parent
81fd572e48
commit
037970d52b
4 changed files with 38 additions and 3 deletions
Binary file not shown.
|
Before Width: | Height: | Size: 20 KiB |
Binary file not shown.
|
|
@ -13,6 +13,20 @@ function b64(input: string) {
|
||||||
return Buffer.from(input, "utf8").toString("base64")
|
return Buffer.from(input, "utf8").toString("base64")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function extractEnvelopeAddress(from: string): string {
|
||||||
|
// Prefer address inside angle brackets
|
||||||
|
const angle = from.match(/<\s*([^>\s]+)\s*>/)
|
||||||
|
if (angle?.[1]) return angle[1]
|
||||||
|
// Fallback: address inside parentheses
|
||||||
|
const paren = from.match(/\(([^)\s]+@[^)\s]+)\)/)
|
||||||
|
if (paren?.[1]) return paren[1]
|
||||||
|
// Fallback: first email-like substring
|
||||||
|
const email = from.match(/[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}/)
|
||||||
|
if (email?.[0]) return email[0]
|
||||||
|
// Last resort: use whole string
|
||||||
|
return from
|
||||||
|
}
|
||||||
|
|
||||||
export async function sendSmtpMail(cfg: SmtpConfig, to: string, subject: string, html: string) {
|
export async function sendSmtpMail(cfg: SmtpConfig, to: string, subject: string, html: string) {
|
||||||
return new Promise<void>((resolve, reject) => {
|
return new Promise<void>((resolve, reject) => {
|
||||||
const socket = tls.connect(cfg.port, cfg.host, { rejectUnauthorized: false }, () => {
|
const socket = tls.connect(cfg.port, cfg.host, { rejectUnauthorized: false }, () => {
|
||||||
|
|
@ -44,7 +58,8 @@ export async function sendSmtpMail(cfg: SmtpConfig, to: string, subject: string,
|
||||||
await wait(/^334 /)
|
await wait(/^334 /)
|
||||||
send(b64(cfg.password))
|
send(b64(cfg.password))
|
||||||
await wait(/^235 /)
|
await wait(/^235 /)
|
||||||
send(`MAIL FROM:<${cfg.from.match(/<(.+)>/)?.[1] ?? cfg.from}>`)
|
const envelopeFrom = extractEnvelopeAddress(cfg.from)
|
||||||
|
send(`MAIL FROM:<${envelopeFrom}>`)
|
||||||
await wait(/^250 /)
|
await wait(/^250 /)
|
||||||
send(`RCPT TO:<${to}>`)
|
send(`RCPT TO:<${to}>`)
|
||||||
await wait(/^250 /)
|
await wait(/^250 /)
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import { describe, it, expect, vi, beforeEach } from "vitest"
|
import { describe, it, expect, vi, beforeEach } from "vitest"
|
||||||
|
|
||||||
// Mock tls to simulate an SMTP server over implicit TLS
|
// Mock tls to simulate an SMTP server over implicit TLS
|
||||||
|
let lastWrites: string[] = []
|
||||||
vi.mock("tls", () => {
|
vi.mock("tls", () => {
|
||||||
class MockSocket {
|
class MockSocket {
|
||||||
listeners: Record<string, Function[]> = {}
|
listeners: Record<string, Function[]> = {}
|
||||||
|
|
@ -58,6 +59,7 @@ vi.mock("tls", () => {
|
||||||
|
|
||||||
function connect(_port: number, _host: string, _opts: unknown, cb?: Function) {
|
function connect(_port: number, _host: string, _opts: unknown, cb?: Function) {
|
||||||
const socket = new MockSocket()
|
const socket = new MockSocket()
|
||||||
|
lastWrites = socket.writes
|
||||||
// initial server greeting
|
// initial server greeting
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
cb?.()
|
cb?.()
|
||||||
|
|
@ -66,7 +68,7 @@ vi.mock("tls", () => {
|
||||||
return socket as unknown as NodeJS.WritableStream & { on: MockSocket["on"] }
|
return socket as unknown as NodeJS.WritableStream & { on: MockSocket["on"] }
|
||||||
}
|
}
|
||||||
|
|
||||||
return { default: { connect }, connect }
|
return { default: { connect }, connect, __getLastWrites: () => lastWrites }
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("sendSmtpMail", () => {
|
describe("sendSmtpMail", () => {
|
||||||
|
|
@ -87,5 +89,23 @@ describe("sendSmtpMail", () => {
|
||||||
)
|
)
|
||||||
).resolves.toBeUndefined()
|
).resolves.toBeUndefined()
|
||||||
})
|
})
|
||||||
})
|
|
||||||
|
|
||||||
|
it("extracts envelope address from parentheses or raw email", async () => {
|
||||||
|
const { sendSmtpMail } = await import("@/server/email-smtp")
|
||||||
|
const tlsMock = await import("tls" as any)
|
||||||
|
await sendSmtpMail(
|
||||||
|
{
|
||||||
|
host: "smtp.mock",
|
||||||
|
port: 465,
|
||||||
|
username: "user@example.com",
|
||||||
|
password: "secret",
|
||||||
|
from: "Chatwoot chat@esdrasrenan.com.br (chat@esdrasrenan.com.br)",
|
||||||
|
},
|
||||||
|
"rcpt@example.com",
|
||||||
|
"Subject",
|
||||||
|
"<p>Hi</p>"
|
||||||
|
)
|
||||||
|
const writes = (tlsMock as any).__getLastWrites() as string[]
|
||||||
|
expect(writes.some((w) => /MAIL FROM:<chat@esdrasrenan.com.br>\r\n/.test(w))).toBe(true)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue