Files
Checkmate/client/src/Validation/settings.ts
T
gorkem-bwl e71892d0bc feat(smtp): add optional sender display name to SMTP configuration
Closes #3600. Previously the From header on test emails and notifications
showed only the raw email address. Adds an optional "Sender name" field
so emails appear from a friendly name like "Checkmate notifications"
<user@example.org>.

## Behaviour
- Field is optional. When empty/null/whitespace, the From header falls
  back to the raw email address — identical to current behaviour, no
  regression for existing installs.
- When set, the email service hands nodemailer { name, address } as the
  message from field. Nodemailer handles RFC-5322 encoding (quotes,
  commas, non-ASCII) so the display name is safe for any input.
- Unsetting works: clearing the input transforms the value to null in
  the Zod parse step, and the settings repository already turns null
  values into a $unset operation.
- Both client and server Zod schemas trim and empty-string-to-null,
  preventing whitespace-only display names from being stored.

## Surfaces
- AppSettings schema, Settings type, EmailTransportConfig pick, server
  Zod validation, client Zod validation, client form defaults, and the
  Settings page form all carry the new systemEmailDisplayName field.
- The transport preview pre-block on /settings now includes a `from`
  key showing exactly what the email service hands nodemailer — the
  string when no display name is set, or { name, address } when it is.
- en.json carries the new label and placeholder. Other locales fall
  back until translated (16 locales pending — separate follow-up).

## Conventions conformance (per docs/frontend-conventions.md)
- The pre-block I touched had three rule #1 violations (sx p,
  borderRadius, backgroundColor); hoisted those to native props using
  theme.shape.borderRadius. Other rule #1 violations elsewhere on this
  page are pre-existing and intentionally left alone.
- No new sx-with-native-equivalents introduced.
- No color="text.X" shorthand introduced.
- No theme module import introduced.
2026-04-29 15:30:16 -04:00

62 lines
1.9 KiB
TypeScript

import { CHECK_TTL_SENTINEL } from "@/Types/Check";
import { z } from "zod";
export const settingsSchema = z.object({
systemEmailIgnoreTLS: z.boolean(),
systemEmailRequireTLS: z.boolean(),
systemEmailRejectUnauthorized: z.boolean(),
systemEmailConnectionHost: z
.string()
.transform((val) => (val.trim() === "" ? null : val.trim()))
.optional(),
systemEmailSecure: z.boolean().optional(),
systemEmailPool: z.boolean().optional(),
showURL: z.boolean().optional(),
checkTTL: z
.number()
.int()
.min(1, "Please enter a value")
.max(CHECK_TTL_SENTINEL, `Maximum ${CHECK_TTL_SENTINEL}`),
pagespeedApiKey: z
.string()
.transform((val) => (val.trim() === "" ? null : val.trim()))
.optional(),
systemEmailHost: z
.string()
.regex(/^[a-zA-Z0-9.-]*$/, "Invalid hostname or IP address")
.transform((val) => (val.trim() === "" ? null : val.trim()))
.optional(),
systemEmailPort: z.number().int().min(1).max(65535).optional(),
systemEmailAddress: z
.email("Please enter a valid email address")
.or(z.literal(""))
.transform((val) => (val === "" ? null : val.toLowerCase().trim()))
.optional(),
systemEmailDisplayName: z
.string()
.max(100, "Display name must be 100 characters or fewer")
.transform((val) => (val.trim() === "" ? null : val.trim()))
.optional(),
systemEmailUser: z
.string()
.transform((val) => (val.trim() === "" ? null : val.trim()))
.optional(),
systemEmailPassword: z
.string()
.transform((val) => (val.trim() === "" ? null : val.trim()))
.optional(),
systemEmailTLSServername: z
.string()
.transform((val) => (val.trim() === "" ? null : val.trim()))
.optional(),
globalThresholds: z.object({
cpu: z.number().int().min(1).max(100),
memory: z.number().int().min(1).max(100),
disk: z.number().int().min(1).max(100),
temperature: z.number().int().min(1).max(150),
}),
});
export type SettingsFormInput = z.input<typeof settingsSchema>;
export type SettingsFormData = z.infer<typeof settingsSchema>;