fix: support SMTP STARTTLS mode and NTLM auth (#5426)

* fix: support SMTP STARTTLS mode and NTLM auth

Add explicit SMTP STARTTLS configuration for 587-style connections and keep SSL/TLS as the implicit TLS mode.

Prefer PLAIN when advertised, keep LOGIN compatibility, and add NTLM as a fallback for Exchange SMTP servers that require it after STARTTLS.

* fix: respect explicit SMTP encryption mode

* fix: preserve SMTP TLS compatibility
This commit is contained in:
Benson Yan
2026-06-24 12:45:39 +08:00
committed by GitHub
parent 9fc9c8f1e3
commit 2f23a66733
27 changed files with 959 additions and 52 deletions
+57 -7
View File
@@ -91,6 +91,7 @@ const SystemSetting = () => {
EmailDomainRestrictionEnabled: '',
EmailAliasRestrictionEnabled: '',
SMTPSSLEnabled: '',
SMTPStartTLSEnabled: '',
SMTPForceAuthLogin: '',
EmailDomainWhitelist: [],
TelegramOAuthEnabled: '',
@@ -183,6 +184,7 @@ const SystemSetting = () => {
case 'EmailDomainRestrictionEnabled':
case 'EmailAliasRestrictionEnabled':
case 'SMTPSSLEnabled':
case 'SMTPStartTLSEnabled':
case 'SMTPForceAuthLogin':
case 'LinuxDOOAuthEnabled':
case 'discord.enabled':
@@ -321,6 +323,13 @@ const SystemSetting = () => {
const submitSMTP = async () => {
const options = [];
const smtpSecurityMode = inputs.SMTPSSLEnabled
? 'ssl_tls'
: inputs.SMTPStartTLSEnabled
? 'starttls'
: 'none';
const nextSMTPSSLEnabled = smtpSecurityMode === 'ssl_tls';
const nextSMTPStartTLSEnabled = smtpSecurityMode === 'starttls';
if (originInputs['SMTPServer'] !== inputs.SMTPServer) {
options.push({ key: 'SMTPServer', value: inputs.SMTPServer });
@@ -343,6 +352,15 @@ const SystemSetting = () => {
) {
options.push({ key: 'SMTPToken', value: inputs.SMTPToken });
}
if (originInputs['SMTPSSLEnabled'] !== nextSMTPSSLEnabled) {
options.push({ key: 'SMTPSSLEnabled', value: nextSMTPSSLEnabled });
}
if (originInputs['SMTPStartTLSEnabled'] !== nextSMTPStartTLSEnabled) {
options.push({
key: 'SMTPStartTLSEnabled',
value: nextSMTPStartTLSEnabled,
});
}
if (options.length > 0) {
await updateOptions(options);
@@ -691,6 +709,23 @@ const SystemSetting = () => {
}
};
const handleSMTPSecurityModeChange = async (event) => {
const mode = event && event.target ? event.target.value : event;
const nextSMTPSSLEnabled = mode === 'ssl_tls';
const nextSMTPStartTLSEnabled = mode === 'starttls';
formApiRef.current?.setValue('SMTPSSLEnabled', nextSMTPSSLEnabled);
formApiRef.current?.setValue(
'SMTPStartTLSEnabled',
nextSMTPStartTLSEnabled,
);
await updateOptions([
{ key: 'SMTPSSLEnabled', value: nextSMTPSSLEnabled },
{ key: 'SMTPStartTLSEnabled', value: nextSMTPStartTLSEnabled },
]);
};
const handlePasswordLoginConfirm = async () => {
await updateOptions([{ key: 'PasswordLoginEnabled', value: false }]);
setShowPasswordLoginConfirmModal(false);
@@ -1328,15 +1363,30 @@ const SystemSetting = () => {
/>
</Col>
<Col xs={24} sm={24} md={8} lg={8} xl={8}>
<Form.Checkbox
field='SMTPSSLEnabled'
noLabel
onChange={(e) =>
handleCheckboxChange('SMTPSSLEnabled', e)
<Text strong>{t('SMTP 加密方式')}</Text>
<Radio.Group
type='button'
value={
inputs.SMTPSSLEnabled
? 'ssl_tls'
: inputs.SMTPStartTLSEnabled
? 'starttls'
: 'none'
}
onChange={handleSMTPSecurityModeChange}
style={{ marginTop: 8, marginBottom: 8 }}
>
{t('启用SMTP SSL')}
</Form.Checkbox>
<Radio value='none'>{t('无加密')}</Radio>
<Radio value='ssl_tls'>{t('SSL/TLS')}</Radio>
<Radio value='starttls'>{t('STARTTLS')}</Radio>
</Radio.Group>
<Text
type='secondary'
size='small'
style={{ display: 'block', marginBottom: 8 }}
>
{t('请选择一种 SMTP 传输加密方式')}
</Text>
<Form.Checkbox
field='SMTPForceAuthLogin'
noLabel
+5
View File
@@ -234,6 +234,9 @@
"SMTP 端口": "SMTP Port",
"SMTP 访问凭证": "SMTP Access Credential",
"SMTP 账户": "SMTP Account",
"SMTP 加密方式": "SMTP encryption",
"SSL/TLS": "SSL/TLS",
"STARTTLS": "STARTTLS",
"Sonnet 模型": "Sonnet Model",
"speed 字段用于控制 Claude 推理速度模式。默认关闭以避免意外切换到 fast 模式": "The speed field controls Claude inference speed mode. Disabled by default to avoid unintentionally switching to fast mode",
"SSE 事件": "SSE Events",
@@ -1808,6 +1811,7 @@
"新获取的模型": "New models",
"新额度:": "New quota: ",
"无": "None",
"无加密": "No encryption",
"无GPU": "No GPU",
"无冲突项": "No conflict items",
"无效的部署信息": "Invalid deployment information",
@@ -3232,6 +3236,7 @@
"请选择该渠道所支持的模型,留空则不更改": "Please select the models supported by the channel, leaving blank will not change",
"请选择过期时间": "Please select expiration time",
"请选择通知方式": "Please select notification method",
"请选择一种 SMTP 传输加密方式": "Please select one SMTP transport security mode",
"调整额度": "Adjust Quota",
"调整额度成功": "Quota adjusted successfully",
"调用次数": "Call Count",
+5
View File
@@ -232,6 +232,9 @@
"SMTP 端口": "Port SMTP",
"SMTP 访问凭证": "Informations d'identification d'accès SMTP",
"SMTP 账户": "Compte SMTP",
"SMTP 加密方式": "Chiffrement SMTP",
"SSL/TLS": "SSL/TLS",
"STARTTLS": "STARTTLS",
"Sonnet 模型": "Modèle Sonnet",
"speed 字段用于控制 Claude 推理速度模式。默认关闭以避免意外切换到 fast 模式": "Le champ speed contrôle le mode de vitesse d'inférence de Claude. Désactivé par défaut pour éviter un passage involontaire au mode fast",
"SSE 事件": "Événement SSE",
@@ -1801,6 +1804,7 @@
"新获取的模型": "Nouveaux modèles",
"新额度:": "Nouveau quota : ",
"无": "Aucun",
"无加密": "Aucun chiffrement",
"无GPU": "No GPU",
"无冲突项": "Aucun élément en conflit",
"无效的部署信息": "Invalid deployment information",
@@ -3216,6 +3220,7 @@
"请选择该渠道所支持的模型,留空则不更改": "Veuillez sélectionner les modèles pris en charge par le canal, laisser vide ne changera rien",
"请选择过期时间": "Veuillez sélectionner une date d'expiration",
"请选择通知方式": "Veuillez sélectionner la méthode de notification",
"请选择一种 SMTP 传输加密方式": "Veuillez sélectionner un mode de sécurité de transport SMTP",
"调整额度": "Ajuster le quota",
"调整额度成功": "Quota ajusté avec succès",
"调用次数": "Nombre d'appels",
+5
View File
@@ -226,6 +226,9 @@
"SMTP 端口": "SMTP ポート",
"SMTP 访问凭证": "SMTP 認証情報",
"SMTP 账户": "SMTP アカウント",
"SMTP 加密方式": "SMTP 暗号化方式",
"SSL/TLS": "SSL/TLS",
"STARTTLS": "STARTTLS",
"Sonnet 模型": "Sonnetモデル",
"speed 字段用于控制 Claude 推理速度模式。默认关闭以避免意外切换到 fast 模式": "speed フィールドは Claude の推論速度モードを制御します。意図せず fast モードへ切り替わるのを避けるため、デフォルトで無効です",
"SSE 事件": "SSEイベント",
@@ -1772,6 +1775,7 @@
"新获取的模型": "新たに取得したモデル",
"新额度:": "新しい残高:",
"无": "なし",
"无加密": "暗号化なし",
"无GPU": "No GPU",
"无冲突项": "競合項目なし",
"无效的部署信息": "Invalid deployment information",
@@ -3185,6 +3189,7 @@
"请选择该渠道所支持的模型,留空则不更改": "このチャネルに対応しているモデルを選択してください。空欄の場合は変更されません",
"请选择过期时间": "有効期限を選択してください",
"请选择通知方式": "通知方法を選択してください",
"请选择一种 SMTP 传输加密方式": "SMTP の転送暗号化方式を 1 つ選択してください",
"调整额度": "残高調整",
"调整额度成功": "残高の調整に成功しました",
"调用次数": "呼び出し回数",
+5
View File
@@ -236,6 +236,9 @@
"SMTP 端口": "Порт SMTP",
"SMTP 访问凭证": "Учетные данные доступа SMTP",
"SMTP 账户": "Учетная запись SMTP",
"SMTP 加密方式": "Шифрование SMTP",
"SSL/TLS": "SSL/TLS",
"STARTTLS": "STARTTLS",
"Sonnet 模型": "Модель Sonnet",
"speed 字段用于控制 Claude 推理速度模式。默认关闭以避免意外切换到 fast 模式": "Поле speed управляет режимом скорости инференса Claude. По умолчанию отключено, чтобы избежать непреднамеренного переключения в режим fast",
"SSE 事件": "Событие SSE",
@@ -1819,6 +1822,7 @@
"新获取的模型": "Новые полученные модели",
"新额度:": "Новая квота: ",
"无": "Нет",
"无加密": "Без шифрования",
"无GPU": "No GPU",
"无冲突项": "Нет конфликтующих элементов",
"无效的部署信息": "Invalid deployment information",
@@ -3236,6 +3240,7 @@
"请选择该渠道所支持的模型,留空则不更改": "Пожалуйста, выберите модели, поддерживаемые этим каналом, оставьте пустым для без изменений",
"请选择过期时间": "Пожалуйста, выберите время истечения",
"请选择通知方式": "Пожалуйста, выберите способ уведомления",
"请选择一种 SMTP 传输加密方式": "Выберите один из режимов защиты SMTP-транспорта",
"调整额度": "Скорректировать квоту",
"调整额度成功": "Квота успешно скорректирована",
"调用次数": "Количество вызовов",
+5
View File
@@ -227,6 +227,9 @@
"SMTP 端口": "Cổng SMTP",
"SMTP 访问凭证": "Thông tin xác thực truy cập SMTP",
"SMTP 账户": "Tài khoản SMTP",
"SMTP 加密方式": "Mã hóa SMTP",
"SSL/TLS": "SSL/TLS",
"STARTTLS": "STARTTLS",
"Sonnet 模型": "Model Sonnet",
"speed 字段用于控制 Claude 推理速度模式。默认关闭以避免意外切换到 fast 模式": "Trường speed kiểm soát chế độ tốc độ suy luận Claude. Mặc định tắt để tránh vô tình chuyển sang chế độ fast",
"SSE 事件": "Sự kiện SSE",
@@ -1773,6 +1776,7 @@
"新获取的模型": "Mô hình mới",
"新额度:": "Hạn ngạch mới: ",
"无": "Không",
"无加密": "Không mã hóa",
"无GPU": "No GPU",
"无冲突项": "Không có mục xung đột",
"无效的部署信息": "Invalid deployment information",
@@ -3632,6 +3636,7 @@
"请选择语言": "Vui lòng chọn ngôn ngữ",
"请选择过期时间": "Vui lòng chọn thời gian hết hạn",
"请选择通知方式": "Vui lòng chọn phương thức thông báo",
"请选择一种 SMTP 传输加密方式": "Vui lòng chọn một chế độ bảo mật truyền tải SMTP",
"请阅读并同意": "Vui lòng đọc và đồng ý",
"读取": "Đọc",
"读取失败": "Đọc thất bại",
+5
View File
@@ -219,6 +219,9 @@
"SMTP 端口": "SMTP 端口",
"SMTP 访问凭证": "SMTP 访问凭证",
"SMTP 账户": "SMTP 账户",
"SMTP 加密方式": "SMTP 加密方式",
"SSL/TLS": "SSL/TLS",
"STARTTLS": "STARTTLS",
"speed 字段用于控制 Claude 推理速度模式。默认关闭以避免意外切换到 fast 模式": "speed 字段用于控制 Claude 推理速度模式。默认关闭以避免意外切换到 fast 模式",
"SSE 事件": "SSE 事件",
"SSE数据流": "SSE数据流",
@@ -1766,6 +1769,7 @@
"新获取的模型": "新获取的模型",
"新额度:": "新额度:",
"无": "无",
"无加密": "无加密",
"无GPU": "无GPU",
"无冲突项": "无冲突项",
"无效的部署信息": "无效的部署信息",
@@ -3187,6 +3191,7 @@
"请选择该渠道所支持的模型,留空则不更改": "请选择该渠道所支持的模型,留空则不更改",
"请选择过期时间": "请选择过期时间",
"请选择通知方式": "请选择通知方式",
"请选择一种 SMTP 传输加密方式": "请选择一种 SMTP 传输加密方式",
"调整额度": "调整额度",
"调整额度成功": "调整额度成功",
"调用次数": "调用次数",
+5
View File
@@ -225,6 +225,9 @@
"SMTP 端口": "SMTP 端口",
"SMTP 访问凭证": "SMTP 訪問憑證",
"SMTP 账户": "SMTP 帳號",
"SMTP 加密方式": "SMTP 加密方式",
"SSL/TLS": "SSL/TLS",
"STARTTLS": "STARTTLS",
"speed 字段用于控制 Claude 推理速度模式。默认关闭以避免意外切换到 fast 模式": "speed 字段用於控制 Claude 推理速度模式。預設關閉以避免意外切換到 fast 模式",
"SSE 事件": "SSE 事件",
"SSE数据流": "SSE數據流",
@@ -1776,6 +1779,7 @@
"新获取的模型": "新獲取的模型",
"新额度:": "新額度:",
"无": "無",
"无加密": "無加密",
"无GPU": "無GPU",
"无冲突项": "無衝突項",
"无效的部署信息": "無效的部署資訊",
@@ -3197,6 +3201,7 @@
"请选择该渠道所支持的模型,留空则不更改": "請選擇該管道所支援的模型,留空則不更改",
"请选择过期时间": "請選擇過期時間",
"请选择通知方式": "請選擇通知方式",
"请选择一种 SMTP 传输加密方式": "請選擇一種 SMTP 傳輸加密方式",
"调整额度": "調整額度",
"调整额度成功": "調整額度成功",
"调用次数": "調用次數",
+5
View File
@@ -144,6 +144,9 @@
"SMTP 端口": "SMTP 端口",
"SMTP 访问凭证": "SMTP 访问凭证",
"SMTP 账户": "SMTP 账户",
"SMTP 加密方式": "SMTP 加密方式",
"SSL/TLS": "SSL/TLS",
"STARTTLS": "STARTTLS",
"SSE 事件": "SSE 事件",
"SSE数据流": "SSE数据流",
"SSRF防护开关详细说明": "总开关控制是否启用SSRF防护功能。关闭后将跳过所有SSRF检查,允许访问任意URL。⚠️ 仅在完全信任环境中关闭此功能。",
@@ -1217,6 +1220,7 @@
"新获取的模型": "新获取的模型",
"新额度:": "新额度:",
"无": "无",
"无加密": "无加密",
"无GPU": "无GPU",
"无冲突项": "无冲突项",
"无效的部署信息": "无效的部署信息",
@@ -2242,6 +2246,7 @@
"请选择该渠道所支持的模型,留空则不更改": "请选择该渠道所支持的模型,留空则不更改",
"请选择过期时间": "请选择过期时间",
"请选择通知方式": "请选择通知方式",
"请选择一种 SMTP 传输加密方式": "请选择一种 SMTP 传输加密方式",
"调用次数": "调用次数",
"调用次数分布": "调用次数分布",
"调用次数排行": "调用次数排行",