btcpayserver/BTCPayServer/Services/SettingsRepository.cs
rockstardev a7e3cbb105
Adding endpoint to set server email settings (#6601)
* Adding endpoint in Greenfield to allow server email settings

* Adding related swagger file

* Refactoring EmailSettingsData to be more readable

* Adding server email masking

* Adding tests

* Update BTCPayServer/wwwroot/swagger/v1/swagger.template.serveremail.json

Co-authored-by: d11n <mail@dennisreimann.de>

* Masking smtp server email returned over greenfield api and test

* Retaining password if password mask is used

* Remove magic string *****

* Flatten request for server's settings. Fix bug on shared setting instances

* Remove useless doc

* Simplify code

* Fix Store Email settings page

---------

Co-authored-by: d11n <mail@dennisreimann.de>
Co-authored-by: nicolas.dorier <nicolas.dorier@gmail.com>
2025-02-27 15:59:17 +09:00

91 lines
3.3 KiB
C#

#nullable enable
using System.Threading;
using System.Threading.Tasks;
using BTCPayServer.Abstractions.Contracts;
using BTCPayServer.Data;
using BTCPayServer.Events;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Caching.Memory;
using Newtonsoft.Json;
namespace BTCPayServer.Services
{
public class SettingsRepository : ISettingsRepository
{
private readonly ApplicationDbContextFactory _ContextFactory;
private readonly EventAggregator _EventAggregator;
private readonly IMemoryCache _memoryCache;
public SettingsRepository(ApplicationDbContextFactory contextFactory, EventAggregator eventAggregator, IMemoryCache memoryCache)
{
_ContextFactory = contextFactory;
_EventAggregator = eventAggregator;
_memoryCache = memoryCache;
}
public async Task<T?> GetSettingAsync<T>(string? name = null) where T : class
{
name ??= typeof(T).FullName ?? string.Empty;
var data = await _memoryCache.GetOrCreateAsync(GetCacheKey(name), async entry =>
{
await using var ctx = _ContextFactory.CreateContext();
var data = await ctx.Settings.Where(s => s.Id == name).FirstOrDefaultAsync();
return data?.Value;
});
return data is string ? Deserialize<T>(data) : null;
}
public async Task UpdateSetting<T>(T obj, string? name = null) where T : class
{
name ??= typeof(T).FullName ?? string.Empty;
await using (var ctx = _ContextFactory.CreateContext())
{
var settings = UpdateSettingInContext<T>(ctx, obj, name);
try
{
await ctx.SaveChangesAsync();
}
catch (DbUpdateException)
{
ctx.Entry(settings).State = EntityState.Added;
await ctx.SaveChangesAsync();
}
}
_memoryCache.Remove(GetCacheKey(name));
_EventAggregator.Publish(new SettingsChanged<T>()
{
Settings = obj,
SettingsName = name
});
}
public SettingData UpdateSettingInContext<T>(ApplicationDbContext ctx, T obj, string? name = null) where T : class
{
name ??= obj.GetType().FullName ?? string.Empty;
_memoryCache.Remove(GetCacheKey(name));
var settings = new SettingData { Id = name, Value = Serialize(obj) };
ctx.Attach(settings);
ctx.Entry(settings).State = EntityState.Modified;
return settings;
}
private T? Deserialize<T>(string value) where T : class
{
return JsonConvert.DeserializeObject<T>(value);
}
private string Serialize<T>(T obj)
{
return JsonConvert.SerializeObject(obj);
}
private string GetCacheKey(string name)
{
return $"{nameof(SettingsRepository)}_{name}";
}
public async Task<T> WaitSettingsChanged<T>(CancellationToken cancellationToken = default) where T : class
{
return (await _EventAggregator.WaitNext<SettingsChanged<T>>(cancellationToken)).Settings;
}
}
}