mirror of
https://github.com/aljazceru/btcpayserver.git
synced 2025-12-17 14:04:26 +01:00
Add default server email rules in migration
This commit is contained in:
@@ -11,7 +11,7 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Migrations.Operations;
|
|||||||
|
|
||||||
namespace BTCPayServer.Abstractions.Contracts
|
namespace BTCPayServer.Abstractions.Contracts
|
||||||
{
|
{
|
||||||
public abstract class BaseDbContextFactory<T> where T : DbContext
|
public abstract class BaseDbContextFactory<T> : IDbContextFactory<T> where T : DbContext
|
||||||
{
|
{
|
||||||
private readonly IOptions<DatabaseOptions> _options;
|
private readonly IOptions<DatabaseOptions> _options;
|
||||||
private readonly string _migrationTableName;
|
private readonly string _migrationTableName;
|
||||||
@@ -90,5 +90,8 @@ namespace BTCPayServer.Abstractions.Contracts
|
|||||||
var searchPaths = connectionStringBuilder.SearchPath?.Split(',');
|
var searchPaths = connectionStringBuilder.SearchPath?.Split(',');
|
||||||
return searchPaths is not { Length: > 0 } ? null : searchPaths[0];
|
return searchPaths is not { Length: > 0 } ? null : searchPaths[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
T IDbContextFactory<T>.CreateDbContext()
|
||||||
|
=> this.CreateContext();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,11 +15,6 @@ public class ConfigureEmailPMO(PlaywrightTester s)
|
|||||||
public string? Password { get; set; }
|
public string? Password { get; set; }
|
||||||
public bool? EnabledCertificateCheck { get; set; }
|
public bool? EnabledCertificateCheck { get; set; }
|
||||||
}
|
}
|
||||||
public async Task UseMailPit()
|
|
||||||
{
|
|
||||||
await s.Page.ClickAsync("#mailpit");
|
|
||||||
await s.FindAlertMessage(StatusMessageModel.StatusSeverity.Info);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<EmailRulesPMO> ConfigureEmailRules()
|
public async Task<EmailRulesPMO> ConfigureEmailRules()
|
||||||
{
|
{
|
||||||
@@ -27,14 +22,14 @@ public class ConfigureEmailPMO(PlaywrightTester s)
|
|||||||
return new EmailRulesPMO(s);
|
return new EmailRulesPMO(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task FillMailPit(Form form)
|
public Task FillMailPit(Form? form = null)
|
||||||
=> Fill(new()
|
=> Fill(new()
|
||||||
{
|
{
|
||||||
Server = s.Server.MailPitSettings.Hostname,
|
Server = s.Server.MailPitSettings.Hostname,
|
||||||
Port = s.Server.MailPitSettings.SmtpPort,
|
Port = s.Server.MailPitSettings.SmtpPort,
|
||||||
From = form.From,
|
From = form?.From ?? "from@example.com",
|
||||||
Login = form.Login,
|
Login = form?.Login ?? "login@example.com",
|
||||||
Password = form.Password,
|
Password = form?.Password ?? "password",
|
||||||
EnabledCertificateCheck = false,
|
EnabledCertificateCheck = false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,11 @@ public class EmailRulesPMO(PlaywrightTester s)
|
|||||||
await s.Page.ClickAsync("#CreateEmailRule");
|
await s.Page.ClickAsync("#CreateEmailRule");
|
||||||
return new EmailRulePMO(s);
|
return new EmailRulePMO(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task EditRule(string trigger, int nth = 0)
|
||||||
|
{
|
||||||
|
await s.Page.ClickAsync($"tr[data-trigger='{trigger}']:nth-child({nth + 1}) a:has-text('Edit')");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class EmailRulePMO(PlaywrightTester s)
|
public class EmailRulePMO(PlaywrightTester s)
|
||||||
|
|||||||
@@ -1535,9 +1535,9 @@ namespace BTCPayServer.Tests
|
|||||||
|
|
||||||
await s.GoToServer(ServerNavPages.Emails);
|
await s.GoToServer(ServerNavPages.Emails);
|
||||||
|
|
||||||
await mailPMO.UseMailPit();
|
await mailPMO.FillMailPit();
|
||||||
var rules = await mailPMO.ConfigureEmailRules();
|
var rules = await mailPMO.ConfigureEmailRules();
|
||||||
await rules.CreateEmailRule();
|
await rules.EditRule("SRV-PasswordReset");
|
||||||
await pmo.Fill(new()
|
await pmo.Fill(new()
|
||||||
{
|
{
|
||||||
Trigger = "SRV-PasswordReset",
|
Trigger = "SRV-PasswordReset",
|
||||||
@@ -1610,11 +1610,10 @@ namespace BTCPayServer.Tests
|
|||||||
await s.GoToInvoices();
|
await s.GoToInvoices();
|
||||||
|
|
||||||
await s.ClickPagePrimary();
|
await s.ClickPagePrimary();
|
||||||
Assert.Contains("To create an invoice, you need to", await s.Page.ContentAsync());
|
|
||||||
|
|
||||||
await s.AddDerivationScheme();
|
await s.AddDerivationScheme();
|
||||||
await s.GoToInvoices();
|
await s.GoToInvoices();
|
||||||
var invoiceId = await s.CreateInvoice();
|
await s.CreateInvoice();
|
||||||
await s.Page.ClickAsync("[data-invoice-state-badge] .dropdown-toggle");
|
await s.Page.ClickAsync("[data-invoice-state-badge] .dropdown-toggle");
|
||||||
await s.Page.ClickAsync("[data-invoice-state-badge] .dropdown-menu button:first-child");
|
await s.Page.ClickAsync("[data-invoice-state-badge] .dropdown-menu button:first-child");
|
||||||
await TestUtils.EventuallyAsync(async () => Assert.Contains("Invalid (marked)", await s.Page.ContentAsync()));
|
await TestUtils.EventuallyAsync(async () => Assert.Contains("Invalid (marked)", await s.Page.ContentAsync()));
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ using NBitpayClient;
|
|||||||
using NBXplorer;
|
using NBXplorer;
|
||||||
using BTCPayServer.Abstractions.Contracts;
|
using BTCPayServer.Abstractions.Contracts;
|
||||||
using System.Diagnostics.Metrics;
|
using System.Diagnostics.Metrics;
|
||||||
|
using System.Threading;
|
||||||
using BTCPayServer.Events;
|
using BTCPayServer.Events;
|
||||||
|
|
||||||
namespace BTCPayServer.Tests
|
namespace BTCPayServer.Tests
|
||||||
@@ -49,7 +50,7 @@ namespace BTCPayServer.Tests
|
|||||||
GetEnvironment("TESTS_MAILPIT_HOST", "127.0.0.1"),
|
GetEnvironment("TESTS_MAILPIT_HOST", "127.0.0.1"),
|
||||||
int.Parse(GetEnvironment("TESTS_MAILPIT_SMTP", "34219")),
|
int.Parse(GetEnvironment("TESTS_MAILPIT_SMTP", "34219")),
|
||||||
int.Parse(GetEnvironment("TESTS_MAILPIT_HTTP", "34218")));
|
int.Parse(GetEnvironment("TESTS_MAILPIT_HTTP", "34218")));
|
||||||
|
TestLogs.LogInformation($"MailPit settings: http://{MailPitSettings.Hostname}:{MailPitSettings.HttpPort} (SMTP: {MailPitSettings.SmtpPort})");
|
||||||
_NetworkProvider = networkProvider;
|
_NetworkProvider = networkProvider;
|
||||||
ExplorerNode = new RPCClient(RPCCredentialString.Parse(GetEnvironment("TESTS_BTCRPCCONNECTION", "server=http://127.0.0.1:43782;ceiwHEbqWI83:DwubwWsoo3")), NetworkProvider.GetNetwork<BTCPayNetwork>("BTC").NBitcoinNetwork);
|
ExplorerNode = new RPCClient(RPCCredentialString.Parse(GetEnvironment("TESTS_BTCRPCCONNECTION", "server=http://127.0.0.1:43782;ceiwHEbqWI83:DwubwWsoo3")), NetworkProvider.GetNetwork<BTCPayNetwork>("BTC").NBitcoinNetwork);
|
||||||
ExplorerNode.ScanRPCCapabilities();
|
ExplorerNode.ScanRPCCapabilities();
|
||||||
@@ -198,6 +199,8 @@ namespace BTCPayServer.Tests
|
|||||||
public async Task<T> WaitForEvent<T>(Func<Task> action, Func<T, bool> correctEvent = null)
|
public async Task<T> WaitForEvent<T>(Func<Task> action, Func<T, bool> correctEvent = null)
|
||||||
{
|
{
|
||||||
var tcs = new TaskCompletionSource<T>(TaskCreationOptions.RunContinuationsAsynchronously);
|
var tcs = new TaskCompletionSource<T>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||||
|
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30));
|
||||||
|
await using var register = cts.Token.Register(() => tcs.TrySetCanceled());
|
||||||
var sub = PayTester.GetService<EventAggregator>().SubscribeAny<T>(evt =>
|
var sub = PayTester.GetService<EventAggregator>().SubscribeAny<T>(evt =>
|
||||||
{
|
{
|
||||||
if (correctEvent is null)
|
if (correctEvent is null)
|
||||||
|
|||||||
@@ -3352,14 +3352,17 @@ namespace BTCPayServer.Tests
|
|||||||
Assert.Equal("admin@admin.com", (await Assert.IsType<ServerEmailSender>(await emailSenderFactory.GetEmailSender()).GetEmailSettings()).Login);
|
Assert.Equal("admin@admin.com", (await Assert.IsType<ServerEmailSender>(await emailSenderFactory.GetEmailSender()).GetEmailSettings()).Login);
|
||||||
Assert.Null(await Assert.IsType<StoreEmailSender>(await emailSenderFactory.GetEmailSender(acc.StoreId)).GetEmailSettings());
|
Assert.Null(await Assert.IsType<StoreEmailSender>(await emailSenderFactory.GetEmailSender(acc.StoreId)).GetEmailSettings());
|
||||||
|
|
||||||
Assert.IsType<RedirectToActionResult>(await acc.GetController<UIServerEmailController>().ServerEmailSettings(new (new EmailSettings
|
Assert.IsType<RedirectToActionResult>(await acc.GetController<UIStoresEmailController>().StoreEmailSettings(acc.StoreId, new(new()
|
||||||
{
|
{
|
||||||
From = "store@store.com",
|
From = "store@store.com",
|
||||||
Login = "store@store.com",
|
Login = "store@store.com",
|
||||||
Password = "store@store.com",
|
Password = "store@store.com",
|
||||||
Port = tester.MailPitSettings.SmtpPort,
|
Port = tester.MailPitSettings.SmtpPort,
|
||||||
Server = tester.MailPitSettings.Hostname
|
Server = tester.MailPitSettings.Hostname
|
||||||
}), ""));
|
})
|
||||||
|
{
|
||||||
|
IsCustomSMTP = true
|
||||||
|
}, ""));
|
||||||
|
|
||||||
Assert.Equal("store@store.com", (await Assert.IsType<StoreEmailSender>(await emailSenderFactory.GetEmailSender(acc.StoreId)).GetEmailSettings()).Login);
|
Assert.Equal("store@store.com", (await Assert.IsType<StoreEmailSender>(await emailSenderFactory.GetEmailSender(acc.StoreId)).GetEmailSettings()).Login);
|
||||||
|
|
||||||
@@ -3370,6 +3373,26 @@ namespace BTCPayServer.Tests
|
|||||||
});
|
});
|
||||||
Assert.Equal("test", message.Subject);
|
Assert.Equal("test", message.Subject);
|
||||||
Assert.Equal("hello world", message.Text);
|
Assert.Equal("hello world", message.Text);
|
||||||
|
|
||||||
|
// Configure at server level
|
||||||
|
Assert.IsType<RedirectToActionResult>(await acc.GetController<UIServerEmailController>().ServerEmailSettings(new(new()
|
||||||
|
{
|
||||||
|
From = "server@server.com",
|
||||||
|
Login = "server@server.com",
|
||||||
|
Password = "server@server.com",
|
||||||
|
Port = tester.MailPitSettings.SmtpPort,
|
||||||
|
Server = tester.MailPitSettings.Hostname
|
||||||
|
})
|
||||||
|
{
|
||||||
|
EnableStoresToUseServerEmailSettings = true
|
||||||
|
}, ""));
|
||||||
|
|
||||||
|
// The store should now use it
|
||||||
|
Assert.IsType<RedirectToActionResult>(await acc.GetController<UIStoresEmailController>().StoreEmailSettings(acc.StoreId, new(new())
|
||||||
|
{
|
||||||
|
IsCustomSMTP = false
|
||||||
|
}, ""));
|
||||||
|
Assert.Equal("server@server.com", (await Assert.IsType<StoreEmailSender>(await emailSenderFactory.GetEmailSender(acc.StoreId)).GetEmailSettings()).Login);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact(Timeout = TestUtils.TestTimeout)]
|
[Fact(Timeout = TestUtils.TestTimeout)]
|
||||||
|
|||||||
13
BTCPayServer/Data/MigrationBase.cs
Normal file
13
BTCPayServer/Data/MigrationBase.cs
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
namespace BTCPayServer.Data;
|
||||||
|
|
||||||
|
public abstract class MigrationBase<TDbContext>(string migrationId)
|
||||||
|
where TDbContext : DbContext
|
||||||
|
{
|
||||||
|
public string MigrationId { get; } = migrationId;
|
||||||
|
|
||||||
|
public abstract Task MigrateAsync(TDbContext dbContext, CancellationToken cancellationToken);
|
||||||
|
}
|
||||||
48
BTCPayServer/Data/MigrationExecutor.cs
Normal file
48
BTCPayServer/Data/MigrationExecutor.cs
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace BTCPayServer.Data;
|
||||||
|
|
||||||
|
public interface IMigrationExecutor
|
||||||
|
{
|
||||||
|
Task Execute(CancellationToken cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class MigrationExecutor<TDbContext>(
|
||||||
|
ILoggerFactory loggerFactory,
|
||||||
|
IDbContextFactory<TDbContext> dbContextFactory,
|
||||||
|
IEnumerable<MigrationBase<TDbContext>> migrations) : IMigrationExecutor
|
||||||
|
where TDbContext : DbContext
|
||||||
|
{
|
||||||
|
ILogger logger = loggerFactory.CreateLogger($"BTCPayServer.Migrations.{typeof(TDbContext).Name}");
|
||||||
|
public async Task Execute(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
await using var dbContext = await dbContextFactory.CreateDbContextAsync(cancellationToken);
|
||||||
|
var history = dbContext.Database.GetService<IHistoryRepository>();
|
||||||
|
var appliedMigrations = (await history.GetAppliedMigrationsAsync(cancellationToken)).Select(m => m.MigrationId).ToHashSet();
|
||||||
|
var insertedRows = new List<HistoryRow>();
|
||||||
|
foreach (var migration in migrations
|
||||||
|
.Where(m => !appliedMigrations.Contains(m.MigrationId))
|
||||||
|
.OrderBy(m => m.MigrationId))
|
||||||
|
{
|
||||||
|
logger.LogInformation("Applying migration '{MigrationId}'", migration.MigrationId);
|
||||||
|
await migration.MigrateAsync(dbContext, cancellationToken);
|
||||||
|
insertedRows.Add(new HistoryRow(migration.MigrationId, ProductInfo.GetVersion()));
|
||||||
|
}
|
||||||
|
if (insertedRows.Count > 0)
|
||||||
|
{
|
||||||
|
await dbContext.SaveChangesAsync(cancellationToken);
|
||||||
|
var insertMigrations =
|
||||||
|
string.Concat(insertedRows
|
||||||
|
.Select(r => history.GetInsertScript(r))
|
||||||
|
.ToArray());
|
||||||
|
await dbContext.Database.ExecuteSqlRawAsync(insertMigrations, cancellationToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -36,6 +36,7 @@ using BTCPayServer.Services.Wallets;
|
|||||||
using Microsoft.AspNetCore.DataProtection;
|
using Microsoft.AspNetCore.DataProtection;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||||
@@ -454,6 +455,15 @@ namespace BTCPayServer
|
|||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static IServiceCollection AddMigration<TDbContext, TMigration>(this IServiceCollection services)
|
||||||
|
where TDbContext : DbContext
|
||||||
|
where TMigration : MigrationBase<TDbContext>
|
||||||
|
{
|
||||||
|
services.TryAddSingleton<IMigrationExecutor, MigrationExecutor<TDbContext>>();
|
||||||
|
services.TryAddSingleton<MigrationBase<TDbContext>, TMigration>();
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
|
||||||
public static async Task CloseSocket(this WebSocket webSocket)
|
public static async Task CloseSocket(this WebSocket webSocket)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
|||||||
@@ -74,6 +74,7 @@ using ExchangeSharp;
|
|||||||
using Microsoft.Extensions.Localization;
|
using Microsoft.Extensions.Localization;
|
||||||
using Microsoft.AspNetCore.Mvc.Localization;
|
using Microsoft.AspNetCore.Mvc.Localization;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace BTCPayServer.Hosting
|
namespace BTCPayServer.Hosting
|
||||||
{
|
{
|
||||||
@@ -96,6 +97,9 @@ namespace BTCPayServer.Hosting
|
|||||||
|
|
||||||
services.AddSingleton<MvcNewtonsoftJsonOptions>(o => o.GetRequiredService<IOptions<MvcNewtonsoftJsonOptions>>().Value);
|
services.AddSingleton<MvcNewtonsoftJsonOptions>(o => o.GetRequiredService<IOptions<MvcNewtonsoftJsonOptions>>().Value);
|
||||||
services.AddSingleton<JsonSerializerSettings>(o => o.GetRequiredService<IOptions<MvcNewtonsoftJsonOptions>>().Value.SerializerSettings);
|
services.AddSingleton<JsonSerializerSettings>(o => o.GetRequiredService<IOptions<MvcNewtonsoftJsonOptions>>().Value.SerializerSettings);
|
||||||
|
|
||||||
|
services.AddSingleton<IDbContextFactory<ApplicationDbContext>, ApplicationDbContextFactory>((provider) => provider.GetRequiredService<ApplicationDbContextFactory>());
|
||||||
|
services.AddSingleton<IMigrationExecutor, MigrationExecutor<ApplicationDbContext>>();
|
||||||
services.AddDbContext<ApplicationDbContext>((provider, o) =>
|
services.AddDbContext<ApplicationDbContext>((provider, o) =>
|
||||||
{
|
{
|
||||||
var factory = provider.GetRequiredService<ApplicationDbContextFactory>();
|
var factory = provider.GetRequiredService<ApplicationDbContextFactory>();
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ namespace BTCPayServer.Hosting
|
|||||||
|
|
||||||
private readonly ApplicationDbContextFactory _DBContextFactory;
|
private readonly ApplicationDbContextFactory _DBContextFactory;
|
||||||
private readonly StoreRepository _StoreRepository;
|
private readonly StoreRepository _StoreRepository;
|
||||||
|
private readonly IEnumerable<IMigrationExecutor> _migrationExecutors;
|
||||||
private readonly PaymentMethodHandlerDictionary _handlers;
|
private readonly PaymentMethodHandlerDictionary _handlers;
|
||||||
private readonly SettingsRepository _Settings;
|
private readonly SettingsRepository _Settings;
|
||||||
private readonly AppService _appService;
|
private readonly AppService _appService;
|
||||||
@@ -54,6 +55,7 @@ namespace BTCPayServer.Hosting
|
|||||||
public IOptions<LightningNetworkOptions> LightningOptions { get; }
|
public IOptions<LightningNetworkOptions> LightningOptions { get; }
|
||||||
|
|
||||||
public MigrationStartupTask(
|
public MigrationStartupTask(
|
||||||
|
IEnumerable<IMigrationExecutor> migrationExecutors,
|
||||||
PaymentMethodHandlerDictionary handlers,
|
PaymentMethodHandlerDictionary handlers,
|
||||||
StoreRepository storeRepository,
|
StoreRepository storeRepository,
|
||||||
ApplicationDbContextFactory dbContextFactory,
|
ApplicationDbContextFactory dbContextFactory,
|
||||||
@@ -67,6 +69,7 @@ namespace BTCPayServer.Hosting
|
|||||||
IFileService fileService,
|
IFileService fileService,
|
||||||
LightningClientFactoryService lightningClientFactoryService)
|
LightningClientFactoryService lightningClientFactoryService)
|
||||||
{
|
{
|
||||||
|
_migrationExecutors = migrationExecutors;
|
||||||
_handlers = handlers;
|
_handlers = handlers;
|
||||||
_DBContextFactory = dbContextFactory;
|
_DBContextFactory = dbContextFactory;
|
||||||
_StoreRepository = storeRepository;
|
_StoreRepository = storeRepository;
|
||||||
@@ -230,6 +233,11 @@ namespace BTCPayServer.Hosting
|
|||||||
settings.MigrateOldDerivationSchemes = true;
|
settings.MigrateOldDerivationSchemes = true;
|
||||||
await _Settings.UpdateSetting(settings);
|
await _Settings.UpdateSetting(settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
foreach (var executor in _migrationExecutors)
|
||||||
|
{
|
||||||
|
await executor.Execute(cancellationToken);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using BTCPayServer.Data;
|
||||||
|
using BTCPayServer.Plugins.Emails.Views;
|
||||||
|
|
||||||
|
namespace BTCPayServer.Plugins.Emails;
|
||||||
|
|
||||||
|
public class DefaultServerEmailRulesMigration(IEnumerable<EmailTriggerViewModel> vms) : MigrationBase<ApplicationDbContext>("20251109_defaultserverrules")
|
||||||
|
{
|
||||||
|
public override Task MigrateAsync(ApplicationDbContext dbContext, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var defaultRules = new string[] {
|
||||||
|
ServerMailTriggers.PasswordReset,
|
||||||
|
ServerMailTriggers.InvitePending,
|
||||||
|
ServerMailTriggers.ApprovalConfirmed,
|
||||||
|
ServerMailTriggers.ApprovalPending,
|
||||||
|
ServerMailTriggers.EmailConfirm,
|
||||||
|
ServerMailTriggers.ApprovalRequest,
|
||||||
|
ServerMailTriggers.InviteConfirmed
|
||||||
|
}.ToHashSet();
|
||||||
|
foreach (var vm in vms.Where(v => defaultRules.Contains(v.Trigger)))
|
||||||
|
{
|
||||||
|
dbContext.EmailRules.Add(new()
|
||||||
|
{
|
||||||
|
To = vm.DefaultEmail.To,
|
||||||
|
CC = vm.DefaultEmail.CC,
|
||||||
|
BCC = vm.DefaultEmail.BCC,
|
||||||
|
Trigger = vm.Trigger,
|
||||||
|
Subject = vm.DefaultEmail.Subject,
|
||||||
|
Body = vm.DefaultEmail.Body,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using BTCPayServer.Abstractions.Models;
|
using BTCPayServer.Abstractions.Models;
|
||||||
|
using BTCPayServer.Data;
|
||||||
using BTCPayServer.Plugins.Emails.HostedServices;
|
using BTCPayServer.Plugins.Emails.HostedServices;
|
||||||
using BTCPayServer.Plugins.Emails.Views;
|
using BTCPayServer.Plugins.Emails.Views;
|
||||||
using BTCPayServer.Plugins.Webhooks;
|
using BTCPayServer.Plugins.Webhooks;
|
||||||
@@ -21,6 +22,7 @@ public class EmailsPlugin : BaseBTCPayServerPlugin
|
|||||||
services.AddSingleton<IDefaultTranslationProvider, EmailsTranslationProvider>();
|
services.AddSingleton<IDefaultTranslationProvider, EmailsTranslationProvider>();
|
||||||
services.AddSingleton<IHostedService, StoreEmailRuleProcessorSender>();
|
services.AddSingleton<IHostedService, StoreEmailRuleProcessorSender>();
|
||||||
services.AddSingleton<IHostedService, UserEventHostedService>();
|
services.AddSingleton<IHostedService, UserEventHostedService>();
|
||||||
|
services.AddMigration<ApplicationDbContext, DefaultServerEmailRulesMigration>();
|
||||||
RegisterServerEmailTriggers(services);
|
RegisterServerEmailTriggers(services);
|
||||||
}
|
}
|
||||||
private static string BODY_STYLE = "font-family: Open Sans, Helvetica Neue,Arial,sans-serif; font-color: #292929;";
|
private static string BODY_STYLE = "font-family: Open Sans, Helvetica Neue,Arial,sans-serif; font-color: #292929;";
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ else
|
|||||||
<tbody>
|
<tbody>
|
||||||
@foreach (var rule in Model.Rules)
|
@foreach (var rule in Model.Rules)
|
||||||
{
|
{
|
||||||
<tr>
|
<tr data-trigger="@rule.Trigger">
|
||||||
<td>@rule.Trigger</td>
|
<td>@rule.Trigger</td>
|
||||||
@if (Model.ShowCustomerEmailColumn)
|
@if (Model.ShowCustomerEmailColumn)
|
||||||
{
|
{
|
||||||
@@ -94,12 +94,12 @@ else
|
|||||||
@if (storeId is not null)
|
@if (storeId is not null)
|
||||||
{
|
{
|
||||||
|
|
||||||
<a asp-action="StoreEmailRulesEdit" asp-route-storeId="@storeId" asp-route-ruleId="@rule.Data.Id">Edit</a>
|
<a asp-action="StoreEmailRulesEdit" asp-route-storeId="@storeId" asp-route-ruleId="@rule.Data.Id" text-translate="true">Edit</a>
|
||||||
<a asp-action="StoreEmailRulesDelete" asp-route-storeId="@storeId" asp-route-ruleId="@rule.Data.Id" data-bs-toggle="modal" data-bs-target="#ConfirmModal" data-description="@ViewLocalizer["This action will remove the rule with the trigger <b>{0}</b>.", Html.Encode(rule.Trigger)]" data-confirm-input="@StringLocalizer["Delete"]" text-translate="true">Remove</a>
|
<a asp-action="StoreEmailRulesDelete" asp-route-storeId="@storeId" asp-route-ruleId="@rule.Data.Id" data-bs-toggle="modal" data-bs-target="#ConfirmModal" data-description="@ViewLocalizer["This action will remove the rule with the trigger <b>{0}</b>.", Html.Encode(rule.Trigger)]" data-confirm-input="@StringLocalizer["Delete"]" text-translate="true">Remove</a>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<a asp-action="ServerEmailRulesEdit" asp-route-ruleId="@rule.Data.Id">Edit</a>
|
<a asp-action="ServerEmailRulesEdit" asp-route-ruleId="@rule.Data.Id" text-translate="true">Edit</a>
|
||||||
<a asp-action="ServerEmailRulesDelete" asp-route-ruleId="@rule.Data.Id" data-bs-toggle="modal" data-bs-target="#ConfirmModal" data-description="@ViewLocalizer["This action will remove the rule with the trigger <b>{0}</b>.", Html.Encode(rule.Trigger)]" data-confirm-input="@StringLocalizer["Delete"]" text-translate="true">Remove</a>
|
<a asp-action="ServerEmailRulesDelete" asp-route-ruleId="@rule.Data.Id" data-bs-toggle="modal" data-bs-target="#ConfirmModal" data-description="@ViewLocalizer["This action will remove the rule with the trigger <b>{0}</b>.", Html.Encode(rule.Trigger)]" data-confirm-input="@StringLocalizer["Delete"]" text-translate="true">Remove</a>
|
||||||
}
|
}
|
||||||
</div >
|
</div >
|
||||||
|
|||||||
@@ -180,7 +180,6 @@
|
|||||||
var body = triggersByType[selectedTrigger].defaultEmail.body;
|
var body = triggersByType[selectedTrigger].defaultEmail.body;
|
||||||
|
|
||||||
if ($(bodyTextarea).summernote) {
|
if ($(bodyTextarea).summernote) {
|
||||||
console.log(body);
|
|
||||||
$(bodyTextarea).summernote('reset');
|
$(bodyTextarea).summernote('reset');
|
||||||
$(bodyTextarea).summernote('code', body.replace(/\n/g, '<br/>'));
|
$(bodyTextarea).summernote('code', body.replace(/\n/g, '<br/>'));
|
||||||
} else {
|
} else {
|
||||||
@@ -213,7 +212,7 @@
|
|||||||
const customerEmailContainer = document.querySelector('.customer-email-container');
|
const customerEmailContainer = document.querySelector('.customer-email-container');
|
||||||
const customerEmailCheckbox = document.querySelector('.customer-email-checkbox');
|
const customerEmailCheckbox = document.querySelector('.customer-email-checkbox');
|
||||||
const selectedTrigger = triggerSelect.value;
|
const selectedTrigger = triggerSelect.value;
|
||||||
if (triggersByType[selectedTrigger].canIncludeCustomerEmail) {
|
if (triggersByType[selectedTrigger].defaultEmail.canIncludeCustomerEmail) {
|
||||||
customerEmailContainer.style.display = 'block';
|
customerEmailContainer.style.display = 'block';
|
||||||
} else {
|
} else {
|
||||||
customerEmailContainer.style.display = 'none';
|
customerEmailContainer.style.display = 'none';
|
||||||
|
|||||||
@@ -34,7 +34,7 @@
|
|||||||
Go to email rules
|
Go to email rules
|
||||||
</a>
|
</a>
|
||||||
}
|
}
|
||||||
<button id="page-primary" type="submit" class="btn btn-primary" name="command" value="Save">Save</button>
|
<button id="page-primary" type="submit" class="btn btn-primary" name="command" value="Save" permission="@Model.ModifyPermission">Save</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<partial name="_StatusMessage" />
|
<partial name="_StatusMessage" />
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ namespace BTCPayServer
|
|||||||
// Uncomment this to see EF queries
|
// Uncomment this to see EF queries
|
||||||
//l.AddFilter("Microsoft.EntityFrameworkCore.Database.Command", LogLevel.Trace);
|
//l.AddFilter("Microsoft.EntityFrameworkCore.Database.Command", LogLevel.Trace);
|
||||||
l.AddFilter("Microsoft.EntityFrameworkCore.Migrations", LogLevel.Information);
|
l.AddFilter("Microsoft.EntityFrameworkCore.Migrations", LogLevel.Information);
|
||||||
|
l.AddFilter("BTCPayServer.Migrations", LogLevel.Information);
|
||||||
l.AddFilter("System.Net.Http.HttpClient", LogLevel.Critical);
|
l.AddFilter("System.Net.Http.HttpClient", LogLevel.Critical);
|
||||||
l.AddFilter("Microsoft.AspNetCore.Antiforgery.Internal", LogLevel.Critical);
|
l.AddFilter("Microsoft.AspNetCore.Antiforgery.Internal", LogLevel.Critical);
|
||||||
l.AddFilter("Fido2NetLib.DistributedCacheMetadataService", LogLevel.Error);
|
l.AddFilter("Fido2NetLib.DistributedCacheMetadataService", LogLevel.Error);
|
||||||
|
|||||||
@@ -47,11 +47,7 @@ namespace BTCPayServer.Services.Mails
|
|||||||
EmailSettings? GetCustomSettings(StoreData store)
|
EmailSettings? GetCustomSettings(StoreData store)
|
||||||
{
|
{
|
||||||
var emailSettings = store.GetStoreBlob().EmailSettings;
|
var emailSettings = store.GetStoreBlob().EmailSettings;
|
||||||
if (emailSettings?.IsComplete() is true)
|
return emailSettings?.IsComplete() is true ? emailSettings : null;
|
||||||
{
|
|
||||||
return emailSettings;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user