feat: 实现联系表单提交与邮件通知功能

添加联系表单提交接口和邮件通知功能,支持从环境变量读取 SMTP 配置
重构 SEO 配置到 site.json,新增 robots.txt 和 sitemap.xml 生成
更新公司电话并添加 PM2 生产运行配置
This commit is contained in:
JanYork
2026-03-18 13:20:40 +08:00
parent 59e80bf938
commit e2850586a9
20 changed files with 872 additions and 28 deletions

79
app/api/contact/route.ts Normal file
View File

@@ -0,0 +1,79 @@
import { NextResponse } from "next/server"
import { ZodError } from "zod"
import { contactFormSchema } from "@/lib/contact-form"
import { sendContactNotification } from "@/lib/contact-mailer"
import { ContactMailerConfigError, loadContactMailerConfig } from "@/lib/contact-mailer-config"
export const runtime = "nodejs"
function getClientIp(request: Request) {
const forwardedFor = request.headers.get("x-forwarded-for")
if (forwardedFor) {
return forwardedFor.split(",")[0]?.trim() || "unknown"
}
return request.headers.get("x-real-ip") ?? "unknown"
}
function formatSubmittedAt() {
return new Intl.DateTimeFormat("zh-CN", {
timeZone: "Asia/Shanghai",
dateStyle: "full",
timeStyle: "long",
}).format(new Date())
}
export async function POST(request: Request) {
let requestBody: unknown
try {
requestBody = await request.json()
} catch {
return NextResponse.json(
{ error: "请求格式不正确,请刷新后重试。" },
{ status: 400 }
)
}
try {
const payload = contactFormSchema.parse(requestBody)
const config = await loadContactMailerConfig()
await sendContactNotification({
config,
payload,
meta: {
submittedAt: formatSubmittedAt(),
sourceUrl: request.headers.get("referer") ?? request.headers.get("origin") ?? new URL(request.url).origin,
ipAddress: getClientIp(request),
userAgent: request.headers.get("user-agent") ?? "unknown",
},
})
return NextResponse.json({ ok: true })
} catch (error) {
if (error instanceof ZodError) {
return NextResponse.json(
{ error: error.issues[0]?.message ?? "提交信息不完整,请检查后重试。" },
{ status: 400 }
)
}
if (error instanceof ContactMailerConfigError) {
console.error(error.message)
return NextResponse.json(
{ error: "邮件通知配置不完整,请联系管理员检查 SMTP 设置。" },
{ status: 500 }
)
}
console.error("Failed to send contact notification", error)
return NextResponse.json(
{ error: "提交失败,邮件通知未发送成功,请稍后重试。" },
{ status: 500 }
)
}
}