@@ -1,6 +1,5 @@
|
||||
using System.Net.Http.Headers;
|
||||
using System.Net.Http.Json;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace ROLAC.API.Services.Notifications;
|
||||
|
||||
@@ -11,12 +10,12 @@ public sealed class LineMessageChannel : IMessageChannel
|
||||
private const string ReplyUrl = "https://api.line.me/v2/bot/message/reply";
|
||||
|
||||
private readonly HttpClient _http;
|
||||
private readonly LineOptions _options;
|
||||
private readonly INotificationSettingsService _settings;
|
||||
|
||||
public LineMessageChannel(HttpClient http, IOptions<LineOptions> options)
|
||||
public LineMessageChannel(HttpClient http, INotificationSettingsService settings)
|
||||
{
|
||||
_http = http;
|
||||
_options = options.Value;
|
||||
_settings = settings;
|
||||
}
|
||||
|
||||
public Task<MessageSendResult> PushToUserAsync(string externalId, string text, CancellationToken ct = default)
|
||||
@@ -36,7 +35,8 @@ public sealed class LineMessageChannel : IMessageChannel
|
||||
{
|
||||
Content = JsonContent.Create(payload),
|
||||
};
|
||||
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _options.ChannelAccessToken);
|
||||
request.Headers.Authorization =
|
||||
new AuthenticationHeaderValue("Bearer", _settings.GetLine().ChannelAccessToken);
|
||||
|
||||
using var response = await _http.SendAsync(request, ct);
|
||||
if (response.IsSuccessStatusCode) return new MessageSendResult(true, null);
|
||||
|
||||
@@ -1,21 +1,22 @@
|
||||
using MailKit.Net.Smtp;
|
||||
using MailKit.Security;
|
||||
using Microsoft.Extensions.Options;
|
||||
using MimeKit;
|
||||
|
||||
namespace ROLAC.API.Services.Notifications;
|
||||
|
||||
/// <summary>Sends a single email via MailKit using the configured SMTP server.</summary>
|
||||
/// <summary>Sends a single email via MailKit using the current (DB-backed) SMTP settings.</summary>
|
||||
public sealed class MailKitSmtpDispatcher : ISmtpDispatcher
|
||||
{
|
||||
private readonly SmtpOptions _options;
|
||||
private readonly INotificationSettingsService _settings;
|
||||
|
||||
public MailKitSmtpDispatcher(IOptions<SmtpOptions> options) => _options = options.Value;
|
||||
public MailKitSmtpDispatcher(INotificationSettingsService settings) => _settings = settings;
|
||||
|
||||
public async Task SendAsync(OutboundEmail email, CancellationToken ct = default)
|
||||
{
|
||||
var options = _settings.GetSmtp();
|
||||
|
||||
var message = new MimeMessage();
|
||||
message.From.Add(new MailboxAddress(_options.FromName, _options.FromAddress));
|
||||
message.From.Add(new MailboxAddress(options.FromName, options.FromAddress));
|
||||
message.To.Add(MailboxAddress.Parse(email.ToAddress));
|
||||
message.Subject = email.Subject;
|
||||
|
||||
@@ -28,10 +29,10 @@ public sealed class MailKitSmtpDispatcher : ISmtpDispatcher
|
||||
message.Body = builder.ToMessageBody();
|
||||
|
||||
using var client = new SmtpClient();
|
||||
var socketOptions = _options.UseSsl ? SecureSocketOptions.StartTls : SecureSocketOptions.Auto;
|
||||
await client.ConnectAsync(_options.Host, _options.Port, socketOptions, ct);
|
||||
if (!string.IsNullOrEmpty(_options.User))
|
||||
await client.AuthenticateAsync(_options.User, _options.Password, ct);
|
||||
var socketOptions = options.UseSsl ? SecureSocketOptions.StartTls : SecureSocketOptions.Auto;
|
||||
await client.ConnectAsync(options.Host, options.Port, socketOptions, ct);
|
||||
if (!string.IsNullOrEmpty(options.User))
|
||||
await client.AuthenticateAsync(options.User, options.Password, ct);
|
||||
await client.SendAsync(message, ct);
|
||||
await client.DisconnectAsync(true, ct);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Options;
|
||||
using ROLAC.API.Data;
|
||||
|
||||
namespace ROLAC.API.Services.Notifications;
|
||||
|
||||
/// <summary>
|
||||
/// Supplies the current SMTP/Line settings from the <c>NotificationSetting</c> singleton row,
|
||||
/// caching a snapshot in memory so send paths don't hit the DB on every message. Registered as a
|
||||
/// singleton; the Settings UI calls <see cref="Reload"/> after an edit so changes take effect
|
||||
/// without restarting the API. Falls back to the "Smtp"/"Line" appsettings sections if the row
|
||||
/// has not been seeded yet.
|
||||
/// </summary>
|
||||
public interface INotificationSettingsService
|
||||
{
|
||||
SmtpOptions GetSmtp();
|
||||
LineOptions GetLine();
|
||||
void Reload();
|
||||
}
|
||||
|
||||
public sealed class NotificationSettingsService : INotificationSettingsService
|
||||
{
|
||||
private readonly IServiceScopeFactory _scopeFactory;
|
||||
private readonly IOptions<SmtpOptions> _smtpFallback;
|
||||
private readonly IOptions<LineOptions> _lineFallback;
|
||||
private readonly object _gate = new();
|
||||
|
||||
private SmtpOptions? _smtp;
|
||||
private LineOptions? _line;
|
||||
|
||||
public NotificationSettingsService(
|
||||
IServiceScopeFactory scopeFactory,
|
||||
IOptions<SmtpOptions> smtpFallback,
|
||||
IOptions<LineOptions> lineFallback)
|
||||
{
|
||||
_scopeFactory = scopeFactory;
|
||||
_smtpFallback = smtpFallback;
|
||||
_lineFallback = lineFallback;
|
||||
}
|
||||
|
||||
public SmtpOptions GetSmtp()
|
||||
{
|
||||
EnsureLoaded();
|
||||
return _smtp!;
|
||||
}
|
||||
|
||||
public LineOptions GetLine()
|
||||
{
|
||||
EnsureLoaded();
|
||||
return _line!;
|
||||
}
|
||||
|
||||
public void Reload()
|
||||
{
|
||||
lock (_gate)
|
||||
{
|
||||
_smtp = null;
|
||||
_line = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void EnsureLoaded()
|
||||
{
|
||||
lock (_gate)
|
||||
{
|
||||
if (_smtp is not null && _line is not null)
|
||||
return;
|
||||
|
||||
using var scope = _scopeFactory.CreateScope();
|
||||
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||
var row = db.NotificationSettings.AsNoTracking().OrderBy(s => s.Id).FirstOrDefault();
|
||||
|
||||
if (row is null)
|
||||
{
|
||||
// Not seeded yet — use the appsettings values so sends still work.
|
||||
_smtp = _smtpFallback.Value;
|
||||
_line = _lineFallback.Value;
|
||||
return;
|
||||
}
|
||||
|
||||
_smtp = new SmtpOptions
|
||||
{
|
||||
Host = row.SmtpHost,
|
||||
Port = row.SmtpPort,
|
||||
UseSsl = row.SmtpUseSsl,
|
||||
User = row.SmtpUser,
|
||||
Password = row.SmtpPassword,
|
||||
FromAddress = row.FromAddress,
|
||||
FromName = row.FromName,
|
||||
};
|
||||
_line = new LineOptions
|
||||
{
|
||||
ChannelAccessToken = row.LineChannelAccessToken,
|
||||
ChannelSecret = row.LineChannelSecret,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user