Files
company-site/lib/contact-mailer.ts
JanYork e2850586a9 feat: 实现联系表单提交与邮件通知功能
添加联系表单提交接口和邮件通知功能,支持从环境变量读取 SMTP 配置
重构 SEO 配置到 site.json,新增 robots.txt 和 sitemap.xml 生成
更新公司电话并添加 PM2 生产运行配置
2026-03-18 13:20:40 +08:00

117 lines
4.3 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import nodemailer from "nodemailer"
import type { ContactFormData } from "@/lib/contact-form"
import type { ContactMailerConfig } from "@/lib/contact-mailer-config"
type ContactNotificationMeta = {
submittedAt: string
sourceUrl: string
ipAddress: string
userAgent: string
}
function escapeHtml(value: string) {
return value
.replaceAll("&", "&")
.replaceAll("<", "&lt;")
.replaceAll(">", "&gt;")
.replaceAll("\"", "&quot;")
.replaceAll("'", "&#39;")
}
function formatOptionalField(value: string) {
return value || "未填写"
}
export async function sendContactNotification({
config,
payload,
meta,
}: {
config: ContactMailerConfig
payload: ContactFormData
meta: ContactNotificationMeta
}) {
const transporter = nodemailer.createTransport({
host: config.smtp.host,
port: config.smtp.port,
secure: config.smtp.secure,
auth: {
user: config.smtp.auth.user,
pass: config.smtp.auth.pass,
},
})
const subject = `${config.notification.subjectPrefix} - ${payload.company} / ${payload.name}`
const text = [
"官网获取初步方案表单有新提交。",
"",
`姓名:${payload.name}`,
`公司 / 品牌:${payload.company}`,
`电话:${payload.phone}`,
`门店规模:${formatOptionalField(payload.scale)}`,
`需求描述:${formatOptionalField(payload.message)}`,
"",
`提交时间:${meta.submittedAt}`,
`来源页面:${meta.sourceUrl}`,
`来源 IP${meta.ipAddress}`,
`User-Agent${meta.userAgent}`,
].join("\n")
const html = `
<div style="font-family: Arial, sans-serif; color: #111827; line-height: 1.7;">
<h2 style="margin: 0 0 16px;">官网获取初步方案表单有新提交</h2>
<table style="border-collapse: collapse; width: 100%; max-width: 720px;">
<tbody>
<tr>
<td style="padding: 8px 12px; border: 1px solid #e5e7eb; width: 160px; background: #f9fafb;">姓名</td>
<td style="padding: 8px 12px; border: 1px solid #e5e7eb;">${escapeHtml(payload.name)}</td>
</tr>
<tr>
<td style="padding: 8px 12px; border: 1px solid #e5e7eb; background: #f9fafb;">公司 / 品牌</td>
<td style="padding: 8px 12px; border: 1px solid #e5e7eb;">${escapeHtml(payload.company)}</td>
</tr>
<tr>
<td style="padding: 8px 12px; border: 1px solid #e5e7eb; background: #f9fafb;">电话</td>
<td style="padding: 8px 12px; border: 1px solid #e5e7eb;">${escapeHtml(payload.phone)}</td>
</tr>
<tr>
<td style="padding: 8px 12px; border: 1px solid #e5e7eb; background: #f9fafb;">门店规模</td>
<td style="padding: 8px 12px; border: 1px solid #e5e7eb;">${escapeHtml(formatOptionalField(payload.scale))}</td>
</tr>
<tr>
<td style="padding: 8px 12px; border: 1px solid #e5e7eb; background: #f9fafb;">需求描述</td>
<td style="padding: 8px 12px; border: 1px solid #e5e7eb; white-space: pre-wrap;">${escapeHtml(formatOptionalField(payload.message))}</td>
</tr>
<tr>
<td style="padding: 8px 12px; border: 1px solid #e5e7eb; background: #f9fafb;">提交时间</td>
<td style="padding: 8px 12px; border: 1px solid #e5e7eb;">${escapeHtml(meta.submittedAt)}</td>
</tr>
<tr>
<td style="padding: 8px 12px; border: 1px solid #e5e7eb; background: #f9fafb;">来源页面</td>
<td style="padding: 8px 12px; border: 1px solid #e5e7eb;">${escapeHtml(meta.sourceUrl)}</td>
</tr>
<tr>
<td style="padding: 8px 12px; border: 1px solid #e5e7eb; background: #f9fafb;">来源 IP</td>
<td style="padding: 8px 12px; border: 1px solid #e5e7eb;">${escapeHtml(meta.ipAddress)}</td>
</tr>
<tr>
<td style="padding: 8px 12px; border: 1px solid #e5e7eb; background: #f9fafb;">User-Agent</td>
<td style="padding: 8px 12px; border: 1px solid #e5e7eb;">${escapeHtml(meta.userAgent)}</td>
</tr>
</tbody>
</table>
</div>
`
await transporter.sendMail({
from: {
name: config.smtp.from.name,
address: config.smtp.from.email,
},
to: config.notification.to,
subject,
text,
html,
})
}