using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Options; using ROLAC.API.Data; namespace ROLAC.API.Services.Notifications; /// /// Supplies the current SMTP/Line settings from the NotificationSetting 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 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. /// public interface INotificationSettingsService { SmtpOptions GetSmtp(); LineOptions GetLine(); void Reload(); } public sealed class NotificationSettingsService : INotificationSettingsService { private readonly IServiceScopeFactory _scopeFactory; private readonly IOptions _smtpFallback; private readonly IOptions _lineFallback; private readonly object _gate = new(); private SmtpOptions? _smtp; private LineOptions? _line; public NotificationSettingsService( IServiceScopeFactory scopeFactory, IOptions smtpFallback, IOptions 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(); 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, }; } } }