Skip to content

Commit 0261417

Browse files
committed
feat(email): improve SMTP configuration with flexible port and secure mode handling
- Change SmtpConfig.Port from int to nullable int? with PortValue property for default fallback - Add SecureMode property to SmtpConfig for string-based SSL/TLS mode representation - Refactor EmailService connection logic to align with nodemailer's secure mode semantics - Update SecureSocketOptions selection: use SslOnConnect for secure=true, StartTls for secure=false with port 587 - Replace SmtpSecure boolean with SmtpSecureMode string property in ConfigViewModel - Add SmtpSecureModeIndex and SmtpSecureModeDescription computed properties for UI binding - Update ConfigPage UI to use ComboBox for secure mode selection instead of CheckBox - Improve SMTP connection handling to support both implicit SSL (465) and STARTTLS (587) patterns - Maintain backward compatibility by defaulting Port to 587 when null
1 parent 5dcd822 commit 0261417

4 files changed

Lines changed: 87 additions & 29 deletions

File tree

Models/EmailConfig.cs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,23 @@ public class SmtpConfig
2323
public string Host { get; set; } = string.Empty;
2424

2525
[JsonPropertyName("port")]
26-
public int Port { get; set; } = 587;
26+
public int? Port { get; set; }
2727

2828
[JsonPropertyName("secure")]
2929
public bool Secure { get; set; } = true;
3030

3131
[JsonPropertyName("auth")]
3232
public SmtpAuth Auth { get; set; } = new();
33+
34+
[JsonIgnore]
35+
public int PortValue => Port ?? 587;
36+
37+
[JsonIgnore]
38+
public string SecureMode
39+
{
40+
get => Secure ? "ssl" : "starttls";
41+
set => Secure = value == "ssl";
42+
}
3343
}
3444

3545
public class SmtpAuth

