Add Greenfield Store Email API

This commit is contained in:
Kukks
2022-03-11 10:17:50 +01:00
committed by Andrew Camilleri
parent 326eb1135b
commit dca986eb2e
10 changed files with 380 additions and 93 deletions

View File

@@ -7,6 +7,24 @@ namespace BTCPayServer.Client
{
public partial class BTCPayServerClient
{
public virtual async Task<EmailSettingsData> GetStoreEmailSettings(string storeId,
CancellationToken token = default)
{
using var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/email", method: HttpMethod.Get),
token);
return await HandleResponse<EmailSettingsData>(response);
}
public virtual async Task<EmailSettingsData> UpdateStoreEmailSettings(string storeId, EmailSettingsData request,
CancellationToken token = default)
{
using var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/email", bodyPayload: request, method: HttpMethod.Put),
token);
return await HandleResponse<EmailSettingsData>(response);
}
public virtual async Task SendEmail(string storeId, SendEmailRequest request,
CancellationToken token = default)
{

View File

@@ -23,7 +23,7 @@ namespace BTCPayServer.Client
await HandleResponse(response);
}
public virtual async Task<StoreData> AddStoreUser(string storeId, StoreUserData request,
public virtual async Task AddStoreUser(string storeId, StoreUserData request,
CancellationToken token = default)
{
if (request == null)
@@ -31,7 +31,7 @@ namespace BTCPayServer.Client
using var response = await _httpClient.SendAsync(
CreateHttpRequest($"api/v1/stores/{storeId}/users", bodyPayload: request, method: HttpMethod.Post),
token);
return await HandleResponse<StoreData>(response);
await HandleResponse(response);
}
}
}

View File

@@ -0,0 +1,33 @@
namespace BTCPayServer.Client.Models;
public class EmailSettingsData
{
public string Server
{
get; set;
}
public int? Port
{
get; set;
}
public string Login
{
get; set;
}
public string Password
{
get; set;
}
public string FromDisplay
{
get; set;
}
public string From
{
get; set;
}
}

View File

@@ -2292,13 +2292,27 @@ namespace BTCPayServer.Tests
var admin = tester.NewAccount();
await admin.GrantAccessAsync(true);
var adminClient = await admin.CreateClient(Policies.Unrestricted);
await adminClient.UpdateStoreEmailSettings(admin.StoreId,
new EmailSettingsData());
await adminClient.SendEmail(admin.StoreId, new SendEmailRequest()
var data = new EmailSettingsData()
{
Body = "lol",
Subject = "subj",
Email = "sdasdas"
});
From = "admin@admin.com",
Login = "admin@admin.com",
Password = "admin@admin.com",
Port = 1234,
Server = "admin.com",
};
await adminClient.UpdateStoreEmailSettings(admin.StoreId, data);
var s = await adminClient.GetStoreEmailSettings(admin.StoreId);
Assert.Equal(JsonConvert.SerializeObject(s), JsonConvert.SerializeObject(data));
await AssertValidationError(new[] { nameof(EmailSettingsData.From) },
async () => await adminClient.UpdateStoreEmailSettings(admin.StoreId,
new EmailSettingsData() { From = "ass" }));
await adminClient.SendEmail(admin.StoreId,
new SendEmailRequest() { Body = "lol", Subject = "subj", Email = "sdasdas" });
}
}
}

View File

