mirror of
https://github.com/aljazceru/btcpayserver.git
synced 2025-12-17 22:14:26 +01:00
Greenfield: Manage notifications (#6058)
Prerequisite for btcpayserver/app#12.
This commit is contained in:
@@ -33,6 +33,16 @@ public partial class BTCPayServerClient
|
|||||||
return await SendHttpRequest<NotificationData>($"api/v1/users/me/notifications/{notificationId}", new UpdateNotification { Seen = seen }, HttpMethod.Put, token);
|
return await SendHttpRequest<NotificationData>($"api/v1/users/me/notifications/{notificationId}", new UpdateNotification { Seen = seen }, HttpMethod.Put, token);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public virtual async Task<NotificationSettingsData> GetNotificationSettings(CancellationToken token = default)
|
||||||
|
{
|
||||||
|
return await SendHttpRequest<NotificationSettingsData>("api/v1/users/me/notification-settings", null, HttpMethod.Get, token);
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual async Task<NotificationSettingsData> UpdateNotificationSettings(UpdateNotificationSettingsRequest request, CancellationToken token = default)
|
||||||
|
{
|
||||||
|
return await SendHttpRequest<NotificationSettingsData>("api/v1/users/me/notification-settings", request, HttpMethod.Put, token);
|
||||||
|
}
|
||||||
|
|
||||||
public virtual async Task RemoveNotification(string notificationId, CancellationToken token = default)
|
public virtual async Task RemoveNotification(string notificationId, CancellationToken token = default)
|
||||||
{
|
{
|
||||||
await SendHttpRequest($"api/v1/users/me/notifications/{notificationId}", null, HttpMethod.Delete, token);
|
await SendHttpRequest($"api/v1/users/me/notifications/{notificationId}", null, HttpMethod.Delete, token);
|
||||||
|
|||||||
15
BTCPayServer.Client/Models/NotificationSettingsData.cs
Normal file
15
BTCPayServer.Client/Models/NotificationSettingsData.cs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace BTCPayServer.Client.Models;
|
||||||
|
|
||||||
|
public class NotificationSettingsData
|
||||||
|
{
|
||||||
|
public List<NotificationSettingsItemData> Notifications { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class NotificationSettingsItemData
|
||||||
|
{
|
||||||
|
public string Identifier { get; set; }
|
||||||
|
public string Name { get; set; }
|
||||||
|
public bool Enabled { get; set; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace BTCPayServer.Client.Models;
|
||||||
|
|
||||||
|
public class UpdateNotificationSettingsRequest
|
||||||
|
{
|
||||||
|
public List<string> Disabled { get; set; }
|
||||||
|
}
|
||||||
@@ -2851,6 +2851,30 @@ namespace BTCPayServer.Tests
|
|||||||
await client.RemoveNotification(notification.Id);
|
await client.RemoveNotification(notification.Id);
|
||||||
Assert.Empty(await viewOnlyClient.GetNotifications(true));
|
Assert.Empty(await viewOnlyClient.GetNotifications(true));
|
||||||
Assert.Empty(await viewOnlyClient.GetNotifications(false));
|
Assert.Empty(await viewOnlyClient.GetNotifications(false));
|
||||||
|
|
||||||
|
// Settings
|
||||||
|
var settings = await client.GetNotificationSettings();
|
||||||
|
Assert.True(settings.Notifications.Find(n => n.Identifier == "newversion").Enabled);
|
||||||
|
Assert.True(settings.Notifications.Find(n => n.Identifier == "pluginupdate").Enabled);
|
||||||
|
Assert.True(settings.Notifications.Find(n => n.Identifier == "inviteaccepted").Enabled);
|
||||||
|
|
||||||
|
var request = new UpdateNotificationSettingsRequest { Disabled = ["newversion", "pluginupdate"] };
|
||||||
|
settings = await client.UpdateNotificationSettings(request);
|
||||||
|
Assert.False(settings.Notifications.Find(n => n.Identifier == "newversion").Enabled);
|
||||||
|
Assert.False(settings.Notifications.Find(n => n.Identifier == "pluginupdate").Enabled);
|
||||||
|
Assert.True(settings.Notifications.Find(n => n.Identifier == "inviteaccepted").Enabled);
|
||||||
|
|
||||||
|
request = new UpdateNotificationSettingsRequest { Disabled = ["all"] };
|
||||||
|
settings = await client.UpdateNotificationSettings(request);
|
||||||
|
Assert.False(settings.Notifications.Find(n => n.Identifier == "newversion").Enabled);
|
||||||
|
Assert.False(settings.Notifications.Find(n => n.Identifier == "pluginupdate").Enabled);
|
||||||
|
Assert.False(settings.Notifications.Find(n => n.Identifier == "inviteaccepted").Enabled);
|
||||||
|
|
||||||
|
request = new UpdateNotificationSettingsRequest { Disabled = [] };
|
||||||
|
settings = await client.UpdateNotificationSettings(request);
|
||||||
|
Assert.True(settings.Notifications.Find(n => n.Identifier == "newversion").Enabled);
|
||||||
|
Assert.True(settings.Notifications.Find(n => n.Identifier == "pluginupdate").Enabled);
|
||||||
|
Assert.True(settings.Notifications.Find(n => n.Identifier == "inviteaccepted").Enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact(Timeout = TestTimeout)]
|
[Fact(Timeout = TestTimeout)]
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using BTCPayServer.Abstractions.Constants;
|
using BTCPayServer.Abstractions.Constants;
|
||||||
@@ -23,12 +24,16 @@ namespace BTCPayServer.Controllers.Greenfield
|
|||||||
{
|
{
|
||||||
private readonly UserManager<ApplicationUser> _userManager;
|
private readonly UserManager<ApplicationUser> _userManager;
|
||||||
private readonly NotificationManager _notificationManager;
|
private readonly NotificationManager _notificationManager;
|
||||||
|
private readonly IEnumerable<INotificationHandler> _notificationHandlers;
|
||||||
|
|
||||||
public GreenfieldNotificationsController(UserManager<ApplicationUser> userManager,
|
public GreenfieldNotificationsController(
|
||||||
NotificationManager notificationManager)
|
UserManager<ApplicationUser> userManager,
|
||||||
|
NotificationManager notificationManager,
|
||||||
|
IEnumerable<INotificationHandler> notificationHandlers)
|
||||||
{
|
{
|
||||||
_userManager = userManager;
|
_userManager = userManager;
|
||||||
_notificationManager = notificationManager;
|
_notificationManager = notificationManager;
|
||||||
|
_notificationHandlers = notificationHandlers;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Authorize(Policy = Policies.CanViewNotificationsForUser,
|
[Authorize(Policy = Policies.CanViewNotificationsForUser,
|
||||||
@@ -95,6 +100,37 @@ namespace BTCPayServer.Controllers.Greenfield
|
|||||||
|
|
||||||
return Ok();
|
return Ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Authorize(Policy = Policies.CanManageNotificationsForUser, AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
|
||||||
|
[HttpGet("~/api/v1/users/me/notification-settings")]
|
||||||
|
public async Task<IActionResult> GetNotificationSettings()
|
||||||
|
{
|
||||||
|
var user = await _userManager.GetUserAsync(User);
|
||||||
|
var model = GetNotificationSettingsData(user);
|
||||||
|
return Ok(model);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Authorize(Policy = Policies.CanManageNotificationsForUser, AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
|
||||||
|
[HttpPut("~/api/v1/users/me/notification-settings")]
|
||||||
|
public async Task<IActionResult> UpdateNotificationSettings(UpdateNotificationSettingsRequest request)
|
||||||
|
{
|
||||||
|
var user = await _userManager.GetUserAsync(User);
|
||||||
|
if (request.Disabled.Contains("all"))
|
||||||
|
{
|
||||||
|
user.DisabledNotifications = "all";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var disabled = _notificationHandlers
|
||||||
|
.SelectMany(handler => handler.Meta.Select(tuple => tuple.identifier))
|
||||||
|
.Where(id => request.Disabled.Contains(id)).ToList();
|
||||||
|
user.DisabledNotifications = disabled.Any() ? string.Join(';', disabled) + ";" : string.Empty;
|
||||||
|
}
|
||||||
|
await _userManager.UpdateAsync(user);
|
||||||
|
|
||||||
|
var model = GetNotificationSettingsData(user);
|
||||||
|
return Ok(model);
|
||||||
|
}
|
||||||
|
|
||||||
private NotificationData ToModel(NotificationViewModel entity)
|
private NotificationData ToModel(NotificationViewModel entity)
|
||||||
{
|
{
|
||||||
@@ -113,5 +149,19 @@ namespace BTCPayServer.Controllers.Greenfield
|
|||||||
{
|
{
|
||||||
return this.CreateAPIError(404, "notification-not-found", "The notification was not found");
|
return this.CreateAPIError(404, "notification-not-found", "The notification was not found");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private NotificationSettingsData GetNotificationSettingsData(ApplicationUser user)
|
||||||
|
{
|
||||||
|
var disabledAll = user.DisabledNotifications == "all";
|
||||||
|
var disabledNotifications = user.DisabledNotifications?.Split(';', StringSplitOptions.RemoveEmptyEntries).ToList() ?? [];
|
||||||
|
var notifications = _notificationHandlers.SelectMany(handler => handler.Meta.Select(tuple =>
|
||||||
|
new NotificationSettingsItemData
|
||||||
|
{
|
||||||
|
Identifier = tuple.identifier,
|
||||||
|
Name = tuple.name,
|
||||||
|
Enabled = !disabledAll && !disabledNotifications.Contains(tuple.identifier, StringComparer.InvariantCultureIgnoreCase)
|
||||||
|
})).ToList();
|
||||||
|
return new NotificationSettingsData { Notifications = notifications };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ using Microsoft.AspNetCore.Mvc;
|
|||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using NBitcoin;
|
using NBitcoin;
|
||||||
using NBXplorer.DerivationStrategy;
|
|
||||||
using NBXplorer.Models;
|
using NBXplorer.Models;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
using InvoiceData = BTCPayServer.Client.Models.InvoiceData;
|
using InvoiceData = BTCPayServer.Client.Models.InvoiceData;
|
||||||
@@ -644,6 +643,18 @@ namespace BTCPayServer.Controllers.Greenfield
|
|||||||
HandleActionResult(await GetController<GreenfieldApiKeysController>().RevokeAPIKey(apikey));
|
HandleActionResult(await GetController<GreenfieldApiKeysController>().RevokeAPIKey(apikey));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override async Task<NotificationSettingsData> GetNotificationSettings(CancellationToken token = default)
|
||||||
|
{
|
||||||
|
return GetFromActionResult<NotificationSettingsData>(
|
||||||
|
await GetController<GreenfieldNotificationsController>().GetNotificationSettings());
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task<NotificationSettingsData> UpdateNotificationSettings(UpdateNotificationSettingsRequest request, CancellationToken token = default)
|
||||||
|
{
|
||||||
|
return GetFromActionResult<NotificationSettingsData>(
|
||||||
|
await GetController<GreenfieldNotificationsController>().UpdateNotificationSettings(request));
|
||||||
|
}
|
||||||
|
|
||||||
public override async Task<IEnumerable<NotificationData>> GetNotifications(bool? seen = null,
|
public override async Task<IEnumerable<NotificationData>> GetNotifications(bool? seen = null,
|
||||||
int? skip = null, int? take = null, CancellationToken token = default)
|
int? skip = null, int? take = null, CancellationToken token = default)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -203,6 +203,80 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"/api/v1/users/me/notification-settings": {
|
||||||
|
"get": {
|
||||||
|
"tags": [
|
||||||
|
"Notifications (Current User)"
|
||||||
|
],
|
||||||
|
"summary": "Get notification settings",
|
||||||
|
"description": "View information about your notification settings",
|
||||||
|
"operationId": "Notifications_GetNotificationSettings",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "The current user's notification settings",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/NotificationSettingsData"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"403": {
|
||||||
|
"description": "If you are authenticated but forbidden to view the notification settings"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"API_Key": [
|
||||||
|
"btcpay.user.canmanagenotificationsforuser"
|
||||||
|
],
|
||||||
|
"Basic": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"put": {
|
||||||
|
"tags": [
|
||||||
|
"Notifications (Current User)"
|
||||||
|
],
|
||||||
|
"summary": "Update notification settings",
|
||||||
|
"description": "Updates the current user's notification settings",
|
||||||
|
"operationId": "Notifications_UpdateNotification",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "The current user's notification settings",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/NotificationSettingsData"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"403": {
|
||||||
|
"description": "If you are authenticated but forbidden to update the notification settings"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"API_Key": [
|
||||||
|
"btcpay.user.canmanagenotificationsforuser"
|
||||||
|
],
|
||||||
|
"Basic": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"requestBody": {
|
||||||
|
"required": true,
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/UpdateNotificationSettingsRequest"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"components": {
|
"components": {
|
||||||
@@ -254,6 +328,117 @@
|
|||||||
"description": "If the notification has been seen by the user"
|
"description": "If the notification has been seen by the user"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"UpdateNotificationSettingsRequest": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"disabled": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"description": "List of the notification type identifiers, which should be disabled. Can also be a single item 'all'.",
|
||||||
|
"example": ["newversion", "pluginupdate"],
|
||||||
|
"nullable": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"NotificationSettingsData": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"notifications": {
|
||||||
|
"type": "array",
|
||||||
|
"description": "The notification types",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/components/schemas/NotificationSettingsItemData"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"example": [
|
||||||
|
{
|
||||||
|
"identifier": "newversion",
|
||||||
|
"name": "New version",
|
||||||
|
"enabled": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identifier": "newuserrequiresapproval",
|
||||||
|
"name": "New user requires approval",
|
||||||
|
"enabled": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identifier": "inviteaccepted",
|
||||||
|
"name": "User accepted invitation",
|
||||||
|
"enabled": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identifier": "pluginupdate",
|
||||||
|
"name": "Plugin update",
|
||||||
|
"enabled": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identifier": "invoicestate",
|
||||||
|
"name": "All invoice updates",
|
||||||
|
"enabled": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identifier": "invoicestate_invoice_paidAfterExpiration",
|
||||||
|
"name": "Invoice was paid after expiration",
|
||||||
|
"enabled": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identifier": "invoicestate_invoice_expiredPaidPartial",
|
||||||
|
"name": "Invoice expired with partial payments",
|
||||||
|
"enabled": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identifier": "invoicestate_invoice_failedToConfirm",
|
||||||
|
"name": "Invoice has payments that failed to confirm on time",
|
||||||
|
"enabled": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identifier": "invoicestate_invoice_confirmed",
|
||||||
|
"name": "Invoice is settled",
|
||||||
|
"enabled": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identifier": "payout",
|
||||||
|
"name": "Payouts",
|
||||||
|
"enabled": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identifier": "external-payout-transaction",
|
||||||
|
"name": "External payout approval",
|
||||||
|
"enabled": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"NotificationSettingsItemData": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"identifier": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The identifier of the notification type",
|
||||||
|
"nullable": false
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The description of the notification type",
|
||||||
|
"nullable": false
|
||||||
|
},
|
||||||
|
"enabled": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "If the notification type is enabled",
|
||||||
|
"nullable": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"example": {
|
||||||
|
"identifier": "newversion",
|
||||||
|
"name": "New version",
|
||||||
|
"enabled": false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user