Merge pull request #2427 from bluewave-labs/feat/test-email

Feat: New Email Options
This commit is contained in:
Alexander Holliday
2025-06-15 17:11:12 +08:00
committed by GitHub
9 changed files with 204 additions and 53 deletions
+105 -23
View File
@@ -12,6 +12,7 @@ import { useTranslation } from "react-i18next";
import { PasswordEndAdornment } from "../../Components/Inputs/TextInput/Adornments";
import { useSendTestEmail } from "../../Hooks/useSendTestEmail";
import { createToast } from "../../Utils/toastUtils";
import { Switch } from "@mui/material";
const SettingsEmail = ({
isAdmin,
@@ -25,6 +26,21 @@ const SettingsEmail = ({
const { t } = useTranslation();
const theme = useTheme();
// Destructure settings with default values
const {
systemEmailHost = "",
systemEmailPort = "",
systemEmailSecure = false,
systemEmailPool = false,
systemEmailUser = "",
systemEmailAddress = "",
systemEmailPassword = "",
systemEmailTLSServername = "",
systemEmailConnectionHost = "localhost",
systemEmailIgnoreTLS = false,
systemEmailRequireTLS = false,
systemEmailRejectUnauthorized = true,
} = settingsData?.settings || {};
// Local state
const [password, setPassword] = useState("");
const [hasBeenReset, setHasBeenReset] = useState(false);
@@ -47,18 +63,32 @@ const SettingsEmail = ({
const handleSendTestEmail = () => {
// Collect current form values
const emailConfig = {
systemEmailHost: settingsData?.settings?.systemEmailHost,
systemEmailPort: settingsData?.settings?.systemEmailPort,
systemEmailUser: settingsData?.settings?.systemEmailUser,
systemEmailAddress: settingsData?.settings?.systemEmailAddress,
systemEmailPassword: password || settingsData?.settings?.systemEmailPassword,
systemEmailConnectionHost: settingsData?.settings?.systemEmailConnectionHost,
systemEmailHost,
systemEmailPort,
systemEmailSecure,
systemEmailPool,
systemEmailUser,
systemEmailAddress,
systemEmailPassword: password || systemEmailPassword,
systemEmailTLSServername,
systemEmailConnectionHost,
systemEmailIgnoreTLS,
systemEmailRequireTLS,
systemEmailRejectUnauthorized,
};
// Basic validation
if (!emailConfig.systemEmailHost || !emailConfig.systemEmailPort) {
if (
!emailConfig.systemEmailHost ||
!emailConfig.systemEmailPort ||
!emailConfig.systemEmailAddress ||
!emailConfig.systemEmailPassword
) {
createToast({
body: t("settingsEmailRequiredFields", "Email host and port are required"),
body: t(
"settingsEmailRequiredFields",
"Email address, host, port and password are required"
),
variant: "error",
});
return;
@@ -90,7 +120,7 @@ const SettingsEmail = ({
<TextInput
name="systemEmailHost"
placeholder="smtp.gmail.com"
value={settingsData?.settings?.systemEmailHost ?? ""}
value={systemEmailHost}
onChange={handleChange}
/>
</Box>
@@ -100,7 +130,7 @@ const SettingsEmail = ({
name="systemEmailPort"
placeholder="425"
type="number"
value={settingsData?.settings?.systemEmailPort ?? ""}
value={systemEmailPort}
onChange={handleChange}
/>
</Box>
@@ -109,7 +139,7 @@ const SettingsEmail = ({
<TextInput
name="systemEmailUser"
placeholder="Leave empty if not required"
value={settingsData?.settings?.systemEmailUser ?? ""}
value={systemEmailUser}
onChange={handleChange}
/>
</Box>
@@ -118,7 +148,7 @@ const SettingsEmail = ({
<TextInput
name="systemEmailAddress"
placeholder="uptime@bluewavelabs.ca"
value={settingsData?.settings?.systemEmailAddress ?? ""}
value={systemEmailAddress}
onChange={handleChange}
/>
</Box>
@@ -156,23 +186,75 @@ const SettingsEmail = ({
</Box>
)}
<Box>
<Typography>{t("settingsEmailConnectionHost")}</Typography>
<Typography>{t("settingsEmailTLSServername")}</Typography>
<TextInput
name="systemEmailConnectionHost"
name="systemEmailTLSServername"
placeholder="bluewavelabs.ca"
value={settingsData?.settings?.systemEmailConnectionHost ?? ""}
value={systemEmailTLSServername}
onChange={handleChange}
/>
</Box>
<Box>
<Button
variant="contained"
color="accent"
loading={isSending}
onClick={handleSendTestEmail}
>
{t("settingsTestEmail", "Send test e-mail")}
</Button>
<Typography>{t("settingsEmailConnectionHost")}</Typography>
<TextInput
name="systemEmailConnectionHost"
placeholder="bluewavelabs.ca"
value={systemEmailConnectionHost}
onChange={handleChange}
/>
</Box>
<Box
sx={{
display: "flex",
flexDirection: "column",
gap: theme.spacing(4),
}}
>
<Typography>{t("settingsEmailSecure")}</Typography>
<Switch
name="systemEmailSecure"
checked={systemEmailSecure}
onChange={handleChange}
/>
<Typography>{t("settingsEmailPool")}</Typography>
<Switch
name="systemEmailPool"
checked={systemEmailPool}
onChange={handleChange}
/>
<Typography>{t("settingsEmailIgnoreTLS")}</Typography>
<Switch
name="systemEmailIgnoreTLS"
checked={systemEmailIgnoreTLS}
onChange={handleChange}
/>
<Typography>{t("settingsEmailRequireTLS")}</Typography>
<Switch
name="systemEmailRequireTLS"
checked={systemEmailRequireTLS}
onChange={handleChange}
/>
<Typography>{t("settingsEmailRejectUnauthorized")}</Typography>
<Switch
name="systemEmailRejectUnauthorized"
checked={systemEmailRejectUnauthorized}
onChange={handleChange}
/>
</Box>
<Box>
{systemEmailHost &&
systemEmailPort &&
systemEmailAddress &&
systemEmailPassword && (
<Button
variant="contained"
color="accent"
loading={isSending}
onClick={handleSendTestEmail}
>
{t("settingsTestEmail", "Send test e-mail")}
</Button>
)}
</Box>
</Stack>
</Box>
+12 -2
View File
@@ -56,17 +56,27 @@ const Settings = () => {
// Handlers
const handleChange = async (e) => {
const { name, value } = e.target;
const { name, value, checked } = e.target;
// Special case for showURL until handled properly in the backend
if (name === "showURL") {
dispatch(setShowURL(value));
return;
}
let newValue;
if (
name === "systemEmailIgnoreTLS" ||
name === "systemEmailRequireTLS" ||
name === "systemEmailRejectUnauthorized" ||
name === "systemEmailSecure" ||
name === "systemEmailPool"
) {
newValue = checked;
}
// Build next state early
const newSettingsData = {
...settingsData,
settings: { ...settingsData.settings, [name]: value },
settings: { ...settingsData.settings, [name]: newValue ?? value },
};
// Validate
+6 -4
View File
@@ -973,10 +973,12 @@ class NetworkService {
systemEmailPort: emailConfig.systemEmailPort,
systemEmailAddress: emailConfig.systemEmailAddress,
systemEmailPassword: emailConfig.systemEmailPassword,
// Only include these if they are present
...(emailConfig.systemEmailConnectionHost && {
systemEmailConnectionHost: emailConfig.systemEmailConnectionHost,
}),
systemEmailSecure: emailConfig.systemEmailSecure,
systemEmailPool: emailConfig.systemEmailPool,
systemEmailIgnoreTLS: emailConfig.systemEmailIgnoreTLS,
systemEmailRequireTLS: emailConfig.systemEmailRequireTLS,
systemEmailRejectUnauthorized: emailConfig.systemEmailRejectUnauthorized,
systemEmailTLSServername: emailConfig.systemEmailTLSServername,
...(emailConfig.systemEmailUser && {
systemEmailUser: emailConfig.systemEmailUser,
}),
+7 -1
View File
@@ -290,10 +290,16 @@ const settingsValidation = joi.object({
timezone: joi.string().allow("").optional(),
systemEmailHost: joi.string().allow(""),
systemEmailPort: joi.number().allow(null, ""),
systemEmailSecure: joi.boolean().optional(),
systemEmailPool: joi.boolean().optional(),
systemEmailAddress: joi.string().allow(""),
systemEmailPassword: joi.string().allow(""),
systemEmailUser: joi.string().allow(""),
systemEmailConnectionHost: joi.string().allow(""),
systemEmailConnectionHost: joi.string().allow("").optional(),
systemEmailTLSServername: joi.string().allow(""),
systemEmailIgnoreTLS: joi.boolean(),
systemEmailRequireTLS: joi.boolean(),
systemEmailRejectUnauthorized: joi.boolean(),
});
const dayjsValidator = (value, helpers) => {
+8 -2
View File
@@ -681,12 +681,18 @@
},
"settingsEmail": "Email",
"settingsEmailDescription": "Configure the email settings for your system. This is used to send notifications and alerts.",
"settingsEmailHost": "Email host - Hostname or IP address of the SMTP server",
"settingsEmailHost": "Email host - Hostname or IP address to connect to",
"settingsEmailPort": "Email port - Port to connect to",
"settingsEmailAddress": "Email address - Used for authentication",
"settingsEmailPassword": "Email password - Password for authentication",
"settingsEmailUser": "Email user - Username for authentication, overrides email address if specified",
"settingsEmailFieldResetLabel": "Password is set. Click Reset to change it.",
"settingsEmailTLSServername": "TLS Servername - Optional Hostname for TLS Validation when host is an IP",
"settingsEmailIgnoreTLS": "Ignore TLS - Disable STARTTLS",
"settingsEmailRequireTLS": "Require TLS - Force STARTTLS",
"settingsEmailRejectUnauthorized": "Reject Unauthorized",
"settingsEmailSecure": "Secure - Use SSL",
"settingsEmailPool": "Pool - Enable connection pooling",
"state": "State",
"statusBreadCrumbsStatusPages": "Status Pages",
"statusBreadCrumbsDetails": "Details",
@@ -716,7 +722,7 @@
"settingsTestEmailFailed": "Failed to send test email",
"settingsTestEmailFailedWithReason": "Failed to send test email: {{reason}}",
"settingsTestEmailUnknownError": "Unknown error",
"settingsEmailRequiredFields": "Email host and port are required",
"settingsEmailRequiredFields": "Email address, host, port and password are required",
"statusMsg": {
"paused": "Monitoring is paused.",
"up": "Your site is up.",