feat: 实现联系表单提交与邮件通知功能
添加联系表单提交接口和邮件通知功能,支持从环境变量读取 SMTP 配置 重构 SEO 配置到 site.json,新增 robots.txt 和 sitemap.xml 生成 更新公司电话并添加 PM2 生产运行配置
This commit is contained in:
99
lib/contact-mailer-config.ts
Normal file
99
lib/contact-mailer-config.ts
Normal file
@@ -0,0 +1,99 @@
|
||||
import { readFile } from "node:fs/promises"
|
||||
import path from "node:path"
|
||||
import { parse } from "yaml"
|
||||
import { z } from "zod"
|
||||
|
||||
export const CONTACT_MAILER_CONFIG_PATH = path.join(
|
||||
process.cwd(),
|
||||
"config",
|
||||
"contact-mailer.yaml"
|
||||
)
|
||||
|
||||
export class ContactMailerConfigError extends Error {}
|
||||
|
||||
function coerceBoolean(value: unknown) {
|
||||
if (typeof value === "boolean") {
|
||||
return value
|
||||
}
|
||||
|
||||
if (typeof value === "string") {
|
||||
const normalized = value.trim().toLowerCase()
|
||||
|
||||
if (normalized === "true") {
|
||||
return true
|
||||
}
|
||||
|
||||
if (normalized === "false") {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
function resolveEnvPlaceholders(value: unknown): unknown {
|
||||
if (typeof value === "string") {
|
||||
return value.replace(/\$\{([A-Z0-9_]+)(?::([^}]*))?\}/gi, (_, envName: string, fallback?: string) => {
|
||||
return process.env[envName] ?? fallback ?? ""
|
||||
})
|
||||
}
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
return value.map(resolveEnvPlaceholders)
|
||||
}
|
||||
|
||||
if (value && typeof value === "object") {
|
||||
return Object.fromEntries(
|
||||
Object.entries(value).map(([key, nestedValue]) => [key, resolveEnvPlaceholders(nestedValue)])
|
||||
)
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
const contactMailerConfigSchema = z.object({
|
||||
smtp: z.object({
|
||||
host: z.string().trim().min(1, "SMTP host 未配置"),
|
||||
port: z.coerce.number().int().positive("SMTP port 未配置"),
|
||||
secure: z.preprocess(coerceBoolean, z.boolean()),
|
||||
auth: z.object({
|
||||
user: z.string().trim().min(1, "SMTP 用户名未配置"),
|
||||
pass: z.string().min(1, "SMTP 密码未配置"),
|
||||
}),
|
||||
from: z.object({
|
||||
email: z.string().email("发件邮箱格式不正确"),
|
||||
name: z.string().trim().min(1).default("智店软件官网"),
|
||||
}),
|
||||
}),
|
||||
notification: z.object({
|
||||
to: z.array(z.string().email("通知邮箱格式不正确")).min(1).default(["bd@zhidiansoft.com"]),
|
||||
subjectPrefix: z.string().trim().min(1).default("官网线索"),
|
||||
}).default({
|
||||
to: ["bd@zhidiansoft.com"],
|
||||
subjectPrefix: "官网线索",
|
||||
}),
|
||||
})
|
||||
|
||||
export type ContactMailerConfig = z.infer<typeof contactMailerConfigSchema>
|
||||
|
||||
export async function loadContactMailerConfig() {
|
||||
try {
|
||||
const fileContent = await readFile(CONTACT_MAILER_CONFIG_PATH, "utf8")
|
||||
const parsed = parse(fileContent) ?? {}
|
||||
const resolved = resolveEnvPlaceholders(parsed)
|
||||
|
||||
return contactMailerConfigSchema.parse(resolved)
|
||||
} catch (error) {
|
||||
if (error instanceof z.ZodError) {
|
||||
throw new ContactMailerConfigError(
|
||||
`contact-mailer.yaml 配置不完整:${error.issues.map((issue) => issue.message).join(";")}`
|
||||
)
|
||||
}
|
||||
|
||||
if (error instanceof Error) {
|
||||
throw new ContactMailerConfigError(`读取邮件配置失败:${error.message}`)
|
||||
}
|
||||
|
||||
throw new ContactMailerConfigError("读取邮件配置失败")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user