New API endpoint: Send email using store SMTP (#3181)

Co-authored-by: Kukks <evilkukka@gmail.com>
This commit is contained in:
Wouter Samaey
2022-03-11 10:17:40 +01:00
committed by GitHub
parent c15f182377
commit c36b0c16b0
6 changed files with 184 additions and 2 deletions

View File

@@ -0,0 +1,19 @@
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using BTCPayServer.Client.Models;
namespace BTCPayServer.Client
{
public partial class BTCPayServerClient
{
public virtual async Task SendEmail(string storeId, SendEmailRequest request,
CancellationToken token = default)
{
using var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/email/send", bodyPayload: request, method: HttpMethod.Post),
token);
await HandleResponse(response);
}
}
}

View File

@@ -0,0 +1,10 @@
namespace BTCPayServer.Client.Models
{
public class SendEmailRequest
{
public string Email;
public string Subject;
public string Body;
}
}

View File

@@ -2266,5 +2266,23 @@ namespace BTCPayServer.Tests
} }
[Fact(Timeout = 60 * 2 * 1000)]
[Trait("Integration", "Integration")]
public async Task StoreEmailTests()
{
using var tester = CreateServerTester();
await tester.StartAsync();
var admin = tester.NewAccount();
await admin.GrantAccessAsync(true);
var adminClient = await admin.CreateClient(Policies.Unrestricted);
await adminClient.SendEmail(admin.StoreId, new SendEmailRequest()
{
Body = "lol",
Subject = "subj",
Email = "sdasdas"
});
}
} }
} }

View File

