feat(ui): EWS transport selector on /notifications/smtp
This commit is contained in:
+108
-26
@@ -46,12 +46,25 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="card-body small">
|
<div class="card-body small">
|
||||||
<div class="row g-2">
|
<div class="row g-2">
|
||||||
<div class="col-md-4 text-muted">Host</div>
|
<div class="col-md-4 text-muted">Transport</div>
|
||||||
<div class="col-md-8">@smtp.Host:@smtp.Port</div>
|
<div class="col-md-8"><span class="badge bg-secondary">@(smtp.Transport ?? "Smtp")</span></div>
|
||||||
|
@if (IsEws(smtp.Transport))
|
||||||
|
{
|
||||||
|
<div class="col-md-4 text-muted">EWS URL</div>
|
||||||
|
<div class="col-md-8">@smtp.Host</div>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<div class="col-md-4 text-muted">Host</div>
|
||||||
|
<div class="col-md-8">@smtp.Host:@smtp.Port</div>
|
||||||
|
}
|
||||||
<div class="col-md-4 text-muted">Auth Type</div>
|
<div class="col-md-4 text-muted">Auth Type</div>
|
||||||
<div class="col-md-8"><span class="badge bg-secondary">@smtp.AuthType</span></div>
|
<div class="col-md-8"><span class="badge bg-secondary">@smtp.AuthType</span></div>
|
||||||
<div class="col-md-4 text-muted">TLS Mode</div>
|
@if (!IsEws(smtp.Transport))
|
||||||
<div class="col-md-8">@(string.IsNullOrWhiteSpace(smtp.TlsMode) ? "(not set)" : smtp.TlsMode)</div>
|
{
|
||||||
|
<div class="col-md-4 text-muted">TLS Mode</div>
|
||||||
|
<div class="col-md-8">@(string.IsNullOrWhiteSpace(smtp.TlsMode) ? "(not set)" : smtp.TlsMode)</div>
|
||||||
|
}
|
||||||
<div class="col-md-4 text-muted">From Address</div>
|
<div class="col-md-4 text-muted">From Address</div>
|
||||||
<div class="col-md-8">@smtp.FromAddress</div>
|
<div class="col-md-8">@smtp.FromAddress</div>
|
||||||
<div class="col-md-4 text-muted">Credentials</div>
|
<div class="col-md-4 text-muted">Credentials</div>
|
||||||
@@ -74,33 +87,44 @@
|
|||||||
<div class="card-header">@(_editingSmtp != null ? "Edit SMTP Configuration" : "Add SMTP Configuration")</div>
|
<div class="card-header">@(_editingSmtp != null ? "Edit SMTP Configuration" : "Add SMTP Configuration")</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div class="row g-3">
|
<div class="row g-3">
|
||||||
|
<div class="col-md-4">
|
||||||
|
<label class="form-label">Transport</label>
|
||||||
|
<select class="form-select" @bind="Transport">
|
||||||
|
<option>Smtp</option>
|
||||||
|
<option>Ews</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<label class="form-label">Host</label>
|
<label class="form-label">@(IsEwsForm ? "EWS URL" : "Host")</label>
|
||||||
<input type="text" class="form-control" @bind="_host" placeholder="smtp.example.com" />
|
<input type="text" class="form-control" @bind="_host"
|
||||||
</div>
|
placeholder="@(IsEwsForm ? "https://mail.example.com/ews/exchange.asmx" : "smtp.example.com")" />
|
||||||
<div class="col-md-4">
|
|
||||||
<label class="form-label">Port</label>
|
|
||||||
<input type="number" class="form-control" @bind="_port" min="1" max="65535" />
|
|
||||||
</div>
|
|
||||||
<div class="col-md-4">
|
|
||||||
<label class="form-label">Auth Type</label>
|
|
||||||
<select class="form-select" @bind="_authType">
|
|
||||||
<option>OAuth2</option>
|
|
||||||
<option>Basic</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-4">
|
|
||||||
<label class="form-label">TLS Mode</label>
|
|
||||||
<select class="form-select" @bind="_tlsMode">
|
|
||||||
<option>None</option>
|
|
||||||
<option>StartTLS</option>
|
|
||||||
<option>SSL</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
</div>
|
||||||
|
@if (!IsEwsForm)
|
||||||
|
{
|
||||||
|
<div class="col-md-4">
|
||||||
|
<label class="form-label">Port</label>
|
||||||
|
<input type="number" class="form-control" @bind="_port" min="1" max="65535" />
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4">
|
||||||
|
<label class="form-label">Auth Type</label>
|
||||||
|
<select class="form-select" @bind="_authType">
|
||||||
|
<option>OAuth2</option>
|
||||||
|
<option>Basic</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4">
|
||||||
|
<label class="form-label">TLS Mode</label>
|
||||||
|
<select class="form-select" @bind="_tlsMode">
|
||||||
|
<option>None</option>
|
||||||
|
<option>StartTLS</option>
|
||||||
|
<option>SSL</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<label class="form-label">Credentials</label>
|
<label class="form-label">Credentials</label>
|
||||||
<input type="password" class="form-control" @bind="_credentials"
|
<input type="password" class="form-control" @bind="_credentials"
|
||||||
placeholder="OAuth2 client secret or SMTP password" />
|
placeholder="@(IsEwsForm ? @"domain\username:password" : "OAuth2 client secret or SMTP password")" />
|
||||||
<div class="form-text">Treat as sensitive — visible to admins only.</div>
|
<div class="form-text">Treat as sensitive — visible to admins only.</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
@@ -153,6 +177,10 @@
|
|||||||
|
|
||||||
private string _host = string.Empty;
|
private string _host = string.Empty;
|
||||||
private int _port = 587;
|
private int _port = 587;
|
||||||
|
private string _transport = "Smtp";
|
||||||
|
// AuthType as it was before the operator switched to Ews, so switching back to Smtp
|
||||||
|
// restores their choice rather than leaving the forced "Basic" behind.
|
||||||
|
private string _authTypeBeforeEws = "OAuth2";
|
||||||
private string _authType = "OAuth2";
|
private string _authType = "OAuth2";
|
||||||
private string? _tlsMode;
|
private string? _tlsMode;
|
||||||
private string? _credentials;
|
private string? _credentials;
|
||||||
@@ -163,6 +191,42 @@
|
|||||||
|
|
||||||
private ToastNotification _toast = default!;
|
private ToastNotification _toast = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Bound by the Transport select. The setter is where the transport's shape rules are
|
||||||
|
/// applied: EWS authenticates with HTTP Basic only, so selecting it forces the auth type
|
||||||
|
/// (which also hides the OAuth2 inputs, gated on AuthType as before). Switching back to
|
||||||
|
/// Smtp restores the auth type the operator had chosen.
|
||||||
|
/// </summary>
|
||||||
|
private string Transport
|
||||||
|
{
|
||||||
|
get => _transport;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (string.Equals(_transport, value, StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsEws(value))
|
||||||
|
{
|
||||||
|
_authTypeBeforeEws = _authType;
|
||||||
|
_authType = "Basic";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_authType = _authTypeBeforeEws;
|
||||||
|
}
|
||||||
|
_transport = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Gets a value indicating whether the edit form is currently in EWS mode.</summary>
|
||||||
|
private bool IsEwsForm => IsEws(_transport);
|
||||||
|
|
||||||
|
/// <summary>Null-safe, case-insensitive test for the EWS transport; null/blank means Smtp.</summary>
|
||||||
|
private static bool IsEws(string? transport)
|
||||||
|
=> string.Equals(transport?.Trim(), "Ews", StringComparison.OrdinalIgnoreCase);
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
await LoadAsync();
|
await LoadAsync();
|
||||||
@@ -188,6 +252,8 @@
|
|||||||
_editingSmtp = null;
|
_editingSmtp = null;
|
||||||
_host = string.Empty;
|
_host = string.Empty;
|
||||||
_port = 587;
|
_port = 587;
|
||||||
|
_transport = "Smtp";
|
||||||
|
_authTypeBeforeEws = "OAuth2";
|
||||||
_authType = "OAuth2";
|
_authType = "OAuth2";
|
||||||
_tlsMode = "None";
|
_tlsMode = "None";
|
||||||
_credentials = null;
|
_credentials = null;
|
||||||
@@ -203,6 +269,8 @@
|
|||||||
_editingSmtp = smtp;
|
_editingSmtp = smtp;
|
||||||
_host = smtp.Host;
|
_host = smtp.Host;
|
||||||
_port = smtp.Port;
|
_port = smtp.Port;
|
||||||
|
_transport = smtp.Transport ?? "Smtp";
|
||||||
|
_authTypeBeforeEws = smtp.AuthType;
|
||||||
_authType = smtp.AuthType;
|
_authType = smtp.AuthType;
|
||||||
_tlsMode = smtp.TlsMode;
|
_tlsMode = smtp.TlsMode;
|
||||||
_credentials = smtp.Credentials;
|
_credentials = smtp.Credentials;
|
||||||
@@ -228,12 +296,25 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This page writes through the repository, so the ManagementActor's EWS shape gate
|
||||||
|
// is not on this path — mirror its Host rule here (auth type is already forced to
|
||||||
|
// Basic by the Transport setter) so an unusable row cannot be saved from the UI.
|
||||||
|
if (IsEwsForm
|
||||||
|
&& (!Uri.TryCreate(_host.Trim(), UriKind.Absolute, out var ewsEndpoint)
|
||||||
|
|| !string.Equals(ewsEndpoint.Scheme, Uri.UriSchemeHttps, StringComparison.Ordinal)))
|
||||||
|
{
|
||||||
|
_formError = "EWS transport requires an absolute https:// EWS endpoint URL "
|
||||||
|
+ "(e.g. https://mail.example.com/ews/exchange.asmx).";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (_editingSmtp != null)
|
if (_editingSmtp != null)
|
||||||
{
|
{
|
||||||
_editingSmtp.Host = _host.Trim();
|
_editingSmtp.Host = _host.Trim();
|
||||||
_editingSmtp.Port = _port;
|
_editingSmtp.Port = _port;
|
||||||
|
_editingSmtp.Transport = _transport;
|
||||||
_editingSmtp.AuthType = _authType;
|
_editingSmtp.AuthType = _authType;
|
||||||
_editingSmtp.TlsMode = _tlsMode;
|
_editingSmtp.TlsMode = _tlsMode;
|
||||||
_editingSmtp.Credentials = _credentials?.Trim();
|
_editingSmtp.Credentials = _credentials?.Trim();
|
||||||
@@ -247,6 +328,7 @@
|
|||||||
var smtp = new SmtpConfigurationEntity(_host.Trim(), _authType, _fromAddress.Trim())
|
var smtp = new SmtpConfigurationEntity(_host.Trim(), _authType, _fromAddress.Trim())
|
||||||
{
|
{
|
||||||
Port = _port,
|
Port = _port,
|
||||||
|
Transport = _transport,
|
||||||
TlsMode = _tlsMode,
|
TlsMode = _tlsMode,
|
||||||
Credentials = _credentials?.Trim(),
|
Credentials = _credentials?.Trim(),
|
||||||
OAuth2Authority = NormalizeOptional(_oauth2Authority),
|
OAuth2Authority = NormalizeOptional(_oauth2Authority),
|
||||||
|
|||||||
Reference in New Issue
Block a user