@@ -5,7 +5,10 @@ using BTCPayServer.Abstractions.Constants;
using BTCPayServer.Abstractions.Extensions;
using BTCPayServer.Client;
using BTCPayServer.Client.Models;
using BTCPayServer.Data;
using BTCPayServer.Services.Mails;
using BTCPayServer.Services.Stores;
using BTCPayServer.Validation;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.Mvc;
@@ -18,10 +21,12 @@ namespace BTCPayServer.Controllers.GreenField
public class GreenfieldStoreEmailController : Controller
{
private readonly EmailSenderFactory _emailSenderFactory;
private readonly StoreRepository _storeRepository;
public GreenfieldStoreEmailController(EmailSenderFactory emailSenderFactory)
public GreenfieldStoreEmailController(EmailSenderFactory emailSenderFactory, StoreRepository storeRepository)
{
_emailSenderFactory = emailSenderFactory;
_storeRepository = storeRepository;
}
[Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
@@ -43,5 +48,48 @@ namespace BTCPayServer.Controllers.GreenField
emailSender.SendEmail(request.Email, request.Subject, request.Body);
return Ok();
}
[Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
[HttpGet("~/api/v1/stores/{storeId}/email")]
public IActionResult GetStoreEmailSettings()
{
var store = HttpContext.GetStoreData();
return store == null ? StoreNotFound() : Ok(FromModel(store));
}
[Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
[HttpPut("~/api/v1/stores/{storeId}/email")]
public async Task<IActionResult> UpdateStoreEmailSettings(string storeId, EmailSettings request)
{
var store = HttpContext.GetStoreData();
if (store == null)
{
return StoreNotFound();
}
if (!string.IsNullOrEmpty(request.From) && !EmailValidator.IsEmail(request.From))
{
request.AddModelError(e => e.From,
"Invalid email address", this);
return this.CreateValidationError(ModelState);
}
var blob = store.GetStoreBlob();
blob.EmailSettings = request;
if (store.SetStoreBlob(blob))
{
await _storeRepository.UpdateStore(store);
}
return Ok(FromModel(store));
}
private EmailSettings FromModel(Data.StoreData data)
{
return data.GetStoreBlob().EmailSettings??new();
}
private IActionResult StoreNotFound()
{
return this.CreateAPIError(404, "store-not-found", "The store was not found");
}
}
}

View File

@@ -12,6 +12,7 @@ using BTCPayServer.Client.Models;
using BTCPayServer.Controllers.GreenField;
using BTCPayServer.Data;
using BTCPayServer.Security.Greenfield;
using BTCPayServer.Services.Mails;
using BTCPayServer.Services.Stores;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
@@ -22,6 +23,8 @@ using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using NBitcoin;
using NBXplorer.Models;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using InvoiceData = BTCPayServer.Client.Models.InvoiceData;
using Language = BTCPayServer.Client.Models.Language;
using NotificationData = BTCPayServer.Client.Models.NotificationData;
@@ -40,7 +43,10 @@ namespace BTCPayServer.Controllers.Greenfield
private readonly IOptionsMonitor<IdentityOptions> _identityOptions;
private readonly GreenfieldStoreOnChainPaymentMethodsController _chainPaymentMethodsController;
private readonly GreenfieldStoreOnChainWalletsController _storeOnChainWalletsController;
private readonly GreenfieldStoreLightningNetworkPaymentMethodsController _storeLightningNetworkPaymentMethodsController;
private readonly GreenfieldStoreLightningNetworkPaymentMethodsController
_storeLightningNetworkPaymentMethodsController;
private readonly GreenfieldStoreLNURLPayPaymentMethodsController _storeLnurlPayPaymentMethodsController;
private readonly GreenfieldHealthController _healthController;
private readonly GreenfieldPaymentRequestsController _paymentRequestController;
@@ -58,6 +64,7 @@ namespace BTCPayServer.Controllers.Greenfield
private readonly UIHomeController _homeController;
private readonly GreenfieldStorePaymentMethodsController _storePaymentMethodsController;
private readonly GreenfieldStoreEmailController _greenfieldStoreEmailController;
private readonly GreenfieldStoreUsersController _greenfieldStoreUsersController;
private readonly IServiceProvider _serviceProvider;
public BTCPayServerClientFactory(StoreRepository storeRepository,
@@ -82,6 +89,7 @@ namespace BTCPayServer.Controllers.Greenfield
UIHomeController homeController,
GreenfieldStorePaymentMethodsController storePaymentMethodsController,
GreenfieldStoreEmailController greenfieldStoreEmailController,
GreenfieldStoreUsersController greenfieldStoreUsersController,
IServiceProvider serviceProvider)
{
_storeRepository = storeRepository;
@@ -106,6 +114,7 @@ namespace BTCPayServer.Controllers.Greenfield
_homeController = homeController;
_storePaymentMethodsController = storePaymentMethodsController;
_greenfieldStoreEmailController = greenfieldStoreEmailController;
_greenfieldStoreUsersController = greenfieldStoreUsersController;
_serviceProvider = serviceProvider;
}
@@ -124,12 +133,14 @@ namespace BTCPayServer.Controllers.Greenfield
claims.AddRange((await _userManager.GetRolesAsync(user)).Select(s =>
new Claim(_identityOptions.CurrentValue.ClaimsIdentity.RoleClaimType, s)));
context.User =
new ClaimsPrincipal(new ClaimsIdentity(claims, $"Local{GreenfieldConstants.AuthenticationType}WithUser"));
new ClaimsPrincipal(new ClaimsIdentity(claims,
$"Local{GreenfieldConstants.AuthenticationType}WithUser"));
}
else
{
context.User =
new ClaimsPrincipal(new ClaimsIdentity(new List<Claim>(), $"Local{GreenfieldConstants.AuthenticationType}"));
new ClaimsPrincipal(new ClaimsIdentity(new List<Claim>(),
$"Local{GreenfieldConstants.AuthenticationType}"));
}
if (storeIds?.Any() is true)
@@ -163,12 +174,13 @@ namespace BTCPayServer.Controllers.Greenfield
_homeController,
_storePaymentMethodsController,
_greenfieldStoreEmailController,
_greenfieldStoreUsersController,
new LocalHttpContextAccessor() { HttpContext = context }
);
}
}
public class LocalHttpContextAccessor: IHttpContextAccessor
public class LocalHttpContextAccessor : IHttpContextAccessor
{
public HttpContext? HttpContext { get; set; }
}
@@ -185,7 +197,10 @@ namespace BTCPayServer.Controllers.Greenfield
private readonly GreenfieldStoresController _storesController;
private readonly GreenfieldStoreLightningNodeApiController _storeLightningNodeApiController;
private readonly GreenfieldInternalLightningNodeApiController _lightningNodeApiController;
private readonly GreenfieldStoreLightningNetworkPaymentMethodsController _storeLightningNetworkPaymentMethodsController;
private readonly GreenfieldStoreLightningNetworkPaymentMethodsController
_storeLightningNetworkPaymentMethodsController;
private readonly GreenfieldStoreLNURLPayPaymentMethodsController _storeLnurlPayPaymentMethodsController;
private readonly GreenfieldInvoiceController _greenFieldInvoiceController;
private readonly GreenfieldServerInfoController _greenFieldServerInfoController;
@@ -194,6 +209,7 @@ namespace BTCPayServer.Controllers.Greenfield
private readonly UIHomeController _homeController;
private readonly GreenfieldStorePaymentMethodsController _storePaymentMethodsController;
private readonly GreenfieldStoreEmailController _greenfieldStoreEmailController;
private readonly GreenfieldStoreUsersController _greenfieldStoreUsersController;
public LocalBTCPayServerClient(
IServiceProvider serviceProvider,
@@ -216,6 +232,7 @@ namespace BTCPayServer.Controllers.Greenfield
UIHomeController homeController,
GreenfieldStorePaymentMethodsController storePaymentMethodsController,
GreenfieldStoreEmailController greenfieldStoreEmailController,
GreenfieldStoreUsersController greenfieldStoreUsersController,
IHttpContextAccessor httpContextAccessor) : base(new Uri("https://dummy.local"), "", "")
{
_chainPaymentMethodsController = chainPaymentMethodsController;
@@ -237,6 +254,7 @@ namespace BTCPayServer.Controllers.Greenfield
_homeController = homeController;
_storePaymentMethodsController = storePaymentMethodsController;
_greenfieldStoreEmailController = greenfieldStoreEmailController;
_greenfieldStoreUsersController = greenfieldStoreUsersController;
var controllers = new[]
{
@@ -244,8 +262,8 @@ namespace BTCPayServer.Controllers.Greenfield
paymentRequestController, apiKeysController, notificationsController, usersController,
storeLightningNetworkPaymentMethodsController, greenFieldInvoiceController, storeWebhooksController,
greenFieldServerInfoController, greenfieldPullPaymentController, storesController, homeController,
lightningNodeApiController, storeLightningNodeApiController as ControllerBase, storePaymentMethodsController,
greenfieldStoreEmailController
lightningNodeApiController, storeLightningNodeApiController as ControllerBase,
storePaymentMethodsController, greenfieldStoreEmailController, greenfieldStoreUsersController
};
var authoverride = new DefaultAuthorizationService(
@@ -259,8 +277,6 @@ namespace BTCPayServer.Controllers.Greenfield
serviceProvider.GetRequiredService<IAuthorizationHandlerContextFactory>(),
serviceProvider.GetRequiredService<IAuthorizationEvaluator>(),
serviceProvider.GetRequiredService<IOptions<AuthorizationOptions>>()
);
@@ -268,7 +284,8 @@ namespace BTCPayServer.Controllers.Greenfield
{
controller.ControllerContext.HttpContext = httpContextAccessor.HttpContext;
var authInterface = typeof(IAuthorizationService);
foreach (FieldInfo fieldInfo in controller.GetType().GetFields().Where(info => authInterface.IsAssignableFrom(info.FieldType)))
foreach (FieldInfo fieldInfo in controller.GetType().GetFields()
.Where(info => authInterface.IsAssignableFrom(info.FieldType)))
{
fieldInfo.SetValue(controller, authoverride);
}
@@ -283,12 +300,14 @@ namespace BTCPayServer.Controllers.Greenfield
private readonly UserManager<ApplicationUser> _userManager;
private readonly StoreRepository _storeRepository;
public AuthHandlerProvider(StoreRepository storeRepository, UserManager<ApplicationUser> userManager, IHttpContextAccessor httpContextAccessor)
public AuthHandlerProvider(StoreRepository storeRepository, UserManager<ApplicationUser> userManager,
IHttpContextAccessor httpContextAccessor)
{
_storeRepository = storeRepository;
_userManager = userManager;
_httpContextAccessor = httpContextAccessor;
}
public Task<IEnumerable<IAuthorizationHandler>> GetHandlersAsync(AuthorizationHandlerContext context)
{
return Task.FromResult<IEnumerable<IAuthorizationHandler>>(new IAuthorizationHandler[]
@@ -297,6 +316,7 @@ namespace BTCPayServer.Controllers.Greenfield
});
}
}
protected override HttpRequestMessage CreateHttpRequest(string path,
Dictionary<string, object> queryPayload = null, HttpMethod method = null)
{
@@ -511,7 +531,8 @@ namespace BTCPayServer.Controllers.Greenfield
await _lightningNodeApiController.GetDepositAddress(cryptoCode));
}
public override async Task<LightningPaymentData> PayLightningInvoice(string cryptoCode, PayLightningInvoiceRequest request,
public override async Task<LightningPaymentData> PayLightningInvoice(string cryptoCode,
PayLightningInvoiceRequest request,
CancellationToken token = default)
{
return GetFromActionResult<LightningPaymentData>(
@@ -575,7 +596,8 @@ namespace BTCPayServer.Controllers.Greenfield
public override Task<IEnumerable<OnChainPaymentMethodData>> GetStoreOnChainPaymentMethods(string storeId,
bool? enabled, CancellationToken token)
{
return Task.FromResult(GetFromActionResult(_chainPaymentMethodsController.GetOnChainPaymentMethods(storeId, enabled)));
return Task.FromResult(
GetFromActionResult(_chainPaymentMethodsController.GetOnChainPaymentMethods(storeId, enabled)));
}
public override Task<OnChainPaymentMethodData> GetStoreOnChainPaymentMethod(string storeId,
@@ -596,7 +618,8 @@ namespace BTCPayServer.Controllers.Greenfield
CancellationToken token = default)
{
return GetFromActionResult<OnChainPaymentMethodData>(
await _chainPaymentMethodsController.UpdateOnChainPaymentMethod(storeId, cryptoCode, new UpdateOnChainPaymentMethodRequest(
await _chainPaymentMethodsController.UpdateOnChainPaymentMethod(storeId, cryptoCode,
new UpdateOnChainPaymentMethodRequest(
enabled: paymentMethod.Enabled,
label: paymentMethod.Label,
accountKeyPath: paymentMethod.AccountKeyPath,
@@ -606,7 +629,8 @@ namespace BTCPayServer.Controllers.Greenfield
public override Task<OnChainPaymentMethodPreviewResultData> PreviewProposedStoreOnChainPaymentMethodAddresses(
string storeId, string cryptoCode,
UpdateOnChainPaymentMethodRequest paymentMethod, int offset = 0, int amount = 10, CancellationToken token = default)
UpdateOnChainPaymentMethodRequest paymentMethod, int offset = 0, int amount = 10,
CancellationToken token = default)
{
return Task.FromResult(GetFromActionResult<OnChainPaymentMethodPreviewResultData>(
_chainPaymentMethodsController.GetProposedOnChainPaymentMethodPreview(storeId, cryptoCode,
@@ -636,7 +660,8 @@ namespace BTCPayServer.Controllers.Greenfield
public override async Task<PaymentRequestData> GetPaymentRequest(string storeId, string paymentRequestId,
CancellationToken token = default)
{
return GetFromActionResult<PaymentRequestData>(await _paymentRequestController.GetPaymentRequest(storeId, paymentRequestId));
return GetFromActionResult<PaymentRequestData>(
await _paymentRequestController.GetPaymentRequest(storeId, paymentRequestId));
}
public override async Task ArchivePaymentRequest(string storeId, string paymentRequestId,
@@ -882,7 +907,8 @@ namespace BTCPayServer.Controllers.Greenfield
{
return GetFromActionResult<LightningNetworkPaymentMethodData>(await
_storeLightningNetworkPaymentMethodsController.UpdateLightningNetworkPaymentMethod(storeId, cryptoCode,
new UpdateLightningNetworkPaymentMethodRequest(paymentMethod.ConnectionString, paymentMethod.Enabled)));
new UpdateLightningNetworkPaymentMethodRequest(paymentMethod.ConnectionString,
paymentMethod.Enabled)));
}
public override async Task<IEnumerable<InvoiceData>> GetInvoices(string storeId, string[] orderId = null,
@@ -894,7 +920,6 @@ namespace BTCPayServer.Controllers.Greenfield
int? skip = null,
int? take = null,
CancellationToken token = default
)
{
return GetFromActionResult<IEnumerable<InvoiceData>>(
@@ -980,7 +1005,8 @@ namespace BTCPayServer.Controllers.Greenfield
public override Task<Language[]> GetAvailableLanguages(CancellationToken token = default)
{
return Task.FromResult(_homeController.LanguageService.GetLanguages().Select(language => new Language(language.Code, language.DisplayName)).ToArray());
return Task.FromResult(_homeController.LanguageService.GetLanguages()
.Select(language => new Language(language.Code, language.DisplayName)).ToArray());
}
public override Task<PermissionMetadata[]> GetPermissionMetadata(CancellationToken token = default)
@@ -988,15 +1014,19 @@ namespace BTCPayServer.Controllers.Greenfield
return Task.FromResult(GetFromActionResult<PermissionMetadata[]>(_homeController.Permissions()));
}
public override async Task<Dictionary<string, GenericPaymentMethodData>> GetStorePaymentMethods(string storeId, bool? enabled = null, CancellationToken token = default)
public override async Task<Dictionary<string, GenericPaymentMethodData>> GetStorePaymentMethods(string storeId,
bool? enabled = null, CancellationToken token = default)
{
return GetFromActionResult(await _storePaymentMethodsController.GetStorePaymentMethods(storeId, enabled));
}
public override async Task<OnChainPaymentMethodDataWithSensitiveData> GenerateOnChainWallet(string storeId, string cryptoCode, GenerateOnChainWalletRequest request,
public override async Task<OnChainPaymentMethodDataWithSensitiveData> GenerateOnChainWallet(string storeId,
string cryptoCode, GenerateOnChainWalletRequest request,
CancellationToken token = default)
{
return GetFromActionResult<OnChainPaymentMethodDataWithSensitiveData>(await _chainPaymentMethodsController.GenerateOnChainWallet(storeId, cryptoCode, new GenerateWalletRequest()
return GetFromActionResult<OnChainPaymentMethodDataWithSensitiveData>(
await _chainPaymentMethodsController.GenerateOnChainWallet(storeId, cryptoCode,
new GenerateWalletRequest()
{
Passphrase = request.Passphrase,
AccountNumber = request.AccountNumber,
@@ -1009,9 +1039,53 @@ namespace BTCPayServer.Controllers.Greenfield
}));
}
public override async Task SendEmail(string storeId, SendEmailRequest request, CancellationToken token = default)
public override async Task SendEmail(string storeId, SendEmailRequest request,
CancellationToken token = default)
{
HandleActionResult(await _greenfieldStoreEmailController.SendEmailFromStore(storeId, request));
}
public override Task<EmailSettingsData> GetStoreEmailSettings(string storeId, CancellationToken token = default)
{
return Task.FromResult(
GetFromActionResult<EmailSettingsData>(_greenfieldStoreEmailController.GetStoreEmailSettings()));
}
public override async Task<EmailSettingsData> UpdateStoreEmailSettings(string storeId,
EmailSettingsData request, CancellationToken token = default)
{
return GetFromActionResult<EmailSettingsData>(
await _greenfieldStoreEmailController.UpdateStoreEmailSettings(storeId,
JObject.FromObject(request).ToObject<EmailSettings>()));
}
public override async Task<ApplicationUserData[]> GetUsers(CancellationToken token = default)
{
return GetFromActionResult(await _usersController.GetUsers());
}
public override Task<IEnumerable<StoreUserData>> GetStoreUsers(string storeId,
CancellationToken token = default)
{
return Task.FromResult(
GetFromActionResult<IEnumerable<StoreUserData>>(_greenfieldStoreUsersController.GetStoreUsers()));
}
public override async Task AddStoreUser(string storeId, StoreUserData request,
CancellationToken token = default)
{
HandleActionResult(await _greenfieldStoreUsersController.AddStoreUser(storeId, request));
}
public override async Task RemoveStoreUser(string storeId, string userId, CancellationToken token = default)
{
HandleActionResult(await _greenfieldStoreUsersController.RemoveStoreUser(storeId, userId));
}
public override async Task<ApplicationUserData> GetUserByIdOrEmail(string idOrEmail,
CancellationToken token = default)
{
return GetFromActionResult<ApplicationUserData>(await _usersController.GetUser(idOrEmail));
}
}
}

View File

@@ -4,48 +4,15 @@ using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using System.Threading;
using System.Threading.Tasks;
using BTCPayServer.Client.Models;
using MailKit.Net.Smtp;
using MimeKit;
using Newtonsoft.Json;
namespace BTCPayServer.Services.Mails
{
public class EmailSettings
public class EmailSettings :EmailSettingsData
{
[Display(Name = "SMTP Server")]
public string Server
{
get; set;
}
public int? Port
{
get; set;
}
public string Login
{
get; set;
}
public string Password
{
get; set;
}
[Display(Name = "Sender's display name")]
public string FromDisplay
{
get; set;
}
[EmailAddress]
[Display(Name = "Sender's email address")]
public string From
{
get; set;
}
public bool IsComplete()
{
return !string.IsNullOrWhiteSpace(Server) &&

View File

@@ -28,7 +28,7 @@
<div asp-validation-summary="All" class="text-danger"></div>
}
<div class="form-group">
<label asp-for="Settings.Server" class="form-label"></label>
<label asp-for="Settings.Server" class="form-label">SMTP Server</label>
<input asp-for="Settings.Server" data-fill="server" class="form-control"/>
<span asp-validation-for="Settings.Server" class="text-danger"></span>
</div>
@@ -38,7 +38,7 @@
<span asp-validation-for="Settings.Port" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Settings.FromDisplay" class="form-label"></label>
<label asp-for="Settings.FromDisplay" class="form-label">Sender's display name</label>
<input asp-for="Settings.FromDisplay" class="form-control"/>
<small class="form-text text-muted">
Some email providers (like Gmail) don't allow you to set your display name, so this setting may not have any effect.
@@ -46,7 +46,7 @@
<span asp-validation-for="Settings.FromDisplay" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Settings.From" class="form-label"></label>
<label asp-for="Settings.From" class="form-label">Sender's email address</label>
<input asp-for="Settings.From" class="form-control"/>
<span asp-validation-for="Settings.From" class="text-danger"></span>
</div>

View File

@@ -1,9 +1,109 @@
{
"paths": {
"/api/v1/stores/{storeId}/email": {
"get": {
"tags": [
"Stores (Email)"
],
"summary": "Get store email settings",
"parameters": [
{
"name": "storeId",
"in": "path",
"required": true,
"description": "The store to fetch",
"schema": {
"type": "string"
}
}
],
"description": "View email settings of the specified store",
"operationId": "Stores_GetStoreEmailSettings",
"responses": {
"200": {
"description": "specified store email settings",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/EmailSettingsData"
}
}
}
},
"403": {
"description": "If you are authenticated but forbidden to view the specified store"
},
"404": {
"description": "The key is not found for this store"
}
},
"security": [
{
"API_Key": [
"btcpay.store.canmodifystoresettings"
],
"Basic": []
}
]
},
"post": {
"tags": [
"Stores (Email)"
],
"summary": "Update store email settings",
"description": "Update a store's email settings",
"operationId": "Stores_UpdateStoreEmailSettings",
"requestBody": {
"x-name": "request",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/EmailSettingsData"
}
}
},
"required": true,
"x-position": 1
},
"responses": {
"200": {
"description": "The settings were updated",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/EmailSettingsData"
}
}
}
},
"400": {
"description": "A list of errors that occurred when updating the settings",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ValidationProblemDetails"
}
}
}
},
"403": {
"description": "If you are authenticated but forbidden to modify the store"
}
},
"security": [
{
"API_Key": [
"btcpay.store.canmodifystoresettings"
],
"Basic": []
}
]
}
},
"/api/v1/stores/{storeId}/email/send": {
"post": {
"tags": [
"Stores"
"Stores (Email)"
],
"summary": "Send an email for a store",
"description": "Send an email using the store's SMTP server",
@@ -63,12 +163,43 @@
"description": "Body of the email to send as plain text."
}
}
},
"EmailSettingsData": {
"type": "object",
"additionalProperties": false,
"properties": {
"server": {
"type": "string",
"description": "Smtp server host"
},
"port": {
"type": "number",
"description": "Smtp server port"
},
"login": {
"type": "string",
"description": "Smtp server username"
},
"password": {
"type": "string",
"description": "Smtp server password"
},
"from": {
"type": "string",
"format": "email",
"description": "Email to send from"
},
"fromDisplay": {
"type": "string",
"description": "The name of the sender"
}
}
}
}
},
"tags": [
{
"name": "Store Email"
"name": "Stores (Email)"
}
]
}

View File

@@ -52,6 +52,7 @@
],
"summary": "Add a store user",
"description": "Add a store user",
"operationId": "Stores_AddStoreUser",
"requestBody": {
"x-name": "request",
"content": {
@@ -66,7 +67,7 @@
},
"responses": {
"200": {
"description": "The user as added"
"description": "The user was added"
},
"400": {
"description": "A list of errors that occurred when creating the store",
@@ -108,6 +109,7 @@
"Stores (Users)"
],
"summary": "Remove Store User",
"operationId": "Stores_RemoveStoreUser",
"description": "Removes the specified store user. If there is no other owner, this endpoint will fail.",
"parameters": [
{