mirror of
https://github.com/aljazceru/btcpayserver.git
synced 2025-12-18 06:24:24 +01:00
Introduce INotificationHandler
This commit is contained in:
@@ -27,13 +27,19 @@ namespace BTCPayServer.Controllers
|
|||||||
private readonly ApplicationDbContext _db;
|
private readonly ApplicationDbContext _db;
|
||||||
private readonly NotificationSender _notificationSender;
|
private readonly NotificationSender _notificationSender;
|
||||||
private readonly UserManager<ApplicationUser> _userManager;
|
private readonly UserManager<ApplicationUser> _userManager;
|
||||||
|
private readonly NotificationManager _notificationManager;
|
||||||
|
|
||||||
public NotificationsController(BTCPayServerEnvironment env, ApplicationDbContext db, NotificationSender notificationSender, UserManager<ApplicationUser> userManager)
|
public NotificationsController(BTCPayServerEnvironment env,
|
||||||
|
ApplicationDbContext db,
|
||||||
|
NotificationSender notificationSender,
|
||||||
|
UserManager<ApplicationUser> userManager,
|
||||||
|
NotificationManager notificationManager)
|
||||||
{
|
{
|
||||||
_env = env;
|
_env = env;
|
||||||
_db = db;
|
_db = db;
|
||||||
_notificationSender = notificationSender;
|
_notificationSender = notificationSender;
|
||||||
_userManager = userManager;
|
_userManager = userManager;
|
||||||
|
_notificationManager = notificationManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
@@ -50,7 +56,7 @@ namespace BTCPayServer.Controllers
|
|||||||
.OrderByDescending(a => a.Created)
|
.OrderByDescending(a => a.Created)
|
||||||
.Skip(skip).Take(count)
|
.Skip(skip).Take(count)
|
||||||
.Where(a => a.ApplicationUserId == userId)
|
.Where(a => a.ApplicationUserId == userId)
|
||||||
.Select(a => a.ToViewModel())
|
.Select(a => _notificationManager.ToViewModel(a))
|
||||||
.ToList(),
|
.ToList(),
|
||||||
Total = _db.Notifications.Where(a => a.ApplicationUserId == userId).Count()
|
Total = _db.Notifications.Where(a => a.ApplicationUserId == userId).Count()
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ using Serilog;
|
|||||||
using BTCPayServer.Security.GreenField;
|
using BTCPayServer.Security.GreenField;
|
||||||
using BTCPayServer.Services.Labels;
|
using BTCPayServer.Services.Labels;
|
||||||
using BTCPayServer.Services.Notifications;
|
using BTCPayServer.Services.Notifications;
|
||||||
|
using BTCPayServer.Services.Notifications.Blobs;
|
||||||
|
|
||||||
namespace BTCPayServer.Hosting
|
namespace BTCPayServer.Hosting
|
||||||
{
|
{
|
||||||
@@ -220,6 +221,7 @@ namespace BTCPayServer.Hosting
|
|||||||
services.AddSingleton<IBackgroundJobClient, BackgroundJobClient>();
|
services.AddSingleton<IBackgroundJobClient, BackgroundJobClient>();
|
||||||
services.AddScoped<IAuthorizationHandler, CookieAuthorizationHandler>();
|
services.AddScoped<IAuthorizationHandler, CookieAuthorizationHandler>();
|
||||||
services.AddScoped<IAuthorizationHandler, BitpayAuthorizationHandler>();
|
services.AddScoped<IAuthorizationHandler, BitpayAuthorizationHandler>();
|
||||||
|
services.AddSingleton<INotificationHandler, NewVersionNotification.Handler>();
|
||||||
|
|
||||||
services.TryAddSingleton<ExplorerClientProvider>();
|
services.TryAddSingleton<ExplorerClientProvider>();
|
||||||
services.TryAddSingleton<Bitpay>(o =>
|
services.TryAddSingleton<Bitpay>(o =>
|
||||||
|
|||||||
@@ -27,31 +27,4 @@ namespace BTCPayServer.Models.NotificationViewModels
|
|||||||
public string ActionLink { get; set; }
|
public string ActionLink { get; set; }
|
||||||
public bool Seen { get; set; }
|
public bool Seen { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class NotificationViewModelExt
|
|
||||||
{
|
|
||||||
static Dictionary<string, Type> _NotificationTypes;
|
|
||||||
static NotificationViewModelExt()
|
|
||||||
{
|
|
||||||
_NotificationTypes = Assembly.GetExecutingAssembly()
|
|
||||||
.GetTypes()
|
|
||||||
.Select(t => (t, NotificationType: t.GetCustomAttribute<NotificationAttribute>()?.NotificationType))
|
|
||||||
.Where(t => t.NotificationType is string)
|
|
||||||
.ToDictionary(o => o.NotificationType, o => o.t);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static NotificationViewModel ToViewModel(this NotificationData data)
|
|
||||||
{
|
|
||||||
var casted = (BaseNotification)JsonConvert.DeserializeObject(ZipUtils.Unzip(data.Blob), _NotificationTypes[data.NotificationType]);
|
|
||||||
var obj = new NotificationViewModel
|
|
||||||
{
|
|
||||||
Id = data.Id,
|
|
||||||
Created = data.Created,
|
|
||||||
Seen = data.Seen
|
|
||||||
};
|
|
||||||
casted.FillViewModel(obj);
|
|
||||||
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
using System;
|
|
||||||
using BTCPayServer.Data;
|
|
||||||
using BTCPayServer.Models.NotificationViewModels;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
|
|
||||||
namespace BTCPayServer.Services.Notifications.Blobs
|
|
||||||
{
|
|
||||||
// Make sure to keep all Blob Notification classes in same namespace
|
|
||||||
// because of dependent initialization and parsing to view models logic
|
|
||||||
// IndexViewModel.cs#32
|
|
||||||
public abstract class BaseNotification
|
|
||||||
{
|
|
||||||
public abstract void FillViewModel(NotificationViewModel data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -4,9 +4,17 @@ using Newtonsoft.Json;
|
|||||||
|
|
||||||
namespace BTCPayServer.Services.Notifications.Blobs
|
namespace BTCPayServer.Services.Notifications.Blobs
|
||||||
{
|
{
|
||||||
[Notification("newversion")]
|
internal class NewVersionNotification
|
||||||
internal class NewVersionNotification : BaseNotification
|
|
||||||
{
|
{
|
||||||
|
internal class Handler : NotificationHandler<NewVersionNotification>
|
||||||
|
{
|
||||||
|
public override string NotificationType => "newversion";
|
||||||
|
protected override void FillViewModel(NewVersionNotification notification, NotificationViewModel vm)
|
||||||
|
{
|
||||||
|
vm.Body = $"New version {notification.Version} released!";
|
||||||
|
vm.ActionLink = $"https://github.com/btcpayserver/btcpayserver/releases/tag/v{notification.Version}";
|
||||||
|
}
|
||||||
|
}
|
||||||
public NewVersionNotification()
|
public NewVersionNotification()
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -16,11 +24,5 @@ namespace BTCPayServer.Services.Notifications.Blobs
|
|||||||
Version = version;
|
Version = version;
|
||||||
}
|
}
|
||||||
public string Version { get; set; }
|
public string Version { get; set; }
|
||||||
|
|
||||||
public override void FillViewModel(NotificationViewModel vm)
|
|
||||||
{
|
|
||||||
vm.Body = $"New version {Version} released!";
|
|
||||||
vm.ActionLink = $"https://github.com/btcpayserver/btcpayserver/releases/tag/v{Version}";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace BTCPayServer.Services.Notifications.Blobs
|
|
||||||
{
|
|
||||||
[AttributeUsage(AttributeTargets.Class)]
|
|
||||||
public class NotificationAttribute : Attribute
|
|
||||||
{
|
|
||||||
public NotificationAttribute(string notificationType)
|
|
||||||
{
|
|
||||||
NotificationType = notificationType;
|
|
||||||
}
|
|
||||||
public string NotificationType { get; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
25
BTCPayServer/Services/Notifications/INotificationHandler.cs
Normal file
25
BTCPayServer/Services/Notifications/INotificationHandler.cs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using BTCPayServer.Models.NotificationViewModels;
|
||||||
|
|
||||||
|
namespace BTCPayServer.Services.Notifications
|
||||||
|
{
|
||||||
|
public interface INotificationHandler
|
||||||
|
{
|
||||||
|
string NotificationType { get; }
|
||||||
|
Type NotificationBlobType { get; }
|
||||||
|
void FillViewModel(object notification, NotificationViewModel vm);
|
||||||
|
}
|
||||||
|
public abstract class NotificationHandler<TNotification> : INotificationHandler
|
||||||
|
{
|
||||||
|
public abstract string NotificationType { get; }
|
||||||
|
Type INotificationHandler.NotificationBlobType => typeof(TNotification);
|
||||||
|
void INotificationHandler.FillViewModel(object notification, NotificationViewModel vm)
|
||||||
|
{
|
||||||
|
FillViewModel((TNotification)notification, vm);
|
||||||
|
}
|
||||||
|
protected abstract void FillViewModel(TNotification notification, NotificationViewModel vm);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,8 +6,10 @@ using System.Text;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using BTCPayServer.Data;
|
using BTCPayServer.Data;
|
||||||
using BTCPayServer.Models.NotificationViewModels;
|
using BTCPayServer.Models.NotificationViewModels;
|
||||||
|
using Google.Apis.Storage.v1.Data;
|
||||||
using Microsoft.AspNetCore.Identity;
|
using Microsoft.AspNetCore.Identity;
|
||||||
using Microsoft.Extensions.Caching.Memory;
|
using Microsoft.Extensions.Caching.Memory;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
namespace BTCPayServer.Services.Notifications
|
namespace BTCPayServer.Services.Notifications
|
||||||
{
|
{
|
||||||
@@ -16,12 +18,16 @@ namespace BTCPayServer.Services.Notifications
|
|||||||
private readonly ApplicationDbContextFactory _factory;
|
private readonly ApplicationDbContextFactory _factory;
|
||||||
private readonly UserManager<ApplicationUser> _userManager;
|
private readonly UserManager<ApplicationUser> _userManager;
|
||||||
private readonly IMemoryCache _memoryCache;
|
private readonly IMemoryCache _memoryCache;
|
||||||
|
private readonly Dictionary<string, INotificationHandler> _handlersByNotificationType;
|
||||||
|
private readonly Dictionary<Type, INotificationHandler> _handlersByBlobType;
|
||||||
|
|
||||||
public NotificationManager(ApplicationDbContextFactory factory, UserManager<ApplicationUser> userManager, IMemoryCache memoryCache)
|
public NotificationManager(ApplicationDbContextFactory factory, UserManager<ApplicationUser> userManager, IMemoryCache memoryCache, IEnumerable<INotificationHandler> handlers)
|
||||||
{
|
{
|
||||||
_factory = factory;
|
_factory = factory;
|
||||||
_userManager = userManager;
|
_userManager = userManager;
|
||||||
_memoryCache = memoryCache;
|
_memoryCache = memoryCache;
|
||||||
|
_handlersByNotificationType = handlers.ToDictionary(h => h.NotificationType);
|
||||||
|
_handlersByBlobType = handlers.ToDictionary(h => h.NotificationBlobType);
|
||||||
}
|
}
|
||||||
|
|
||||||
private const int _cacheExpiryMs = 5000;
|
private const int _cacheExpiryMs = 5000;
|
||||||
@@ -55,7 +61,7 @@ namespace BTCPayServer.Services.Notifications
|
|||||||
.Where(a => a.ApplicationUserId == userId && !a.Seen)
|
.Where(a => a.ApplicationUserId == userId && !a.Seen)
|
||||||
.OrderByDescending(a => a.Created)
|
.OrderByDescending(a => a.Created)
|
||||||
.Take(5)
|
.Take(5)
|
||||||
.Select(a => a.ToViewModel())
|
.Select(a => ToViewModel(a))
|
||||||
.ToList();
|
.ToList();
|
||||||
}
|
}
|
||||||
catch (System.IO.InvalidDataException)
|
catch (System.IO.InvalidDataException)
|
||||||
@@ -77,6 +83,33 @@ namespace BTCPayServer.Services.Notifications
|
|||||||
|
|
||||||
return resp;
|
return resp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public NotificationViewModel ToViewModel(NotificationData data)
|
||||||
|
{
|
||||||
|
var handler = GetHandler(data.NotificationType);
|
||||||
|
var notification = JsonConvert.DeserializeObject(ZipUtils.Unzip(data.Blob), handler.NotificationBlobType);
|
||||||
|
var obj = new NotificationViewModel
|
||||||
|
{
|
||||||
|
Id = data.Id,
|
||||||
|
Created = data.Created,
|
||||||
|
Seen = data.Seen
|
||||||
|
};
|
||||||
|
handler.FillViewModel(notification, obj);
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
public INotificationHandler GetHandler(string notificationId)
|
||||||
|
{
|
||||||
|
if (_handlersByNotificationType.TryGetValue(notificationId, out var h))
|
||||||
|
return h;
|
||||||
|
throw new InvalidOperationException($"No INotificationHandler found for {notificationId}");
|
||||||
|
}
|
||||||
|
public INotificationHandler GetHandler(Type blobType)
|
||||||
|
{
|
||||||
|
if (_handlersByBlobType.TryGetValue(blobType, out var h))
|
||||||
|
return h;
|
||||||
|
throw new InvalidOperationException($"No INotificationHandler found for {blobType.Name}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class NotificationSummaryViewModel
|
public class NotificationSummaryViewModel
|
||||||
|
|||||||
@@ -16,15 +16,17 @@ namespace BTCPayServer.Services.Notifications
|
|||||||
private readonly ApplicationDbContextFactory _contextFactory;
|
private readonly ApplicationDbContextFactory _contextFactory;
|
||||||
private readonly UserManager<ApplicationUser> _userManager;
|
private readonly UserManager<ApplicationUser> _userManager;
|
||||||
private readonly EventAggregator _eventAggregator;
|
private readonly EventAggregator _eventAggregator;
|
||||||
|
private readonly NotificationManager _notificationManager;
|
||||||
|
|
||||||
public NotificationSender(ApplicationDbContextFactory contextFactory, UserManager<ApplicationUser> userManager, EventAggregator eventAggregator)
|
public NotificationSender(ApplicationDbContextFactory contextFactory, UserManager<ApplicationUser> userManager, EventAggregator eventAggregator, NotificationManager notificationManager)
|
||||||
{
|
{
|
||||||
_contextFactory = contextFactory;
|
_contextFactory = contextFactory;
|
||||||
_userManager = userManager;
|
_userManager = userManager;
|
||||||
_eventAggregator = eventAggregator;
|
_eventAggregator = eventAggregator;
|
||||||
|
_notificationManager = notificationManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task SendNotification(NotificationScope scope, BaseNotification notification)
|
public async Task SendNotification(NotificationScope scope, object notification)
|
||||||
{
|
{
|
||||||
if (scope == null)
|
if (scope == null)
|
||||||
throw new ArgumentNullException(nameof(scope));
|
throw new ArgumentNullException(nameof(scope));
|
||||||
@@ -41,25 +43,16 @@ namespace BTCPayServer.Services.Notifications
|
|||||||
Id = Guid.NewGuid().ToString(),
|
Id = Guid.NewGuid().ToString(),
|
||||||
Created = DateTimeOffset.UtcNow,
|
Created = DateTimeOffset.UtcNow,
|
||||||
ApplicationUserId = uid,
|
ApplicationUserId = uid,
|
||||||
NotificationType = GetNotificationTypeString(notification.GetType()),
|
NotificationType = _notificationManager.GetHandler(notification.GetType()).NotificationType,
|
||||||
Blob = ZipUtils.Zip(obj),
|
Blob = ZipUtils.Zip(obj),
|
||||||
Seen = false
|
Seen = false
|
||||||
};
|
};
|
||||||
db.Notifications.Add(data);
|
db.Notifications.Add(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
await db.SaveChangesAsync();
|
await db.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private string GetNotificationTypeString(Type type)
|
|
||||||
{
|
|
||||||
var str = type.GetCustomAttribute<NotificationAttribute>()?.NotificationType;
|
|
||||||
if (str is null)
|
|
||||||
throw new NotSupportedException($"{type} is not a notification");
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<string[]> GetUsers(NotificationScope scope)
|
private async Task<string[]> GetUsers(NotificationScope scope)
|
||||||
{
|
{
|
||||||
if (scope is AdminScope)
|
if (scope is AdminScope)
|
||||||
|
|||||||
Reference in New Issue
Block a user