mirror of
https://github.com/btcpayserver/btcpayserver.git
synced 2025-02-22 14:22:40 +01:00
Merge pull request #1666 from NicolasDorier/refactor/notificatoin
Small refactoring of Notifications
This commit is contained in:
commit
677cc3bee9
9 changed files with 126 additions and 62 deletions
|
@ -9,7 +9,9 @@ using BTCPayServer.Filters;
|
|||
using BTCPayServer.HostedServices;
|
||||
using BTCPayServer.Models.NotificationViewModels;
|
||||
using BTCPayServer.Security;
|
||||
using BTCPayServer.Services;
|
||||
using BTCPayServer.Services.Notifications;
|
||||
using BTCPayServer.Services.Notifications.Blobs;
|
||||
using Google;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
|
@ -21,12 +23,14 @@ namespace BTCPayServer.Controllers
|
|||
[Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie)]
|
||||
public class NotificationsController : Controller
|
||||
{
|
||||
private readonly BTCPayServerEnvironment _env;
|
||||
private readonly ApplicationDbContext _db;
|
||||
private readonly NotificationSender _notificationSender;
|
||||
private readonly UserManager<ApplicationUser> _userManager;
|
||||
|
||||
public NotificationsController(ApplicationDbContext db, NotificationSender notificationSender, UserManager<ApplicationUser> userManager)
|
||||
public NotificationsController(BTCPayServerEnvironment env, ApplicationDbContext db, NotificationSender notificationSender, UserManager<ApplicationUser> userManager)
|
||||
{
|
||||
_env = env;
|
||||
_db = db;
|
||||
_notificationSender = notificationSender;
|
||||
_userManager = userManager;
|
||||
|
@ -46,7 +50,7 @@ namespace BTCPayServer.Controllers
|
|||
.OrderByDescending(a => a.Created)
|
||||
.Skip(skip).Take(count)
|
||||
.Where(a => a.ApplicationUserId == userId)
|
||||
.Select(a => a.ViewModel())
|
||||
.Select(a => a.ToViewModel())
|
||||
.ToList(),
|
||||
Total = _db.Notifications.Where(a => a.ApplicationUserId == userId).Count()
|
||||
};
|
||||
|
@ -57,9 +61,9 @@ namespace BTCPayServer.Controllers
|
|||
[HttpGet]
|
||||
public async Task<IActionResult> Generate(string version)
|
||||
{
|
||||
await _notificationSender.NoticeNewVersionAsync(version);
|
||||
// waiting for event handler to catch up
|
||||
await Task.Delay(500);
|
||||
if (_env.NetworkType != NBitcoin.NetworkType.Regtest)
|
||||
return NotFound();
|
||||
await _notificationSender.SendNotification(new AdminScope(), new NewVersionNotification(version));
|
||||
return RedirectToAction(nameof(Index));
|
||||
}
|
||||
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
using BTCPayServer.Services.Notifications.Blobs;
|
||||
|
||||
namespace BTCPayServer.Events
|
||||
{
|
||||
internal class NotificationEvent
|
||||
{
|
||||
internal string[] ApplicationUserIds { get; set; }
|
||||
internal BaseNotification Notification { get; set; }
|
||||
}
|
||||
}
|
|
@ -30,22 +30,26 @@ namespace BTCPayServer.Models.NotificationViewModels
|
|||
|
||||
public static class NotificationViewModelExt
|
||||
{
|
||||
public static NotificationViewModel ViewModel(this NotificationData data)
|
||||
static Dictionary<string, Type> _NotificationTypes;
|
||||
static NotificationViewModelExt()
|
||||
{
|
||||
var baseType = typeof(BaseNotification);
|
||||
_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);
|
||||
}
|
||||
|
||||
var fullTypeName = baseType.FullName.Replace(nameof(BaseNotification), data.NotificationType, StringComparison.OrdinalIgnoreCase);
|
||||
var parsedType = baseType.Assembly.GetType(fullTypeName);
|
||||
|
||||
var casted = (BaseNotification)JsonConvert.DeserializeObject(ZipUtils.Unzip(data.Blob), parsedType);
|
||||
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(ref obj);
|
||||
casted.FillViewModel(obj);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
|
|
@ -8,26 +8,8 @@ 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
|
||||
internal abstract class BaseNotification
|
||||
public abstract class BaseNotification
|
||||
{
|
||||
internal virtual string NotificationType { get { return GetType().Name; } }
|
||||
|
||||
public NotificationData ToData(string applicationUserId)
|
||||
{
|
||||
var obj = JsonConvert.SerializeObject(this);
|
||||
|
||||
var data = new NotificationData
|
||||
{
|
||||
Id = Guid.NewGuid().ToString(),
|
||||
Created = DateTimeOffset.UtcNow,
|
||||
ApplicationUserId = applicationUserId,
|
||||
NotificationType = NotificationType,
|
||||
Blob = ZipUtils.Zip(obj),
|
||||
Seen = false
|
||||
};
|
||||
return data;
|
||||
}
|
||||
|
||||
public abstract void FillViewModel(ref NotificationViewModel data);
|
||||
public abstract void FillViewModel(NotificationViewModel data);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,13 +4,20 @@ using Newtonsoft.Json;
|
|||
|
||||
namespace BTCPayServer.Services.Notifications.Blobs
|
||||
{
|
||||
[Notification("newversion")]
|
||||
internal class NewVersionNotification : BaseNotification
|
||||
{
|
||||
internal override string NotificationType => "NewVersionNotification";
|
||||
public NewVersionNotification()
|
||||
{
|
||||
|
||||
}
|
||||
public NewVersionNotification(string version)
|
||||
{
|
||||
Version = version;
|
||||
}
|
||||
public string Version { get; set; }
|
||||
|
||||
public override void FillViewModel(ref NotificationViewModel vm)
|
||||
public override void FillViewModel(NotificationViewModel vm)
|
||||
{
|
||||
vm.Body = $"New version {Version} released!";
|
||||
vm.ActionLink = $"https://github.com/btcpayserver/btcpayserver/releases/tag/v{Version}";
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
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; }
|
||||
}
|
||||
}
|
|
@ -55,7 +55,7 @@ namespace BTCPayServer.Services.Notifications
|
|||
.Where(a => a.ApplicationUserId == userId && !a.Seen)
|
||||
.OrderByDescending(a => a.Created)
|
||||
.Take(5)
|
||||
.Select(a => a.ViewModel())
|
||||
.Select(a => a.ToViewModel())
|
||||
.ToList();
|
||||
}
|
||||
catch (System.IO.InvalidDataException)
|
||||
|
|
30
BTCPayServer/Services/Notifications/NotificationScopes.cs
Normal file
30
BTCPayServer/Services/Notifications/NotificationScopes.cs
Normal file
|
@ -0,0 +1,30 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace BTCPayServer.Services.Notifications
|
||||
{
|
||||
public class AdminScope : NotificationScope
|
||||
{
|
||||
public AdminScope()
|
||||
{
|
||||
}
|
||||
}
|
||||
public class StoreScope : NotificationScope
|
||||
{
|
||||
public StoreScope(string storeId)
|
||||
{
|
||||
if (storeId == null)
|
||||
throw new ArgumentNullException(nameof(storeId));
|
||||
StoreId = storeId;
|
||||
}
|
||||
public string StoreId { get; }
|
||||
}
|
||||
|
||||
public interface NotificationScope
|
||||
{
|
||||
}
|
||||
}
|
|
@ -1,9 +1,13 @@
|
|||
using System.Linq;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.Events;
|
||||
using BTCPayServer.Services.Notifications.Blobs;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace BTCPayServer.Services.Notifications
|
||||
{
|
||||
|
@ -20,32 +24,58 @@ namespace BTCPayServer.Services.Notifications
|
|||
_eventAggregator = eventAggregator;
|
||||
}
|
||||
|
||||
internal async Task NoticeNewVersionAsync(string version)
|
||||
public async Task SendNotification(NotificationScope scope, BaseNotification notification)
|
||||
{
|
||||
var admins = await _userManager.GetUsersInRoleAsync(Roles.ServerAdmin);
|
||||
var adminUids = admins.Select(a => a.Id).ToArray();
|
||||
|
||||
var notif = new NewVersionNotification
|
||||
{
|
||||
Version = version
|
||||
};
|
||||
if (scope == null)
|
||||
throw new ArgumentNullException(nameof(scope));
|
||||
if (notification == null)
|
||||
throw new ArgumentNullException(nameof(notification));
|
||||
var users = await GetUsers(scope);
|
||||
using (var db = _contextFactory.CreateContext())
|
||||
{
|
||||
foreach (var uid in adminUids)
|
||||
foreach (var uid in users)
|
||||
{
|
||||
var data = notif.ToData(uid);
|
||||
var obj = JsonConvert.SerializeObject(notification);
|
||||
var data = new NotificationData
|
||||
{
|
||||
Id = Guid.NewGuid().ToString(),
|
||||
Created = DateTimeOffset.UtcNow,
|
||||
ApplicationUserId = uid,
|
||||
NotificationType = GetNotificationTypeString(notification.GetType()),
|
||||
Blob = ZipUtils.Zip(obj),
|
||||
Seen = false
|
||||
};
|
||||
db.Notifications.Add(data);
|
||||
}
|
||||
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
|
||||
// propagate notification
|
||||
_eventAggregator.Publish(new NotificationEvent
|
||||
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)
|
||||
{
|
||||
if (scope is AdminScope)
|
||||
{
|
||||
ApplicationUserIds = adminUids,
|
||||
Notification = notif
|
||||
});
|
||||
var admins = await _userManager.GetUsersInRoleAsync(Roles.ServerAdmin);
|
||||
return admins.Select(a => a.Id).ToArray();
|
||||
}
|
||||
if (scope is StoreScope s)
|
||||
{
|
||||
using var ctx = _contextFactory.CreateContext();
|
||||
return ctx.UserStore
|
||||
.Where(u => u.StoreDataId == s.StoreId)
|
||||
.Select(u => u.ApplicationUserId)
|
||||
.ToArray();
|
||||
}
|
||||
throw new NotSupportedException("Notification scope not supported");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue