mirror of
https://github.com/kyantech/Palmr.git
synced 2025-11-05 14:35:30 +00:00
feat: enhance SMTP configuration options in settings (#107)
This commit is contained in:
@@ -117,6 +117,18 @@ const defaultConfigs = [
|
|||||||
type: "string",
|
type: "string",
|
||||||
group: "email",
|
group: "email",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: "smtpSecure",
|
||||||
|
value: "auto",
|
||||||
|
type: "string",
|
||||||
|
group: "email",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "smtpNoAuth",
|
||||||
|
value: "false",
|
||||||
|
type: "boolean",
|
||||||
|
group: "email",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
key: "passwordResetTokenExpiration",
|
key: "passwordResetTokenExpiration",
|
||||||
value: "3600",
|
value: "3600",
|
||||||
|
|||||||
@@ -148,6 +148,11 @@ export async function appRoutes(app: FastifyInstance) {
|
|||||||
.describe("SMTP server port (typically 587 for TLS, 25 for non-secure)"),
|
.describe("SMTP server port (typically 587 for TLS, 25 for non-secure)"),
|
||||||
smtpUser: z.string().describe("Username for SMTP authentication (e.g., email address)"),
|
smtpUser: z.string().describe("Username for SMTP authentication (e.g., email address)"),
|
||||||
smtpPass: z.string().describe("Password for SMTP authentication (for Gmail, use App Password)"),
|
smtpPass: z.string().describe("Password for SMTP authentication (for Gmail, use App Password)"),
|
||||||
|
smtpSecure: z
|
||||||
|
.string()
|
||||||
|
.optional()
|
||||||
|
.describe("Connection security method ('auto', 'ssl', 'tls', or 'none')"),
|
||||||
|
smtpNoAuth: z.string().optional().describe("Disable SMTP authentication ('true' or 'false')"),
|
||||||
})
|
})
|
||||||
.optional()
|
.optional()
|
||||||
.describe("SMTP configuration to test. If not provided, uses currently saved configuration"),
|
.describe("SMTP configuration to test. If not provided, uses currently saved configuration"),
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ interface SmtpConfig {
|
|||||||
smtpPort: string;
|
smtpPort: string;
|
||||||
smtpUser: string;
|
smtpUser: string;
|
||||||
smtpPass: string;
|
smtpPass: string;
|
||||||
|
smtpSecure?: string;
|
||||||
|
smtpNoAuth?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class EmailService {
|
export class EmailService {
|
||||||
@@ -18,15 +20,46 @@ export class EmailService {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return nodemailer.createTransport({
|
const port = Number(await this.configService.getValue("smtpPort"));
|
||||||
|
const smtpSecure = (await this.configService.getValue("smtpSecure")) || "auto";
|
||||||
|
const smtpNoAuth = await this.configService.getValue("smtpNoAuth");
|
||||||
|
|
||||||
|
let secure = false;
|
||||||
|
let requireTLS = false;
|
||||||
|
|
||||||
|
if (smtpSecure === "ssl") {
|
||||||
|
secure = true;
|
||||||
|
} else if (smtpSecure === "tls") {
|
||||||
|
requireTLS = true;
|
||||||
|
} else if (smtpSecure === "none") {
|
||||||
|
secure = false;
|
||||||
|
requireTLS = false;
|
||||||
|
} else if (smtpSecure === "auto") {
|
||||||
|
if (port === 465) {
|
||||||
|
secure = true;
|
||||||
|
} else if (port === 587 || port === 25) {
|
||||||
|
requireTLS = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const transportConfig: any = {
|
||||||
host: await this.configService.getValue("smtpHost"),
|
host: await this.configService.getValue("smtpHost"),
|
||||||
port: Number(await this.configService.getValue("smtpPort")),
|
port: port,
|
||||||
secure: false,
|
secure: secure,
|
||||||
auth: {
|
requireTLS: requireTLS,
|
||||||
|
tls: {
|
||||||
|
rejectUnauthorized: false,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
if (smtpNoAuth !== "true") {
|
||||||
|
transportConfig.auth = {
|
||||||
user: await this.configService.getValue("smtpUser"),
|
user: await this.configService.getValue("smtpUser"),
|
||||||
pass: await this.configService.getValue("smtpPass"),
|
pass: await this.configService.getValue("smtpPass"),
|
||||||
},
|
};
|
||||||
});
|
}
|
||||||
|
|
||||||
|
return nodemailer.createTransport(transportConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
async testConnection(config?: SmtpConfig) {
|
async testConnection(config?: SmtpConfig) {
|
||||||
@@ -43,6 +76,8 @@ export class EmailService {
|
|||||||
smtpPort: await this.configService.getValue("smtpPort"),
|
smtpPort: await this.configService.getValue("smtpPort"),
|
||||||
smtpUser: await this.configService.getValue("smtpUser"),
|
smtpUser: await this.configService.getValue("smtpUser"),
|
||||||
smtpPass: await this.configService.getValue("smtpPass"),
|
smtpPass: await this.configService.getValue("smtpPass"),
|
||||||
|
smtpSecure: (await this.configService.getValue("smtpSecure")) || "auto",
|
||||||
|
smtpNoAuth: await this.configService.getValue("smtpNoAuth"),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,15 +85,46 @@ export class EmailService {
|
|||||||
throw new Error("SMTP is not enabled");
|
throw new Error("SMTP is not enabled");
|
||||||
}
|
}
|
||||||
|
|
||||||
const transporter = nodemailer.createTransport({
|
const port = Number(smtpConfig.smtpPort);
|
||||||
|
const smtpSecure = smtpConfig.smtpSecure || "auto";
|
||||||
|
const smtpNoAuth = smtpConfig.smtpNoAuth;
|
||||||
|
|
||||||
|
let secure = false;
|
||||||
|
let requireTLS = false;
|
||||||
|
|
||||||
|
if (smtpSecure === "ssl") {
|
||||||
|
secure = true;
|
||||||
|
} else if (smtpSecure === "tls") {
|
||||||
|
requireTLS = true;
|
||||||
|
} else if (smtpSecure === "none") {
|
||||||
|
secure = false;
|
||||||
|
requireTLS = false;
|
||||||
|
} else if (smtpSecure === "auto") {
|
||||||
|
if (port === 465) {
|
||||||
|
secure = true;
|
||||||
|
} else if (port === 587 || port === 25) {
|
||||||
|
requireTLS = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const transportConfig: any = {
|
||||||
host: smtpConfig.smtpHost,
|
host: smtpConfig.smtpHost,
|
||||||
port: Number(smtpConfig.smtpPort),
|
port: port,
|
||||||
secure: false,
|
secure: secure,
|
||||||
auth: {
|
requireTLS: requireTLS,
|
||||||
|
tls: {
|
||||||
|
rejectUnauthorized: false,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
if (smtpNoAuth !== "true") {
|
||||||
|
transportConfig.auth = {
|
||||||
user: smtpConfig.smtpUser,
|
user: smtpConfig.smtpUser,
|
||||||
pass: smtpConfig.smtpPass,
|
pass: smtpConfig.smtpPass,
|
||||||
},
|
};
|
||||||
});
|
}
|
||||||
|
|
||||||
|
const transporter = nodemailer.createTransport(transportConfig);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await transporter.verify();
|
await transporter.verify();
|
||||||
|
|||||||
@@ -465,6 +465,20 @@
|
|||||||
"title": "Sender Email",
|
"title": "Sender Email",
|
||||||
"description": "Sender email address"
|
"description": "Sender email address"
|
||||||
},
|
},
|
||||||
|
"smtpSecure": {
|
||||||
|
"title": "Connection Security",
|
||||||
|
"description": "SMTP connection security method - Auto (recommended), SSL, STARTTLS, or None (insecure)",
|
||||||
|
"options": {
|
||||||
|
"auto": "Auto (Recommended)",
|
||||||
|
"ssl": "SSL (Port 465)",
|
||||||
|
"tls": "STARTTLS (Port 587)",
|
||||||
|
"none": "None (Insecure)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"smtpNoAuth": {
|
||||||
|
"title": "No Authentication",
|
||||||
|
"description": "Enable this for internal servers that don't require username/password (hides auth fields)"
|
||||||
|
},
|
||||||
"testSmtp": {
|
"testSmtp": {
|
||||||
"title": "Test SMTP Connection",
|
"title": "Test SMTP Connection",
|
||||||
"description": "Test if the SMTP configuration is valid"
|
"description": "Test if the SMTP configuration is valid"
|
||||||
@@ -548,7 +562,10 @@
|
|||||||
"updateSuccess": "{group} settings updated successfully",
|
"updateSuccess": "{group} settings updated successfully",
|
||||||
"smtpTestSuccess": "SMTP connection successful! Your email configuration is working correctly.",
|
"smtpTestSuccess": "SMTP connection successful! Your email configuration is working correctly.",
|
||||||
"smtpTestFailed": "SMTP connection failed: {error}",
|
"smtpTestFailed": "SMTP connection failed: {error}",
|
||||||
"smtpTestGenericError": "Failed to test SMTP connection. Please check your settings and try again."
|
"smtpTestGenericError": "Failed to test SMTP connection. Please check your settings and try again.",
|
||||||
|
"smtpNotEnabled": "SMTP is not enabled. Please enable SMTP first.",
|
||||||
|
"smtpMissingHostPort": "Please fill in SMTP Host and Port before testing.",
|
||||||
|
"smtpMissingAuth": "Please fill in SMTP Username and Password, or enable 'No Authentication' option."
|
||||||
},
|
},
|
||||||
"title": "Settings",
|
"title": "Settings",
|
||||||
"breadcrumb": "Settings",
|
"breadcrumb": "Settings",
|
||||||
@@ -556,7 +573,8 @@
|
|||||||
"tooltips": {
|
"tooltips": {
|
||||||
"oidcScope": "Enter a scope and press Enter to add",
|
"oidcScope": "Enter a scope and press Enter to add",
|
||||||
"oidcAdminEmailDomains": "Enter a domain and press Enter to add",
|
"oidcAdminEmailDomains": "Enter a domain and press Enter to add",
|
||||||
"testSmtp": "Tests the SMTP connection with the values currently entered in the form. To make changes permanent, remember to save your settings after testing."
|
"testSmtp": "Tests the SMTP connection with the values currently entered in the form. To make changes permanent, remember to save your settings after testing.",
|
||||||
|
"defaultPlaceholder": "Enter and press Enter"
|
||||||
},
|
},
|
||||||
"redirectUri": {
|
"redirectUri": {
|
||||||
"placeholder": "https://mysite.com",
|
"placeholder": "https://mysite.com",
|
||||||
|
|||||||
@@ -51,42 +51,71 @@ export function SettingsGroup({ group, configs, form, isCollapsed, onToggleColla
|
|||||||
<div className="flex flex-col gap-4">
|
<div className="flex flex-col gap-4">
|
||||||
{configs
|
{configs
|
||||||
.filter((config) => !isFieldHidden(config.key))
|
.filter((config) => !isFieldHidden(config.key))
|
||||||
.map((config) => (
|
.map((config) => {
|
||||||
<div key={config.key} className="space-y-2 mb-3">
|
const smtpEnabled = form.watch("configs.smtpEnabled");
|
||||||
<SettingsInput
|
const smtpNoAuth = form.watch("configs.smtpNoAuth");
|
||||||
config={config}
|
const isSmtpAuthField = config.key === "smtpUser" || config.key === "smtpPass";
|
||||||
error={form.formState.errors.configs?.[config.key]}
|
|
||||||
register={form.register}
|
const smtpFields = [
|
||||||
setValue={form.setValue}
|
"smtpHost",
|
||||||
smtpEnabled={form.watch("configs.smtpEnabled")}
|
"smtpPort",
|
||||||
oidcEnabled={form.watch("configs.oidcEnabled")}
|
"smtpUser",
|
||||||
watch={form.watch}
|
"smtpPass",
|
||||||
/>
|
"smtpSecure",
|
||||||
<p className="text-xs text-muted-foreground ml-1">
|
"smtpNoAuth",
|
||||||
{t(`settings.fields.${config.key}.description`, {
|
"smtpFromName",
|
||||||
defaultValue:
|
"smtpFromEmail",
|
||||||
FIELD_DESCRIPTIONS[config.key as keyof typeof FIELD_DESCRIPTIONS] ||
|
];
|
||||||
config.description ||
|
|
||||||
t("settings.fields.noDescription"),
|
if (smtpEnabled !== "true" && smtpFields.includes(config.key)) {
|
||||||
})}
|
return null;
|
||||||
</p>
|
}
|
||||||
</div>
|
|
||||||
))}
|
if (isSmtpAuthField && smtpNoAuth === "true") {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div key={config.key} className="space-y-2 mb-3">
|
||||||
|
<SettingsInput
|
||||||
|
config={config}
|
||||||
|
error={form.formState.errors.configs?.[config.key]}
|
||||||
|
register={form.register}
|
||||||
|
setValue={form.setValue}
|
||||||
|
smtpEnabled={form.watch("configs.smtpEnabled")}
|
||||||
|
oidcEnabled={form.watch("configs.oidcEnabled")}
|
||||||
|
watch={form.watch}
|
||||||
|
/>
|
||||||
|
<p className="text-xs text-muted-foreground ml-1">
|
||||||
|
{t(`settings.fields.${config.key}.description`, {
|
||||||
|
defaultValue:
|
||||||
|
FIELD_DESCRIPTIONS[config.key as keyof typeof FIELD_DESCRIPTIONS] ||
|
||||||
|
config.description ||
|
||||||
|
t("settings.fields.noDescription"),
|
||||||
|
})}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-between items-center mt-4">
|
<div className="flex justify-between items-center mt-4">
|
||||||
{isEmailGroup && (
|
<div className="flex">
|
||||||
<SmtpTestButton
|
{isEmailGroup && form.watch("configs.smtpEnabled") === "true" && (
|
||||||
smtpEnabled={form.watch("configs.smtpEnabled") || "false"}
|
<SmtpTestButton
|
||||||
getFormValues={() => ({
|
smtpEnabled={form.watch("configs.smtpEnabled") || "false"}
|
||||||
smtpEnabled: form.getValues("configs.smtpEnabled") || "false",
|
getFormValues={() => ({
|
||||||
smtpHost: form.getValues("configs.smtpHost") || "",
|
smtpEnabled: form.getValues("configs.smtpEnabled") || "false",
|
||||||
smtpPort: form.getValues("configs.smtpPort") || "",
|
smtpHost: form.getValues("configs.smtpHost") || "",
|
||||||
smtpUser: form.getValues("configs.smtpUser") || "",
|
smtpPort: form.getValues("configs.smtpPort") || "",
|
||||||
smtpPass: form.getValues("configs.smtpPass") || "",
|
smtpUser: form.getValues("configs.smtpUser") || "",
|
||||||
})}
|
smtpPass: form.getValues("configs.smtpPass") || "",
|
||||||
/>
|
smtpSecure: form.getValues("configs.smtpSecure") || "auto",
|
||||||
)}
|
smtpNoAuth: form.getValues("configs.smtpNoAuth") || "false",
|
||||||
<div className={`flex ${isEmailGroup ? "ml-auto" : ""}`}>
|
})}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="flex">
|
||||||
<Button
|
<Button
|
||||||
variant="default"
|
variant="default"
|
||||||
disabled={form.formState.isSubmitting}
|
disabled={form.formState.isSubmitting}
|
||||||
|
|||||||
@@ -2,8 +2,10 @@ import { IconInfoCircle } from "@tabler/icons-react";
|
|||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
import { UseFormRegister, UseFormSetValue, UseFormWatch } from "react-hook-form";
|
import { UseFormRegister, UseFormSetValue, UseFormWatch } from "react-hook-form";
|
||||||
|
|
||||||
|
import { Checkbox } from "@/components/ui/checkbox";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { PasswordInput } from "@/components/ui/password-input";
|
import { PasswordInput } from "@/components/ui/password-input";
|
||||||
|
import { Switch } from "@/components/ui/switch";
|
||||||
import { TagsInput } from "@/components/ui/tags-input";
|
import { TagsInput } from "@/components/ui/tags-input";
|
||||||
import { createFieldTitles } from "../constants";
|
import { createFieldTitles } from "../constants";
|
||||||
import { Config } from "../types";
|
import { Config } from "../types";
|
||||||
@@ -129,7 +131,7 @@ export function SettingsInput({
|
|||||||
? "openid profile email"
|
? "openid profile email"
|
||||||
: config.key === "oidcAdminEmailDomains"
|
: config.key === "oidcAdminEmailDomains"
|
||||||
? "admin.com company.org"
|
? "admin.com company.org"
|
||||||
: "Digite e pressione Enter"
|
: t("settings.tooltips.defaultPlaceholder", { defaultValue: "Enter and press Enter" })
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
{error && <p className="text-danger text-xs mt-1">{error.message}</p>}
|
{error && <p className="text-danger text-xs mt-1">{error.message}</p>}
|
||||||
@@ -137,6 +139,73 @@ export function SettingsInput({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (config.key === "smtpEnabled") {
|
||||||
|
const currentValue = watch(`configs.${config.key}`) === "true";
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="space-y-2">
|
||||||
|
<div className="flex items-center space-x-3">
|
||||||
|
<Switch
|
||||||
|
id={`configs.${config.key}`}
|
||||||
|
checked={currentValue}
|
||||||
|
onCheckedChange={(checked) => {
|
||||||
|
setValue(`configs.${config.key}`, checked ? "true" : "false", { shouldDirty: true });
|
||||||
|
}}
|
||||||
|
disabled={isDisabled}
|
||||||
|
/>
|
||||||
|
<label htmlFor={`configs.${config.key}`} className="text-sm font-semibold cursor-pointer">
|
||||||
|
{friendlyLabel}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
{config.description && <p className="text-xs text-muted-foreground ml-11">{config.description}</p>}
|
||||||
|
{error && <p className="text-danger text-xs mt-1 ml-11">{error.message}</p>}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.key === "smtpSecure") {
|
||||||
|
return (
|
||||||
|
<div className="space-y-2">
|
||||||
|
<label className="block text-sm font-semibold">{friendlyLabel}</label>
|
||||||
|
<select
|
||||||
|
{...register(`configs.${config.key}`)}
|
||||||
|
className="w-full rounded-md border border-input bg-transparent px-3 py-2 text-sm"
|
||||||
|
disabled={isDisabled}
|
||||||
|
>
|
||||||
|
<option value="auto">{t("settings.fields.smtpSecure.options.auto")}</option>
|
||||||
|
<option value="ssl">{t("settings.fields.smtpSecure.options.ssl")}</option>
|
||||||
|
<option value="tls">{t("settings.fields.smtpSecure.options.tls")}</option>
|
||||||
|
<option value="none">{t("settings.fields.smtpSecure.options.none")}</option>
|
||||||
|
</select>
|
||||||
|
{error && <p className="text-danger text-xs mt-1">{error.message}</p>}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.key === "smtpNoAuth") {
|
||||||
|
const currentValue = watch(`configs.${config.key}`) === "true";
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="space-y-2">
|
||||||
|
<div className="flex items-center space-x-3">
|
||||||
|
<Checkbox
|
||||||
|
id={`configs.${config.key}`}
|
||||||
|
checked={currentValue}
|
||||||
|
onCheckedChange={(checked) => {
|
||||||
|
setValue(`configs.${config.key}`, checked ? "true" : "false", { shouldDirty: true });
|
||||||
|
}}
|
||||||
|
disabled={isDisabled}
|
||||||
|
/>
|
||||||
|
<label htmlFor={`configs.${config.key}`} className="text-sm font-semibold cursor-pointer">
|
||||||
|
{friendlyLabel}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
{config.description && <p className="text-xs text-muted-foreground ml-7">{config.description}</p>}
|
||||||
|
{error && <p className="text-danger text-xs mt-1 ml-7">{error.message}</p>}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
switch (config.type) {
|
switch (config.type) {
|
||||||
case "boolean":
|
case "boolean":
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ interface SmtpTestButtonProps {
|
|||||||
smtpPort: string;
|
smtpPort: string;
|
||||||
smtpUser: string;
|
smtpUser: string;
|
||||||
smtpPass: string;
|
smtpPass: string;
|
||||||
|
smtpSecure: string;
|
||||||
|
smtpNoAuth: string;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -27,13 +29,17 @@ export function SmtpTestButton({ smtpEnabled, getFormValues }: SmtpTestButtonPro
|
|||||||
const formValues = getFormValues();
|
const formValues = getFormValues();
|
||||||
|
|
||||||
if (formValues.smtpEnabled !== "true") {
|
if (formValues.smtpEnabled !== "true") {
|
||||||
toast.error("SMTP is not enabled. Please enable SMTP first.");
|
toast.error(t("settings.messages.smtpNotEnabled"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if required fields are filled
|
if (!formValues.smtpHost || !formValues.smtpPort) {
|
||||||
if (!formValues.smtpHost || !formValues.smtpPort || !formValues.smtpUser || !formValues.smtpPass) {
|
toast.error(t("settings.messages.smtpMissingHostPort"));
|
||||||
toast.error("Please fill in all SMTP configuration fields before testing.");
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (formValues.smtpNoAuth !== "true" && (!formValues.smtpUser || !formValues.smtpPass)) {
|
||||||
|
toast.error(t("settings.messages.smtpMissingAuth"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,6 +52,8 @@ export function SmtpTestButton({ smtpEnabled, getFormValues }: SmtpTestButtonPro
|
|||||||
smtpPort: formValues.smtpPort,
|
smtpPort: formValues.smtpPort,
|
||||||
smtpUser: formValues.smtpUser,
|
smtpUser: formValues.smtpUser,
|
||||||
smtpPass: formValues.smtpPass,
|
smtpPass: formValues.smtpPass,
|
||||||
|
smtpSecure: formValues.smtpSecure,
|
||||||
|
smtpNoAuth: formValues.smtpNoAuth,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -55,7 +63,7 @@ export function SmtpTestButton({ smtpEnabled, getFormValues }: SmtpTestButtonPro
|
|||||||
toast.error(t("settings.messages.smtpTestGenericError"));
|
toast.error(t("settings.messages.smtpTestGenericError"));
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
const errorMessage = error?.response?.data?.error || error?.message || "Unknown error";
|
const errorMessage = error?.response?.data?.error || error?.message || t("common.unexpectedError");
|
||||||
toast.error(t("settings.messages.smtpTestFailed", { error: errorMessage }));
|
toast.error(t("settings.messages.smtpTestFailed", { error: errorMessage }));
|
||||||
} finally {
|
} finally {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
|
|||||||
@@ -46,6 +46,8 @@ export const createFieldDescriptions = (t: ReturnType<typeof createTranslator>)
|
|||||||
smtpPass: t("settings.fields.smtpPass.description"),
|
smtpPass: t("settings.fields.smtpPass.description"),
|
||||||
smtpFromName: t("settings.fields.smtpFromName.description"),
|
smtpFromName: t("settings.fields.smtpFromName.description"),
|
||||||
smtpFromEmail: t("settings.fields.smtpFromEmail.description"),
|
smtpFromEmail: t("settings.fields.smtpFromEmail.description"),
|
||||||
|
smtpSecure: t("settings.fields.smtpSecure.description"),
|
||||||
|
smtpNoAuth: t("settings.fields.smtpNoAuth.description"),
|
||||||
|
|
||||||
// OIDC settings (nomes corretos do seed)
|
// OIDC settings (nomes corretos do seed)
|
||||||
oidcEnabled: t("settings.fields.oidcEnabled.description"),
|
oidcEnabled: t("settings.fields.oidcEnabled.description"),
|
||||||
@@ -85,6 +87,8 @@ export const createFieldTitles = (t: ReturnType<typeof createTranslator>) => ({
|
|||||||
smtpPass: t("settings.fields.smtpPass.title"),
|
smtpPass: t("settings.fields.smtpPass.title"),
|
||||||
smtpFromName: t("settings.fields.smtpFromName.title"),
|
smtpFromName: t("settings.fields.smtpFromName.title"),
|
||||||
smtpFromEmail: t("settings.fields.smtpFromEmail.title"),
|
smtpFromEmail: t("settings.fields.smtpFromEmail.title"),
|
||||||
|
smtpSecure: t("settings.fields.smtpSecure.title"),
|
||||||
|
smtpNoAuth: t("settings.fields.smtpNoAuth.title"),
|
||||||
|
|
||||||
// OIDC settings
|
// OIDC settings
|
||||||
oidcEnabled: t("settings.fields.oidcEnabled.title"),
|
oidcEnabled: t("settings.fields.oidcEnabled.title"),
|
||||||
|
|||||||
@@ -77,8 +77,26 @@ export function useSettings() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (group === "email") {
|
if (group === "email") {
|
||||||
if (a.key === "smtpEnabled") return -1;
|
const smtpOrder = [
|
||||||
if (b.key === "smtpEnabled") return 1;
|
"smtpEnabled",
|
||||||
|
"smtpHost",
|
||||||
|
"smtpPort",
|
||||||
|
"smtpSecure",
|
||||||
|
"smtpNoAuth",
|
||||||
|
"smtpUser",
|
||||||
|
"smtpPass",
|
||||||
|
"smtpFromName",
|
||||||
|
"smtpFromEmail",
|
||||||
|
];
|
||||||
|
|
||||||
|
const aIndex = smtpOrder.indexOf(a.key);
|
||||||
|
const bIndex = smtpOrder.indexOf(b.key);
|
||||||
|
|
||||||
|
if (aIndex !== -1 && bIndex !== -1) {
|
||||||
|
return aIndex - bIndex;
|
||||||
|
}
|
||||||
|
if (aIndex !== -1) return -1;
|
||||||
|
if (bIndex !== -1) return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (group === "oidc") {
|
if (group === "oidc") {
|
||||||
|
|||||||
@@ -90,6 +90,8 @@ export interface TestSmtpConnectionBody {
|
|||||||
smtpPort: string;
|
smtpPort: string;
|
||||||
smtpUser: string;
|
smtpUser: string;
|
||||||
smtpPass: string;
|
smtpPass: string;
|
||||||
|
smtpSecure: string;
|
||||||
|
smtpNoAuth: string;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ docker buildx build \
|
|||||||
--no-cache \
|
--no-cache \
|
||||||
-t kyantech/palmr:latest \
|
-t kyantech/palmr:latest \
|
||||||
-t kyantech/palmr:$TAG \
|
-t kyantech/palmr:$TAG \
|
||||||
--load \
|
--push \
|
||||||
.
|
.
|
||||||
|
|
||||||
if [ $? -eq 0 ]; then
|
if [ $? -eq 0 ]; then
|
||||||
|
|||||||
Reference in New Issue
Block a user