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
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
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

@ -2265,6 +2265,24 @@ namespace BTCPayServer.Tests
await AssertAPIError("store-user-role-orphaned", async () => await user2Client.RemoveStoreUser(user.StoreId, user2.UserId));
}
[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.Client;
using BTCPayServer.Client.Models;
using BTCPayServer.Controllers.GreenField;
using BTCPayServer.Data;
using BTCPayServer.Security.Greenfield;
using BTCPayServer.Services.Stores;
@ -56,6 +57,7 @@ namespace BTCPayServer.Controllers.Greenfield
private readonly GreenfieldPullPaymentController _greenfieldPullPaymentController;
private readonly UIHomeController _homeController;
private readonly GreenfieldStorePaymentMethodsController _storePaymentMethodsController;
private readonly GreenfieldStoreEmailController _greenfieldStoreEmailController;
private readonly IServiceProvider _serviceProvider;
public BTCPayServerClientFactory(StoreRepository storeRepository,
@ -79,6 +81,7 @@ namespace BTCPayServer.Controllers.Greenfield
GreenfieldPullPaymentController greenfieldPullPaymentController,
UIHomeController homeController,
GreenfieldStorePaymentMethodsController storePaymentMethodsController,
GreenfieldStoreEmailController greenfieldStoreEmailController,
IServiceProvider serviceProvider)
{
_storeRepository = storeRepository;
@ -102,6 +105,7 @@ namespace BTCPayServer.Controllers.Greenfield
_greenfieldPullPaymentController = greenfieldPullPaymentController;
_homeController = homeController;
_storePaymentMethodsController = storePaymentMethodsController;
_greenfieldStoreEmailController = greenfieldStoreEmailController;
_serviceProvider = serviceProvider;
}
@ -158,6 +162,7 @@ namespace BTCPayServer.Controllers.Greenfield
_greenfieldPullPaymentController,
_homeController,
_storePaymentMethodsController,
_greenfieldStoreEmailController,
new LocalHttpContextAccessor() { HttpContext = context }
);
}
@ -188,6 +193,7 @@ namespace BTCPayServer.Controllers.Greenfield
private readonly GreenfieldPullPaymentController _greenfieldPullPaymentController;
private readonly UIHomeController _homeController;
private readonly GreenfieldStorePaymentMethodsController _storePaymentMethodsController;
private readonly GreenfieldStoreEmailController _greenfieldStoreEmailController;
public LocalBTCPayServerClient(
IServiceProvider serviceProvider,
@ -209,6 +215,7 @@ namespace BTCPayServer.Controllers.Greenfield
GreenfieldPullPaymentController greenfieldPullPaymentController,
UIHomeController homeController,
GreenfieldStorePaymentMethodsController storePaymentMethodsController,
GreenfieldStoreEmailController greenfieldStoreEmailController,
IHttpContextAccessor httpContextAccessor) : base(new Uri("https://dummy.local"), "", "")
{
_chainPaymentMethodsController = chainPaymentMethodsController;
@ -229,6 +236,7 @@ namespace BTCPayServer.Controllers.Greenfield
_greenfieldPullPaymentController = greenfieldPullPaymentController;
_homeController = homeController;
_storePaymentMethodsController = storePaymentMethodsController;
_greenfieldStoreEmailController = greenfieldStoreEmailController;
var controllers = new[]
{
@ -236,7 +244,8 @@ namespace BTCPayServer.Controllers.Greenfield
paymentRequestController, apiKeysController, notificationsController, usersController,
storeLightningNetworkPaymentMethodsController, greenFieldInvoiceController, storeWebhooksController,
greenFieldServerInfoController, greenfieldPullPaymentController, storesController, homeController,
lightningNodeApiController, storeLightningNodeApiController as ControllerBase, storePaymentMethodsController
lightningNodeApiController, storeLightningNodeApiController as ControllerBase, storePaymentMethodsController,
greenfieldStoreEmailController
};
var authoverride = new DefaultAuthorizationService(
@ -999,5 +1008,10 @@ namespace BTCPayServer.Controllers.Greenfield
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"
}
]
}