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(); WebPushOptions GetWebPush(); 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; private WebPushOptions? _webPush; 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 WebPushOptions GetWebPush() { EnsureLoaded(); return _webPush!; } public void Reload() { lock (_gate) { _smtp = null; _line = null; _webPush = null; } } private void EnsureLoaded() { lock (_gate) { if (_smtp is not null && _line is not null && _webPush 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. Web push has no // appsettings fallback; it stays disabled until the row exists with VAPID keys. _smtp = _smtpFallback.Value; _line = _lineFallback.Value; _webPush = new WebPushOptions(); 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, }; _webPush = new WebPushOptions { PublicKey = row.VapidPublicKey, PrivateKey = row.VapidPrivateKey, Subject = row.VapidSubject, }; } } }