Services/EmailService.cs

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -134,14 +134,27 @@ private async Task<bool> SendEmailAsync(EmailConfig config, string subject, stri
134134

135135
using var client = new SmtpClient();
136136

137-
// 根据端口和 Secure 设置选择合适的连接方式
138-
var secureSocketOptions = config.Smtp.Port == 465 && config.Smtp.Secure
139-
? SecureSocketOptions.SslOnConnect // 465 端口使用隐式 SSL
140-
: config.Smtp.Secure
141-
? SecureSocketOptions.StartTls // 587 端口使用 STARTTLS
142-
: SecureSocketOptions.None; // 不加密
143-
144-
await client.ConnectAsync(config.Smtp.Host, config.Smtp.Port, secureSocketOptions);
137+
// 与 nodemailer 保持一致的加密逻辑
138+
// secure=true 表示使用 SSL/TLS (通常用于 465 端口)
139+
// secure=false 表示使用 STARTTLS (通常用于 587 端口) 或不加密
140+
SecureSocketOptions secureSocketOptions;
141+
if (config.Smtp.Secure)
142+
{
143+
// secure=true: 使用隐式 SSL (465 端口)
144+
secureSocketOptions = SecureSocketOptions.SslOnConnect;
145+
}
146+
else if (config.Smtp.PortValue == 587)
147+
{
148+
// secure=false + 587 端口: 使用 STARTTLS
149+
secureSocketOptions = SecureSocketOptions.StartTls;
150+
}
151+
else
152+
{
153+
// secure=false + 其他端口: 不加密
154+
secureSocketOptions = SecureSocketOptions.None;
155+
}
156+
157+
await client.ConnectAsync(config.Smtp.Host, config.Smtp.PortValue, secureSocketOptions);
145158
await client.AuthenticateAsync(config.Smtp.Auth.User, config.Smtp.Auth.Pass);
146159
await client.SendAsync(message);
147160
await client.DisconnectAsync(true);

ViewModels/ConfigViewModel.cs

Lines changed: 42 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,8 @@ private void CheckUnsavedChanges()
3535
{
3636
var emailChanged = EmailEnabled != _savedEmailConfig.Enabled ||
3737
SmtpHost != _savedEmailConfig.Smtp.Host ||
38-
SmtpPort != _savedEmailConfig.Smtp.Port ||
39-
SmtpSecure != _savedEmailConfig.Smtp.Secure ||
38+
SmtpPort != _savedEmailConfig.Smtp.PortValue ||
39+
SmtpSecureMode != _savedEmailConfig.Smtp.SecureMode ||
4040
SmtpUser != _savedEmailConfig.Smtp.Auth.User ||
4141
SmtpPass != _savedEmailConfig.Smtp.Auth.Pass ||
4242
EmailFrom != _savedEmailConfig.From ||
@@ -204,8 +204,8 @@ public int LogRetentionHours
204204

205205
private bool _emailEnabled;
206206
private string _smtpHost = string.Empty;
207-
private int _smtpPort = 587;
208-
private bool _smtpSecure = true;
207+
private int? _smtpPort = 587;
208+
private string _smtpSecureMode = "starttls";
209209
private string _smtpUser = string.Empty;
210210
private string _smtpPass = string.Empty;
211211
private string _emailFrom = string.Empty;
@@ -223,16 +223,42 @@ public string SmtpHost
223223
set { this.RaiseAndSetIfChanged(ref _smtpHost, value); CheckUnsavedChanges(); }
224224
}
225225

226-
public int SmtpPort
226+
public int? SmtpPort
227227
{
228228
get => _smtpPort;
229229
set { this.RaiseAndSetIfChanged(ref _smtpPort, value); CheckUnsavedChanges(); }
230230
}
231231

232-
public bool SmtpSecure
232+
public string SmtpSecureMode
233233
{
234-
get => _smtpSecure;
235-
set { this.RaiseAndSetIfChanged(ref _smtpSecure, value); CheckUnsavedChanges(); }
234+
get => _smtpSecureMode;
235+
set
236+
{
237+
this.RaiseAndSetIfChanged(ref _smtpSecureMode, value);
238+
this.RaisePropertyChanged(nameof(SmtpSecureModeIndex));
239+
this.RaisePropertyChanged(nameof(SmtpSecureModeDescription));
240+
CheckUnsavedChanges();
241+
}
242+
}
243+
244+
public int SmtpSecureModeIndex
245+
{
246+
get => _smtpSecureMode == "ssl" ? 1 : 0;
247+
set
248+
{
249+
var newMode = value == 1 ? "ssl" : "starttls";
250+
if (_smtpSecureMode != newMode)
251+
{
252+
SmtpSecureMode = newMode;
253+
}
254+
}
255+
}
256+
257+
public string SmtpSecureModeDescription
258+
{
259+
get => _smtpSecureMode == "ssl"
260+
? "SSL/TLS 直接加密连接"
261+
: "STARTTLS 升级加密连接";
236262
}
237263

238264
public string SmtpUser
@@ -463,8 +489,8 @@ private async Task LoadConfigAsync()
463489
var emailConfig = await _emailService.LoadConfigAsync();
464490
EmailEnabled = emailConfig.Enabled;
465491
SmtpHost = emailConfig.Smtp.Host;
466-
SmtpPort = emailConfig.Smtp.Port;
467-
SmtpSecure = emailConfig.Smtp.Secure;
492+
SmtpPort = emailConfig.Smtp.PortValue;
493+
SmtpSecureMode = emailConfig.Smtp.SecureMode;
468494
SmtpUser = emailConfig.Smtp.Auth.User;
469495
SmtpPass = emailConfig.Smtp.Auth.Pass;
470496
EmailFrom = emailConfig.From;
@@ -492,8 +518,8 @@ private async Task LoadConfigAsync()
492518
Smtp = new SmtpConfig
493519
{
494520
Host = SmtpHost,
495-
Port = SmtpPort,
496-
Secure = SmtpSecure,
521+
Port = SmtpPort ?? 587,
522+
SecureMode = SmtpSecureMode,
497523
Auth = new SmtpAuth
498524
{
499525
User = SmtpUser,
@@ -542,8 +568,8 @@ private async Task SaveConfigAsync()
542568
Smtp = new SmtpConfig
543569
{
544570
Host = SmtpHost,
545-
Port = SmtpPort,
546-
Secure = SmtpSecure,
571+
Port = SmtpPort ?? 587,
572+
SecureMode = SmtpSecureMode,
547573
Auth = new SmtpAuth
548574
{
549575
User = SmtpUser,
@@ -600,8 +626,8 @@ private async Task TestEmailAsync()
600626
Smtp = new SmtpConfig
601627
{
602628
Host = SmtpHost,
603-
Port = SmtpPort,
604-
Secure = SmtpSecure,
629+
Port = SmtpPort ?? 587,
630+
SecureMode = SmtpSecureMode,
605631
Auth = new SmtpAuth
606632
{
607633
User = SmtpUser,

Views/Pages/ConfigPage.axaml

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -159,10 +159,19 @@
159159
ShowButtonSpinner="False"/>
160160
</StackPanel>
161161

162-
<!-- SSL/TLS -->
163-
<CheckBox Content="使用 SSL/TLS 加密连接"
164-
IsChecked="{Binding SmtpSecure}"
165-
Foreground="{DynamicResource TextPrimary}"/>
162+
<!-- 加密方式 -->
163+
<StackPanel Spacing="8">
164+
<TextBlock Text="加密方式" Foreground="{DynamicResource TextSecondary}"/>
165+
<ComboBox SelectedIndex="{Binding SmtpSecureModeIndex}"
166+
Width="300"
167+
HorizontalAlignment="Left">
168+
<ComboBoxItem Content="STARTTLS(常用端口 587)"/>
169+
<ComboBoxItem Content="SSL/TLS(常用端口 465)"/>
170+
</ComboBox>
171+
<TextBlock Foreground="{DynamicResource TextSecondary}" FontSize="12">
172+
<Run Text="{Binding SmtpSecureModeDescription}"/>
173+
</TextBlock>
174+
</StackPanel>
166175

167176
<Border Height="1" Background="{DynamicResource BorderColor}"/>
168177

0 commit comments

Comments
 (0)