From 671137b40bc6bb3fda110596cb4ba63dc1070a51 Mon Sep 17 00:00:00 2001 From: npmrun <1549469775@qq.com> Date: Mon, 20 Apr 2026 19:48:16 +0800 Subject: [PATCH] feat(config): add comment email and notify preference keys Made-with: Cursor --- server/service/config/registry.test.ts | 32 +++++++++++++++++ server/service/config/registry.ts | 66 ++++++++++++++++++++++++++++++++++ 2 files changed, 98 insertions(+) create mode 100644 server/service/config/registry.test.ts diff --git a/server/service/config/registry.test.ts b/server/service/config/registry.test.ts new file mode 100644 index 0000000..3cbbaec --- /dev/null +++ b/server/service/config/registry.test.ts @@ -0,0 +1,32 @@ +import { describe, expect, test } from "bun:test"; +import { getConfigDefinition, validateConfigValue } from "./registry"; + +describe("comment email config validation", () => { + test("accepts a valid commentMailFromEmail", () => { + expect(validateConfigValue("commentMailFromEmail" as never, "noreply@example.com")).toBe(true); + }); + + test("rejects an invalid commentMailFromEmail", () => { + expect(validateConfigValue("commentMailFromEmail" as never, "invalid-email")).toBe(false); + }); + + test("validates commentNotifyEnabled as boolean", () => { + expect(validateConfigValue("commentNotifyEnabled" as never, true)).toBe(true); + expect(validateConfigValue("commentNotifyEnabled" as never, false)).toBe(true); + expect(validateConfigValue("commentNotifyEnabled" as never, "true")).toBe(false); + }); + + test("enforces commentSmtpPort boundaries", () => { + expect(validateConfigValue("commentSmtpPort" as never, 0)).toBe(false); + expect(validateConfigValue("commentSmtpPort" as never, 65536)).toBe(false); + expect(validateConfigValue("commentSmtpPort" as never, 465)).toBe(true); + }); + + test("defines commentNotifyEnabled as user-overridable both-scope key", () => { + const definition = getConfigDefinition("commentNotifyEnabled" as never); + expect(definition.scope).toBe("both"); + expect(definition.valueType).toBe("boolean"); + expect(definition.defaultValue).toBe(true); + expect(definition.userOverridable).toBe(true); + }); +}); diff --git a/server/service/config/registry.ts b/server/service/config/registry.ts index a0314fb..a2a38d9 100644 --- a/server/service/config/registry.ts +++ b/server/service/config/registry.ts @@ -15,6 +15,14 @@ function defineConfig(config: ConfigDefinition): Co return config; } +function isValidEmail(value: string): boolean { + const trimmed = value.trim(); + if (!trimmed.length) { + return true; + } + return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(trimmed); +} + const CONFIG_REGISTRY = { siteName: defineConfig({ key: "siteName", @@ -96,6 +104,64 @@ const CONFIG_REGISTRY = { } }, }), + commentEmailNotifyEnabled: defineConfig({ + key: "commentEmailNotifyEnabled", + scope: "global", + valueType: "boolean", + defaultValue: false, + userOverridable: false, + }), + commentMailFromEmail: defineConfig({ + key: "commentMailFromEmail", + scope: "global", + valueType: "string", + defaultValue: "", + userOverridable: false, + validate: (value: string) => isValidEmail(value), + }), + commentSmtpHost: defineConfig({ + key: "commentSmtpHost", + scope: "global", + valueType: "string", + defaultValue: "", + userOverridable: false, + }), + commentSmtpPort: defineConfig({ + key: "commentSmtpPort", + scope: "global", + valueType: "number", + defaultValue: 465, + userOverridable: false, + validate: (value: number) => Number.isInteger(value) && value >= 1 && value <= 65535, + }), + commentSmtpSecure: defineConfig({ + key: "commentSmtpSecure", + scope: "global", + valueType: "boolean", + defaultValue: true, + userOverridable: false, + }), + commentSmtpUser: defineConfig({ + key: "commentSmtpUser", + scope: "global", + valueType: "string", + defaultValue: "", + userOverridable: false, + }), + commentSmtpPass: defineConfig({ + key: "commentSmtpPass", + scope: "global", + valueType: "string", + defaultValue: "", + userOverridable: false, + }), + commentNotifyEnabled: defineConfig({ + key: "commentNotifyEnabled", + scope: "both", + valueType: "boolean", + defaultValue: true, + userOverridable: true, + }), } as const; export type KnownConfigKey = keyof typeof CONFIG_REGISTRY;