Files
btcpayserver/BTCPayServer/Controllers/NotificationsController.cs
d11n e2d0b7c5f7 Store centric UI: Part 3 (#3224)
* Set store context in cookie

* Fix page id usages in view

* Move Pay Button to nav

* Move integrations to plugins nav

* Store switch links to wallet if present

* Test fixes

* Nav fixes

* Fix altcoin view

* Main nav updates

* Wallet setttings nav update

* Move storeId cookie fallback to cookie auth handler

* View fixes

* Test fixes

* Fix profile check

* Rename integrations nav extension point to store-integrations-nav-list

* Allow strings for Active page/category for plugins

* Make invoice list filter based on store context

* Do not set context if we are running authorizer through tag helper

* Fix test and unfiltered invoices

* Add permission helper for wallet links

* Add sanity checks for payment requests and invoices

* Store context in home controller

* Fix PayjoinViaUI test

* Store context for notifications

* Minor UI improvements

* Store context for userstores and vault controller

* Bring back integrations page

* Rename notifications nav pages file

* Fix user stores controller policies

* Controller policy fixes from code review

* CookieAuthHandler: Simplify CanViewInvoices case

* Revert "Controller policy fixes from code review"

This reverts commit 97e8b8379c2f2f373bac15a96632d2c8913ef4bd.

* Simplify LayoutSimple

* Fix CanViewInvoices condition

Co-authored-by: Kukks <evilkukka@gmail.com>
2021-12-31 16:36:38 +09:00

231 lines
8.0 KiB
C#

using System;
using System.Linq;
using System.Net.WebSockets;
using System.Threading;
using System.Threading.Tasks;
using BTCPayServer.Abstractions.Constants;
using BTCPayServer.Client;
using BTCPayServer.Data;
using BTCPayServer.Filters;
using BTCPayServer.Models.NotificationViewModels;
using BTCPayServer.Security;
using BTCPayServer.Services;
using BTCPayServer.Services.Notifications;
using BTCPayServer.Services.Notifications.Blobs;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
namespace BTCPayServer.Controllers
{
[BitpayAPIConstraint(false)]
[Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie, Policy = Policies.CanViewNotificationsForUser)]
[Route("[controller]/[action]")]
public class NotificationsController : Controller
{
private readonly BTCPayServerEnvironment _env;
private readonly NotificationSender _notificationSender;
private readonly UserManager<ApplicationUser> _userManager;
private readonly NotificationManager _notificationManager;
private readonly EventAggregator _eventAggregator;
public NotificationsController(BTCPayServerEnvironment env,
NotificationSender notificationSender,
UserManager<ApplicationUser> userManager,
NotificationManager notificationManager,
EventAggregator eventAggregator)
{
_env = env;
_notificationSender = notificationSender;
_userManager = userManager;
_notificationManager = notificationManager;
_eventAggregator = eventAggregator;
}
[HttpGet]
public IActionResult GetNotificationDropdownUI()
{
return ViewComponent("NotificationsDropdown");
}
[HttpGet]
public async Task<IActionResult> SubscribeUpdates(CancellationToken cancellationToken)
{
if (!HttpContext.WebSockets.IsWebSocketRequest)
{
return BadRequest();
}
var websocket = await HttpContext.WebSockets.AcceptWebSocketAsync();
var userId = _userManager.GetUserId(User);
var websocketHelper = new WebSocketHelper(websocket);
IEventAggregatorSubscription subscription = null;
try
{
subscription = _eventAggregator.SubscribeAsync<UserNotificationsUpdatedEvent>(async evt =>
{
if (evt.UserId == userId)
{
await websocketHelper.Send("update");
}
});
await websocketHelper.NextMessageAsync(cancellationToken);
}
catch (OperationCanceledException)
{
// ignored
}
catch (WebSocketException)
{
}
finally
{
subscription?.Dispose();
await websocketHelper.DisposeAsync(CancellationToken.None);
}
return new EmptyResult();
}
#if DEBUG
[HttpGet]
public async Task<IActionResult> GenerateJunk(int x = 100, bool admin = true)
{
for (int i = 0; i < x; i++)
{
await _notificationSender.SendNotification(
admin ? (NotificationScope)new AdminScope() : new UserScope(_userManager.GetUserId(User)),
new JunkNotification());
}
return RedirectToAction("Index");
}
#endif
[HttpGet]
public async Task<IActionResult> Index(int skip = 0, int count = 50, int timezoneOffset = 0)
{
if (!ValidUserClaim(out var userId))
return RedirectToAction("Index", "Home");
var res = await _notificationManager.GetNotifications(new NotificationsQuery()
{
Skip = skip, Take = count, UserId = userId
});
var model = new IndexViewModel() {Skip = skip, Count = count, Items = res.Items, Total = res.Count};
return View(model);
}
[HttpGet]
public async Task<IActionResult> Generate(string version)
{
if (_env.NetworkType != NBitcoin.ChainName.Regtest)
return NotFound();
await _notificationSender.SendNotification(new AdminScope(), new NewVersionNotification(version));
return RedirectToAction(nameof(Index));
}
[HttpPost]
[Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie, Policy = Policies.CanManageNotificationsForUser)]
public async Task<IActionResult> FlipRead(string id)
{
if (ValidUserClaim(out var userId))
{
await _notificationManager.ToggleSeen(new NotificationsQuery() {Ids = new[] {id}, UserId = userId}, null);
return RedirectToAction(nameof(Index));
}
return BadRequest();
}
[HttpGet]
public async Task<IActionResult> NotificationPassThrough(string id)
{
if (ValidUserClaim(out var userId))
{
var items = await
_notificationManager.ToggleSeen(new NotificationsQuery()
{
Ids = new[] {id}, UserId = userId
}, true);
var link = items.FirstOrDefault()?.ActionLink ?? "";
if (string.IsNullOrEmpty(link))
{
return RedirectToAction(nameof(Index));
}
return Redirect(link);
}
return NotFound();
}
[HttpPost]
[Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie, Policy = Policies.CanManageNotificationsForUser)]
public async Task<IActionResult> MassAction(string command, string[] selectedItems)
{
if (!ValidUserClaim(out var userId))
{
return NotFound();
}
if (command.StartsWith("flip-individual", StringComparison.InvariantCulture))
{
var id = command.Split(":")[1];
return await FlipRead(id);
}
if (selectedItems != null)
{
switch (command)
{
case "delete":
await _notificationManager.Remove(new NotificationsQuery()
{
UserId = userId, Ids = selectedItems
});
break;
case "mark-seen":
await _notificationManager.ToggleSeen(new NotificationsQuery()
{
UserId = userId, Ids = selectedItems, Seen = false
}, true);
break;
case "mark-unseen":
await _notificationManager.ToggleSeen(new NotificationsQuery()
{
UserId = userId, Ids = selectedItems, Seen = true
}, false);
break;
}
return RedirectToAction(nameof(Index));
}
return RedirectToAction(nameof(Index));
}
[HttpPost]
[Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie, Policy = Policies.CanManageNotificationsForUser)]
public async Task<IActionResult> MarkAllAsSeen(string returnUrl)
{
if (!ValidUserClaim(out var userId))
{
return NotFound();
}
await _notificationManager.ToggleSeen(new NotificationsQuery() {Seen = false, UserId = userId}, true);
return Redirect(returnUrl);
}
private bool ValidUserClaim(out string userId)
{
userId = _userManager.GetUserId(User);
return userId != null;
}
}
}