2020-05-27 18:41:00 -05:00
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
2020-06-23 03:06:02 +02:00
|
|
|
|
using System.Globalization;
|
2020-05-27 18:41:00 -05:00
|
|
|
|
using System.Linq;
|
2020-05-28 22:48:09 -05:00
|
|
|
|
using System.Security.Claims;
|
2020-06-15 01:22:09 -05:00
|
|
|
|
using System.Text;
|
2020-05-27 18:41:00 -05:00
|
|
|
|
using System.Threading.Tasks;
|
2020-05-28 15:15:24 -05:00
|
|
|
|
using BTCPayServer.Data;
|
2020-06-10 18:55:31 -05:00
|
|
|
|
using BTCPayServer.Models.NotificationViewModels;
|
2020-06-16 23:29:25 +09:00
|
|
|
|
using Google.Apis.Storage.v1.Data;
|
2020-05-28 16:19:02 -05:00
|
|
|
|
using Microsoft.AspNetCore.Identity;
|
2020-06-17 11:26:21 +09:00
|
|
|
|
using Microsoft.EntityFrameworkCore;
|
2020-06-12 00:01:45 -05:00
|
|
|
|
using Microsoft.Extensions.Caching.Memory;
|
2020-06-16 23:29:25 +09:00
|
|
|
|
using Newtonsoft.Json;
|
2020-05-27 18:41:00 -05:00
|
|
|
|
|
2020-06-15 01:22:09 -05:00
|
|
|
|
namespace BTCPayServer.Services.Notifications
|
2020-05-27 18:41:00 -05:00
|
|
|
|
{
|
2020-05-28 22:48:09 -05:00
|
|
|
|
public class NotificationManager
|
|
|
|
|
{
|
2020-06-12 00:01:45 -05:00
|
|
|
|
private readonly ApplicationDbContextFactory _factory;
|
2020-06-11 17:58:50 -05:00
|
|
|
|
private readonly UserManager<ApplicationUser> _userManager;
|
2020-06-14 23:26:04 -05:00
|
|
|
|
private readonly IMemoryCache _memoryCache;
|
2020-06-16 23:29:25 +09:00
|
|
|
|
private readonly Dictionary<string, INotificationHandler> _handlersByNotificationType;
|
|
|
|
|
private readonly Dictionary<Type, INotificationHandler> _handlersByBlobType;
|
2020-05-28 22:48:09 -05:00
|
|
|
|
|
2020-06-23 03:06:02 +02:00
|
|
|
|
public NotificationManager(ApplicationDbContextFactory factory, UserManager<ApplicationUser> userManager,
|
|
|
|
|
IMemoryCache memoryCache, IEnumerable<INotificationHandler> handlers)
|
2020-05-28 22:48:09 -05:00
|
|
|
|
{
|
2020-06-12 00:01:45 -05:00
|
|
|
|
_factory = factory;
|
2020-06-11 17:58:50 -05:00
|
|
|
|
_userManager = userManager;
|
2020-06-14 23:26:04 -05:00
|
|
|
|
_memoryCache = memoryCache;
|
2020-06-16 23:29:25 +09:00
|
|
|
|
_handlersByNotificationType = handlers.ToDictionary(h => h.NotificationType);
|
|
|
|
|
_handlersByBlobType = handlers.ToDictionary(h => h.NotificationBlobType);
|
2020-05-28 22:48:09 -05:00
|
|
|
|
}
|
|
|
|
|
|
2020-06-12 00:01:45 -05:00
|
|
|
|
private const int _cacheExpiryMs = 5000;
|
2020-06-23 03:06:02 +02:00
|
|
|
|
|
2020-06-17 11:26:21 +09:00
|
|
|
|
public async Task<NotificationSummaryViewModel> GetSummaryNotifications(ClaimsPrincipal user)
|
2020-05-28 22:48:09 -05:00
|
|
|
|
{
|
2020-06-11 17:58:50 -05:00
|
|
|
|
var userId = _userManager.GetUserId(user);
|
2020-06-23 03:06:02 +02:00
|
|
|
|
var cacheKey = GetNotificationsCacheId(userId);
|
|
|
|
|
if (_memoryCache.TryGetValue<NotificationSummaryViewModel>(cacheKey, out var obj))
|
2020-06-12 00:01:45 -05:00
|
|
|
|
return obj;
|
2020-06-15 01:22:09 -05:00
|
|
|
|
|
2020-06-17 11:26:21 +09:00
|
|
|
|
var resp = await FetchNotificationsFromDb(userId);
|
2020-06-23 03:06:02 +02:00
|
|
|
|
_memoryCache.Set(cacheKey, resp,
|
|
|
|
|
new MemoryCacheEntryOptions().SetAbsoluteExpiration(TimeSpan.FromMilliseconds(_cacheExpiryMs)));
|
2020-06-12 00:01:45 -05:00
|
|
|
|
|
|
|
|
|
return resp;
|
|
|
|
|
}
|
2020-06-10 18:55:31 -05:00
|
|
|
|
|
2020-06-23 03:06:02 +02:00
|
|
|
|
public void InvalidateNotificationCache(string userId)
|
|
|
|
|
{
|
|
|
|
|
_memoryCache.Remove(GetNotificationsCacheId(userId));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static string GetNotificationsCacheId(string userId)
|
|
|
|
|
{
|
|
|
|
|
return $"notifications-{userId}";
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-17 11:26:21 +09:00
|
|
|
|
private async Task<NotificationSummaryViewModel> FetchNotificationsFromDb(string userId)
|
2020-06-12 00:01:45 -05:00
|
|
|
|
{
|
|
|
|
|
var resp = new NotificationSummaryViewModel();
|
|
|
|
|
using (var _db = _factory.CreateContext())
|
2020-06-10 18:55:31 -05:00
|
|
|
|
{
|
2020-06-12 00:01:45 -05:00
|
|
|
|
resp.UnseenCount = _db.Notifications
|
|
|
|
|
.Where(a => a.ApplicationUserId == userId && !a.Seen)
|
|
|
|
|
.Count();
|
|
|
|
|
|
|
|
|
|
if (resp.UnseenCount > 0)
|
2020-06-11 18:14:23 -05:00
|
|
|
|
{
|
2020-06-12 00:01:45 -05:00
|
|
|
|
try
|
|
|
|
|
{
|
2020-06-17 11:26:21 +09:00
|
|
|
|
resp.Last5 = (await _db.Notifications
|
2020-06-23 03:06:02 +02:00
|
|
|
|
.Where(a => a.ApplicationUserId == userId && !a.Seen)
|
|
|
|
|
.OrderByDescending(a => a.Created)
|
|
|
|
|
.Take(5)
|
|
|
|
|
.ToListAsync())
|
2020-06-16 23:29:25 +09:00
|
|
|
|
.Select(a => ToViewModel(a))
|
2020-06-12 00:01:45 -05:00
|
|
|
|
.ToList();
|
|
|
|
|
}
|
2020-06-14 23:04:36 -05:00
|
|
|
|
catch (System.IO.InvalidDataException)
|
2020-06-12 00:01:45 -05:00
|
|
|
|
{
|
|
|
|
|
// invalid notifications that are not pkuzipable, burn them all
|
|
|
|
|
var notif = _db.Notifications.Where(a => a.ApplicationUserId == userId);
|
|
|
|
|
_db.Notifications.RemoveRange(notif);
|
|
|
|
|
_db.SaveChanges();
|
|
|
|
|
|
|
|
|
|
resp.UnseenCount = 0;
|
|
|
|
|
resp.Last5 = new List<NotificationViewModel>();
|
|
|
|
|
}
|
2020-06-11 18:14:23 -05:00
|
|
|
|
}
|
2020-06-12 00:01:45 -05:00
|
|
|
|
else
|
2020-06-11 18:14:23 -05:00
|
|
|
|
{
|
|
|
|
|
resp.Last5 = new List<NotificationViewModel>();
|
|
|
|
|
}
|
2020-06-10 18:55:31 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return resp;
|
2020-05-28 22:48:09 -05:00
|
|
|
|
}
|
2020-06-16 23:29:25 +09:00
|
|
|
|
|
|
|
|
|
public NotificationViewModel ToViewModel(NotificationData data)
|
|
|
|
|
{
|
|
|
|
|
var handler = GetHandler(data.NotificationType);
|
|
|
|
|
var notification = JsonConvert.DeserializeObject(ZipUtils.Unzip(data.Blob), handler.NotificationBlobType);
|
2020-06-23 03:06:02 +02:00
|
|
|
|
var obj = new NotificationViewModel {Id = data.Id, Created = data.Created, Seen = data.Seen};
|
2020-06-16 23:29:25 +09:00
|
|
|
|
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}");
|
|
|
|
|
}
|
2020-06-23 03:06:02 +02:00
|
|
|
|
|
2020-06-16 23:29:25 +09:00
|
|
|
|
public INotificationHandler GetHandler(Type blobType)
|
|
|
|
|
{
|
|
|
|
|
if (_handlersByBlobType.TryGetValue(blobType, out var h))
|
|
|
|
|
return h;
|
|
|
|
|
throw new InvalidOperationException($"No INotificationHandler found for {blobType.Name}");
|
|
|
|
|
}
|
2020-05-28 22:48:09 -05:00
|
|
|
|
}
|
2020-06-10 18:55:31 -05:00
|
|
|
|
|
|
|
|
|
public class NotificationSummaryViewModel
|
|
|
|
|
{
|
|
|
|
|
public int UnseenCount { get; set; }
|
|
|
|
|
public List<NotificationViewModel> Last5 { get; set; }
|
|
|
|
|
}
|
2020-05-27 18:41:00 -05:00
|
|
|
|
}
|