@@ -0,0 +1,47 @@
#nullable enable
using System;
using System.Threading.Tasks;
using BTCPayServer.Abstractions.Constants;
using BTCPayServer.Abstractions.Extensions;
using BTCPayServer.Client;
using BTCPayServer.Client.Models;
using BTCPayServer.Services.Mails;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.Mvc;
namespace BTCPayServer.Controllers.GreenField
{
[ApiController]
[Authorize(AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
[EnableCors(CorsPolicies.All)]
public class GreenfieldStoreEmailController : Controller
{
private readonly EmailSenderFactory _emailSenderFactory;
public GreenfieldStoreEmailController(EmailSenderFactory emailSenderFactory)
{
_emailSenderFactory = emailSenderFactory;
}
[Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
[HttpPost("~/api/v1/stores/{storeId}/email/send")]
public async Task<IActionResult> SendEmailFromStore(string storeId,
[FromBody] SendEmailRequest request)
{
var store = HttpContext.GetStoreData();
if (store == null)
{
return this.CreateAPIError(404, "store-not-found", "The store was not found");
}
var emailSender = await _emailSenderFactory.GetEmailSender(storeId);
if (emailSender is null )
{
return this.CreateAPIError(404,"smtp-not-configured", "Store does not have an SMTP server configured.");
}
emailSender.SendEmail(request.Email, request.Subject, request.Body);
return Ok();
}
}
}

View File

@@ -9,6 +9,7 @@ using System.Threading.Tasks;
using BTCPayServer.Abstractions.Contracts; using BTCPayServer.Abstractions.Contracts;
using BTCPayServer.Client; using BTCPayServer.Client;
using BTCPayServer.Client.Models; using BTCPayServer.Client.Models;
using BTCPayServer.Controllers.GreenField;
using BTCPayServer.Data; using BTCPayServer.Data;
using BTCPayServer.Security.Greenfield; using BTCPayServer.Security.Greenfield;
using BTCPayServer.Services.Stores; using BTCPayServer.Services.Stores;
@@ -56,6 +57,7 @@ namespace BTCPayServer.Controllers.Greenfield
private readonly GreenfieldPullPaymentController _greenfieldPullPaymentController; private readonly GreenfieldPullPaymentController _greenfieldPullPaymentController;
private readonly UIHomeController _homeController; private readonly UIHomeController _homeController;
private readonly GreenfieldStorePaymentMethodsController _storePaymentMethodsController; private readonly GreenfieldStorePaymentMethodsController _storePaymentMethodsController;
private readonly GreenfieldStoreEmailController _greenfieldStoreEmailController;
private readonly IServiceProvider _serviceProvider; private readonly IServiceProvider _serviceProvider;
public BTCPayServerClientFactory(StoreRepository storeRepository, public BTCPayServerClientFactory(StoreRepository storeRepository,
@@ -79,6 +81,7 @@ namespace BTCPayServer.Controllers.Greenfield
GreenfieldPullPaymentController greenfieldPullPaymentController, GreenfieldPullPaymentController greenfieldPullPaymentController,
UIHomeController homeController, UIHomeController homeController,
GreenfieldStorePaymentMethodsController storePaymentMethodsController, GreenfieldStorePaymentMethodsController storePaymentMethodsController,
GreenfieldStoreEmailController greenfieldStoreEmailController,
IServiceProvider serviceProvider) IServiceProvider serviceProvider)
{ {
_storeRepository = storeRepository; _storeRepository = storeRepository;
@@ -102,6 +105,7 @@ namespace BTCPayServer.Controllers.Greenfield
_greenfieldPullPaymentController = greenfieldPullPaymentController; _greenfieldPullPaymentController = greenfieldPullPaymentController;
_homeController = homeController; _homeController = homeController;
_storePaymentMethodsController = storePaymentMethodsController; _storePaymentMethodsController = storePaymentMethodsController;
_greenfieldStoreEmailController = greenfieldStoreEmailController;
_serviceProvider = serviceProvider; _serviceProvider = serviceProvider;
} }
@@ -158,6 +162,7 @@ namespace BTCPayServer.Controllers.Greenfield
_greenfieldPullPaymentController, _greenfieldPullPaymentController,
_homeController, _homeController,
_storePaymentMethodsController, _storePaymentMethodsController,
_greenfieldStoreEmailController,
new LocalHttpContextAccessor() { HttpContext = context } new LocalHttpContextAccessor() { HttpContext = context }
); );
} }
@@ -188,6 +193,7 @@ namespace BTCPayServer.Controllers.Greenfield
private readonly GreenfieldPullPaymentController _greenfieldPullPaymentController; private readonly GreenfieldPullPaymentController _greenfieldPullPaymentController;
private readonly UIHomeController _homeController; private readonly UIHomeController _homeController;
private readonly GreenfieldStorePaymentMethodsController _storePaymentMethodsController; private readonly GreenfieldStorePaymentMethodsController _storePaymentMethodsController;
private readonly GreenfieldStoreEmailController _greenfieldStoreEmailController;
public LocalBTCPayServerClient( public LocalBTCPayServerClient(
IServiceProvider serviceProvider, IServiceProvider serviceProvider,
@@ -209,6 +215,7 @@ namespace BTCPayServer.Controllers.Greenfield
GreenfieldPullPaymentController greenfieldPullPaymentController, GreenfieldPullPaymentController greenfieldPullPaymentController,
UIHomeController homeController, UIHomeController homeController,
GreenfieldStorePaymentMethodsController storePaymentMethodsController, GreenfieldStorePaymentMethodsController storePaymentMethodsController,
GreenfieldStoreEmailController greenfieldStoreEmailController,
IHttpContextAccessor httpContextAccessor) : base(new Uri("https://dummy.local"), "", "") IHttpContextAccessor httpContextAccessor) : base(new Uri("https://dummy.local"), "", "")
{ {
_chainPaymentMethodsController = chainPaymentMethodsController; _chainPaymentMethodsController = chainPaymentMethodsController;
@@ -229,6 +236,7 @@ namespace BTCPayServer.Controllers.Greenfield
_greenfieldPullPaymentController = greenfieldPullPaymentController; _greenfieldPullPaymentController = greenfieldPullPaymentController;
_homeController = homeController; _homeController = homeController;
_storePaymentMethodsController = storePaymentMethodsController; _storePaymentMethodsController = storePaymentMethodsController;
_greenfieldStoreEmailController = greenfieldStoreEmailController;
var controllers = new[] var controllers = new[]
{ {
@@ -236,7 +244,8 @@ namespace BTCPayServer.Controllers.Greenfield
paymentRequestController, apiKeysController, notificationsController, usersController, paymentRequestController, apiKeysController, notificationsController, usersController,
storeLightningNetworkPaymentMethodsController, greenFieldInvoiceController, storeWebhooksController, storeLightningNetworkPaymentMethodsController, greenFieldInvoiceController, storeWebhooksController,
greenFieldServerInfoController, greenfieldPullPaymentController, storesController, homeController, greenFieldServerInfoController, greenfieldPullPaymentController, storesController, homeController,
lightningNodeApiController, storeLightningNodeApiController as ControllerBase, storePaymentMethodsController lightningNodeApiController, storeLightningNodeApiController as ControllerBase, storePaymentMethodsController,
greenfieldStoreEmailController
}; };
var authoverride = new DefaultAuthorizationService( var authoverride = new DefaultAuthorizationService(
@@ -999,5 +1008,10 @@ namespace BTCPayServer.Controllers.Greenfield
ImportKeysToRPC = request.ImportKeysToRPC ImportKeysToRPC = request.ImportKeysToRPC
})); }));
} }
public override async Task SendEmail(string storeId, SendEmailRequest request, CancellationToken token = default)
{
HandleActionResult(await _greenfieldStoreEmailController.SendEmailFromStore(storeId, request));
}
} }
} }

View File

@@ -0,0 +1,74 @@
{
"paths": {
"/api/v1/stores/{storeId}/email/send": {
"post": {
"tags": [
"Stores"
],
"summary": "Send an email for a store",
"description": "Send an email using the store's SMTP server",
"requestBody": {
"x-name": "request",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/EmailData"
}
}
},
"required": true,
"x-position": 1
},
"responses": {
"201": {
"description": "The email was sent (scheduled) successfully"
},
"400": {
"description": "The store's SMTP is not configured"
},
"403": {
"description": "If you are authenticated but forbidden to add new stores"
},
"404": {
"description": "The store was not found"
}
},
"security": [
{
"API Key": [
"btcpay.store.canmodifystoresettings"
],
"Basic": []
}
]
}
}
},
"components": {
"schemas": {
"EmailData": {
"type": "object",
"additionalProperties": false,
"properties": {
"email": {
"type": "string",
"description": "Email of the recipient"
},
"subject": {
"type": "string",
"description": "Subject of the email"
},
"body": {
"type": "string",
"description": "Body of the email to send as plain text."
}
}
}
}
},
"tags": [
{
"name": "Store Email"
}
]
}