diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 00000000..cd967fc3
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,25 @@
+**/.dockerignore
+**/.env
+**/.git
+**/.gitignore
+**/.project
+**/.settings
+**/.toolstarget
+**/.vs
+**/.vscode
+**/.idea
+**/*.*proj.user
+**/*.dbmdl
+**/*.jfm
+**/azds.yaml
+**/bin
+**/charts
+**/docker-compose*
+**/Dockerfile*
+**/node_modules
+**/npm-debug.log
+**/obj
+**/secrets.dev.yaml
+**/values.dev.yaml
+LICENSE
+README.md
\ No newline at end of file
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 00000000..9591e75e
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,59 @@
+FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
+USER $APP_UID
+WORKDIR /app
+
+FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
+ARG BUILD_CONFIGURATION=Release
+ARG BUILD_VERSION=1.0.0.0
+WORKDIR /work
+COPY ["src/Papercut.Service/Papercut.Service.csproj", "/work/Papercut.Service/"]
+COPY ["src/Papercut.Common/Papercut.Common.csproj", "/work/Papercut.Common/"]
+COPY ["src/Papercut.Core/Papercut.Core.csproj", "/work/Papercut.Core/"]
+COPY ["src/Papercut.Infrastructure.IPComm/Papercut.Infrastructure.IPComm.csproj", "/work/Papercut.Infrastructure.IPComm/"]
+COPY ["src/Papercut.Infrastructure.Smtp/Papercut.Infrastructure.Smtp.csproj", "/work/Papercut.Infrastructure.Smtp/"]
+COPY ["src/Papercut.Message/Papercut.Message.csproj", "/work/Papercut.Message/"]
+COPY ["src/Papercut.Rules/Papercut.Rules.csproj", "/work/Papercut.Rules/"]
+RUN dotnet restore "/work/Papercut.Service/Papercut.Service.csproj"
+COPY . .
+
+#RUN sed "s/\(Assembly\(Informational\|File\)Version(\d34[0-9]\+\.[0-9]\+\.[0-9]\+\.\)[0-9]\+/\1$BUILD_VERSION/" src/GlobalAssemblyInfo.cs
+
+WORKDIR "/work/src/Papercut.Service"
+
+RUN dotnet build "Papercut.Service.csproj" -c $BUILD_CONFIGURATION -o /app/build
+
+FROM build AS publish
+ARG BUILD_CONFIGURATION=Release
+RUN dotnet publish "Papercut.Service.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false
+
+FROM base AS final
+
+ARG BUILD_VERSION
+ARG BUILD_DATE
+ARG VCS_REF
+
+LABEL org.opencontainers.image.title="Papercut SMTP Service" \
+ org.opencontainers.image.description="Papercut SMTP Service is a 2-in-1 quick email viewer AND built-in SMTP server" \
+ org.opencontainers.image.version=${BUILD_VERSION} \
+ org.opencontainers.image.url="https://www.papercut-smtp.com/" \
+ org.opencontainers.image.source="https://github.com/ChangemakerStudios/Papercut-SMTP" \
+ org.opencontainers.image.created=${BUILD_DATE} \
+ org.opencontainers.image.revision=${VCS_REF} \
+ org.opencontainers.image.licenses="Apache License, Version 2.0"
+
+WORKDIR /app
+
+COPY --from=publish /app/publish .
+
+ENV ASPNETCORE_HTTP_PORTS=80
+
+# HTTP
+EXPOSE 80
+
+# SMTP
+EXPOSE 25
+
+# optional -- should only be used locally: IPComm
+# EXPOSE 37403
+
+CMD ["dotnet", "Papercut.Service.dll"]
diff --git a/Papercut.sln b/Papercut.sln
index 5fb21731..4f709f21 100644
--- a/Papercut.sln
+++ b/Papercut.sln
@@ -34,6 +34,11 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".Build", ".Build", "{8661B3
GitVersion.yml = GitVersion.yml
build\ReleaseNotes.cake = build\ReleaseNotes.cake
ReleaseNotes.md = ReleaseNotes.md
+ .dockerignore = .dockerignore
+ Dockerfile = Dockerfile
+ README.md = README.md
+ build-docker.sh = build-docker.sh
+ build-docker.ps1 = build-docker.ps1
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Papercut.Infrastructure.Smtp", "src\Papercut.Infrastructure.Smtp\Papercut.Infrastructure.Smtp.csproj", "{873EC485-8E94-4877-8EA7-A7DFE7612E0A}"
diff --git a/README.md b/README.md
index 946f76e8..a0d4b63d 100644
--- a/README.md
+++ b/README.md
@@ -4,16 +4,16 @@
[![Build status](https://ci.appveyor.com/api/projects/status/bs2asxoafdwbkcxa?svg=true)](https://ci.appveyor.com/project/Jaben/papercut-smtp)
## The problem
-If you ever send emails from an application or web site during development, you're familiar with the fear of an email being released into the wild. Are you positive none of the 'test' emails are addressed to colleagues or worse, customers? Of course, you can set up and maintain a test email server for development -- but that's a chore. Plus, the delay when you are waiting to view new test emails can radically slow your development cycle.
+If you ever send emails from an application or website during development, you're familiar with the fear of an email being released into the wild. Are you positive none of the 'test' emails are addressed to colleagues or worse, customers? Of course, you can set up and maintain a test email server for development -- but that's a chore. Plus, the delay when waiting to view new test emails can radically slow your development cycle.
## Papercut SMTP to the rescue!
-Papercut SMTP is a 2-in-1 quick email viewer AND built-in SMTP server (designed to receive messages only). Papercut SMTP doesn't enforce any restrictions how you prepare your email, but it allows you to view the whole email-chilada: body, html, headers, attachment down right down to the naughty raw encoded bits. Papercut can be configured to run on startup and sit quietly (minimized in the tray) only providing a notification when a new message has arrived.
+Papercut SMTP is a 2-in-1 quick email viewer AND built-in SMTP server (designed to receive messages only). Papercut SMTP doesn't enforce any restrictions on how you prepare your email, but it allows you to view the whole email-chilada: body, HTML, headers, and attachment right down to the naughty raw encoded bits. Papercut can be configured to run on startup and sit quietly (minimized in the tray) only providing a notification when a new message has arrived.
## Download Now
### [Download the Papercut.Setup.exe installer](https://github.com/ChangemakerStudios/Papercut-SMTP/releases)
## Requirements
-Papercut SMTP UI Requires the "WebView2" Microsoft shared system component be installed on your system. If you have any problems getting it running go to this site:
+Papercut SMTP UI Requires the "WebView2" Microsoft shared system component to be installed on your system. If you have any problems getting it running go to this site:
[WebView2 Download](https://developer.microsoft.com/en-us/microsoft-edge/webview2) and install it.
## Features
@@ -28,9 +28,26 @@ Papercut SMTP UI Requires the "WebView2" Microsoft shared system component be in
#### Logging View
![Logging View](https://changemakerstudios.us/content/images/2020/07/Papercut-Log.png)
-## Papercut SMTP Background Service
-Papercut SMTP has an optional "always on" service to receive emails even when the client is not running. It's installed by default with [Papercut.Setup.exe](https://github.com/ChangemakerStudios/Papercut/releases).
+## Papercut SMTP Service
+Papercut SMTP has an optional HTTP server to receive emails even when the client is not running. It's installed by default with [Papercut.Setup.exe](https://github.com/ChangemakerStudios/Papercut/releases).
Alternatively, it can be run in an almost portable way by downloading [Papercut.Service.zip](https://github.com/ChangemakerStudios/Papercut/releases), unzipping and [following the service installation instructions](https://github.com/ChangemakerStudios/Papercut/tree/develop/src/Papercut.Service).
+### Host in Docker
+
+Optionally you can run Papercut SMTP Service in docker: [Papercut SMTP Service in Docker](https://hub.docker.com/r/changemakerstudiosus/papercut-smtp)
+
+#### Pull Image:
+
+```powershell
+> docker pull changemakerstudiosus/papercut-smtp:latest
+```
+
+#### Run Papercut STMP Server Locally in Docker (HTTP Port :8080 and STMP port 25)
+```powershell
+docker run -d -p 8080:80 -p 25:25 changemakerstudiosus/papercut-smtp:latest
+```
+
+The Papercut-SMTP Server Site will be accessible at http://localhost:8080.
+
## License
Papercut SMTP is Licensed under the [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0).
diff --git a/ReleaseNotes.md b/ReleaseNotes.md
index eadb8b14..83e89b99 100644
--- a/ReleaseNotes.md
+++ b/ReleaseNotes.md
@@ -1,6 +1,6 @@
# Release Notes
-## Papercut SMTP v7.0.0 [2024-05-17]
+## Papercut SMTP v7.0.0 [2024-10-14]
_NOTE: Uninstall any existing Papercut SMTP installations BEFORE installing this new version._
@@ -8,6 +8,8 @@ _NOTE: Uninstall any existing Papercut SMTP installations BEFORE installing this
- Upgraded to latest dependencies (Caliburn Micro, Autofac, MahApps) and associated systems to support .NET 8.
- Switched to [Velopack](https://github.com/velopack/velopack) auto-upgradable installation system. Great project! (Thanks, [caesay](https://github.com/caesay)!)
- Fix for log updating constantly causing unnecessary WebView2 loading. (PR thanks to [arthurzaczek](https://github.com/arthurzaczek))
+- Fix for double request when clicking on links in emails. Issue #232.
+- Added InvokeProcess Rule (#274)
## Papercut SMTP v6.2.0 [2022-04-24]
diff --git a/build-docker.ps1 b/build-docker.ps1
new file mode 100644
index 00000000..e427ba3b
--- /dev/null
+++ b/build-docker.ps1
@@ -0,0 +1,17 @@
+param (
+ [string]$BuildVersion
+)
+
+if (-not $BuildVersion) {
+ Write-Host "You must specify a build version. E.g.: 7.0.1"
+ exit 1
+}
+
+$BuildDate = (Get-Date).ToString("yyyy-MM-ddTHH:mm:ssZ")
+$VcsRef = (git rev-parse --short HEAD)
+
+docker build -t "changemakerstudiosus/papercut-smtp:$BuildVersion" . `
+ --build-arg BUILD_VERSION=$BuildVersion `
+ --build-arg BUILD_DATE=$BuildDate `
+ --build-arg VCS_REF=$VcsRef `
+ --no-cache
\ No newline at end of file
diff --git a/build-docker.sh b/build-docker.sh
new file mode 100644
index 00000000..f929709f
--- /dev/null
+++ b/build-docker.sh
@@ -0,0 +1,14 @@
+BuildVersion=$1
+if [ -z "$BuildVersion" ]; then
+ echo "You must specify a build version. E.g.: 7.0.1"
+ exit 1
+fi
+
+BuildDate=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
+VcsRef=$(git rev-parse --short HEAD)
+
+docker build -t changemakerstudiosus/papercut-smtp:"$BuildVersion" . \
+ --build-arg BUILD_VERSION="$BuildVersion" \
+ --build-arg BUILD_DATE="$BuildDate" \
+ --build-arg VCS_REF="$VcsRef" \
+ --no-cache
\ No newline at end of file
diff --git a/build.ps1 b/build.ps1
index 0738f701..06add0bd 100644
--- a/build.ps1
+++ b/build.ps1
@@ -1,3 +1,3 @@
dotnet tool install --global Cake.Tool --version 3.2.0
-dotnet tool install --global vpk --version 0.0.359
+dotnet tool install --global vpk --version 0.0.626
dotnet-cake --configuration=Release
\ No newline at end of file
diff --git a/src/GlobalAssemblyInfo.cs b/src/GlobalAssemblyInfo.cs
index ca84b9d4..4e4c04d2 100644
--- a/src/GlobalAssemblyInfo.cs
+++ b/src/GlobalAssemblyInfo.cs
@@ -5,6 +5,6 @@
//------------------------------------------------------------------------------
using System.Reflection;
-[assembly: AssemblyVersion("7.0.0.0")]
-[assembly: AssemblyFileVersion("7.0.0.0")]
-[assembly: AssemblyInformationalVersion("7.0.0.0")]
\ No newline at end of file
+[assembly: AssemblyVersion("7.1.0.0")]
+[assembly: AssemblyFileVersion("7.1.0.0")]
+[assembly: AssemblyInformationalVersion("7.1.0-dev.111+Branch.develop.Sha.c6c2a6557ac3b3a8d1ebd93f45de2ff515018f18")]
\ No newline at end of file
diff --git a/src/Papercut.Common/Papercut.Common.csproj b/src/Papercut.Common/Papercut.Common.csproj
index 38a0f8f2..832d475a 100644
--- a/src/Papercut.Common/Papercut.Common.csproj
+++ b/src/Papercut.Common/Papercut.Common.csproj
@@ -5,7 +5,12 @@
-
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Papercut.Core/AppConstants.cs b/src/Papercut.Core/AppConstants.cs
index 1a2c8efc..bcf2d9c4 100644
--- a/src/Papercut.Core/AppConstants.cs
+++ b/src/Papercut.Core/AppConstants.cs
@@ -24,6 +24,8 @@ public static class AppConstants
public const string CompanyName = "Changemaker Studios";
+ public const string UpgradeUrl = "https://github.com/ChangemakerStudios/Papercut-SMTP";
+
public static string AppDataDirectory { get; } =
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData),
CompanyName,
diff --git a/src/Papercut.Core/Domain/Application/ApplicationMeta.cs b/src/Papercut.Core/Domain/Application/ApplicationMeta.cs
index 63e7f590..8ece1bf8 100644
--- a/src/Papercut.Core/Domain/Application/ApplicationMeta.cs
+++ b/src/Papercut.Core/Domain/Application/ApplicationMeta.cs
@@ -20,16 +20,10 @@
namespace Papercut.Core.Domain.Application
{
- public class ApplicationMeta : IAppMeta
+ public class ApplicationMeta(string appName, string? appVersion = null) : IAppMeta
{
- public ApplicationMeta(string appName, string? appVersion = null)
- {
- this.AppName = appName;
- this.AppVersion = appVersion ?? Assembly.GetCallingAssembly().GetName().Version.ToString(3);
- }
+ public string AppName { get; } = appName;
- public string AppName { get; }
-
- public string AppVersion { get; }
+ public string AppVersion { get; } = appVersion ?? Assembly.GetCallingAssembly().GetName().Version?.ToString(3) ?? "1.0.0.0";
}
}
diff --git a/src/Papercut.Core/Infrastructure/Container/RegisterMethodExtensions.cs b/src/Papercut.Core/Infrastructure/Container/RegisterMethodExtensions.cs
index a4975db2..d1499c6f 100644
--- a/src/Papercut.Core/Infrastructure/Container/RegisterMethodExtensions.cs
+++ b/src/Papercut.Core/Infrastructure/Container/RegisterMethodExtensions.cs
@@ -59,7 +59,7 @@ public static IReadOnlyList> GetStaticRegisterMethods(t
z => new Action(
c =>
{
- Log.Debug(
+ Log.Verbose(
"Invoking Registration Method {MethodType} {MethodName}",
z.DeclaringType?.ToString(),
z.ToString());
diff --git a/src/Papercut.Core/Infrastructure/Logging/LoggerExtensions.cs b/src/Papercut.Core/Infrastructure/Logging/LoggerExtensions.cs
new file mode 100644
index 00000000..dd749ae7
--- /dev/null
+++ b/src/Papercut.Core/Infrastructure/Logging/LoggerExtensions.cs
@@ -0,0 +1,31 @@
+// Papercut
+//
+// Copyright © 2008 - 2012 Ken Robertson
+// Copyright © 2013 - 2024 Jaben Cargman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+
+namespace Papercut.Core.Infrastructure.Logging;
+
+public static class LoggerExtensions
+{
+ public static bool ErrorWithContext(this ILogger logger, Exception? exception, string message, params object?[] propertyValues)
+ {
+ if (logger == null) throw new ArgumentNullException(nameof(logger));
+
+ logger.Error(exception, message, propertyValues);
+
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/src/Papercut.Core/Papercut.Core.csproj b/src/Papercut.Core/Papercut.Core.csproj
index 7e39a1a3..d1c5a514 100644
--- a/src/Papercut.Core/Papercut.Core.csproj
+++ b/src/Papercut.Core/Papercut.Core.csproj
@@ -10,9 +10,9 @@
- 8.0.0
+ 8.1.1
-
+
5.0.0
@@ -20,12 +20,17 @@
13.0.3
-
-
-
-
+
+
+
+
-
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Papercut.Infrastructure.IPComm/Papercut.Infrastructure.IPComm.csproj b/src/Papercut.Infrastructure.IPComm/Papercut.Infrastructure.IPComm.csproj
index 8001a993..e7000dcd 100644
--- a/src/Papercut.Infrastructure.IPComm/Papercut.Infrastructure.IPComm.csproj
+++ b/src/Papercut.Infrastructure.IPComm/Papercut.Infrastructure.IPComm.csproj
@@ -13,11 +13,16 @@
- 8.0.0
+ 8.1.1
13.0.3
-
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Papercut.Infrastructure.Smtp/Papercut.Infrastructure.Smtp.csproj b/src/Papercut.Infrastructure.Smtp/Papercut.Infrastructure.Smtp.csproj
index f4bcfe47..47d3ebe4 100644
--- a/src/Papercut.Infrastructure.Smtp/Papercut.Infrastructure.Smtp.csproj
+++ b/src/Papercut.Infrastructure.Smtp/Papercut.Infrastructure.Smtp.csproj
@@ -14,4 +14,9 @@
+
+
+
+
+
diff --git a/src/Papercut.Message/Papercut.Message.csproj b/src/Papercut.Message/Papercut.Message.csproj
index 59bc6ff4..5a80c73a 100644
--- a/src/Papercut.Message/Papercut.Message.csproj
+++ b/src/Papercut.Message/Papercut.Message.csproj
@@ -13,15 +13,20 @@
- 8.0.0
+ 8.1.1
- 4.4.0
+ 4.8.0
- 4.4.0
+ 4.8.0
-
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Papercut.Rules/App/Conditional/ConditionalRuleExtensions.cs b/src/Papercut.Rules/App/Conditional/ConditionalRuleExtensions.cs
new file mode 100644
index 00000000..01606b0a
--- /dev/null
+++ b/src/Papercut.Rules/App/Conditional/ConditionalRuleExtensions.cs
@@ -0,0 +1,59 @@
+// Papercut
+//
+// Copyright © 2008 - 2012 Ken Robertson
+// Copyright © 2013 - 2024 Jaben Cargman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+
+using System.Text.RegularExpressions;
+
+using MimeKit;
+using Papercut.Common.Helper;
+using Papercut.Rules.Domain.Conditional;
+
+namespace Papercut.Rules.App.Conditional;
+
+public static class ConditionalRuleExtensions
+{
+ public static bool IsConditionalForwardRuleMatch(this IConditionalRule rule, MimeMessage mimeMessage)
+ {
+ if (rule == null) throw new ArgumentNullException(nameof(rule));
+ if (mimeMessage == null) throw new ArgumentNullException(nameof(mimeMessage));
+
+ if (rule.RegexHeaderMatch.IsSet())
+ {
+ var allHeaders = string.Join("\r\n", mimeMessage.Headers.Select(h => h.ToString()));
+
+ if (!IsMatch(rule.RegexHeaderMatch, allHeaders))
+ return false;
+ }
+
+ if (rule.RegexBodyMatch.IsSet())
+ {
+ var bodyText = string.Join("\r\n",
+ mimeMessage.BodyParts.OfType().Where(s => !s.IsAttachment));
+
+ if (!IsMatch(rule.RegexBodyMatch, bodyText))
+ return false;
+ }
+
+ return true;
+ }
+
+ private static bool IsMatch(string match, string searchText)
+ {
+ var regex = new Regex(match, RegexOptions.IgnoreCase);
+ return regex.IsMatch(searchText);
+ }
+}
\ No newline at end of file
diff --git a/src/Papercut.Rules/App/Conditional/Forwarding/ConditionalForwardRuleDispatch.cs b/src/Papercut.Rules/App/Conditional/Forwarding/ConditionalForwardRuleDispatch.cs
new file mode 100644
index 00000000..b1fcd9b0
--- /dev/null
+++ b/src/Papercut.Rules/App/Conditional/Forwarding/ConditionalForwardRuleDispatch.cs
@@ -0,0 +1,57 @@
+// Papercut
+//
+// Copyright © 2008 - 2012 Ken Robertson
+// Copyright © 2013 - 2024 Jaben Cargman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+
+using Autofac;
+
+using MimeKit;
+using Papercut.Core.Domain.Rules;
+using Papercut.Message;
+using Papercut.Rules.App.Relaying;
+using Papercut.Rules.Domain.Conditional.Forwarding;
+
+namespace Papercut.Rules.App.Conditional.Forwarding;
+
+public class ConditionalForwardRuleDispatch : BaseRelayRuleDispatch
+{
+ public ConditionalForwardRuleDispatch(Lazy mimeMessageLoader, ILogger logger)
+ : base(mimeMessageLoader, logger)
+ {
+ }
+
+ protected override bool RuleMatches(ConditionalForwardRule rule, MimeMessage mimeMessage)
+ {
+ return rule.IsConditionalForwardRuleMatch(mimeMessage);
+ }
+
+ #region Begin Static Container Registrations
+
+ ///
+ /// Called dynamically from the RegisterStaticMethods() call in the container module.
+ ///
+ ///
+ [UsedImplicitly]
+ static void Register(ContainerBuilder builder)
+ {
+ if (builder == null) throw new ArgumentNullException(nameof(builder));
+
+ builder.RegisterType()
+ .As>().InstancePerDependency();
+ }
+
+ #endregion
+}
\ No newline at end of file
diff --git a/src/Papercut.Rules/App/Conditional/Forwarding/ConditionalForwardWithRetryRuleDispatch.cs b/src/Papercut.Rules/App/Conditional/Forwarding/ConditionalForwardWithRetryRuleDispatch.cs
new file mode 100644
index 00000000..1f0a0ce5
--- /dev/null
+++ b/src/Papercut.Rules/App/Conditional/Forwarding/ConditionalForwardWithRetryRuleDispatch.cs
@@ -0,0 +1,106 @@
+// Papercut
+//
+// Copyright © 2008 - 2012 Ken Robertson
+// Copyright © 2013 - 2024 Jaben Cargman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+
+using Autofac;
+
+using MimeKit;
+using Papercut.Core.Domain.Message;
+using Papercut.Core.Domain.Rules;
+using Papercut.Message;
+using Papercut.Message.Helpers;
+using Papercut.Rules.App.Relaying;
+using Papercut.Rules.Domain.Conditional.Forwarding;
+
+using Polly;
+
+namespace Papercut.Rules.App.Conditional.Forwarding;
+
+public class ConditionalForwardWithRetryRuleDispatch : IRuleDispatcher
+{
+ private readonly ILogger _logger;
+
+ private readonly Lazy _mimeMessageLoader;
+
+ public ConditionalForwardWithRetryRuleDispatch(Lazy mimeMessageLoader, ILogger logger)
+ {
+ _mimeMessageLoader = mimeMessageLoader;
+ _logger = logger;
+ }
+
+ public async Task DispatchAsync(ConditionalForwardWithRetryRule rule, MessageEntry messageEntry, CancellationToken token)
+ {
+ if (rule == null) throw new ArgumentNullException(nameof(rule));
+ if (messageEntry == null) throw new ArgumentNullException(nameof(messageEntry));
+
+ var message = await _mimeMessageLoader.Value.GetClonedAsync(messageEntry, token);
+
+ if (!RuleMatches(rule, message))
+ {
+ return;
+ }
+
+ rule.PopulateFromRule(message);
+
+ var polly = Policy
+ .Handle()
+ .WaitAndRetryAsync(
+ rule.RetryAttempts,
+ (attempt) => TimeSpan.FromSeconds(rule.RetryAttemptDelaySeconds),
+ (exception, span) =>
+ {
+ _logger.Error(
+ exception,
+ "Failed to send {@MessageEntry} after {RetryAttempts}",
+ messageEntry,
+ rule.RetryAttempts);
+ });
+
+ async Task SendMessage()
+ {
+ using (var client = await rule.CreateConnectedSmtpClientAsync(token))
+ {
+ await client.SendAsync(message, token);
+ await client.DisconnectAsync(true, token);
+ }
+ }
+
+ await polly.ExecuteAsync(async () => await SendMessage());
+ }
+
+ protected virtual bool RuleMatches(ConditionalForwardWithRetryRule rule, MimeMessage mimeMessage)
+ {
+ return rule.IsConditionalForwardRuleMatch(mimeMessage);
+ }
+
+ #region Begin Static Container Registrations
+
+ ///
+ /// Called dynamically from the RegisterStaticMethods() call in the container module.
+ ///
+ ///
+ [UsedImplicitly]
+ static void Register(ContainerBuilder builder)
+ {
+ if (builder == null) throw new ArgumentNullException(nameof(builder));
+
+ builder.RegisterType()
+ .As>().AsSelf().InstancePerDependency();
+ }
+
+ #endregion
+}
\ No newline at end of file
diff --git a/src/Papercut.Rules/App/Conditional/Relaying/ConditionalRelayRuleDispatch.cs b/src/Papercut.Rules/App/Conditional/Relaying/ConditionalRelayRuleDispatch.cs
new file mode 100644
index 00000000..ac9dd272
--- /dev/null
+++ b/src/Papercut.Rules/App/Conditional/Relaying/ConditionalRelayRuleDispatch.cs
@@ -0,0 +1,57 @@
+// Papercut
+//
+// Copyright © 2008 - 2012 Ken Robertson
+// Copyright © 2013 - 2024 Jaben Cargman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+
+using Autofac;
+
+using MimeKit;
+using Papercut.Core.Domain.Rules;
+using Papercut.Message;
+using Papercut.Rules.App.Relaying;
+using Papercut.Rules.Domain.Conditional.Relaying;
+
+namespace Papercut.Rules.App.Conditional.Relaying;
+
+public class ConditionalRelayRuleDispatch : BaseRelayRuleDispatch
+{
+ public ConditionalRelayRuleDispatch(Lazy mimeMessageLoader, ILogger logger)
+ : base(mimeMessageLoader, logger)
+ {
+ }
+
+ protected override bool RuleMatches(ConditionalRelayRule rule, MimeMessage mimeMessage)
+ {
+ return rule.IsConditionalForwardRuleMatch(mimeMessage);
+ }
+
+ #region Begin Static Container Registrations
+
+ ///
+ /// Called dynamically from the RegisterStaticMethods() call in the container module.
+ ///
+ ///
+ [UsedImplicitly]
+ static void Register(ContainerBuilder builder)
+ {
+ if (builder == null) throw new ArgumentNullException(nameof(builder));
+
+ builder.RegisterType()
+ .As>().AsSelf().InstancePerDependency();
+ }
+
+ #endregion
+}
\ No newline at end of file
diff --git a/src/Papercut.Rules/App/Forwarding/ForwardRuleDispatch.cs b/src/Papercut.Rules/App/Forwarding/ForwardRuleDispatch.cs
new file mode 100644
index 00000000..f0c3b0ec
--- /dev/null
+++ b/src/Papercut.Rules/App/Forwarding/ForwardRuleDispatch.cs
@@ -0,0 +1,50 @@
+// Papercut
+//
+// Copyright © 2008 - 2012 Ken Robertson
+// Copyright © 2013 - 2024 Jaben Cargman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+
+using Autofac;
+using Papercut.Core.Domain.Rules;
+using Papercut.Message;
+using Papercut.Rules.App.Relaying;
+using Papercut.Rules.Domain.Forwarding;
+
+namespace Papercut.Rules.App.Forwarding;
+
+public class ForwardRuleDispatch : BaseRelayRuleDispatch
+{
+ public ForwardRuleDispatch(Lazy mimeMessageLoader, ILogger logger)
+ : base(mimeMessageLoader, logger)
+ {
+ }
+
+ #region Begin Static Container Registrations
+
+ ///
+ /// Called dynamically from the RegisterStaticMethods() call in the container module.
+ ///
+ ///
+ [UsedImplicitly]
+ static void Register(ContainerBuilder builder)
+ {
+ if (builder == null) throw new ArgumentNullException(nameof(builder));
+
+ builder.RegisterType()
+ .As>().AsSelf().InstancePerDependency();
+ }
+
+ #endregion
+}
\ No newline at end of file
diff --git a/src/Papercut.Rules/App/Invoking/InvokeProcessRuleDispatch.cs b/src/Papercut.Rules/App/Invoking/InvokeProcessRuleDispatch.cs
new file mode 100644
index 00000000..ab013520
--- /dev/null
+++ b/src/Papercut.Rules/App/Invoking/InvokeProcessRuleDispatch.cs
@@ -0,0 +1,110 @@
+// Papercut
+//
+// Copyright © 2008 - 2012 Ken Robertson
+// Copyright © 2013 - 2024 Jaben Cargman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System.Diagnostics;
+
+using Autofac;
+
+using Papercut.Core.Domain.Message;
+using Papercut.Core.Domain.Rules;
+using Papercut.Core.Infrastructure.Logging;
+using Papercut.Rules.Domain.Invoking;
+
+using Serilog.Context;
+
+namespace Papercut.Rules.App.Invoking;
+
+public class InvokeProcessRuleDispatch(ILogger logger) : IRuleDispatcher
+{
+ public async Task DispatchAsync(InvokeProcessRule rule, MessageEntry messageEntry, CancellationToken token)
+ {
+ if (string.IsNullOrWhiteSpace(rule.ProcessToRun))
+ {
+ logger.Warning("Invoke Process Rule 'Process to Run' is not set -- nothing done");
+
+ return;
+ }
+
+ try
+ {
+ var arguments =
+ (rule.ProcessCommandLine ?? string.Empty).Replace("%e", messageEntry.File);
+
+ using var _01 = LogContext.PushProperty("Arguments", arguments);
+ using var _02 = LogContext.PushProperty("ProcessToRun", rule.ProcessToRun);
+
+ using var process = new Process();
+
+ process.StartInfo.FileName = rule.ProcessToRun;
+ process.StartInfo.Arguments = arguments;
+ process.StartInfo.RedirectStandardOutput = true;
+ process.StartInfo.RedirectStandardError = true;
+ process.StartInfo.UseShellExecute = false;
+ process.StartInfo.CreateNoWindow = true;
+
+ if (!process.Start())
+ {
+ logger.Error("Process {ProcessToRun} Failed to Start", rule.ProcessToRun);
+
+ return;
+ }
+
+ await process.WaitForExitAsync(token);
+
+ string output = await process.StandardOutput.ReadToEndAsync(token);
+ string error = await process.StandardError.ReadToEndAsync(token);
+
+ if (process.ExitCode != 0)
+ {
+ logger.Warning(
+ "Process {ProcessToRun} Process failed with exit code {ExitCode}: {Error}",
+ rule.ProcessToRun, process.ExitCode, error);
+ }
+
+ if (!string.IsNullOrWhiteSpace(output))
+ {
+ logger.Information(
+ "Process {ProcessToRun} Ran Successfully. Output {ProcessOutput}",
+ rule.ProcessToRun, output);
+ }
+ }
+ catch (OperationCanceledException)
+ {
+ // ignore
+ }
+ catch (Exception ex) when (logger.ErrorWithContext(ex, "Invoke Process Rule Failed to Start Process"))
+ {
+ }
+ }
+
+ #region Begin Static Container Registrations
+
+ ///
+ /// Called dynamically from the RegisterStaticMethods() call in the container module.
+ ///
+ ///
+ [UsedImplicitly]
+ static void Register(ContainerBuilder builder)
+ {
+ if (builder == null) throw new ArgumentNullException(nameof(builder));
+
+ builder.RegisterType()
+ .As>().AsSelf().InstancePerDependency();
+ }
+
+ #endregion
+}
\ No newline at end of file
diff --git a/src/Papercut.Rules/App/Relaying/BaseRelayRuleDispatch.cs b/src/Papercut.Rules/App/Relaying/BaseRelayRuleDispatch.cs
new file mode 100644
index 00000000..de5b8867
--- /dev/null
+++ b/src/Papercut.Rules/App/Relaying/BaseRelayRuleDispatch.cs
@@ -0,0 +1,75 @@
+// Papercut
+//
+// Copyright © 2008 - 2012 Ken Robertson
+// Copyright © 2013 - 2024 Jaben Cargman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+
+using MimeKit;
+using Papercut.Core.Domain.Message;
+using Papercut.Core.Domain.Rules;
+using Papercut.Message;
+using Papercut.Message.Helpers;
+using Papercut.Rules.Domain.Relaying;
+
+namespace Papercut.Rules.App.Relaying;
+
+public abstract class BaseRelayRuleDispatch : IRuleDispatcher
+ where T : RelayRule
+{
+ private readonly Lazy _mimeMessageLoader;
+
+ protected BaseRelayRuleDispatch(Lazy mimeMessageLoader, ILogger logger)
+ {
+ Logger = logger;
+ _mimeMessageLoader = mimeMessageLoader;
+ }
+
+ protected ILogger Logger { get; }
+
+ public virtual async Task DispatchAsync(T rule, MessageEntry messageEntry, CancellationToken token = default)
+ {
+ if (rule == null) throw new ArgumentNullException(nameof(rule));
+ if (messageEntry == null) throw new ArgumentNullException(nameof(messageEntry));
+
+ var mimeMessage = await _mimeMessageLoader.Value.GetClonedAsync(messageEntry, token);
+
+ if (!RuleMatches(rule, mimeMessage)) return;
+
+ try
+ {
+ using var client = await rule.CreateConnectedSmtpClientAsync(token);
+
+ rule.PopulateFromRule(mimeMessage);
+
+ await client.SendAsync(mimeMessage, token);
+ await client.DisconnectAsync(true, token);
+ }
+ catch (Exception ex)
+ {
+ HandleSendFailure(rule, messageEntry, ex);
+ }
+ }
+
+ protected virtual bool RuleMatches(T rule, MimeMessage mimeMessage)
+ {
+ return true;
+ }
+
+ protected virtual void HandleSendFailure(T rule, MessageEntry messageEntry, Exception exception)
+ {
+ // log failure
+ Logger.Error(exception, "Failure sending {@MessageEntry} for rule {@Rule}", messageEntry, rule);
+ }
+}
\ No newline at end of file
diff --git a/src/Papercut.Rules/App/Relaying/RelayRuleDispatch.cs b/src/Papercut.Rules/App/Relaying/RelayRuleDispatch.cs
new file mode 100644
index 00000000..f9d34a6f
--- /dev/null
+++ b/src/Papercut.Rules/App/Relaying/RelayRuleDispatch.cs
@@ -0,0 +1,50 @@
+// Papercut
+//
+// Copyright © 2008 - 2012 Ken Robertson
+// Copyright © 2013 - 2024 Jaben Cargman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+
+using Autofac;
+using Papercut.Core.Domain.Rules;
+using Papercut.Message;
+using Papercut.Rules.Domain.Relaying;
+
+namespace Papercut.Rules.App.Relaying;
+
+[UsedImplicitly]
+public class RelayRuleDispatch : BaseRelayRuleDispatch
+{
+ public RelayRuleDispatch(Lazy mimeMessageLoader, ILogger logger)
+ : base(mimeMessageLoader, logger)
+ {
+ }
+
+ #region Begin Static Container Registrations
+
+ ///
+ /// Called dynamically from the RegisterStaticMethods() call in the container module.
+ ///
+ ///
+ [UsedImplicitly]
+ static void Register(ContainerBuilder builder)
+ {
+ if (builder == null) throw new ArgumentNullException(nameof(builder));
+
+ builder.RegisterType()
+ .As>().AsSelf().InstancePerDependency();
+ }
+
+ #endregion
+}
\ No newline at end of file
diff --git a/src/Papercut.Rules/App/Relaying/RelayRuleExtensions.cs b/src/Papercut.Rules/App/Relaying/RelayRuleExtensions.cs
new file mode 100644
index 00000000..eaab918e
--- /dev/null
+++ b/src/Papercut.Rules/App/Relaying/RelayRuleExtensions.cs
@@ -0,0 +1,62 @@
+// Papercut
+//
+// Copyright © 2008 - 2012 Ken Robertson
+// Copyright © 2013 - 2024 Jaben Cargman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+
+using MailKit.Net.Smtp;
+using MailKit.Security;
+using Papercut.Rules.Domain.Relaying;
+
+namespace Papercut.Rules.App.Relaying;
+
+public static class RelayRuleExtensions
+{
+ public static void PopulateServerFromUri(this RelayRule rule, string smtpServer)
+ {
+ if (rule == null) throw new ArgumentNullException(nameof(rule));
+
+ var uri = new Uri("smtp://" + smtpServer);
+
+ rule.SmtpServer = uri.DnsSafeHost;
+ rule.SmtpPort = uri.IsDefaultPort ? 25 : uri.Port;
+ }
+
+ public static async Task CreateConnectedSmtpClientAsync(this RelayRule forwardRule, CancellationToken token)
+ {
+ if (forwardRule == null) throw new ArgumentNullException(nameof(forwardRule));
+
+ var client = new SmtpClient();
+
+ await client.ConnectAsync(
+ forwardRule.SmtpServer,
+ forwardRule.SmtpPort,
+ forwardRule.SmtpUseSSL ? SecureSocketOptions.Auto : SecureSocketOptions.None,
+ token);
+
+ // Note: since we don't have an OAuth2 token, disable
+ // the XOAUTH2 authentication mechanism.
+ client.AuthenticationMechanisms.Remove("XOAUTH2");
+
+ if (!string.IsNullOrWhiteSpace(forwardRule.SmtpPassword)
+ && !string.IsNullOrWhiteSpace(forwardRule.SmtpUsername))
+ {
+ // Note: only needed if the SMTP server requires authentication
+ await client.AuthenticateAsync(forwardRule.SmtpUsername, forwardRule.SmtpPassword, token);
+ }
+
+ return client;
+ }
+}
\ No newline at end of file
diff --git a/src/Papercut.Rules/App/RuleServiceBase.cs b/src/Papercut.Rules/App/RuleServiceBase.cs
index 47f7e46c..fd35ec7a 100644
--- a/src/Papercut.Rules/App/RuleServiceBase.cs
+++ b/src/Papercut.Rules/App/RuleServiceBase.cs
@@ -27,69 +27,68 @@
using Papercut.Core.Domain.Rules;
using Papercut.Rules.Domain.Rules;
-namespace Papercut.Rules.App
+namespace Papercut.Rules.App;
+
+public class RuleServiceBase : Disposable
{
- public class RuleServiceBase : Disposable
- {
- protected readonly ILogger _logger;
+ protected readonly ILogger _logger;
- protected readonly IRuleRepository _ruleRepository;
+ protected readonly IRuleRepository _ruleRepository;
- readonly Lazy> _rules;
+ readonly Lazy> _rules;
- protected RuleServiceBase(IRuleRepository ruleRepository, ILogger logger)
- {
- this._ruleRepository = ruleRepository;
- this._logger = logger;
- this.RuleFileName = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "rules.json");
- this._rules = new Lazy>(this.GetRulesCollection);
- }
+ protected RuleServiceBase(IRuleRepository ruleRepository, ILogger logger)
+ {
+ this._ruleRepository = ruleRepository;
+ this._logger = logger;
+ this.RuleFileName = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "rules.json");
+ this._rules = new Lazy>(this.GetRulesCollection);
+ }
+
+ public string RuleFileName { get; set; }
- public string RuleFileName { get; set; }
+ public ObservableCollection Rules => this._rules.Value;
- public ObservableCollection Rules => this._rules.Value;
+ public IObservable> GetRuleChangedObservable(IScheduler? scheduler = null)
+ {
+ return Observable
+ .FromEventPattern(
+ h => new NotifyCollectionChangedEventHandler(h),
+ h => this._rules.Value.CollectionChanged += h,
+ h => this._rules.Value.CollectionChanged -= h,
+ scheduler ?? Scheduler.Default);
+ }
- public IObservable> GetRuleChangedObservable(IScheduler? scheduler = null)
+ protected virtual ObservableCollection GetRulesCollection()
+ {
+ IList? loadRules = null;
+
+ try
{
- return Observable
- .FromEventPattern(
- h => new NotifyCollectionChangedEventHandler(h),
- h => this._rules.Value.CollectionChanged += h,
- h => this._rules.Value.CollectionChanged -= h,
- scheduler ?? Scheduler.Default);
+ loadRules = this._ruleRepository.LoadRules(this.RuleFileName);
}
-
- protected virtual ObservableCollection GetRulesCollection()
+ catch (Exception ex)
{
- IList? loadRules = null;
-
- try
- {
- loadRules = this._ruleRepository.LoadRules(this.RuleFileName);
- }
- catch (Exception ex)
- {
- this._logger.Warning(ex, "Failed to load rules in file {RuleFileName}", this.RuleFileName);
- }
-
- return new ObservableCollection(loadRules ?? new List(0));
+ this._logger.Warning(ex, "Failed to load rules in file {RuleFileName}", this.RuleFileName);
}
- public void Save()
+ return new ObservableCollection(loadRules ?? new List(0));
+ }
+
+ public void Save()
+ {
+ try
+ {
+ this._ruleRepository.SaveRules(this.Rules, this.RuleFileName);
+ this._logger.Information(
+ "Saved {RuleCount} to {RuleFileName}",
+ this.Rules.Count,
+ this.RuleFileName);
+ }
+ catch (Exception ex)
{
- try
- {
- this._ruleRepository.SaveRules(this.Rules, this.RuleFileName);
- this._logger.Information(
- "Saved {RuleCount} to {RuleFileName}",
- this.Rules.Count,
- this.RuleFileName);
- }
- catch (Exception ex)
- {
- this._logger.Error(ex, "Error saving rules to file {RuleFileName}", this.RuleFileName);
- }
+ this._logger.Error(ex, "Error saving rules to file {RuleFileName}", this.RuleFileName);
}
}
}
\ No newline at end of file
diff --git a/src/Papercut.Rules/App/RulesRunner.cs b/src/Papercut.Rules/App/RulesRunner.cs
index 28f4f0db..40f4f7d9 100644
--- a/src/Papercut.Rules/App/RulesRunner.cs
+++ b/src/Papercut.Rules/App/RulesRunner.cs
@@ -17,90 +17,99 @@
using System.Reflection;
-
using Autofac;
using Papercut.Core.Domain.Message;
using Papercut.Core.Domain.Rules;
-namespace Papercut.Rules.App
+namespace Papercut.Rules.App;
+
+public class RulesRunner : IRulesRunner
{
- public class RulesRunner : IRulesRunner
- {
- readonly MethodInfo _dispatchRuleMethod;
+ readonly MethodInfo _dispatchRuleMethod;
- readonly ILifetimeScope _lifetimeScope;
+ readonly ILifetimeScope _lifetimeScope;
- readonly ILogger _logger;
+ readonly ILogger _logger;
- public RulesRunner(ILifetimeScope lifetimeScope, ILogger logger)
+ public RulesRunner(ILifetimeScope lifetimeScope, ILogger logger)
+ {
+ this._lifetimeScope = lifetimeScope;
+ this._logger = logger;
+ var dispatchRuleMethod = this.GetType()
+ .GetMethod(
+ nameof(this.DispatchRuleAsync),
+ BindingFlags.NonPublic | BindingFlags.Instance);
+
+ if (dispatchRuleMethod == null)
{
- this._lifetimeScope = lifetimeScope;
- this._logger = logger;
- this._dispatchRuleMethod = this.GetType()
- .GetMethod(
- nameof(this.DispatchRuleAsync),
- BindingFlags.NonPublic | BindingFlags.Instance);
+ throw new ArgumentNullException(nameof(dispatchRuleMethod), "Dispatch rule method is null");
}
- public async Task RunAsync(IRule[] rules, MessageEntry messageEntry, CancellationToken token)
+ this._dispatchRuleMethod = dispatchRuleMethod;
+ }
+
+ public async Task RunAsync(IRule[] rules, MessageEntry messageEntry, CancellationToken token)
+ {
+ if (rules == null) throw new ArgumentNullException(nameof(rules));
+ if (messageEntry == null) throw new ArgumentNullException(nameof(messageEntry));
+
+ var ruleTasks = new List();
+
+ foreach (IRule rule in rules.Where(r => r.IsEnabled))
{
- if (rules == null) throw new ArgumentNullException(nameof(rules));
- if (messageEntry == null) throw new ArgumentNullException(nameof(messageEntry));
+ token.ThrowIfCancellationRequested();
- var ruleTasks = new List();
+ var invoke = this._dispatchRuleMethod.MakeGenericMethod(rule.GetType()).Invoke(
+ this,
+ [rule, messageEntry, token]);
- foreach (IRule rule in rules.Where(_ => _.IsEnabled))
+ if (invoke is Task invokeTask)
{
- token.ThrowIfCancellationRequested();
-
- ruleTasks.Add(
- (Task)this._dispatchRuleMethod.MakeGenericMethod(rule.GetType()).Invoke(
- this,
- new object[] { rule, messageEntry, token }));
+ ruleTasks.Add(invokeTask);
}
-
- await Task.WhenAll(ruleTasks);
}
- [UsedImplicitly]
- async Task DispatchRuleAsync(TRule rule, MessageEntry messageEntry, CancellationToken token)
- where TRule : IRule
+ await Task.WhenAll(ruleTasks).WaitAsync(token);
+ }
+
+ [UsedImplicitly]
+ async Task DispatchRuleAsync(TRule rule, MessageEntry messageEntry, CancellationToken token)
+ where TRule : IRule
+ {
+ this._logger.Information(
+ "Running Rule Dispatch for Rule {Rule} on Message {@MessageEntry}",
+ rule,
+ messageEntry);
+
+ try
+ {
+ var ruleDispatcher = this._lifetimeScope.Resolve>();
+ await ruleDispatcher.DispatchAsync(rule, messageEntry, token);
+ }
+ catch (Exception ex)
{
- this._logger.Information(
- "Running Rule Dispatch for Rule {Rule} on Message {@MessageEntry}",
+ this._logger.Warning(
+ ex,
+ "Failure Dispatching Rule {Rule} for Message {@MessageEntry}",
rule,
messageEntry);
-
- try
- {
- var ruleDispatcher = this._lifetimeScope.Resolve>();
- await ruleDispatcher.DispatchAsync(rule, messageEntry, token);
- }
- catch (Exception ex)
- {
- this._logger.Warning(
- ex,
- "Failure Dispatching Rule {Rule} for Message {@MessageEntry}",
- rule,
- messageEntry);
- }
}
+ }
- #region Begin Static Container Registrations
-
- ///
- /// Called dynamically from the RegisterStaticMethods() call in the container module.
- ///
- ///
- [UsedImplicitly]
- static void Register(ContainerBuilder builder)
- {
- if (builder == null) throw new ArgumentNullException(nameof(builder));
+ #region Begin Static Container Registrations
- builder.RegisterType().As().SingleInstance();
- }
+ ///
+ /// Called dynamically from the RegisterStaticMethods() call in the container module.
+ ///
+ ///
+ [UsedImplicitly]
+ static void Register(ContainerBuilder builder)
+ {
+ if (builder == null) throw new ArgumentNullException(nameof(builder));
- #endregion
+ builder.RegisterType().As().SingleInstance();
}
+
+ #endregion
}
\ No newline at end of file
diff --git a/src/Papercut.Rules/Domain/Conditional/ConditionalRuleExtensions.cs b/src/Papercut.Rules/Domain/Conditional/ConditionalRuleExtensions.cs
deleted file mode 100644
index ce07ef72..00000000
--- a/src/Papercut.Rules/Domain/Conditional/ConditionalRuleExtensions.cs
+++ /dev/null
@@ -1,60 +0,0 @@
-// Papercut
-//
-// Copyright © 2008 - 2012 Ken Robertson
-// Copyright © 2013 - 2024 Jaben Cargman
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-
-using System.Text.RegularExpressions;
-
-using MimeKit;
-
-using Papercut.Common.Helper;
-
-namespace Papercut.Rules.Domain.Conditional
-{
- public static class ConditionalRuleExtensions
- {
- public static bool IsConditionalForwardRuleMatch(this IConditionalRule rule, MimeMessage mimeMessage)
- {
- if (rule == null) throw new ArgumentNullException(nameof(rule));
- if (mimeMessage == null) throw new ArgumentNullException(nameof(mimeMessage));
-
- if (rule.RegexHeaderMatch.IsSet())
- {
- var allHeaders = string.Join("\r\n", mimeMessage.Headers.Select(h => h.ToString()));
-
- if (!IsMatch(rule.RegexHeaderMatch, allHeaders))
- return false;
- }
-
- if (rule.RegexBodyMatch.IsSet())
- {
- var bodyText = string.Join("\r\n",
- mimeMessage.BodyParts.OfType().Where(s => !s.IsAttachment));
-
- if (!IsMatch(rule.RegexBodyMatch, bodyText))
- return false;
- }
-
- return true;
- }
-
- private static bool IsMatch(string match, string searchText)
- {
- var regex = new Regex(match, RegexOptions.IgnoreCase);
- return regex.IsMatch(searchText);
- }
- }
-}
\ No newline at end of file
diff --git a/src/Papercut.Rules/Domain/Conditional/Forwarding/ConditionalForwardRule.cs b/src/Papercut.Rules/Domain/Conditional/Forwarding/ConditionalForwardRule.cs
index 662ee2f0..42ab399e 100644
--- a/src/Papercut.Rules/Domain/Conditional/Forwarding/ConditionalForwardRule.cs
+++ b/src/Papercut.Rules/Domain/Conditional/Forwarding/ConditionalForwardRule.cs
@@ -19,70 +19,69 @@
using System.ComponentModel;
using Autofac;
-
using Papercut.Common.Extensions;
using Papercut.Common.Helper;
using Papercut.Core.Domain.Rules;
+using Papercut.Rules.Domain.Conditional;
using Papercut.Rules.Domain.Forwarding;
-namespace Papercut.Rules.Domain.Conditional.Forwarding
+namespace Papercut.Rules.Domain.Conditional.Forwarding;
+
+[Serializable]
+public class ConditionalForwardRule : ForwardRule, IConditionalRule
{
- [Serializable]
- public class ConditionalForwardRule : ForwardRule, IConditionalRule
- {
- string _regexBodyMatch;
+ string _regexBodyMatch;
- string _regexHeaderMatch;
+ string _regexHeaderMatch;
- [Category("Information")]
- public override string Type => "Conditional Forward";
+ [Category("Information")]
+ public override string Type => "Conditional Forward";
- [DisplayName("Regex Header Match")]
- public string RegexHeaderMatch
+ [DisplayName("Regex Header Match")]
+ public string RegexHeaderMatch
+ {
+ get => _regexHeaderMatch;
+ set
{
- get => this._regexHeaderMatch;
- set
- {
- if (value == this._regexHeaderMatch)
- return;
- this._regexHeaderMatch = value.IsSet() && value.IsValidRegex() ? value : null; ;
- this.OnPropertyChanged(nameof(this.RegexHeaderMatch));
- }
+ if (value == _regexHeaderMatch)
+ return;
+ _regexHeaderMatch = value.IsSet() && value.IsValidRegex() ? value : null; ;
+ OnPropertyChanged(nameof(RegexHeaderMatch));
}
+ }
- [DisplayName("Regex Body Match")]
- public string RegexBodyMatch
+ [DisplayName("Regex Body Match")]
+ public string RegexBodyMatch
+ {
+ get => _regexBodyMatch;
+ set
{
- get => this._regexBodyMatch;
- set
- {
- if (value == this._regexBodyMatch)
- return;
-
- this._regexBodyMatch = value.IsSet() && value.IsValidRegex() ? value : null;
- this.OnPropertyChanged(nameof(this.RegexBodyMatch));
- }
- }
+ if (value == _regexBodyMatch)
+ return;
- protected override IEnumerable>> GetPropertiesForDescription()
- {
- return base.GetPropertiesForDescription().Concat(this.GetProperties());
+ _regexBodyMatch = value.IsSet() && value.IsValidRegex() ? value : null;
+ OnPropertyChanged(nameof(RegexBodyMatch));
}
+ }
- #region Begin Static Container Registrations
+ protected override IEnumerable>> GetPropertiesForDescription()
+ {
+ return base.GetPropertiesForDescription().Concat(this.GetProperties());
+ }
- ///
- /// Called dynamically from the RegisterStaticMethods() call in the container module.
- ///
- ///
- [UsedImplicitly]
- static void Register(ContainerBuilder builder)
- {
- if (builder == null) throw new ArgumentNullException(nameof(builder));
+ #region Begin Static Container Registrations
- builder.RegisterType().AsSelf().As().InstancePerDependency();
- }
+ ///
+ /// Called dynamically from the RegisterStaticMethods() call in the container module.
+ ///
+ ///
+ [UsedImplicitly]
+ static void Register(ContainerBuilder builder)
+ {
+ if (builder == null) throw new ArgumentNullException(nameof(builder));
- #endregion
+ builder.RegisterType().AsSelf().As().InstancePerDependency();
}
+
+ #endregion
}
\ No newline at end of file
diff --git a/src/Papercut.Rules/Domain/Conditional/Forwarding/ConditionalForwardRuleDispatch.cs b/src/Papercut.Rules/Domain/Conditional/Forwarding/ConditionalForwardRuleDispatch.cs
deleted file mode 100644
index 2c5937b5..00000000
--- a/src/Papercut.Rules/Domain/Conditional/Forwarding/ConditionalForwardRuleDispatch.cs
+++ /dev/null
@@ -1,58 +0,0 @@
-// Papercut
-//
-// Copyright © 2008 - 2012 Ken Robertson
-// Copyright © 2013 - 2024 Jaben Cargman
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-
-using Autofac;
-
-using MimeKit;
-
-using Papercut.Core.Domain.Rules;
-using Papercut.Message;
-using Papercut.Rules.Domain.Relaying;
-
-namespace Papercut.Rules.Domain.Conditional.Forwarding
-{
- public class ConditionalForwardRuleDispatch : BaseRelayRuleDispatch
- {
- public ConditionalForwardRuleDispatch(Lazy mimeMessageLoader, ILogger logger)
- : base(mimeMessageLoader, logger)
- {
- }
-
- protected override bool RuleMatches(ConditionalForwardRule rule, MimeMessage mimeMessage)
- {
- return rule.IsConditionalForwardRuleMatch(mimeMessage);
- }
-
- #region Begin Static Container Registrations
-
- ///
- /// Called dynamically from the RegisterStaticMethods() call in the container module.
- ///
- ///
- [UsedImplicitly]
- static void Register(ContainerBuilder builder)
- {
- if (builder == null) throw new ArgumentNullException(nameof(builder));
-
- builder.RegisterType()
- .As>().InstancePerDependency();
- }
-
- #endregion
- }
-}
\ No newline at end of file
diff --git a/src/Papercut.Rules/Domain/Conditional/Forwarding/ConditionalForwardWithRetryRule.cs b/src/Papercut.Rules/Domain/Conditional/Forwarding/ConditionalForwardWithRetryRule.cs
index 05612e1c..8e60ddbb 100644
--- a/src/Papercut.Rules/Domain/Conditional/Forwarding/ConditionalForwardWithRetryRule.cs
+++ b/src/Papercut.Rules/Domain/Conditional/Forwarding/ConditionalForwardWithRetryRule.cs
@@ -19,74 +19,72 @@
using System.ComponentModel;
using Autofac;
-
using Papercut.Common.Extensions;
using Papercut.Common.Helper;
using Papercut.Core.Domain.Rules;
-namespace Papercut.Rules.Domain.Conditional.Forwarding
+namespace Papercut.Rules.Domain.Conditional.Forwarding;
+
+[Serializable]
+public class ConditionalForwardWithRetryRule : ConditionalForwardRule
{
- [Serializable]
- public class ConditionalForwardWithRetryRule : ConditionalForwardRule
- {
- private int _retryAttemptDelaySeconds;
+ private int _retryAttemptDelaySeconds;
- private int _retryAttempts;
+ private int _retryAttempts;
- public ConditionalForwardWithRetryRule()
- {
- this.RetryAttempts = 5;
- this.RetryAttemptDelaySeconds = 60;
- }
+ public ConditionalForwardWithRetryRule()
+ {
+ RetryAttempts = 5;
+ RetryAttemptDelaySeconds = 60;
+ }
- [Category("Settings")]
- [DisplayName("Retry Attempts")]
- public int RetryAttempts
+ [Category("Settings")]
+ [DisplayName("Retry Attempts")]
+ public int RetryAttempts
+ {
+ get => _retryAttempts;
+ set
{
- get => this._retryAttempts;
- set
- {
- if (value == this._retryAttempts) return;
- this._retryAttempts = value;
- this.OnPropertyChanged(nameof(this.RetryAttempts));
- }
+ if (value == _retryAttempts) return;
+ _retryAttempts = value;
+ OnPropertyChanged(nameof(RetryAttempts));
}
+ }
- [Category("Settings")]
- [DisplayName("Retry Attempt Delay in Seconds")]
- public int RetryAttemptDelaySeconds
+ [Category("Settings")]
+ [DisplayName("Retry Attempt Delay in Seconds")]
+ public int RetryAttemptDelaySeconds
+ {
+ get => _retryAttemptDelaySeconds;
+ set
{
- get => this._retryAttemptDelaySeconds;
- set
- {
- if (value == this._retryAttemptDelaySeconds) return;
- this._retryAttemptDelaySeconds = value;
- this.OnPropertyChanged(nameof(this.RetryAttemptDelaySeconds));
- }
+ if (value == _retryAttemptDelaySeconds) return;
+ _retryAttemptDelaySeconds = value;
+ OnPropertyChanged(nameof(RetryAttemptDelaySeconds));
}
+ }
- [Category("Information")]
- public override string Type => "Conditional Forward with Retry";
+ [Category("Information")]
+ public override string Type => "Conditional Forward with Retry";
- public override string ToString()
- {
- return this.GetProperties().OrderBy(s => s.Key).ToFormattedPairs().Join("\r\n");
- }
-
- #region Begin Static Container Registrations
+ public override string ToString()
+ {
+ return this.GetProperties().OrderBy(s => s.Key).ToFormattedPairs().Join("\r\n");
+ }
- ///
- /// Called dynamically from the RegisterStaticMethods() call in the container module.
- ///
- ///
- [UsedImplicitly]
- static void Register(ContainerBuilder builder)
- {
- if (builder == null) throw new ArgumentNullException(nameof(builder));
+ #region Begin Static Container Registrations
- builder.RegisterType().AsSelf().As().InstancePerDependency();
- }
+ ///
+ /// Called dynamically from the RegisterStaticMethods() call in the container module.
+ ///
+ ///
+ [UsedImplicitly]
+ static void Register(ContainerBuilder builder)
+ {
+ if (builder == null) throw new ArgumentNullException(nameof(builder));
- #endregion
+ builder.RegisterType().AsSelf().As().InstancePerDependency();
}
+
+ #endregion
}
\ No newline at end of file
diff --git a/src/Papercut.Rules/Domain/Conditional/Forwarding/ConditionalForwardWithRetryRuleDispatch.cs b/src/Papercut.Rules/Domain/Conditional/Forwarding/ConditionalForwardWithRetryRuleDispatch.cs
deleted file mode 100644
index abf6a435..00000000
--- a/src/Papercut.Rules/Domain/Conditional/Forwarding/ConditionalForwardWithRetryRuleDispatch.cs
+++ /dev/null
@@ -1,107 +0,0 @@
-// Papercut
-//
-// Copyright © 2008 - 2012 Ken Robertson
-// Copyright © 2013 - 2024 Jaben Cargman
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-
-using Autofac;
-
-using MimeKit;
-
-using Papercut.Core.Domain.Message;
-using Papercut.Core.Domain.Rules;
-using Papercut.Message;
-using Papercut.Message.Helpers;
-using Papercut.Rules.Domain.Relaying;
-
-using Polly;
-
-namespace Papercut.Rules.Domain.Conditional.Forwarding
-{
- public class ConditionalForwardWithRetryRuleDispatch : IRuleDispatcher
- {
- private readonly ILogger _logger;
-
- private readonly Lazy _mimeMessageLoader;
-
- public ConditionalForwardWithRetryRuleDispatch(Lazy mimeMessageLoader, ILogger logger)
- {
- this._mimeMessageLoader = mimeMessageLoader;
- this._logger = logger;
- }
-
- public async Task DispatchAsync(ConditionalForwardWithRetryRule rule, MessageEntry messageEntry, CancellationToken token)
- {
- if (rule == null) throw new ArgumentNullException(nameof(rule));
- if (messageEntry == null) throw new ArgumentNullException(nameof(messageEntry));
-
- var message = await this._mimeMessageLoader.Value.GetClonedAsync(messageEntry, token);
-
- if (!this.RuleMatches(rule, message))
- {
- return;
- }
-
- rule.PopulateFromRule(message);
-
- var polly = Policy
- .Handle()
- .WaitAndRetryAsync(
- rule.RetryAttempts,
- (attempt) => TimeSpan.FromSeconds(rule.RetryAttemptDelaySeconds),
- (exception, span) =>
- {
- this._logger.Error(
- exception,
- "Failed to send {@MessageEntry} after {RetryAttempts}",
- messageEntry,
- rule.RetryAttempts);
- });
-
- async Task SendMessage()
- {
- using (var client = await rule.CreateConnectedSmtpClientAsync(token))
- {
- await client.SendAsync(message, token);
- await client.DisconnectAsync(true, token);
- }
- }
-
- await polly.ExecuteAsync(async () => await SendMessage());
- }
-
- protected virtual bool RuleMatches(ConditionalForwardWithRetryRule rule, MimeMessage mimeMessage)
- {
- return rule.IsConditionalForwardRuleMatch(mimeMessage);
- }
-
- #region Begin Static Container Registrations
-
- ///
- /// Called dynamically from the RegisterStaticMethods() call in the container module.
- ///
- ///
- [UsedImplicitly]
- static void Register(ContainerBuilder builder)
- {
- if (builder == null) throw new ArgumentNullException(nameof(builder));
-
- builder.RegisterType()
- .As>().AsSelf().InstancePerDependency();
- }
-
- #endregion
- }
-}
\ No newline at end of file
diff --git a/src/Papercut.Rules/Domain/Conditional/IConditionalRule.cs b/src/Papercut.Rules/Domain/Conditional/IConditionalRule.cs
index a24935d8..bb4c4336 100644
--- a/src/Papercut.Rules/Domain/Conditional/IConditionalRule.cs
+++ b/src/Papercut.Rules/Domain/Conditional/IConditionalRule.cs
@@ -16,12 +16,11 @@
// limitations under the License.
-namespace Papercut.Rules.Domain.Conditional
+namespace Papercut.Rules.Domain.Conditional;
+
+public interface IConditionalRule
{
- public interface IConditionalRule
- {
- string RegexHeaderMatch { get; set; }
+ string RegexHeaderMatch { get; set; }
- string RegexBodyMatch { get; set; }
- }
+ string RegexBodyMatch { get; set; }
}
\ No newline at end of file
diff --git a/src/Papercut.Rules/Domain/Conditional/Relaying/ConditionalRelayRule.cs b/src/Papercut.Rules/Domain/Conditional/Relaying/ConditionalRelayRule.cs
index f1a390b7..8fe823c0 100644
--- a/src/Papercut.Rules/Domain/Conditional/Relaying/ConditionalRelayRule.cs
+++ b/src/Papercut.Rules/Domain/Conditional/Relaying/ConditionalRelayRule.cs
@@ -19,70 +19,69 @@
using System.ComponentModel;
using Autofac;
-
using Papercut.Common.Extensions;
using Papercut.Common.Helper;
using Papercut.Core.Domain.Rules;
+using Papercut.Rules.Domain.Conditional;
using Papercut.Rules.Domain.Relaying;
-namespace Papercut.Rules.Domain.Conditional.Relaying
+namespace Papercut.Rules.Domain.Conditional.Relaying;
+
+[Serializable]
+public class ConditionalRelayRule : RelayRule, IConditionalRule
{
- [Serializable]
- public class ConditionalRelayRule : RelayRule, IConditionalRule
- {
- string? _regexBodyMatch;
+ string? _regexBodyMatch;
- string? _regexHeaderMatch;
+ string? _regexHeaderMatch;
- [Category("Information")]
- public override string Type => "Conditional Relay";
+ [Category("Information")]
+ public override string Type => "Conditional Relay";
- [DisplayName("Regex Header Match")]
- public string RegexHeaderMatch
+ [DisplayName("Regex Header Match")]
+ public string RegexHeaderMatch
+ {
+ get => _regexHeaderMatch;
+ set
{
- get => this._regexHeaderMatch;
- set
- {
- if (value == this._regexHeaderMatch)
- return;
- this._regexHeaderMatch = value.IsSet() && value.IsValidRegex() ? value : null; ;
- this.OnPropertyChanged(nameof(this.RegexHeaderMatch));
- }
+ if (value == _regexHeaderMatch)
+ return;
+ _regexHeaderMatch = value.IsSet() && value.IsValidRegex() ? value : null; ;
+ OnPropertyChanged(nameof(RegexHeaderMatch));
}
+ }
- [DisplayName("Regex Body Match")]
- public string RegexBodyMatch
+ [DisplayName("Regex Body Match")]
+ public string RegexBodyMatch
+ {
+ get => _regexBodyMatch;
+ set
{
- get => this._regexBodyMatch;
- set
- {
- if (value == this._regexBodyMatch)
- return;
-
- this._regexBodyMatch = value.IsSet() && value.IsValidRegex() ? value : null;
- this.OnPropertyChanged(nameof(this.RegexBodyMatch));
- }
- }
+ if (value == _regexBodyMatch)
+ return;
- protected override IEnumerable>> GetPropertiesForDescription()
- {
- return base.GetPropertiesForDescription().Concat(this.GetProperties());
+ _regexBodyMatch = value.IsSet() && value.IsValidRegex() ? value : null;
+ OnPropertyChanged(nameof(RegexBodyMatch));
}
+ }
- #region Begin Static Container Registrations
+ protected override IEnumerable>> GetPropertiesForDescription()
+ {
+ return base.GetPropertiesForDescription().Concat(this.GetProperties());
+ }
- ///
- /// Called dynamically from the RegisterStaticMethods() call in the container module.
- ///
- ///
- [UsedImplicitly]
- static void Register(ContainerBuilder builder)
- {
- if (builder == null) throw new ArgumentNullException(nameof(builder));
+ #region Begin Static Container Registrations
- builder.RegisterType().AsSelf().As().InstancePerDependency();
- }
+ ///
+ /// Called dynamically from the RegisterStaticMethods() call in the container module.
+ ///
+ ///
+ [UsedImplicitly]
+ static void Register(ContainerBuilder builder)
+ {
+ if (builder == null) throw new ArgumentNullException(nameof(builder));
- #endregion
+ builder.RegisterType().AsSelf().As().InstancePerDependency();
}
+
+ #endregion
}
\ No newline at end of file
diff --git a/src/Papercut.Rules/Domain/Conditional/Relaying/ConditionalRelayRuleDispatch.cs b/src/Papercut.Rules/Domain/Conditional/Relaying/ConditionalRelayRuleDispatch.cs
deleted file mode 100644
index ee065f46..00000000
--- a/src/Papercut.Rules/Domain/Conditional/Relaying/ConditionalRelayRuleDispatch.cs
+++ /dev/null
@@ -1,58 +0,0 @@
-// Papercut
-//
-// Copyright © 2008 - 2012 Ken Robertson
-// Copyright © 2013 - 2024 Jaben Cargman
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-
-using Autofac;
-
-using MimeKit;
-
-using Papercut.Core.Domain.Rules;
-using Papercut.Message;
-using Papercut.Rules.Domain.Relaying;
-
-namespace Papercut.Rules.Domain.Conditional.Relaying
-{
- public class ConditionalRelayRuleDispatch : BaseRelayRuleDispatch
- {
- public ConditionalRelayRuleDispatch(Lazy mimeMessageLoader, ILogger logger)
- : base(mimeMessageLoader, logger)
- {
- }
-
- protected override bool RuleMatches(ConditionalRelayRule rule, MimeMessage mimeMessage)
- {
- return rule.IsConditionalForwardRuleMatch(mimeMessage);
- }
-
- #region Begin Static Container Registrations
-
- ///
- /// Called dynamically from the RegisterStaticMethods() call in the container module.
- ///
- ///
- [UsedImplicitly]
- static void Register(ContainerBuilder builder)
- {
- if (builder == null) throw new ArgumentNullException(nameof(builder));
-
- builder.RegisterType()
- .As>().AsSelf().InstancePerDependency();
- }
-
- #endregion
- }
-}
\ No newline at end of file
diff --git a/src/Papercut.Rules/Domain/Forwarding/ForwardRule.cs b/src/Papercut.Rules/Domain/Forwarding/ForwardRule.cs
index 172f1ec4..af8d5750 100644
--- a/src/Papercut.Rules/Domain/Forwarding/ForwardRule.cs
+++ b/src/Papercut.Rules/Domain/Forwarding/ForwardRule.cs
@@ -26,87 +26,86 @@
using Papercut.Core.Domain.Rules;
using Papercut.Rules.Domain.Relaying;
-namespace Papercut.Rules.Domain.Forwarding
+namespace Papercut.Rules.Domain.Forwarding;
+
+[Serializable]
+public class ForwardRule : RelayRule
{
- [Serializable]
- public class ForwardRule : RelayRule
- {
- private string _fromEmail;
+ private string _fromEmail;
- private string _toEmail;
+ private string _toEmail;
- [Category("Information")]
- public override string Type => "Forward";
+ [Category("Information")]
+ public override string Type => "Forward";
- [Category("Settings")]
- [DisplayName("From Email")]
- [Description("Forward From Email")]
- public string FromEmail
+ [Category("Settings")]
+ [DisplayName("From Email")]
+ [Description("Forward From Email")]
+ public string FromEmail
+ {
+ get => this._fromEmail;
+ set
{
- get => this._fromEmail;
- set
- {
- if (value == this._fromEmail) return;
- this._fromEmail = value;
- this.OnPropertyChanged(nameof(this.FromEmail));
- }
+ if (value == this._fromEmail) return;
+ this._fromEmail = value;
+ this.OnPropertyChanged(nameof(this.FromEmail));
}
+ }
- [Category("Settings")]
- [DisplayName("To Email")]
- [Description("Foward To Email")]
- public string ToEmail
+ [Category("Settings")]
+ [DisplayName("To Email")]
+ [Description("Foward To Email")]
+ public string ToEmail
+ {
+ get => this._toEmail;
+ set
{
- get => this._toEmail;
- set
- {
- if (value == this._toEmail) return;
- this._toEmail = value;
- this.OnPropertyChanged(nameof(this.ToEmail));
- }
+ if (value == this._toEmail) return;
+ this._toEmail = value;
+ this.OnPropertyChanged(nameof(this.ToEmail));
}
+ }
+
+ public override void PopulateFromRule(MimeMessage mimeMessage)
+ {
+ if (mimeMessage == null) throw new ArgumentNullException(nameof(mimeMessage));
- public override void PopulateFromRule(MimeMessage mimeMessage)
+ if (!string.IsNullOrWhiteSpace(this.FromEmail))
{
- if (mimeMessage == null) throw new ArgumentNullException(nameof(mimeMessage));
-
- if (!string.IsNullOrWhiteSpace(this.FromEmail))
- {
- mimeMessage.From.Clear();
- mimeMessage.From.Add(
- new MailboxAddress(this.FromEmail, this.FromEmail));
- }
-
- if (!string.IsNullOrWhiteSpace(this.ToEmail))
- {
- mimeMessage.To.Clear();
- mimeMessage.Bcc.Clear();
- mimeMessage.Cc.Clear();
- mimeMessage.To.Add(new MailboxAddress(this.ToEmail, this.ToEmail));
- }
-
- base.PopulateFromRule(mimeMessage);
+ mimeMessage.From.Clear();
+ mimeMessage.From.Add(
+ new MailboxAddress(this.FromEmail, this.FromEmail));
}
- protected override IEnumerable>> GetPropertiesForDescription()
+ if (!string.IsNullOrWhiteSpace(this.ToEmail))
{
- return base.GetPropertiesForDescription().Concat(this.GetProperties());
+ mimeMessage.To.Clear();
+ mimeMessage.Bcc.Clear();
+ mimeMessage.Cc.Clear();
+ mimeMessage.To.Add(new MailboxAddress(this.ToEmail, this.ToEmail));
}
- #region Begin Static Container Registrations
+ base.PopulateFromRule(mimeMessage);
+ }
- ///
- /// Called dynamically from the RegisterStaticMethods() call in the container module.
- ///
- ///
- [UsedImplicitly]
- static void Register(ContainerBuilder builder)
- {
- if (builder == null) throw new ArgumentNullException(nameof(builder));
+ protected override IEnumerable>> GetPropertiesForDescription()
+ {
+ return base.GetPropertiesForDescription().Concat(this.GetProperties());
+ }
- builder.RegisterType().AsSelf().As().InstancePerDependency();
- }
+ #region Begin Static Container Registrations
+
+ ///
+ /// Called dynamically from the RegisterStaticMethods() call in the container module.
+ ///
+ ///
+ [UsedImplicitly]
+ static void Register(ContainerBuilder builder)
+ {
+ if (builder == null) throw new ArgumentNullException(nameof(builder));
- #endregion
+ builder.RegisterType().AsSelf().As().InstancePerDependency();
}
+
+ #endregion
}
\ No newline at end of file
diff --git a/src/Papercut.Rules/Domain/Forwarding/ForwardRuleDispatch.cs b/src/Papercut.Rules/Domain/Forwarding/ForwardRuleDispatch.cs
deleted file mode 100644
index a0ea5585..00000000
--- a/src/Papercut.Rules/Domain/Forwarding/ForwardRuleDispatch.cs
+++ /dev/null
@@ -1,51 +0,0 @@
-// Papercut
-//
-// Copyright © 2008 - 2012 Ken Robertson
-// Copyright © 2013 - 2024 Jaben Cargman
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-
-using Autofac;
-
-using Papercut.Core.Domain.Rules;
-using Papercut.Message;
-using Papercut.Rules.Domain.Relaying;
-
-namespace Papercut.Rules.Domain.Forwarding
-{
- public class ForwardRuleDispatch : BaseRelayRuleDispatch
- {
- public ForwardRuleDispatch(Lazy mimeMessageLoader, ILogger logger)
- : base(mimeMessageLoader, logger)
- {
- }
-
- #region Begin Static Container Registrations
-
- ///
- /// Called dynamically from the RegisterStaticMethods() call in the container module.
- ///
- ///
- [UsedImplicitly]
- static void Register(ContainerBuilder builder)
- {
- if (builder == null) throw new ArgumentNullException(nameof(builder));
-
- builder.RegisterType()
- .As>().AsSelf().InstancePerDependency();
- }
-
- #endregion
- }
-}
\ No newline at end of file
diff --git a/src/Papercut.Rules/Domain/Invoking/InvokeProcessRule.cs b/src/Papercut.Rules/Domain/Invoking/InvokeProcessRule.cs
new file mode 100644
index 00000000..451ec43e
--- /dev/null
+++ b/src/Papercut.Rules/Domain/Invoking/InvokeProcessRule.cs
@@ -0,0 +1,88 @@
+// Papercut
+//
+// Copyright © 2008 - 2012 Ken Robertson
+// Copyright © 2013 - 2024 Jaben Cargman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+
+using System.ComponentModel;
+
+using Autofac;
+
+using MimeKit;
+
+using Papercut.Common.Extensions;
+using Papercut.Core.Domain.Rules;
+using Papercut.Rules.Domain.Rules;
+
+namespace Papercut.Rules.Domain.Invoking;
+
+[Serializable]
+public class InvokeProcessRule : RuleBase
+{
+ private string? _processToRun;
+ private string? _processCommandLine = @"""%e""";
+
+ [Category("Information")]
+ public override string Type => "Invoke Process";
+
+ [Category("Settings")]
+ [DisplayName("Process to Run")]
+ [Description("Full Path and EXE of Process to Run on New Message")]
+ public string? ProcessToRun
+ {
+ get => _processToRun;
+ set
+ {
+ if (value == _processToRun) return;
+ _processToRun = value;
+ OnPropertyChanged(nameof(ProcessToRun));
+ }
+ }
+
+ [Category("Settings")]
+ [DisplayName("Process Command Line")]
+ [Description(@"Command line to use when running process. Note ""%e"" will be replaced with the full path to the email that triggered the rule.")]
+ public string? ProcessCommandLine
+ {
+ get => this._processCommandLine;
+ set
+ {
+ if (value == this._processCommandLine) return;
+ this._processCommandLine = value;
+ this.OnPropertyChanged(nameof(this.ProcessCommandLine));
+ }
+ }
+
+ protected override IEnumerable>> GetPropertiesForDescription()
+ {
+ return base.GetPropertiesForDescription().Concat(this.GetProperties());
+ }
+
+ #region Begin Static Container Registrations
+
+ ///
+ /// Called dynamically from the RegisterStaticMethods() call in the container module.
+ ///
+ ///
+ [UsedImplicitly]
+ static void Register(ContainerBuilder builder)
+ {
+ if (builder == null) throw new ArgumentNullException(nameof(builder));
+
+ builder.RegisterType().AsSelf().As().InstancePerDependency();
+ }
+
+ #endregion
+}
\ No newline at end of file
diff --git a/src/Papercut.Rules/Domain/Relaying/BaseRelayRuleDispatch.cs b/src/Papercut.Rules/Domain/Relaying/BaseRelayRuleDispatch.cs
deleted file mode 100644
index 7cead752..00000000
--- a/src/Papercut.Rules/Domain/Relaying/BaseRelayRuleDispatch.cs
+++ /dev/null
@@ -1,76 +0,0 @@
-// Papercut
-//
-// Copyright © 2008 - 2012 Ken Robertson
-// Copyright © 2013 - 2024 Jaben Cargman
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-
-using MimeKit;
-
-using Papercut.Core.Domain.Message;
-using Papercut.Core.Domain.Rules;
-using Papercut.Message;
-using Papercut.Message.Helpers;
-
-namespace Papercut.Rules.Domain.Relaying
-{
- public abstract class BaseRelayRuleDispatch : IRuleDispatcher
- where T : RelayRule
- {
- private readonly Lazy _mimeMessageLoader;
-
- protected BaseRelayRuleDispatch(Lazy mimeMessageLoader, ILogger logger)
- {
- this.Logger = logger;
- this._mimeMessageLoader = mimeMessageLoader;
- }
-
- protected ILogger Logger { get; }
-
- public virtual async Task DispatchAsync(T rule, MessageEntry messageEntry, CancellationToken token = default)
- {
- if (rule == null) throw new ArgumentNullException(nameof(rule));
- if (messageEntry == null) throw new ArgumentNullException(nameof(messageEntry));
-
- var mimeMessage = await this._mimeMessageLoader.Value.GetClonedAsync(messageEntry, token);
-
- if (!this.RuleMatches(rule, mimeMessage)) return;
-
- try
- {
- using (var client = await rule.CreateConnectedSmtpClientAsync(token))
- {
- rule.PopulateFromRule(mimeMessage);
- await client.SendAsync(mimeMessage, token);
- await client.DisconnectAsync(true, token);
- }
- }
- catch (Exception ex)
- {
- this.HandleSendFailure(rule, messageEntry, ex);
- }
- }
-
- protected virtual bool RuleMatches(T rule, MimeMessage mimeMessage)
- {
- return true;
- }
-
- protected virtual void HandleSendFailure(T rule, MessageEntry messageEntry, Exception exception)
- {
- // log failure
- this.Logger.Error(exception, "Failure sending {@MessageEntry} for rule {@Rule}", messageEntry, rule);
- }
- }
-}
\ No newline at end of file
diff --git a/src/Papercut.Rules/Domain/Relaying/RelayRule.cs b/src/Papercut.Rules/Domain/Relaying/RelayRule.cs
index eb930b52..6e56b6fd 100644
--- a/src/Papercut.Rules/Domain/Relaying/RelayRule.cs
+++ b/src/Papercut.Rules/Domain/Relaying/RelayRule.cs
@@ -26,140 +26,139 @@
using Papercut.Core.Domain.Rules;
using Papercut.Rules.Domain.Rules;
-namespace Papercut.Rules.Domain.Relaying
+namespace Papercut.Rules.Domain.Relaying;
+
+[Serializable]
+public class RelayRule : RuleBase
{
- [Serializable]
- public class RelayRule : RuleBase
- {
- string _smtpPassword;
+ string _smtpPassword;
- int _smtpPort = 25;
+ int _smtpPort = 25;
- string _smtpServer = "10.0.0.1";
+ string _smtpServer = "10.0.0.1";
- string _smtpUsername;
+ string _smtpUsername;
- bool _smtpUseSsl;
+ bool _smtpUseSsl;
- private string _toBcc;
+ private string _toBcc;
- [Category("Settings")]
- [DisplayName("SMTP Password")]
- [PasswordPropertyText]
- public string SmtpPassword
+ [Category("Settings")]
+ [DisplayName("SMTP Password")]
+ [PasswordPropertyText]
+ public string SmtpPassword
+ {
+ get => this._smtpPassword;
+ set
{
- get => this._smtpPassword;
- set
- {
- if (value == this._smtpPassword) return;
- this._smtpPassword = value;
- this.OnPropertyChanged(nameof(this.SmtpPassword));
- }
+ if (value == this._smtpPassword) return;
+ this._smtpPassword = value;
+ this.OnPropertyChanged(nameof(this.SmtpPassword));
}
+ }
- [Category("Settings")]
- [DisplayName("SMTP Username")]
- public string SmtpUsername
+ [Category("Settings")]
+ [DisplayName("SMTP Username")]
+ public string SmtpUsername
+ {
+ get => this._smtpUsername;
+ set
{
- get => this._smtpUsername;
- set
- {
- if (value == this._smtpUsername) return;
- this._smtpUsername = value;
- this.OnPropertyChanged(nameof(this.SmtpUsername));
- }
+ if (value == this._smtpUsername) return;
+ this._smtpUsername = value;
+ this.OnPropertyChanged(nameof(this.SmtpUsername));
}
+ }
- [Category("Settings")]
- [DisplayName("SMTP Port")]
- public int SmtpPort
+ [Category("Settings")]
+ [DisplayName("SMTP Port")]
+ public int SmtpPort
+ {
+ get => this._smtpPort;
+ set
{
- get => this._smtpPort;
- set
- {
- if (value == this._smtpPort) return;
- this._smtpPort = value;
- this.OnPropertyChanged(nameof(this.SmtpPort));
- }
+ if (value == this._smtpPort) return;
+ this._smtpPort = value;
+ this.OnPropertyChanged(nameof(this.SmtpPort));
}
+ }
- [Category("Settings")]
- [DisplayName("SMTP Use SSL")]
- public bool SmtpUseSSL
+ [Category("Settings")]
+ [DisplayName("SMTP Use SSL")]
+ public bool SmtpUseSSL
+ {
+ get => this._smtpUseSsl;
+ set
{
- get => this._smtpUseSsl;
- set
- {
- if (value.Equals(this._smtpUseSsl)) return;
- this._smtpUseSsl = value;
- this.OnPropertyChanged(nameof(this.SmtpUseSSL));
- }
+ if (value.Equals(this._smtpUseSsl)) return;
+ this._smtpUseSsl = value;
+ this.OnPropertyChanged(nameof(this.SmtpUseSSL));
}
+ }
- [Category("Information")]
- public override string Type => "Relay";
+ [Category("Information")]
+ public override string Type => "Relay";
- [Category("Settings")]
- [DisplayName("SMTP Server")]
- public string SmtpServer
+ [Category("Settings")]
+ [DisplayName("SMTP Server")]
+ public string SmtpServer
+ {
+ get => this._smtpServer;
+ set
{
- get => this._smtpServer;
- set
- {
- if (value == this._smtpServer) return;
- this._smtpServer = value;
- this.OnPropertyChanged(nameof(this.SmtpServer));
- }
+ if (value == this._smtpServer) return;
+ this._smtpServer = value;
+ this.OnPropertyChanged(nameof(this.SmtpServer));
}
+ }
- [Category("Settings")]
- [DisplayName("To Bcc Email(s)")]
- [Description("To Bcc Email(s) (comma delimited)")]
- public string ToBcc
+ [Category("Settings")]
+ [DisplayName("To Bcc Email(s)")]
+ [Description("To Bcc Email(s) (comma delimited)")]
+ public string ToBcc
+ {
+ get => this._toBcc;
+ set
{
- get => this._toBcc;
- set
- {
- if (value == this._toBcc) return;
- this._toBcc = value;
- this.OnPropertyChanged(nameof(this.ToBcc));
- }
+ if (value == this._toBcc) return;
+ this._toBcc = value;
+ this.OnPropertyChanged(nameof(this.ToBcc));
}
+ }
- public override void PopulateFromRule(MimeMessage message)
- {
- if (message == null) throw new ArgumentNullException(nameof(message));
-
- if (string.IsNullOrWhiteSpace(this.ToBcc))
- {
- return;
- }
+ public virtual void PopulateFromRule(MimeMessage message)
+ {
+ if (message == null) throw new ArgumentNullException(nameof(message));
- foreach (var bcc in this.ToBcc.Split(new[] { ',', '|', ';' }, StringSplitOptions.RemoveEmptyEntries).Select(s => s.Trim()))
- {
- message.Bcc.Add(new MailboxAddress(bcc, bcc));
- }
+ if (string.IsNullOrWhiteSpace(this.ToBcc))
+ {
+ return;
}
- protected override IEnumerable>> GetPropertiesForDescription()
+ foreach (var bcc in this.ToBcc.Split(new[] { ',', '|', ';' }, StringSplitOptions.RemoveEmptyEntries).Select(s => s.Trim()))
{
- return base.GetPropertiesForDescription().Concat(this.GetProperties());
+ message.Bcc.Add(new MailboxAddress(bcc, bcc));
}
+ }
- #region Begin Static Container Registrations
+ protected override IEnumerable>> GetPropertiesForDescription()
+ {
+ return base.GetPropertiesForDescription().Concat(this.GetProperties());
+ }
- ///
- /// Called dynamically from the RegisterStaticMethods() call in the container module.
- ///
- ///
- [UsedImplicitly]
- static void Register(ContainerBuilder builder)
- {
- if (builder == null) throw new ArgumentNullException(nameof(builder));
+ #region Begin Static Container Registrations
- builder.RegisterType().AsSelf().As().InstancePerDependency();
- }
+ ///
+ /// Called dynamically from the RegisterStaticMethods() call in the container module.
+ ///
+ ///
+ [UsedImplicitly]
+ static void Register(ContainerBuilder builder)
+ {
+ if (builder == null) throw new ArgumentNullException(nameof(builder));
- #endregion
+ builder.RegisterType().AsSelf().As().InstancePerDependency();
}
+
+ #endregion
}
\ No newline at end of file
diff --git a/src/Papercut.Rules/Domain/Relaying/RelayRuleDispatch.cs b/src/Papercut.Rules/Domain/Relaying/RelayRuleDispatch.cs
deleted file mode 100644
index 08e24623..00000000
--- a/src/Papercut.Rules/Domain/Relaying/RelayRuleDispatch.cs
+++ /dev/null
@@ -1,51 +0,0 @@
-// Papercut
-//
-// Copyright © 2008 - 2012 Ken Robertson
-// Copyright © 2013 - 2024 Jaben Cargman
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-
-using Autofac;
-
-using Papercut.Core.Domain.Rules;
-using Papercut.Message;
-
-namespace Papercut.Rules.Domain.Relaying
-{
- [UsedImplicitly]
- public class RelayRuleDispatch : BaseRelayRuleDispatch
- {
- public RelayRuleDispatch(Lazy mimeMessageLoader, ILogger logger)
- : base(mimeMessageLoader, logger)
- {
- }
-
- #region Begin Static Container Registrations
-
- ///
- /// Called dynamically from the RegisterStaticMethods() call in the container module.
- ///
- ///
- [UsedImplicitly]
- static void Register(ContainerBuilder builder)
- {
- if (builder == null) throw new ArgumentNullException(nameof(builder));
-
- builder.RegisterType()
- .As>().AsSelf().InstancePerDependency();
- }
-
- #endregion
- }
-}
\ No newline at end of file
diff --git a/src/Papercut.Rules/Domain/Relaying/RelayRuleExtensions.cs b/src/Papercut.Rules/Domain/Relaying/RelayRuleExtensions.cs
deleted file mode 100644
index 9a035ae4..00000000
--- a/src/Papercut.Rules/Domain/Relaying/RelayRuleExtensions.cs
+++ /dev/null
@@ -1,62 +0,0 @@
-// Papercut
-//
-// Copyright © 2008 - 2012 Ken Robertson
-// Copyright © 2013 - 2024 Jaben Cargman
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-
-using MailKit.Net.Smtp;
-using MailKit.Security;
-
-namespace Papercut.Rules.Domain.Relaying
-{
- public static class RelayRuleExtensions
- {
- public static void PopulateServerFromUri(this RelayRule rule, string smtpServer)
- {
- if (rule == null) throw new ArgumentNullException(nameof(rule));
-
- var uri = new Uri("smtp://" + smtpServer);
-
- rule.SmtpServer = uri.DnsSafeHost;
- rule.SmtpPort = uri.IsDefaultPort ? 25 : uri.Port;
- }
-
- public static async Task CreateConnectedSmtpClientAsync(this RelayRule forwardRule, CancellationToken token)
- {
- if (forwardRule == null) throw new ArgumentNullException(nameof(forwardRule));
-
- var client = new SmtpClient();
-
- await client.ConnectAsync(
- forwardRule.SmtpServer,
- forwardRule.SmtpPort,
- forwardRule.SmtpUseSSL ? SecureSocketOptions.Auto : SecureSocketOptions.None,
- token);
-
- // Note: since we don't have an OAuth2 token, disable
- // the XOAUTH2 authentication mechanism.
- client.AuthenticationMechanisms.Remove("XOAUTH2");
-
- if (!string.IsNullOrWhiteSpace(forwardRule.SmtpPassword)
- && !string.IsNullOrWhiteSpace(forwardRule.SmtpUsername))
- {
- // Note: only needed if the SMTP server requires authentication
- await client.AuthenticateAsync(forwardRule.SmtpUsername, forwardRule.SmtpPassword, token);
- }
-
- return client;
- }
- }
-}
\ No newline at end of file
diff --git a/src/Papercut.Rules/Domain/Rules/IRuleRepository.cs b/src/Papercut.Rules/Domain/Rules/IRuleRepository.cs
index a46ee1a3..4cca035d 100644
--- a/src/Papercut.Rules/Domain/Rules/IRuleRepository.cs
+++ b/src/Papercut.Rules/Domain/Rules/IRuleRepository.cs
@@ -18,12 +18,11 @@
using Papercut.Core.Domain.Rules;
-namespace Papercut.Rules.Domain.Rules
+namespace Papercut.Rules.Domain.Rules;
+
+public interface IRuleRepository
{
- public interface IRuleRepository
- {
- void SaveRules(IList rules, string path);
+ void SaveRules(IList rules, string path);
- IList LoadRules(string path);
- }
+ IList LoadRules(string path);
}
\ No newline at end of file
diff --git a/src/Papercut.Rules/Domain/Rules/RuleBase.cs b/src/Papercut.Rules/Domain/Rules/RuleBase.cs
index 6719a06b..09c1de07 100644
--- a/src/Papercut.Rules/Domain/Rules/RuleBase.cs
+++ b/src/Papercut.Rules/Domain/Rules/RuleBase.cs
@@ -26,69 +26,66 @@
using Papercut.Common.Helper;
using Papercut.Core.Domain.Rules;
-namespace Papercut.Rules.Domain.Rules
+namespace Papercut.Rules.Domain.Rules;
+
+[Serializable]
+public abstract class RuleBase : IRule
{
- [Serializable]
- public abstract class RuleBase : IRule
- {
- bool _isEnabled;
+ bool _isEnabled;
- protected RuleBase()
- {
- this.Id = Guid.NewGuid();
- }
+ protected RuleBase()
+ {
+ this.Id = Guid.NewGuid();
+ }
- [Category("Information")]
- public Guid Id { get; protected set; }
+ [Category("Information")]
+ public Guid Id { get; protected set; }
- [Category("State")]
- [Browsable(true)]
- [DisplayName("Is Enabled")]
- [Description("Is the Rule Enabled for Processing?")]
- public virtual bool IsEnabled
+ [Category("State")]
+ [Browsable(true)]
+ [DisplayName("Is Enabled")]
+ [Description("Is the Rule Enabled for Processing?")]
+ public virtual bool IsEnabled
+ {
+ get => this._isEnabled;
+ set
{
- get => this._isEnabled;
- set
- {
- if (value.Equals(this._isEnabled)) return;
- this._isEnabled = value;
- this.OnPropertyChanged(nameof(this.IsEnabled));
- }
+ if (value.Equals(this._isEnabled)) return;
+ this._isEnabled = value;
+ this.OnPropertyChanged(nameof(this.IsEnabled));
}
+ }
- [Category("Information")]
- [Browsable(false)]
- public virtual string Type => this.GetType().Name;
-
- [Category("Information")]
- [Browsable(false)]
- [JsonIgnore]
- public virtual string Description
- =>
- this.GetPropertiesForDescription()
- .Where(s => !s.Key.IsAny("Id", "Type", "Description"))
- .OrderBy(s => s.Key)
- .ToFormattedPairs()
- .Join("\r\n");
+ [Category("Information")]
+ [Browsable(false)]
+ public virtual string Type => this.GetType().Name;
- public event PropertyChangedEventHandler? PropertyChanged;
+ [Category("Information")]
+ [Browsable(false)]
+ [JsonIgnore]
+ public virtual string Description
+ =>
+ this.GetPropertiesForDescription()
+ .Where(s => !s.Key.IsAny("Id", "Type", "Description"))
+ .OrderBy(s => s.Key)
+ .ToFormattedPairs()
+ .Join("\r\n");
- public abstract void PopulateFromRule(MimeMessage message);
+ public event PropertyChangedEventHandler? PropertyChanged;
- protected virtual IEnumerable>> GetPropertiesForDescription()
- {
- return this.GetProperties();
- }
+ protected virtual IEnumerable>> GetPropertiesForDescription()
+ {
+ return this.GetProperties();
+ }
- [NotifyPropertyChangedInvocator]
- protected virtual void OnPropertyChanged(string propertyName)
+ [NotifyPropertyChangedInvocator]
+ protected virtual void OnPropertyChanged(string propertyName)
+ {
+ PropertyChangedEventHandler? handler = this.PropertyChanged;
+ if (handler != null)
{
- PropertyChangedEventHandler? handler = this.PropertyChanged;
- if (handler != null)
- {
- handler(this, new PropertyChangedEventArgs(propertyName));
- handler(this, new PropertyChangedEventArgs("Description"));
- }
+ handler(this, new PropertyChangedEventArgs(propertyName));
+ handler(this, new PropertyChangedEventArgs(nameof(Description)));
}
}
}
\ No newline at end of file
diff --git a/src/Papercut.Rules/Infrastructure/ObservableExtensions.cs b/src/Papercut.Rules/Infrastructure/ObservableExtensions.cs
index 2fcbece5..f8a304aa 100644
--- a/src/Papercut.Rules/Infrastructure/ObservableExtensions.cs
+++ b/src/Papercut.Rules/Infrastructure/ObservableExtensions.cs
@@ -18,20 +18,19 @@
using System.Reactive.Linq;
-namespace Papercut.Rules.Infrastructure
+namespace Papercut.Rules.Infrastructure;
+
+public static class ObservableExtensions
{
- public static class ObservableExtensions
+ public static IObservable RetryWithDelay(this IObservable source, int retryCount, TimeSpan timeSpan)
{
- public static IObservable RetryWithDelay(this IObservable source, int retryCount, TimeSpan timeSpan)
- {
- if (source == null)
- throw new ArgumentNullException(nameof(source));
- if (timeSpan < TimeSpan.Zero)
- throw new ArgumentOutOfRangeException(nameof(timeSpan));
- if (timeSpan == TimeSpan.Zero)
- return source.Retry(retryCount);
+ if (source == null)
+ throw new ArgumentNullException(nameof(source));
+ if (timeSpan < TimeSpan.Zero)
+ throw new ArgumentOutOfRangeException(nameof(timeSpan));
+ if (timeSpan == TimeSpan.Zero)
+ return source.Retry(retryCount);
- return source.Catch(Observable.Timer(timeSpan).SelectMany(_ => source).Retry(retryCount));
- }
+ return source.Catch(Observable.Timer(timeSpan).SelectMany(_ => source).Retry(retryCount));
}
}
\ No newline at end of file
diff --git a/src/Papercut.Rules/Infrastructure/RuleRepository.cs b/src/Papercut.Rules/Infrastructure/RuleRepository.cs
index 2c6dd8c6..48ef64a4 100644
--- a/src/Papercut.Rules/Infrastructure/RuleRepository.cs
+++ b/src/Papercut.Rules/Infrastructure/RuleRepository.cs
@@ -29,130 +29,129 @@
using Papercut.Rules.Domain.Relaying;
using Papercut.Rules.Domain.Rules;
-namespace Papercut.Rules.Infrastructure
+namespace Papercut.Rules.Infrastructure;
+
+public class RuleRepository : IRuleRepository
{
- public class RuleRepository : IRuleRepository
+ private readonly JsonSerializerSettings _serializationSettings;
+
+ public RuleRepository()
{
- private readonly JsonSerializerSettings _serializationSettings;
+ this._serializationSettings =
+ new JsonSerializerSettings
+ {
+ TypeNameHandling = TypeNameHandling.Auto,
+ Formatting = Formatting.Indented,
+ NullValueHandling = NullValueHandling.Include,
+ TypeNameAssemblyFormatHandling = TypeNameAssemblyFormatHandling.Simple,
+ SerializationBinder = new NamespaceMigrationSerializationBinder(this.NamespaceMigrations)
+ };
+ }
- public RuleRepository()
+ IEnumerable NamespaceMigrations
+ {
+ get
{
- this._serializationSettings =
- new JsonSerializerSettings
- {
- TypeNameHandling = TypeNameHandling.Auto,
- Formatting = Formatting.Indented,
- NullValueHandling = NullValueHandling.Include,
- TypeNameAssemblyFormatHandling = TypeNameAssemblyFormatHandling.Simple,
- SerializationBinder = new NamespaceMigrationSerializationBinder(this.NamespaceMigrations)
- };
+ var assembly = "Papercut.Rules";
+
+ yield return new NamespaceMigrationImpl(
+ assembly,
+ "Papercut.Rules.Implementations.ConditionalForwardWithRelayRule",
+ typeof(ConditionalForwardWithRetryRule));
+
+ yield return new NamespaceMigrationImpl(
+ assembly,
+ "Papercut.Rules.Implementations.ConditionalForwardRule",
+ typeof(ConditionalForwardRule));
+
+ yield return new NamespaceMigrationImpl(
+ assembly,
+ "Papercut.Rules.Implementations.RelayRule",
+ typeof(RelayRule));
+
+ yield return new NamespaceMigrationImpl(
+ assembly,
+ "Papercut.Rules.Implementations.ForwardRule",
+ typeof(ForwardRule));
}
+ }
- IEnumerable NamespaceMigrations
- {
- get
- {
- var assembly = "Papercut.Rules";
-
- yield return new NamespaceMigrationImpl(
- assembly,
- "Papercut.Rules.Implementations.ConditionalForwardWithRelayRule",
- typeof(ConditionalForwardWithRetryRule));
-
- yield return new NamespaceMigrationImpl(
- assembly,
- "Papercut.Rules.Implementations.ConditionalForwardRule",
- typeof(ConditionalForwardRule));
-
- yield return new NamespaceMigrationImpl(
- assembly,
- "Papercut.Rules.Implementations.RelayRule",
- typeof(RelayRule));
-
- yield return new NamespaceMigrationImpl(
- assembly,
- "Papercut.Rules.Implementations.ForwardRule",
- typeof(ForwardRule));
- }
- }
+ public void SaveRules([NotNull] IList rules, string path)
+ {
+ if (rules == null) throw new ArgumentNullException(nameof(rules));
+ if (path == null) throw new ArgumentNullException(nameof(path));
- public void SaveRules([NotNull] IList rules, string path)
- {
- if (rules == null) throw new ArgumentNullException(nameof(rules));
- if (path == null) throw new ArgumentNullException(nameof(path));
+ JsonHelpers.SaveJson(rules, path, setting: this._serializationSettings);
+ }
- JsonHelpers.SaveJson(rules, path, setting: this._serializationSettings);
- }
+ public IList LoadRules([NotNull] string path)
+ {
+ if (path == null) throw new ArgumentNullException(nameof(path));
- public IList LoadRules([NotNull] string path)
- {
- if (path == null) throw new ArgumentNullException(nameof(path));
+ return JsonHelpers.LoadJson>(
+ path,
+ () => new List(0),
+ setting: this._serializationSettings);
+ }
- return JsonHelpers.LoadJson>(
- path,
- () => new List(0),
- setting: this._serializationSettings);
- }
+ #region Begin Static Container Registrations
- #region Begin Static Container Registrations
+ ///
+ /// Called dynamically from the RegisterStaticMethods() call in the container module.
+ ///
+ ///
+ [UsedImplicitly]
+ static void Register([NotNull] ContainerBuilder builder)
+ {
+ if (builder == null) throw new ArgumentNullException(nameof(builder));
- ///
- /// Called dynamically from the RegisterStaticMethods() call in the container module.
- ///
- ///
- [UsedImplicitly]
- static void Register([NotNull] ContainerBuilder builder)
- {
- if (builder == null) throw new ArgumentNullException(nameof(builder));
+ builder.RegisterType().As().InstancePerDependency();
+ }
- builder.RegisterType().As().InstancePerDependency();
- }
+ #endregion
- #endregion
+ public interface INamespaceMigration
+ {
+ string FromAssembly { get; }
- public interface INamespaceMigration
- {
- string FromAssembly { get; }
+ string FromType { get; }
- string FromType { get; }
+ Type ToType { get; }
+ }
- Type ToType { get; }
+ internal class NamespaceMigrationImpl : INamespaceMigration
+ {
+ public NamespaceMigrationImpl(string fromAssembly, string fromType, Type toType)
+ {
+ this.FromAssembly = fromAssembly;
+ this.FromType = fromType;
+ this.ToType = toType;
}
- internal class NamespaceMigrationImpl : INamespaceMigration
- {
- public NamespaceMigrationImpl(string fromAssembly, string fromType, Type toType)
- {
- this.FromAssembly = fromAssembly;
- this.FromType = fromType;
- this.ToType = toType;
- }
+ public string FromAssembly { get; }
- public string FromAssembly { get; }
+ public string FromType { get; }
- public string FromType { get; }
+ public Type ToType { get; }
+ }
- public Type ToType { get; }
- }
+ public class NamespaceMigrationSerializationBinder : DefaultSerializationBinder
+ {
+ private readonly INamespaceMigration[] _migrations;
- public class NamespaceMigrationSerializationBinder : DefaultSerializationBinder
+ public NamespaceMigrationSerializationBinder(IEnumerable migrations)
{
- private readonly INamespaceMigration[] _migrations;
-
- public NamespaceMigrationSerializationBinder(IEnumerable migrations)
- {
- this._migrations = migrations.IfNullEmpty().ToArray();
- }
+ this._migrations = migrations.IfNullEmpty().ToArray();
+ }
- public override Type BindToType(string assemblyName, string typeName)
+ public override Type BindToType(string assemblyName, string typeName)
+ {
+ var migration = this._migrations.SingleOrDefault(p => p.FromAssembly == assemblyName && p.FromType == typeName);
+ if(migration != null)
{
- var migration = this._migrations.SingleOrDefault(p => p.FromAssembly == assemblyName && p.FromType == typeName);
- if(migration != null)
- {
- return migration.ToType;
- }
- return base.BindToType(assemblyName, typeName);
+ return migration.ToType;
}
+ return base.BindToType(assemblyName, typeName);
}
}
}
\ No newline at end of file
diff --git a/src/Papercut.Rules/Papercut.Rules.csproj b/src/Papercut.Rules/Papercut.Rules.csproj
index 51a1b790..27684f72 100644
--- a/src/Papercut.Rules/Papercut.Rules.csproj
+++ b/src/Papercut.Rules/Papercut.Rules.csproj
@@ -15,18 +15,23 @@
- 8.0.0
+ 8.1.1
- 4.4.0
+ 4.8.0
- 4.4.0
+ 4.8.0
13.0.3
-
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Papercut.Rules/PapercutRuleModule.cs b/src/Papercut.Rules/PapercutRuleModule.cs
index d8612a4c..c406fde5 100644
--- a/src/Papercut.Rules/PapercutRuleModule.cs
+++ b/src/Papercut.Rules/PapercutRuleModule.cs
@@ -20,13 +20,12 @@
using Papercut.Core.Infrastructure.Container;
-namespace Papercut.Rules
+namespace Papercut.Rules;
+
+public class PapercutRuleModule : Module
{
- public class PapercutRuleModule : Module
+ protected override void Load(ContainerBuilder builder)
{
- protected override void Load(ContainerBuilder builder)
- {
- builder.RegisterStaticMethods(this.ThisAssembly);
- }
+ builder.RegisterStaticMethods(this.ThisAssembly);
}
}
\ No newline at end of file
diff --git a/src/Papercut.Service/Infrastructure/IPComm/PublishAppEventsHandlerToClientService.cs b/src/Papercut.Service/Infrastructure/IPComm/PublishAppEventsHandlerToClientService.cs
index c9252b12..401cf4ea 100644
--- a/src/Papercut.Service/Infrastructure/IPComm/PublishAppEventsHandlerToClientService.cs
+++ b/src/Papercut.Service/Infrastructure/IPComm/PublishAppEventsHandlerToClientService.cs
@@ -15,62 +15,61 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-namespace Papercut.Service.Infrastructure.IPComm
+namespace Papercut.Service.Infrastructure.IPComm;
+
+public class PublishAppEventsHandlerToClientService(
+ PapercutIPCommClientFactory ipCommClientFactory,
+ ILogger logger) : IEventHandler,
+ IEventHandler,
+ IEventHandler
{
- public class PublishAppEventsHandlerToClientService(
- PapercutIPCommClientFactory ipCommClientFactory,
- ILogger logger) : IEventHandler,
- IEventHandler,
- IEventHandler
+ public Task HandleAsync(PapercutServiceExitEvent @event, CancellationToken token = default)
{
- public Task HandleAsync(PapercutServiceExitEvent @event, CancellationToken token = default)
- {
- return this.PublishAsync(@event);
- }
+ return this.PublishAsync(@event);
+ }
- public Task HandleAsync(PapercutServicePreStartEvent @event, CancellationToken token = default)
- {
- return this.PublishAsync(@event);
- }
+ public Task HandleAsync(PapercutServicePreStartEvent @event, CancellationToken token = default)
+ {
+ return this.PublishAsync(@event);
+ }
- public Task HandleAsync(PapercutServiceReadyEvent @event, CancellationToken token = default)
- {
- return this.PublishAsync(@event);
- }
+ public Task HandleAsync(PapercutServiceReadyEvent @event, CancellationToken token = default)
+ {
+ return this.PublishAsync(@event);
+ }
- public async Task PublishAsync(T @event)
- where T : IEvent
- {
- var ipCommClient = ipCommClientFactory.GetClient(PapercutIPCommClientConnectTo.UI);
+ public async Task PublishAsync(T @event)
+ where T : IEvent
+ {
+ var ipCommClient = ipCommClientFactory.GetClient(PapercutIPCommClientConnectTo.UI);
- try
- {
- logger.Information(
- $"Publishing {{@{@event.GetType().Name}}} to the Papercut Client",
- @event);
+ try
+ {
+ logger.Information(
+ $"Publishing {{@{@event.GetType().Name}}} to the Papercut Client",
+ @event);
- await ipCommClient.PublishEventServer(@event, TimeSpan.FromMilliseconds(500));
- }
- catch (TaskCanceledException)
- {
- }
- catch (Exception ex)
- {
- logger.Warning(
- ex,
- "Failed to publish {Endpoint} specified. Papercut UI is most likely not running.",
- ipCommClient.Endpoint);
- }
+ await ipCommClient.PublishEventServer(@event, TimeSpan.FromMilliseconds(500));
}
-
- #region Begin Static Container Registrations
-
- static void Register(ContainerBuilder builder)
+ catch (TaskCanceledException)
{
- builder.RegisterType().AsImplementedInterfaces().AsSelf()
- .InstancePerLifetimeScope();
}
+ catch (Exception ex)
+ {
+ logger.Warning(
+ ex,
+ "Failed to publish {Endpoint} specified. Papercut UI is most likely not running.",
+ ipCommClient.Endpoint);
+ }
+ }
+
+ #region Begin Static Container Registrations
- #endregion
+ static void Register(ContainerBuilder builder)
+ {
+ builder.RegisterType().AsImplementedInterfaces().AsSelf()
+ .InstancePerLifetimeScope();
}
+
+ #endregion
}
\ No newline at end of file
diff --git a/src/Papercut.Service/Infrastructure/Logging/AddReadFromConfiguration.cs b/src/Papercut.Service/Infrastructure/Logging/AddReadFromConfiguration.cs
new file mode 100644
index 00000000..378d5999
--- /dev/null
+++ b/src/Papercut.Service/Infrastructure/Logging/AddReadFromConfiguration.cs
@@ -0,0 +1,40 @@
+// Papercut
+//
+// Copyright © 2008 - 2012 Ken Robertson
+// Copyright © 2013 - 2024 Jaben Cargman
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+
+using Microsoft.Extensions.Configuration;
+
+using Serilog.Configuration;
+
+namespace Papercut.Service.Infrastructure.Logging;
+
+public class AddReadFromConfiguration(IConfiguration configuration) : ILoggerSettings
+{
+ public void Configure(LoggerConfiguration loggerConfiguration)
+ {
+ loggerConfiguration.ReadFrom.Configuration(configuration);
+ }
+
+ #region Begin Static Container Registrations
+
+ static void Register(ContainerBuilder builder)
+ {
+ builder.RegisterType().As();
+ }
+
+ #endregion
+}
\ No newline at end of file
diff --git a/src/Papercut.Service/Infrastructure/Paths/ReplyWithDefaultMessageSavePathService.cs b/src/Papercut.Service/Infrastructure/Paths/ReplyWithDefaultMessageSavePathService.cs
index e0a09536..bf6535f0 100644
--- a/src/Papercut.Service/Infrastructure/Paths/ReplyWithDefaultMessageSavePathService.cs
+++ b/src/Papercut.Service/Infrastructure/Paths/ReplyWithDefaultMessageSavePathService.cs
@@ -18,40 +18,30 @@
using Papercut.Core.Infrastructure.Network;
-namespace Papercut.Service.Infrastructure.Paths
+namespace Papercut.Service.Infrastructure.Paths;
+
+public class ReplyWithDefaultMessageSavePathService(MessagePathConfigurator messagePathConfigurator, SmtpServerOptions smtpServerOptions)
+ : IEventHandler
{
- public class ReplyWithDefaultMessageSavePathService : IEventHandler
+ public Task HandleAsync(AppProcessExchangeEvent @event, CancellationToken token = default)
{
- readonly MessagePathConfigurator _messagePathConfigurator;
-
- readonly SmtpServerOptions _smtpServerOptions;
-
- public ReplyWithDefaultMessageSavePathService(MessagePathConfigurator messagePathConfigurator, SmtpServerOptions smtpServerOptions)
- {
- this._messagePathConfigurator = messagePathConfigurator;
- this._smtpServerOptions = smtpServerOptions;
- }
-
- public Task HandleAsync(AppProcessExchangeEvent @event, CancellationToken token = default)
- {
- // respond with the current save path...
- @event.MessageWritePath = this._messagePathConfigurator.DefaultSavePath;
+ // respond with the current save path...
+ @event.MessageWritePath = messagePathConfigurator.DefaultSavePath;
- // share our current ip and port binding for the SMTP server.
- @event.IP = this._smtpServerOptions.IP;
- @event.Port = this._smtpServerOptions.Port;
+ // share our current ip and port binding for the SMTP server.
+ @event.IP = smtpServerOptions.IP;
+ @event.Port = smtpServerOptions.Port;
- return Task.CompletedTask;
- }
-
- #region Begin Static Container Registrations
+ return Task.CompletedTask;
+ }
- static void Register(ContainerBuilder builder)
- {
- builder.RegisterType().AsImplementedInterfaces().AsSelf()
- .InstancePerLifetimeScope();
- }
+ #region Begin Static Container Registrations
- #endregion
+ static void Register(ContainerBuilder builder)
+ {
+ builder.RegisterType().AsImplementedInterfaces().AsSelf()
+ .InstancePerLifetimeScope();
}
+
+ #endregion
}
\ No newline at end of file
diff --git a/src/Papercut.Service/Papercut.Service.csproj b/src/Papercut.Service/Papercut.Service.csproj
index 70b97359..f8f8b012 100644
--- a/src/Papercut.Service/Papercut.Service.csproj
+++ b/src/Papercut.Service/Papercut.Service.csproj
@@ -8,12 +8,16 @@
enable
enable
icons\Papercut-icon.ico
+ Linux
+
+ Always
+
@@ -21,14 +25,14 @@
-
+
-
-
+
+
-
-
+
+
@@ -56,4 +60,8 @@
Always
+
+
+
+
\ No newline at end of file
diff --git a/src/Papercut.Service/PapercutServiceModule.cs b/src/Papercut.Service/PapercutServiceModule.cs
index 763b76a7..022d9861 100644
--- a/src/Papercut.Service/PapercutServiceModule.cs
+++ b/src/Papercut.Service/PapercutServiceModule.cs
@@ -16,22 +16,14 @@
// limitations under the License.
-using System.Reflection;
-
-using Autofac;
-
using AutofacSerilogIntegration;
-using Papercut.Common.Helper;
-
namespace Papercut.Service;
public class PapercutServiceModule : Module
{
protected override void Load(ContainerBuilder builder)
{
- builder.Register(_ => new ApplicationMeta("Papercut.Service", Assembly.GetExecutingAssembly().GetVersion())).As().SingleInstance();
-
builder.RegisterLogger();
builder.RegisterStaticMethods(this.ThisAssembly);
diff --git a/src/Papercut.Service/Program.cs b/src/Papercut.Service/Program.cs
index 07e95a99..e91a92ed 100644
--- a/src/Papercut.Service/Program.cs
+++ b/src/Papercut.Service/Program.cs
@@ -16,31 +16,30 @@
// limitations under the License.
+using System.Reflection;
+
using Autofac.Extensions.DependencyInjection;
using ElectronNET.API;
-using Microsoft.Extensions.Configuration;
+using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
+using Papercut.Common.Helper;
using Papercut.Core.Infrastructure.Logging;
-using Serilog.Core;
-using Serilog.Debugging;
-using Serilog.ExceptionalLogContext;
-
namespace Papercut.Service;
public class Program
{
- const string AppName= "Papercut.SMTP.Service";
+ private static readonly IAppMeta AppMeta = new ApplicationMeta("Papercut.SMTP.Service", Assembly.GetExecutingAssembly().GetVersion());
private static readonly CancellationTokenSource _cancellationTokenSource = new();
public static async Task Main(string[] args)
{
- Console.Title = AppName;
+ Console.Title = AppMeta.AppName;
Log.Logger = BootstrapLogger.CreateBootstrapLogger(args);
@@ -51,7 +50,7 @@ public static async Task RunAsync(string[] args)
{
try
{
- await CreateHostBuilder(args).Build().RunAsync(_cancellationTokenSource.Token);
+ await CreateWebApp(args).RunAsync(_cancellationTokenSource.Token);
}
catch (Exception ex) when (ex is not TaskCanceledException and not ObjectDisposedException)
{
@@ -69,56 +68,49 @@ public static void Shutdown()
_cancellationTokenSource.Cancel();
}
- public static IHostBuilder CreateHostBuilder(string[] args)
+ private static WebApplication CreateWebApp(string[] args)
{
- return Host.CreateDefaultBuilder(args)
- .UseWindowsService(
- options =>
- {
- options.ServiceName = AppName;
- })
- .UseContentRoot(AppContext.BaseDirectory)
- .UseServiceProviderFactory(new AutofacServiceProviderFactory())
- .ConfigureServices(
- (context, sp) =>
- {
- sp.AddSingleton(context.Configuration);
- sp.AddSingleton(context.HostingEnvironment);
- sp.AddSingleton(new LoggingLevelSwitch());
- })
- .ConfigureWebHostDefaults(
- (webBuilder) =>
- {
- webBuilder.ConfigureLogging(
- s =>
- {
- s.ClearProviders();
- });
- webBuilder.UseElectron(args);
- webBuilder.UseStartup();
- })
- .UseSerilog(CreateDefaultLogger)
- .ConfigureServices(
- (context, sp) =>
- {
- if (HybridSupport.IsElectronActive)
- sp.AddHostedService();
- });
+ var applicationOptions = new WebApplicationOptions()
+ { ContentRootPath = AppContext.BaseDirectory, Args = args };
+
+ var builder = WebApplication.CreateBuilder(applicationOptions);
+
+ builder.Host.UseWindowsService(
+ options =>
+ {
+ options.ServiceName = AppMeta.AppName;
+ }).ConfigureServices(
+ (context, sp) =>
+ {
+ sp.AddSingleton(context.Configuration);
+ sp.AddSingleton(context.HostingEnvironment);
+
+ if (HybridSupport.IsElectronActive)
+ sp.AddHostedService();
+ });
+
+ builder.Logging.ClearProviders();
+ builder.Host.UseSerilog();
+ builder.WebHost.UseElectron(args);
+
+ var startup = new PapercutServiceStartup();
+
+ builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory(startup.ConfigureContainer));
+ startup.ConfigureServices(builder.Services);
+
+ var webApp = builder.Build();
+
+ startup.Configure(webApp);
+
+ return webApp;
}
- private static void CreateDefaultLogger(HostBuilderContext context, IServiceProvider services, LoggerConfiguration configuration)
+ #region Begin Static Container Registrations
+
+ static void Register(ContainerBuilder builder)
{
- var appMeta = services.GetRequiredService();
-
- configuration
- .Enrich.FromLogContext()
- .Enrich.WithExceptionalLogContext()
- .Enrich.WithProperty("AppName", appMeta.AppName)
- .Enrich.WithProperty("AppVersion", appMeta.AppVersion)
- .WriteTo.Console()
- .ReadFrom.Configuration(context.Configuration)
- .ReadFrom.Services(services);
-
- SelfLog.Enable(s => Console.Error.WriteLine(s));
+ builder.RegisterInstance(AppMeta).As().SingleInstance();
}
+
+ #endregion
}
\ No newline at end of file
diff --git a/src/Papercut.Service/appsettings.Development.json b/src/Papercut.Service/appsettings.Development.json
index bd749f97..3b944a2e 100644
--- a/src/Papercut.Service/appsettings.Development.json
+++ b/src/Papercut.Service/appsettings.Development.json
@@ -1,5 +1,6 @@
{
"Serilog": {
+ "Using": [ "Serilog.Sinks.Seq" ],
"WriteTo": [
{
"Name": "Seq",
@@ -7,12 +8,14 @@
}
],
"MinimumLevel": {
- "Default": "Verbose",
+ "Default": "Debug",
"Override": {
"Microsoft": "Information",
"Microsoft.Hosting.Lifetime": "Information"
}
}
},
- "AllowedHosts": "*"
+ "AllowedHosts": "*",
+
+ "Urls": "http://localhost:37408"
}
\ No newline at end of file
diff --git a/src/Papercut.Service/appsettings.Production.json b/src/Papercut.Service/appsettings.Production.json
new file mode 100644
index 00000000..8593c62d
--- /dev/null
+++ b/src/Papercut.Service/appsettings.Production.json
@@ -0,0 +1,2 @@
+{
+}
\ No newline at end of file
diff --git a/src/Papercut.Service/appsettings.json b/src/Papercut.Service/appsettings.json
index 4deccdc2..578e131d 100644
--- a/src/Papercut.Service/appsettings.json
+++ b/src/Papercut.Service/appsettings.json
@@ -16,7 +16,5 @@
"LoggingPath": "%DataDirectory%\\Logs;%BaseDirectory%\\Logs"
},
- "AllowedHosts": "*",
-
- "Urls": "http://localhost:37408"
+ "AllowedHosts": "*"
}
\ No newline at end of file
diff --git a/src/Papercut.Service/package-lock.json b/src/Papercut.Service/package-lock.json
new file mode 100644
index 00000000..96fe13b3
--- /dev/null
+++ b/src/Papercut.Service/package-lock.json
@@ -0,0 +1,2113 @@
+{
+ "name": "Papercut",
+ "version": "1.0.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "Papercut",
+ "version": "1.0.0",
+ "license": "ISC",
+ "dependencies": {
+ "detect-port": "^1.2.1",
+ "electron": "^29.1.1",
+ "electron-localshortcut": "^3.1.0",
+ "socket.io": "4.7.4",
+ "sudo-prompt": "^9.2.1",
+ "tmp": "^0.2.1"
+ },
+ "devDependencies": {
+ "eslint": "^8.57.0"
+ }
+ },
+ "node_modules/@electron/get": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/@electron/get/-/get-2.0.3.tgz",
+ "integrity": "sha512-Qkzpg2s9GnVV2I2BjRksUi43U5e6+zaQMcjoJy0C+C5oxaKl+fmckGDQFtRpZpZV0NQekuZZ+tGz7EA9TVnQtQ==",
+ "dependencies": {
+ "debug": "^4.1.1",
+ "env-paths": "^2.2.0",
+ "fs-extra": "^8.1.0",
+ "got": "^11.8.5",
+ "progress": "^2.0.3",
+ "semver": "^6.2.0",
+ "sumchecker": "^3.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "optionalDependencies": {
+ "global-agent": "^3.0.0"
+ }
+ },
+ "node_modules/@eslint-community/eslint-utils": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz",
+ "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==",
+ "dev": true,
+ "dependencies": {
+ "eslint-visitor-keys": "^3.3.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "peerDependencies": {
+ "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
+ }
+ },
+ "node_modules/@eslint-community/regexpp": {
+ "version": "4.10.1",
+ "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.1.tgz",
+ "integrity": "sha512-Zm2NGpWELsQAD1xsJzGQpYfvICSsFkEpU0jxBjfdC6uNEWXcHnfs9hScFWtXVDVl+rBQJGrl4g1vcKIejpH9dA==",
+ "dev": true,
+ "engines": {
+ "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
+ }
+ },
+ "node_modules/@eslint/eslintrc": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz",
+ "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==",
+ "dev": true,
+ "dependencies": {
+ "ajv": "^6.12.4",
+ "debug": "^4.3.2",
+ "espree": "^9.6.0",
+ "globals": "^13.19.0",
+ "ignore": "^5.2.0",
+ "import-fresh": "^3.2.1",
+ "js-yaml": "^4.1.0",
+ "minimatch": "^3.1.2",
+ "strip-json-comments": "^3.1.1"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@eslint/js": {
+ "version": "8.57.0",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz",
+ "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==",
+ "dev": true,
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ }
+ },
+ "node_modules/@humanwhocodes/config-array": {
+ "version": "0.11.14",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz",
+ "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==",
+ "dev": true,
+ "dependencies": {
+ "@humanwhocodes/object-schema": "^2.0.2",
+ "debug": "^4.3.1",
+ "minimatch": "^3.0.5"
+ },
+ "engines": {
+ "node": ">=10.10.0"
+ }
+ },
+ "node_modules/@humanwhocodes/module-importer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
+ "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
+ "dev": true,
+ "engines": {
+ "node": ">=12.22"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@humanwhocodes/object-schema": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz",
+ "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==",
+ "dev": true
+ },
+ "node_modules/@nodelib/fs.scandir": {
+ "version": "2.1.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
+ "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
+ "dev": true,
+ "dependencies": {
+ "@nodelib/fs.stat": "2.0.5",
+ "run-parallel": "^1.1.9"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.stat": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
+ "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
+ "dev": true,
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.walk": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
+ "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
+ "dev": true,
+ "dependencies": {
+ "@nodelib/fs.scandir": "2.1.5",
+ "fastq": "^1.6.0"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@sindresorhus/is": {
+ "version": "4.6.0",
+ "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz",
+ "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sindresorhus/is?sponsor=1"
+ }
+ },
+ "node_modules/@socket.io/component-emitter": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz",
+ "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA=="
+ },
+ "node_modules/@szmarczak/http-timer": {
+ "version": "4.0.6",
+ "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz",
+ "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==",
+ "dependencies": {
+ "defer-to-connect": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@types/cacheable-request": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz",
+ "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==",
+ "dependencies": {
+ "@types/http-cache-semantics": "*",
+ "@types/keyv": "^3.1.4",
+ "@types/node": "*",
+ "@types/responselike": "^1.0.0"
+ }
+ },
+ "node_modules/@types/cookie": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz",
+ "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q=="
+ },
+ "node_modules/@types/cors": {
+ "version": "2.8.17",
+ "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz",
+ "integrity": "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==",
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/http-cache-semantics": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz",
+ "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA=="
+ },
+ "node_modules/@types/keyv": {
+ "version": "3.1.4",
+ "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz",
+ "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==",
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/node": {
+ "version": "20.14.2",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.2.tgz",
+ "integrity": "sha512-xyu6WAMVwv6AKFLB+e/7ySZVr/0zLCzOa7rSpq6jNwpqOrUbcACDWC+53d4n2QHOnDou0fbIsg8wZu/sxrnI4Q==",
+ "dependencies": {
+ "undici-types": "~5.26.4"
+ }
+ },
+ "node_modules/@types/responselike": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz",
+ "integrity": "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==",
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/yauzl": {
+ "version": "2.10.3",
+ "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz",
+ "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==",
+ "optional": true,
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@ungap/structured-clone": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz",
+ "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==",
+ "dev": true
+ },
+ "node_modules/accepts": {
+ "version": "1.3.8",
+ "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
+ "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
+ "dependencies": {
+ "mime-types": "~2.1.34",
+ "negotiator": "0.6.3"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/acorn": {
+ "version": "8.11.3",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz",
+ "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==",
+ "dev": true,
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/acorn-jsx": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
+ "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+ "dev": true,
+ "peerDependencies": {
+ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ }
+ },
+ "node_modules/address": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/address/-/address-1.2.2.tgz",
+ "integrity": "sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA==",
+ "engines": {
+ "node": ">= 10.0.0"
+ }
+ },
+ "node_modules/ajv": {
+ "version": "6.12.6",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+ "dev": true,
+ "dependencies": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+ "dev": true
+ },
+ "node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "dev": true
+ },
+ "node_modules/base64id": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz",
+ "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==",
+ "engines": {
+ "node": "^4.5.0 || >= 5.9"
+ }
+ },
+ "node_modules/boolean": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz",
+ "integrity": "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==",
+ "optional": true
+ },
+ "node_modules/brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/buffer-crc32": {
+ "version": "0.2.13",
+ "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
+ "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==",
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/cacheable-lookup": {
+ "version": "5.0.4",
+ "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz",
+ "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==",
+ "engines": {
+ "node": ">=10.6.0"
+ }
+ },
+ "node_modules/cacheable-request": {
+ "version": "7.0.4",
+ "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz",
+ "integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==",
+ "dependencies": {
+ "clone-response": "^1.0.2",
+ "get-stream": "^5.1.0",
+ "http-cache-semantics": "^4.0.0",
+ "keyv": "^4.0.0",
+ "lowercase-keys": "^2.0.0",
+ "normalize-url": "^6.0.1",
+ "responselike": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/callsites": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/clone-response": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz",
+ "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==",
+ "dependencies": {
+ "mimic-response": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "node_modules/concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+ "dev": true
+ },
+ "node_modules/cookie": {
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz",
+ "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/cors": {
+ "version": "2.8.5",
+ "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
+ "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
+ "dependencies": {
+ "object-assign": "^4",
+ "vary": "^1"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/cross-spawn": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
+ "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
+ "dev": true,
+ "dependencies": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/debug": {
+ "version": "4.3.5",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz",
+ "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==",
+ "dependencies": {
+ "ms": "2.1.2"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/decompress-response": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",
+ "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",
+ "dependencies": {
+ "mimic-response": "^3.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/decompress-response/node_modules/mimic-response": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz",
+ "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/deep-is": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
+ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
+ "dev": true
+ },
+ "node_modules/defer-to-connect": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz",
+ "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/define-data-property": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
+ "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
+ "optional": true,
+ "dependencies": {
+ "es-define-property": "^1.0.0",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/define-properties": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz",
+ "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==",
+ "optional": true,
+ "dependencies": {
+ "define-data-property": "^1.0.1",
+ "has-property-descriptors": "^1.0.0",
+ "object-keys": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/detect-node": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz",
+ "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==",
+ "optional": true
+ },
+ "node_modules/detect-port": {
+ "version": "1.6.1",
+ "resolved": "https://registry.npmjs.org/detect-port/-/detect-port-1.6.1.tgz",
+ "integrity": "sha512-CmnVc+Hek2egPx1PeTFVta2W78xy2K/9Rkf6cC4T59S50tVnzKj+tnx5mmx5lwvCkujZ4uRrpRSuV+IVs3f90Q==",
+ "dependencies": {
+ "address": "^1.0.1",
+ "debug": "4"
+ },
+ "bin": {
+ "detect": "bin/detect-port.js",
+ "detect-port": "bin/detect-port.js"
+ },
+ "engines": {
+ "node": ">= 4.0.0"
+ }
+ },
+ "node_modules/doctrine": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
+ "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
+ "dev": true,
+ "dependencies": {
+ "esutils": "^2.0.2"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/electron": {
+ "version": "29.4.2",
+ "resolved": "https://registry.npmjs.org/electron/-/electron-29.4.2.tgz",
+ "integrity": "sha512-XyIkuWQguwY8hGtLg0j5Q4Fqphdbh0ctBsKCSVzJ/R7Z2+2WN/oQ1M+zYwchmfiDgiuL3EKkrBrfPdxXYdMr+A==",
+ "hasInstallScript": true,
+ "dependencies": {
+ "@electron/get": "^2.0.0",
+ "@types/node": "^20.9.0",
+ "extract-zip": "^2.0.1"
+ },
+ "bin": {
+ "electron": "cli.js"
+ },
+ "engines": {
+ "node": ">= 12.20.55"
+ }
+ },
+ "node_modules/electron-is-accelerator": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/electron-is-accelerator/-/electron-is-accelerator-0.1.2.tgz",
+ "integrity": "sha512-fLGSAjXZtdn1sbtZxx52+krefmtNuVwnJCV2gNiVt735/ARUboMl8jnNC9fZEqQdlAv2ZrETfmBUsoQci5evJA=="
+ },
+ "node_modules/electron-localshortcut": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/electron-localshortcut/-/electron-localshortcut-3.2.1.tgz",
+ "integrity": "sha512-DWvhKv36GsdXKnaFFhEiK8kZZA+24/yFLgtTwJJHc7AFgDjNRIBJZ/jq62Y/dWv9E4ypYwrVWN2bVrCYw1uv7Q==",
+ "dependencies": {
+ "debug": "^4.0.1",
+ "electron-is-accelerator": "^0.1.0",
+ "keyboardevent-from-electron-accelerator": "^2.0.0",
+ "keyboardevents-areequal": "^0.2.1"
+ }
+ },
+ "node_modules/end-of-stream": {
+ "version": "1.4.4",
+ "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
+ "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
+ "dependencies": {
+ "once": "^1.4.0"
+ }
+ },
+ "node_modules/engine.io": {
+ "version": "6.5.4",
+ "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.5.4.tgz",
+ "integrity": "sha512-KdVSDKhVKyOi+r5uEabrDLZw2qXStVvCsEB/LN3mw4WFi6Gx50jTyuxYVCwAAC0U46FdnzP/ScKRBTXb/NiEOg==",
+ "dependencies": {
+ "@types/cookie": "^0.4.1",
+ "@types/cors": "^2.8.12",
+ "@types/node": ">=10.0.0",
+ "accepts": "~1.3.4",
+ "base64id": "2.0.0",
+ "cookie": "~0.4.1",
+ "cors": "~2.8.5",
+ "debug": "~4.3.1",
+ "engine.io-parser": "~5.2.1",
+ "ws": "~8.11.0"
+ },
+ "engines": {
+ "node": ">=10.2.0"
+ }
+ },
+ "node_modules/engine.io-parser": {
+ "version": "5.2.2",
+ "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.2.tgz",
+ "integrity": "sha512-RcyUFKA93/CXH20l4SoVvzZfrSDMOTUS3bWVpTt2FuFP+XYrL8i8oonHP7WInRyVHXh0n/ORtoeiE1os+8qkSw==",
+ "engines": {
+ "node": ">=10.0.0"
+ }
+ },
+ "node_modules/env-paths": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz",
+ "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/es-define-property": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz",
+ "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==",
+ "optional": true,
+ "dependencies": {
+ "get-intrinsic": "^1.2.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-errors": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+ "optional": true,
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es6-error": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz",
+ "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==",
+ "optional": true
+ },
+ "node_modules/escape-string-regexp": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+ "devOptional": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint": {
+ "version": "8.57.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz",
+ "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==",
+ "dev": true,
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.2.0",
+ "@eslint-community/regexpp": "^4.6.1",
+ "@eslint/eslintrc": "^2.1.4",
+ "@eslint/js": "8.57.0",
+ "@humanwhocodes/config-array": "^0.11.14",
+ "@humanwhocodes/module-importer": "^1.0.1",
+ "@nodelib/fs.walk": "^1.2.8",
+ "@ungap/structured-clone": "^1.2.0",
+ "ajv": "^6.12.4",
+ "chalk": "^4.0.0",
+ "cross-spawn": "^7.0.2",
+ "debug": "^4.3.2",
+ "doctrine": "^3.0.0",
+ "escape-string-regexp": "^4.0.0",
+ "eslint-scope": "^7.2.2",
+ "eslint-visitor-keys": "^3.4.3",
+ "espree": "^9.6.1",
+ "esquery": "^1.4.2",
+ "esutils": "^2.0.2",
+ "fast-deep-equal": "^3.1.3",
+ "file-entry-cache": "^6.0.1",
+ "find-up": "^5.0.0",
+ "glob-parent": "^6.0.2",
+ "globals": "^13.19.0",
+ "graphemer": "^1.4.0",
+ "ignore": "^5.2.0",
+ "imurmurhash": "^0.1.4",
+ "is-glob": "^4.0.0",
+ "is-path-inside": "^3.0.3",
+ "js-yaml": "^4.1.0",
+ "json-stable-stringify-without-jsonify": "^1.0.1",
+ "levn": "^0.4.1",
+ "lodash.merge": "^4.6.2",
+ "minimatch": "^3.1.2",
+ "natural-compare": "^1.4.0",
+ "optionator": "^0.9.3",
+ "strip-ansi": "^6.0.1",
+ "text-table": "^0.2.0"
+ },
+ "bin": {
+ "eslint": "bin/eslint.js"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint-scope": {
+ "version": "7.2.2",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz",
+ "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==",
+ "dev": true,
+ "dependencies": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint-visitor-keys": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+ "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+ "dev": true,
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/espree": {
+ "version": "9.6.1",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz",
+ "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==",
+ "dev": true,
+ "dependencies": {
+ "acorn": "^8.9.0",
+ "acorn-jsx": "^5.3.2",
+ "eslint-visitor-keys": "^3.4.1"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/esquery": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz",
+ "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==",
+ "dev": true,
+ "dependencies": {
+ "estraverse": "^5.1.0"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/esrecurse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+ "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+ "dev": true,
+ "dependencies": {
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "dev": true,
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/esutils": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/extract-zip": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz",
+ "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==",
+ "dependencies": {
+ "debug": "^4.1.1",
+ "get-stream": "^5.1.0",
+ "yauzl": "^2.10.0"
+ },
+ "bin": {
+ "extract-zip": "cli.js"
+ },
+ "engines": {
+ "node": ">= 10.17.0"
+ },
+ "optionalDependencies": {
+ "@types/yauzl": "^2.9.1"
+ }
+ },
+ "node_modules/fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+ "dev": true
+ },
+ "node_modules/fast-json-stable-stringify": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+ "dev": true
+ },
+ "node_modules/fast-levenshtein": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
+ "dev": true
+ },
+ "node_modules/fastq": {
+ "version": "1.17.1",
+ "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz",
+ "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==",
+ "dev": true,
+ "dependencies": {
+ "reusify": "^1.0.4"
+ }
+ },
+ "node_modules/fd-slicer": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz",
+ "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==",
+ "dependencies": {
+ "pend": "~1.2.0"
+ }
+ },
+ "node_modules/file-entry-cache": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
+ "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==",
+ "dev": true,
+ "dependencies": {
+ "flat-cache": "^3.0.4"
+ },
+ "engines": {
+ "node": "^10.12.0 || >=12.0.0"
+ }
+ },
+ "node_modules/find-up": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+ "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+ "dev": true,
+ "dependencies": {
+ "locate-path": "^6.0.0",
+ "path-exists": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/flat-cache": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz",
+ "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==",
+ "dev": true,
+ "dependencies": {
+ "flatted": "^3.2.9",
+ "keyv": "^4.5.3",
+ "rimraf": "^3.0.2"
+ },
+ "engines": {
+ "node": "^10.12.0 || >=12.0.0"
+ }
+ },
+ "node_modules/flatted": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz",
+ "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==",
+ "dev": true
+ },
+ "node_modules/fs-extra": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
+ "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
+ "dependencies": {
+ "graceful-fs": "^4.2.0",
+ "jsonfile": "^4.0.0",
+ "universalify": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=6 <7 || >=8"
+ }
+ },
+ "node_modules/fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
+ "dev": true
+ },
+ "node_modules/function-bind": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+ "optional": true,
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-intrinsic": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz",
+ "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==",
+ "optional": true,
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2",
+ "has-proto": "^1.0.1",
+ "has-symbols": "^1.0.3",
+ "hasown": "^2.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-stream": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
+ "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
+ "dependencies": {
+ "pump": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/glob": {
+ "version": "7.2.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+ "deprecated": "Glob versions prior to v9 are no longer supported",
+ "dev": true,
+ "dependencies": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.1.1",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ },
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/glob-parent": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
+ "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+ "dev": true,
+ "dependencies": {
+ "is-glob": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/global-agent": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/global-agent/-/global-agent-3.0.0.tgz",
+ "integrity": "sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==",
+ "optional": true,
+ "dependencies": {
+ "boolean": "^3.0.1",
+ "es6-error": "^4.1.1",
+ "matcher": "^3.0.0",
+ "roarr": "^2.15.3",
+ "semver": "^7.3.2",
+ "serialize-error": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=10.0"
+ }
+ },
+ "node_modules/global-agent/node_modules/semver": {
+ "version": "7.6.2",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz",
+ "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==",
+ "optional": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/globals": {
+ "version": "13.24.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz",
+ "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==",
+ "dev": true,
+ "dependencies": {
+ "type-fest": "^0.20.2"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/globalthis": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz",
+ "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==",
+ "optional": true,
+ "dependencies": {
+ "define-properties": "^1.2.1",
+ "gopd": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/gopd": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
+ "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==",
+ "optional": true,
+ "dependencies": {
+ "get-intrinsic": "^1.1.3"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/got": {
+ "version": "11.8.6",
+ "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz",
+ "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==",
+ "dependencies": {
+ "@sindresorhus/is": "^4.0.0",
+ "@szmarczak/http-timer": "^4.0.5",
+ "@types/cacheable-request": "^6.0.1",
+ "@types/responselike": "^1.0.0",
+ "cacheable-lookup": "^5.0.3",
+ "cacheable-request": "^7.0.2",
+ "decompress-response": "^6.0.0",
+ "http2-wrapper": "^1.0.0-beta.5.2",
+ "lowercase-keys": "^2.0.0",
+ "p-cancelable": "^2.0.0",
+ "responselike": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10.19.0"
+ },
+ "funding": {
+ "url": "https://github.com/sindresorhus/got?sponsor=1"
+ }
+ },
+ "node_modules/graceful-fs": {
+ "version": "4.2.11",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
+ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="
+ },
+ "node_modules/graphemer": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
+ "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
+ "dev": true
+ },
+ "node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/has-property-descriptors": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
+ "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
+ "optional": true,
+ "dependencies": {
+ "es-define-property": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-proto": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz",
+ "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==",
+ "optional": true,
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-symbols": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
+ "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
+ "optional": true,
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/hasown": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
+ "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
+ "optional": true,
+ "dependencies": {
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/http-cache-semantics": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz",
+ "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ=="
+ },
+ "node_modules/http2-wrapper": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz",
+ "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==",
+ "dependencies": {
+ "quick-lru": "^5.1.1",
+ "resolve-alpn": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=10.19.0"
+ }
+ },
+ "node_modules/ignore": {
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz",
+ "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==",
+ "dev": true,
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/import-fresh": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
+ "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
+ "dev": true,
+ "dependencies": {
+ "parent-module": "^1.0.0",
+ "resolve-from": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/imurmurhash": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+ "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.8.19"
+ }
+ },
+ "node_modules/inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
+ "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.",
+ "dev": true,
+ "dependencies": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "node_modules/inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+ "dev": true
+ },
+ "node_modules/is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "dev": true,
+ "dependencies": {
+ "is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-path-inside": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz",
+ "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+ "dev": true
+ },
+ "node_modules/js-yaml": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
+ "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+ "dev": true,
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/json-buffer": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
+ "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="
+ },
+ "node_modules/json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true
+ },
+ "node_modules/json-stable-stringify-without-jsonify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
+ "dev": true
+ },
+ "node_modules/json-stringify-safe": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
+ "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==",
+ "optional": true
+ },
+ "node_modules/jsonfile": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
+ "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==",
+ "optionalDependencies": {
+ "graceful-fs": "^4.1.6"
+ }
+ },
+ "node_modules/keyboardevent-from-electron-accelerator": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/keyboardevent-from-electron-accelerator/-/keyboardevent-from-electron-accelerator-2.0.0.tgz",
+ "integrity": "sha512-iQcmNA0M4ETMNi0kG/q0h/43wZk7rMeKYrXP7sqKIJbHkTU8Koowgzv+ieR/vWJbOwxx5nDC3UnudZ0aLSu4VA=="
+ },
+ "node_modules/keyboardevents-areequal": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/keyboardevents-areequal/-/keyboardevents-areequal-0.2.2.tgz",
+ "integrity": "sha512-Nv+Kr33T0mEjxR500q+I6IWisOQ0lK1GGOncV0kWE6n4KFmpcu7RUX5/2B0EUtX51Cb0HjZ9VJsSY3u4cBa0kw=="
+ },
+ "node_modules/keyv": {
+ "version": "4.5.4",
+ "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
+ "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
+ "dependencies": {
+ "json-buffer": "3.0.1"
+ }
+ },
+ "node_modules/levn": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
+ "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+ "dev": true,
+ "dependencies": {
+ "prelude-ls": "^1.2.1",
+ "type-check": "~0.4.0"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/locate-path": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+ "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+ "dev": true,
+ "dependencies": {
+ "p-locate": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/lodash.merge": {
+ "version": "4.6.2",
+ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
+ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
+ "dev": true
+ },
+ "node_modules/lowercase-keys": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz",
+ "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/matcher": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz",
+ "integrity": "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==",
+ "optional": true,
+ "dependencies": {
+ "escape-string-regexp": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "dependencies": {
+ "mime-db": "1.52.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mimic-response": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz",
+ "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+ },
+ "node_modules/natural-compare": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
+ "dev": true
+ },
+ "node_modules/negotiator": {
+ "version": "0.6.3",
+ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
+ "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/normalize-url": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz",
+ "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/object-assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+ "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-keys": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
+ "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
+ "optional": true,
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+ "dependencies": {
+ "wrappy": "1"
+ }
+ },
+ "node_modules/optionator": {
+ "version": "0.9.4",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
+ "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==",
+ "dev": true,
+ "dependencies": {
+ "deep-is": "^0.1.3",
+ "fast-levenshtein": "^2.0.6",
+ "levn": "^0.4.1",
+ "prelude-ls": "^1.2.1",
+ "type-check": "^0.4.0",
+ "word-wrap": "^1.2.5"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/p-cancelable": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz",
+ "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/p-limit": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+ "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+ "dev": true,
+ "dependencies": {
+ "yocto-queue": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-locate": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+ "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+ "dev": true,
+ "dependencies": {
+ "p-limit": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/parent-module": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+ "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+ "dev": true,
+ "dependencies": {
+ "callsites": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/path-exists": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/path-key": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/pend": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
+ "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg=="
+ },
+ "node_modules/prelude-ls": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
+ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/progress": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
+ "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/pump": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
+ "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
+ "dependencies": {
+ "end-of-stream": "^1.1.0",
+ "once": "^1.3.1"
+ }
+ },
+ "node_modules/punycode": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
+ "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/queue-microtask": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
+ "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ]
+ },
+ "node_modules/quick-lru": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz",
+ "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/resolve-alpn": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz",
+ "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g=="
+ },
+ "node_modules/resolve-from": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/responselike": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz",
+ "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==",
+ "dependencies": {
+ "lowercase-keys": "^2.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/reusify": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
+ "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
+ "dev": true,
+ "engines": {
+ "iojs": ">=1.0.0",
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/rimraf": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
+ "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+ "deprecated": "Rimraf versions prior to v4 are no longer supported",
+ "dev": true,
+ "dependencies": {
+ "glob": "^7.1.3"
+ },
+ "bin": {
+ "rimraf": "bin.js"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/roarr": {
+ "version": "2.15.4",
+ "resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz",
+ "integrity": "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==",
+ "optional": true,
+ "dependencies": {
+ "boolean": "^3.0.1",
+ "detect-node": "^2.0.4",
+ "globalthis": "^1.0.1",
+ "json-stringify-safe": "^5.0.1",
+ "semver-compare": "^1.0.0",
+ "sprintf-js": "^1.1.2"
+ },
+ "engines": {
+ "node": ">=8.0"
+ }
+ },
+ "node_modules/run-parallel": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
+ "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "dependencies": {
+ "queue-microtask": "^1.2.2"
+ }
+ },
+ "node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/semver-compare": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz",
+ "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==",
+ "optional": true
+ },
+ "node_modules/serialize-error": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz",
+ "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==",
+ "optional": true,
+ "dependencies": {
+ "type-fest": "^0.13.1"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/serialize-error/node_modules/type-fest": {
+ "version": "0.13.1",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz",
+ "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==",
+ "optional": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/shebang-command": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+ "dev": true,
+ "dependencies": {
+ "shebang-regex": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shebang-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/socket.io": {
+ "version": "4.7.4",
+ "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.7.4.tgz",
+ "integrity": "sha512-DcotgfP1Zg9iP/dH9zvAQcWrE0TtbMVwXmlV4T4mqsvY+gw+LqUGPfx2AoVyRk0FLME+GQhufDMyacFmw7ksqw==",
+ "dependencies": {
+ "accepts": "~1.3.4",
+ "base64id": "~2.0.0",
+ "cors": "~2.8.5",
+ "debug": "~4.3.2",
+ "engine.io": "~6.5.2",
+ "socket.io-adapter": "~2.5.2",
+ "socket.io-parser": "~4.2.4"
+ },
+ "engines": {
+ "node": ">=10.2.0"
+ }
+ },
+ "node_modules/socket.io-adapter": {
+ "version": "2.5.4",
+ "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.4.tgz",
+ "integrity": "sha512-wDNHGXGewWAjQPt3pyeYBtpWSq9cLE5UW1ZUPL/2eGK9jtse/FpXib7epSTsz0Q0m+6sg6Y4KtcFTlah1bdOVg==",
+ "dependencies": {
+ "debug": "~4.3.4",
+ "ws": "~8.11.0"
+ }
+ },
+ "node_modules/socket.io-parser": {
+ "version": "4.2.4",
+ "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz",
+ "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==",
+ "dependencies": {
+ "@socket.io/component-emitter": "~3.1.0",
+ "debug": "~4.3.1"
+ },
+ "engines": {
+ "node": ">=10.0.0"
+ }
+ },
+ "node_modules/sprintf-js": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz",
+ "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==",
+ "optional": true
+ },
+ "node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-json-comments": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/sudo-prompt": {
+ "version": "9.2.1",
+ "resolved": "https://registry.npmjs.org/sudo-prompt/-/sudo-prompt-9.2.1.tgz",
+ "integrity": "sha512-Mu7R0g4ig9TUuGSxJavny5Rv0egCEtpZRNMrZaYS1vxkiIxGiGUwoezU3LazIQ+KE04hTrTfNPgxU5gzi7F5Pw=="
+ },
+ "node_modules/sumchecker": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.1.tgz",
+ "integrity": "sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg==",
+ "dependencies": {
+ "debug": "^4.1.0"
+ },
+ "engines": {
+ "node": ">= 8.0"
+ }
+ },
+ "node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/text-table": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
+ "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
+ "dev": true
+ },
+ "node_modules/tmp": {
+ "version": "0.2.3",
+ "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz",
+ "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==",
+ "engines": {
+ "node": ">=14.14"
+ }
+ },
+ "node_modules/type-check": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
+ "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
+ "dev": true,
+ "dependencies": {
+ "prelude-ls": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/type-fest": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
+ "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/undici-types": {
+ "version": "5.26.5",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
+ "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="
+ },
+ "node_modules/universalify": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
+ "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
+ "engines": {
+ "node": ">= 4.0.0"
+ }
+ },
+ "node_modules/uri-js": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+ "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+ "dev": true,
+ "dependencies": {
+ "punycode": "^2.1.0"
+ }
+ },
+ "node_modules/vary": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
+ "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "dev": true,
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "node-which": "bin/node-which"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/word-wrap": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
+ "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
+ },
+ "node_modules/ws": {
+ "version": "8.11.0",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz",
+ "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==",
+ "engines": {
+ "node": ">=10.0.0"
+ },
+ "peerDependencies": {
+ "bufferutil": "^4.0.1",
+ "utf-8-validate": "^5.0.2"
+ },
+ "peerDependenciesMeta": {
+ "bufferutil": {
+ "optional": true
+ },
+ "utf-8-validate": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/yauzl": {
+ "version": "2.10.0",
+ "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz",
+ "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==",
+ "dependencies": {
+ "buffer-crc32": "~0.2.3",
+ "fd-slicer": "~1.1.0"
+ }
+ },
+ "node_modules/yocto-queue": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
+ "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ }
+ }
+}
diff --git a/src/Papercut.UI/AppLayer/AppCommands/AppCommandHub.cs b/src/Papercut.UI/AppLayer/AppCommands/AppCommandHub.cs
index c6bd6ccf..72e3908f 100644
--- a/src/Papercut.UI/AppLayer/AppCommands/AppCommandHub.cs
+++ b/src/Papercut.UI/AppLayer/AppCommands/AppCommandHub.cs
@@ -23,42 +23,41 @@
using Papercut.Domain.AppCommands;
-namespace Papercut.AppLayer.AppCommands
+namespace Papercut.AppLayer.AppCommands;
+
+public class AppCommandHub : Disposable, IAppCommandHub
{
- public class AppCommandHub : Disposable, IAppCommandHub
- {
- private readonly Subject _onShutdown = new Subject();
+ private readonly Subject _onShutdown = new Subject();
- public IObservable OnShutdown => this._onShutdown;
+ public IObservable OnShutdown => this._onShutdown;
- public void Shutdown(int exitCode = 0)
- {
- var command = new ShutdownCommand(exitCode);
- this._onShutdown.OnNext(command);
- }
+ public void Shutdown(int exitCode = 0)
+ {
+ var command = new ShutdownCommand(exitCode);
+ this._onShutdown.OnNext(command);
+ }
- protected override void Dispose(bool disposing)
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing)
{
- if (disposing)
- {
- this._onShutdown.Dispose();
- }
+ this._onShutdown.Dispose();
}
+ }
- #region Begin Static Container Registrations
-
- ///
- /// Called dynamically from the RegisterStaticMethods() call in the container module.
- ///
- ///
- [UsedImplicitly]
- static void Register(ContainerBuilder builder)
- {
- ArgumentNullException.ThrowIfNull(builder);
+ #region Begin Static Container Registrations
- builder.RegisterType().As().SingleInstance();
- }
+ ///
+ /// Called dynamically from the RegisterStaticMethods() call in the container module.
+ ///
+ ///
+ [UsedImplicitly]
+ static void Register(ContainerBuilder builder)
+ {
+ ArgumentNullException.ThrowIfNull(builder);
- #endregion
+ builder.RegisterType().As().SingleInstance();
}
+
+ #endregion
}
\ No newline at end of file
diff --git a/src/Papercut.UI/AppLayer/AppCommands/ShutdownCommandHandler.cs b/src/Papercut.UI/AppLayer/AppCommands/ShutdownCommandHandler.cs
index 0f139f26..ef7d2d66 100644
--- a/src/Papercut.UI/AppLayer/AppCommands/ShutdownCommandHandler.cs
+++ b/src/Papercut.UI/AppLayer/AppCommands/ShutdownCommandHandler.cs
@@ -29,73 +29,72 @@
using Papercut.Core.Infrastructure.Lifecycle;
using Papercut.Domain.AppCommands;
-namespace Papercut.AppLayer.AppCommands
+namespace Papercut.AppLayer.AppCommands;
+
+public class ShutdownCommandHandler : Disposable, IStartable
{
- public class ShutdownCommandHandler : Disposable, IStartable
- {
- private readonly IAppCommandHub _appCommandHub;
+ private readonly IAppCommandHub _appCommandHub;
- private readonly IAppMeta _appMeta;
+ private readonly IAppMeta _appMeta;
- private readonly ILogger _logger;
+ private readonly ILogger _logger;
- private readonly IMessageBus _messageBus;
+ private readonly IMessageBus _messageBus;
- private IDisposable _shutdownObservable;
+ private IDisposable _shutdownObservable;
- public ShutdownCommandHandler(IAppCommandHub appCommandHub, IMessageBus messageBus, IAppMeta appMeta, ILogger logger)
- {
- this._appCommandHub = appCommandHub;
- this._messageBus = messageBus;
- this._appMeta = appMeta;
- this._logger = logger.ForContext();
- }
+ public ShutdownCommandHandler(IAppCommandHub appCommandHub, IMessageBus messageBus, IAppMeta appMeta, ILogger logger)
+ {
+ this._appCommandHub = appCommandHub;
+ this._messageBus = messageBus;
+ this._appMeta = appMeta;
+ this._logger = logger.ForContext();
+ }
- public void Start()
- {
- this.InitObservables();
- }
+ public void Start()
+ {
+ this.InitObservables();
+ }
- private void InitObservables()
- {
- this._shutdownObservable = this._appCommandHub.OnShutdown
- .ObserveOn(Dispatcher.CurrentDispatcher)
- .SubscribeAsync(
- async @event =>
- {
- this._logger.Information("Shutdown Executed {ExitCode}", @event.ExitCode);
+ private void InitObservables()
+ {
+ this._shutdownObservable = this._appCommandHub.OnShutdown
+ .ObserveOn(Dispatcher.CurrentDispatcher)
+ .SubscribeAsync(
+ async @event =>
+ {
+ this._logger.Information("Shutdown Executed {ExitCode}", @event.ExitCode);
- // fire shutdown event
- await this._messageBus.PublishAsync(
- new PapercutClientExitEvent() { AppMeta = this._appMeta });
+ // fire shutdown event
+ await this._messageBus.PublishAsync(
+ new PapercutClientExitEvent() { AppMeta = this._appMeta });
- Application.Current.ShutdownMode = ShutdownMode.OnExplicitShutdown;
- Application.Current.Shutdown(@event.ExitCode);
- });
- }
+ Application.Current.ShutdownMode = ShutdownMode.OnExplicitShutdown;
+ Application.Current.Shutdown(@event.ExitCode);
+ });
+ }
- protected override void Dispose(bool disposing)
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing)
{
- if (disposing)
- {
- this._shutdownObservable?.Dispose();
- }
+ this._shutdownObservable?.Dispose();
}
+ }
- #region Begin Static Container Registrations
-
- ///
- /// Called dynamically from the RegisterStaticMethods() call in the container module.
- ///
- ///
- [UsedImplicitly]
- static void Register(ContainerBuilder builder)
- {
- ArgumentNullException.ThrowIfNull(builder);
+ #region Begin Static Container Registrations
- builder.RegisterType().AsImplementedInterfaces().SingleInstance();
- }
+ ///
+ /// Called dynamically from the RegisterStaticMethods() call in the container module.
+ ///
+ ///
+ [UsedImplicitly]
+ static void Register(ContainerBuilder builder)
+ {
+ ArgumentNullException.ThrowIfNull(builder);
- #endregion
+ builder.RegisterType().AsImplementedInterfaces().SingleInstance();
}
+
+ #endregion
}
\ No newline at end of file
diff --git a/src/Papercut.UI/AppLayer/Behaviors/DragDropIFile.cs b/src/Papercut.UI/AppLayer/Behaviors/DragDropIFile.cs
index 981b2bd1..7f061ebc 100644
--- a/src/Papercut.UI/AppLayer/Behaviors/DragDropIFile.cs
+++ b/src/Papercut.UI/AppLayer/Behaviors/DragDropIFile.cs
@@ -26,57 +26,56 @@
using Papercut.Core.Domain.Message;
using Papercut.Helpers;
-namespace Papercut.AppLayer.Behaviors
+namespace Papercut.AppLayer.Behaviors;
+
+public class DragDropIFile : Behavior
{
- public class DragDropIFile : Behavior
+ Point? _dragStartPoint;
+
+ protected override void OnAttached()
{
- Point? _dragStartPoint;
+ base.OnAttached();
- protected override void OnAttached()
- {
- base.OnAttached();
+ // bind it!
+ this.AssociatedObject.PreviewMouseMove += this.ListBoxPreviewMouseMove;
+ this.AssociatedObject.PreviewMouseLeftButtonDown += this.ListBoxPreviewLeftMouseDown;
+ this.AssociatedObject.PreviewMouseUp += this.ListBoxPreviewMouseUp;
+ }
- // bind it!
- this.AssociatedObject.PreviewMouseMove += this.ListBoxPreviewMouseMove;
- this.AssociatedObject.PreviewMouseLeftButtonDown += this.ListBoxPreviewLeftMouseDown;
- this.AssociatedObject.PreviewMouseUp += this.ListBoxPreviewMouseUp;
- }
+ void ListBoxPreviewLeftMouseDown(object sender, MouseButtonEventArgs e)
+ {
+ if (sender is not ListBox parent) return;
- void ListBoxPreviewLeftMouseDown(object sender, MouseButtonEventArgs e)
- {
- if (sender is not ListBox parent) return;
+ if (this._dragStartPoint == null) this._dragStartPoint = e.GetPosition(parent);
+ }
- if (this._dragStartPoint == null) this._dragStartPoint = e.GetPosition(parent);
- }
+ void ListBoxPreviewMouseMove(object sender, MouseEventArgs e)
+ {
+ if (sender is not ListBox parent || this._dragStartPoint == null) return;
- void ListBoxPreviewMouseMove(object sender, MouseEventArgs e)
- {
- if (sender is not ListBox parent || this._dragStartPoint == null) return;
+ if (((DependencyObject)e.OriginalSource).FindAncestor() != null) return;
- if (((DependencyObject)e.OriginalSource).FindAncestor() != null) return;
+ Point dragPoint = e.GetPosition(parent);
- Point dragPoint = e.GetPosition(parent);
+ Vector potentialDragLength = dragPoint - this._dragStartPoint.Value;
- Vector potentialDragLength = dragPoint - this._dragStartPoint.Value;
+ if (potentialDragLength.Length > 10)
+ {
+ // Get the object source for the selected item
- if (potentialDragLength.Length > 10)
+ // If the data is not null then start the drag drop operation
+ if (parent.GetObjectDataFromPoint(this._dragStartPoint.Value) is IFile entry && !string.IsNullOrWhiteSpace(entry.File))
{
- // Get the object source for the selected item
-
- // If the data is not null then start the drag drop operation
- if (parent.GetObjectDataFromPoint(this._dragStartPoint.Value) is IFile entry && !string.IsNullOrWhiteSpace(entry.File))
- {
- var dataObject = new DataObject(DataFormats.FileDrop, new[] { entry.File });
- DragDrop.DoDragDrop(parent, dataObject, DragDropEffects.Copy);
- }
-
- this._dragStartPoint = null;
+ var dataObject = new DataObject(DataFormats.FileDrop, new[] { entry.File });
+ DragDrop.DoDragDrop(parent, dataObject, DragDropEffects.Copy);
}
- }
- void ListBoxPreviewMouseUp(object sender, MouseButtonEventArgs e)
- {
this._dragStartPoint = null;
}
}
+
+ void ListBoxPreviewMouseUp(object sender, MouseButtonEventArgs e)
+ {
+ this._dragStartPoint = null;
+ }
}
\ No newline at end of file
diff --git a/src/Papercut.UI/AppLayer/Behaviors/InteractivityBlurBase.cs b/src/Papercut.UI/AppLayer/Behaviors/InteractivityBlurBase.cs
index 299420bb..36205da4 100644
--- a/src/Papercut.UI/AppLayer/Behaviors/InteractivityBlurBase.cs
+++ b/src/Papercut.UI/AppLayer/Behaviors/InteractivityBlurBase.cs
@@ -21,28 +21,27 @@
using Microsoft.Xaml.Behaviors;
-namespace Papercut.AppLayer.Behaviors
+namespace Papercut.AppLayer.Behaviors;
+
+public class InteractivityBlurBase : Behavior
+ where T : DependencyObject
{
- public class InteractivityBlurBase : Behavior
- where T : DependencyObject
- {
- // Using a DependencyProperty as the backing store for BlurRadius. This enables animation, styling, binding, etc...
- public static readonly DependencyProperty BlurRadiusProperty =
- DependencyProperty.Register(
- $"BlurRadius_{typeof(T).Name}",
- typeof(int),
- typeof(FrameworkElement),
- new UIPropertyMetadata(0));
+ // Using a DependencyProperty as the backing store for BlurRadius. This enables animation, styling, binding, etc...
+ public static readonly DependencyProperty BlurRadiusProperty =
+ DependencyProperty.Register(
+ $"BlurRadius_{typeof(T).Name}",
+ typeof(int),
+ typeof(FrameworkElement),
+ new UIPropertyMetadata(0));
- public int BlurRadius
- {
- get => (int) this.GetValue(BlurRadiusProperty);
- set => this.SetValue(BlurRadiusProperty, value);
- }
+ public int BlurRadius
+ {
+ get => (int) this.GetValue(BlurRadiusProperty);
+ set => this.SetValue(BlurRadiusProperty, value);
+ }
- protected virtual BlurEffect GetBlurEffect()
- {
- return new BlurEffect { Radius = this.BlurRadius };
- }
+ protected virtual BlurEffect GetBlurEffect()
+ {
+ return new BlurEffect { Radius = this.BlurRadius };
}
}
\ No newline at end of file
diff --git a/src/Papercut.UI/AppLayer/Behaviors/InteractivityBlurOnDisabled.cs b/src/Papercut.UI/AppLayer/Behaviors/InteractivityBlurOnDisabled.cs
index e15c9895..34f386c6 100644
--- a/src/Papercut.UI/AppLayer/Behaviors/InteractivityBlurOnDisabled.cs
+++ b/src/Papercut.UI/AppLayer/Behaviors/InteractivityBlurOnDisabled.cs
@@ -18,21 +18,20 @@
using System.Windows;
-namespace Papercut.AppLayer.Behaviors
+namespace Papercut.AppLayer.Behaviors;
+
+public class InteractivityBlurOnDisabled : InteractivityBlurBase
{
- public class InteractivityBlurOnDisabled : InteractivityBlurBase
+ protected override void OnAttached()
{
- protected override void OnAttached()
- {
- base.OnAttached();
- this.AssociatedObject.IsEnabledChanged += this.AssociatedObjectIsEnabledChanged;
- }
+ base.OnAttached();
+ this.AssociatedObject.IsEnabledChanged += this.AssociatedObjectIsEnabledChanged;
+ }
- void AssociatedObjectIsEnabledChanged(object sender, DependencyPropertyChangedEventArgs e)
- {
- this.AssociatedObject.Effect = this.AssociatedObject.IsEnabled
- ? null
- : this.GetBlurEffect();
- }
+ void AssociatedObjectIsEnabledChanged(object sender, DependencyPropertyChangedEventArgs e)
+ {
+ this.AssociatedObject.Effect = this.AssociatedObject.IsEnabled
+ ? null
+ : this.GetBlurEffect();
}
}
\ No newline at end of file
diff --git a/src/Papercut.UI/AppLayer/Behaviors/InteractivityBlurWindowOnDeactivate.cs b/src/Papercut.UI/AppLayer/Behaviors/InteractivityBlurWindowOnDeactivate.cs
index 4cde2aa4..b85d4793 100644
--- a/src/Papercut.UI/AppLayer/Behaviors/InteractivityBlurWindowOnDeactivate.cs
+++ b/src/Papercut.UI/AppLayer/Behaviors/InteractivityBlurWindowOnDeactivate.cs
@@ -18,32 +18,31 @@
using System.Windows;
-namespace Papercut.AppLayer.Behaviors
+namespace Papercut.AppLayer.Behaviors;
+
+public class InteractivityBlurWindowOnDeactivate : InteractivityBlurBase
{
- public class InteractivityBlurWindowOnDeactivate : InteractivityBlurBase
+ protected override void OnAttached()
+ {
+ base.OnAttached();
+ this.AssociatedObject.Deactivated += this.AssociatedObjectOnDeactivated;
+ this.AssociatedObject.Activated += this.AssociatedObjectOnActivated;
+ }
+
+ void AssociatedObjectOnActivated(object sender, EventArgs eventArgs)
+ {
+ this.ToggleBlurEffect(false);
+ }
+
+ void AssociatedObjectOnDeactivated(object sender, EventArgs eventArgs)
+ {
+ this.ToggleBlurEffect(true);
+ }
+
+ protected void ToggleBlurEffect(bool enable)
{
- protected override void OnAttached()
- {
- base.OnAttached();
- this.AssociatedObject.Deactivated += this.AssociatedObjectOnDeactivated;
- this.AssociatedObject.Activated += this.AssociatedObjectOnActivated;
- }
-
- void AssociatedObjectOnActivated(object sender, EventArgs eventArgs)
- {
- this.ToggleBlurEffect(false);
- }
-
- void AssociatedObjectOnDeactivated(object sender, EventArgs eventArgs)
- {
- this.ToggleBlurEffect(true);
- }
-
- protected void ToggleBlurEffect(bool enable)
- {
- this.AssociatedObject.Effect = enable ? this.GetBlurEffect() : null;
- }
+ this.AssociatedObject.Effect = enable ? this.GetBlurEffect() : null;
}
}
\ No newline at end of file
diff --git a/src/Papercut.UI/AppLayer/Behaviors/SettingBindingExtension.cs b/src/Papercut.UI/AppLayer/Behaviors/SettingBindingExtension.cs
index c7f4e6e4..f2b1dde3 100644
--- a/src/Papercut.UI/AppLayer/Behaviors/SettingBindingExtension.cs
+++ b/src/Papercut.UI/AppLayer/Behaviors/SettingBindingExtension.cs
@@ -18,29 +18,28 @@
using System.Windows.Data;
-namespace Papercut.AppLayer.Behaviors
+namespace Papercut.AppLayer.Behaviors;
+
+///
+/// Very useful code from here:
+/// http://tomlev2.wordpress.com/2008/11/18/wpf-binding-to-application-settings-using-a-markup-extension/
+///
+public class SettingBindingExtension : Binding
{
- ///
- /// Very useful code from here:
- /// http://tomlev2.wordpress.com/2008/11/18/wpf-binding-to-application-settings-using-a-markup-extension/
- ///
- public class SettingBindingExtension : Binding
+ public SettingBindingExtension()
{
- public SettingBindingExtension()
- {
- this.Initialize();
- }
+ this.Initialize();
+ }
- public SettingBindingExtension(string path)
- : base(path)
- {
- this.Initialize();
- }
+ public SettingBindingExtension(string path)
+ : base(path)
+ {
+ this.Initialize();
+ }
- void Initialize()
- {
- this.Source = Properties.Settings.Default;
- this.Mode = BindingMode.TwoWay;
- }
+ void Initialize()
+ {
+ this.Source = Properties.Settings.Default;
+ this.Mode = BindingMode.TwoWay;
}
}
\ No newline at end of file
diff --git a/src/Papercut.UI/AppLayer/Cleanup/TempDirectoryCleanupService.cs b/src/Papercut.UI/AppLayer/Cleanup/TempDirectoryCleanupService.cs
index 0aca5a8e..8f210503 100644
--- a/src/Papercut.UI/AppLayer/Cleanup/TempDirectoryCleanupService.cs
+++ b/src/Papercut.UI/AppLayer/Cleanup/TempDirectoryCleanupService.cs
@@ -23,68 +23,67 @@
using Papercut.Core.Domain.Application;
using Papercut.Domain.LifecycleHooks;
-namespace Papercut.AppLayer.Cleanup
+namespace Papercut.AppLayer.Cleanup;
+
+public class TempDirectoryCleanupService(IAppMeta appMeta, ILogger logger) : IAppLifecyclePreExit
{
- public class TempDirectoryCleanupService(IAppMeta appMeta, ILogger logger) : IAppLifecyclePreExit
+ public Task OnPreExit()
{
- public Task OnPreExit()
- {
- // time for temp file cleanup
- this.TryCleanUpTempDirectories();
+ // time for temp file cleanup
+ this.TryCleanUpTempDirectories();
- return Task.FromResult(AppLifecycleActionResultType.Continue);
- }
+ return Task.FromResult(AppLifecycleActionResultType.Continue);
+ }
+
+ private void TryCleanUpTempDirectories()
+ {
+ int deleteCount = 0;
+ string tempPath = Path.GetTempPath();
- private void TryCleanUpTempDirectories()
+ // try cleanup...
+ try
{
- int deleteCount = 0;
- string tempPath = Path.GetTempPath();
+ string[] tmpDirs = Directory.GetDirectories(tempPath, $"{appMeta.AppName}-*");
- // try cleanup...
- try
+ foreach (string tmpDir in tmpDirs)
{
- string[] tmpDirs = Directory.GetDirectories(tempPath, $"{appMeta.AppName}-*");
-
- foreach (string tmpDir in tmpDirs)
+ try
{
- try
- {
- Directory.Delete(tmpDir, true);
- deleteCount++;
- }
- catch (Exception ex)
- {
- logger.Warning(ex, @"Unable to delete {TempDirectory}", tmpDir);
- }
+ Directory.Delete(tmpDir, true);
+ deleteCount++;
+ }
+ catch (Exception ex)
+ {
+ logger.Warning(ex, @"Unable to delete {TempDirectory}", tmpDir);
}
}
- catch (Exception ex)
- {
- logger.Warning(
- ex,
- @"Failure running temp directory cleanup on temp path {TempPath}",
- tempPath);
- }
-
- if (deleteCount > 0)
- logger.Information("Deleted {DeleteCount} temporary directories", deleteCount);
+ }
+ catch (Exception ex)
+ {
+ logger.Warning(
+ ex,
+ @"Failure running temp directory cleanup on temp path {TempPath}",
+ tempPath);
}
- #region Begin Static Container Registrations
+ if (deleteCount > 0)
+ logger.Information("Deleted {DeleteCount} temporary directories", deleteCount);
+ }
- ///
- /// Called dynamically from the RegisterStaticMethods() call in the container module.
- ///
- ///
- [UsedImplicitly]
- static void Register(ContainerBuilder builder)
- {
- ArgumentNullException.ThrowIfNull(builder);
+ #region Begin Static Container Registrations
- builder.RegisterType().AsImplementedInterfaces()
- .SingleInstance();
- }
+ ///
+ /// Called dynamically from the RegisterStaticMethods() call in the container module.
+ ///
+ ///
+ [UsedImplicitly]
+ static void Register(ContainerBuilder builder)
+ {
+ ArgumentNullException.ThrowIfNull(builder);
- #endregion
+ builder.RegisterType().AsImplementedInterfaces()
+ .SingleInstance();
}
+
+ #endregion
}
\ No newline at end of file
diff --git a/src/Papercut.UI/AppLayer/Diagnostics/ReportVersionService.cs b/src/Papercut.UI/AppLayer/Diagnostics/ReportVersionService.cs
index 1e177b7e..d36178c6 100644
--- a/src/Papercut.UI/AppLayer/Diagnostics/ReportVersionService.cs
+++ b/src/Papercut.UI/AppLayer/Diagnostics/ReportVersionService.cs
@@ -21,31 +21,30 @@
using Autofac;
-namespace Papercut.AppLayer.Diagnostics
+namespace Papercut.AppLayer.Diagnostics;
+
+public class ReportVersionService(ILogger logger) : IStartable
{
- public class ReportVersionService(ILogger logger) : IStartable
+ public void Start()
{
- public void Start()
- {
- var productVersion = FileVersionInfo.GetVersionInfo(Assembly.GetExecutingAssembly().Location).ProductVersion;
-
- logger.Information("Papercut Version {PapercutVersion:l}", productVersion);
- }
+ var productVersion = FileVersionInfo.GetVersionInfo(Assembly.GetExecutingAssembly().Location).ProductVersion;
- #region Begin Static Container Registrations
+ logger.Information("Papercut Version {PapercutVersion:l}", productVersion);
+ }
- ///
- /// Called dynamically from the RegisterStaticMethods() call in the container module.
- ///
- ///
- [UsedImplicitly]
- static void Register(ContainerBuilder builder)
- {
- ArgumentNullException.ThrowIfNull(builder);
+ #region Begin Static Container Registrations
- builder.RegisterType().AsImplementedInterfaces().SingleInstance();
- }
+ ///
+ /// Called dynamically from the RegisterStaticMethods() call in the container module.
+ ///
+ ///
+ [UsedImplicitly]
+ static void Register(ContainerBuilder builder)
+ {
+ ArgumentNullException.ThrowIfNull(builder);
- #endregion
+ builder.RegisterType().AsImplementedInterfaces().SingleInstance();
}
+
+ #endregion
}
\ No newline at end of file
diff --git a/src/Papercut.UI/AppLayer/Events/EventPublishAll.cs b/src/Papercut.UI/AppLayer/Events/EventPublishAll.cs
index accfc4ac..9f3ca10c 100644
--- a/src/Papercut.UI/AppLayer/Events/EventPublishAll.cs
+++ b/src/Papercut.UI/AppLayer/Events/EventPublishAll.cs
@@ -23,47 +23,46 @@
using Papercut.Common.Domain;
using Papercut.Core.Infrastructure.MessageBus;
-namespace Papercut.AppLayer.Events
-{
- public class EventPublishAll : AutofacMessageBus
- {
- private readonly IEventAggregator _eventAggregator;
+namespace Papercut.AppLayer.Events;
- public EventPublishAll(
- ILifetimeScope scope,
- IEventAggregator eventAggregator)
- : base(scope)
- {
- this._eventAggregator = eventAggregator;
- }
+public class EventPublishAll : AutofacMessageBus
+{
+ private readonly IEventAggregator _eventAggregator;
- public override async Task PublishAsync(T eventObject, CancellationToken token)
- {
- if (eventObject == null) throw new ArgumentNullException(nameof(eventObject));
+ public EventPublishAll(
+ ILifetimeScope scope,
+ IEventAggregator eventAggregator)
+ : base(scope)
+ {
+ this._eventAggregator = eventAggregator;
+ }
- await base.PublishAsync(eventObject, token);
- await this._eventAggregator.PublishOnUIThreadAsync(eventObject, token);
- }
+ public override async Task PublishAsync(T eventObject, CancellationToken token)
+ {
+ if (eventObject == null) throw new ArgumentNullException(nameof(eventObject));
- protected override async Task HandleAsync(T eventObject, IEventHandler @event, CancellationToken token)
- {
- await Execute.OnUIThreadAsync(async () => await base.HandleAsync(eventObject, @event, token));
- }
+ await base.PublishAsync(eventObject, token);
+ await this._eventAggregator.PublishOnUIThreadAsync(eventObject, token);
+ }
- #region Begin Static Container Registrations
+ protected override async Task HandleAsync(T eventObject, IEventHandler @event, CancellationToken token)
+ {
+ await Execute.OnUIThreadAsync(async () => await base.HandleAsync(eventObject, @event, token));
+ }
- ///
- /// Called dynamically from the RegisterStaticMethods() call in the container module.
- ///
- ///
- [UsedImplicitly]
- static void Register(ContainerBuilder builder)
- {
- ArgumentNullException.ThrowIfNull(builder);
+ #region Begin Static Container Registrations
- builder.RegisterType().As().InstancePerLifetimeScope();
- }
+ ///
+ /// Called dynamically from the RegisterStaticMethods() call in the container module.
+ ///
+ ///
+ [UsedImplicitly]
+ static void Register(ContainerBuilder builder)
+ {
+ ArgumentNullException.ThrowIfNull(builder);
- #endregion
+ builder.RegisterType().As().InstancePerLifetimeScope();
}
+
+ #endregion
}
\ No newline at end of file
diff --git a/src/Papercut.UI/AppLayer/HtmlPreviews/HtmlPreviewGeneratorImpl.cs b/src/Papercut.UI/AppLayer/HtmlPreviews/HtmlPreviewGeneratorImpl.cs
index c6489475..17219b30 100644
--- a/src/Papercut.UI/AppLayer/HtmlPreviews/HtmlPreviewGeneratorImpl.cs
+++ b/src/Papercut.UI/AppLayer/HtmlPreviews/HtmlPreviewGeneratorImpl.cs
@@ -27,76 +27,75 @@
using Papercut.Domain.HtmlPreviews;
using Papercut.Helpers;
-namespace Papercut.AppLayer.HtmlPreviews
+namespace Papercut.AppLayer.HtmlPreviews;
+
+public class HtmlPreviewGeneratorImpl : IHtmlPreviewGenerator
{
- public class HtmlPreviewGeneratorImpl : IHtmlPreviewGenerator
- {
- readonly IAppMeta _appMeta;
+ readonly IAppMeta _appMeta;
- readonly ILogger _logger;
+ readonly ILogger _logger;
- public HtmlPreviewGeneratorImpl(ILogger logger, IAppMeta appMeta)
- {
- this._logger = logger;
- this._appMeta = appMeta;
- }
+ public HtmlPreviewGeneratorImpl(ILogger logger, IAppMeta appMeta)
+ {
+ this._logger = logger;
+ this._appMeta = appMeta;
+ }
- public string GetHtmlPreview(MimeMessage? mailMessageEx, string? tempDir = null)
- {
- ArgumentNullException.ThrowIfNull(mailMessageEx);
+ public string GetHtmlPreview(MimeMessage? mailMessageEx, string? tempDir = null)
+ {
+ ArgumentNullException.ThrowIfNull(mailMessageEx);
- tempDir = tempDir ?? this.CreateUniqueTempDirectory();
- var visitor = new HtmlPreviewVisitor(tempDir);
- mailMessageEx.Accept(visitor);
+ tempDir = tempDir ?? this.CreateUniqueTempDirectory();
+ var visitor = new HtmlPreviewVisitor(tempDir);
+ mailMessageEx.Accept(visitor);
- return visitor.HtmlBody;
- }
+ return visitor.HtmlBody;
+ }
- public string? GetHtmlPreviewFile(MimeMessage? mailMessageEx, string? tempDir = null)
- {
- tempDir = tempDir ?? this.CreateUniqueTempDirectory();
+ public string? GetHtmlPreviewFile(MimeMessage? mailMessageEx, string? tempDir = null)
+ {
+ tempDir = tempDir ?? this.CreateUniqueTempDirectory();
- var htmlPreview = this.GetHtmlPreview(mailMessageEx, tempDir);
+ var htmlPreview = this.GetHtmlPreview(mailMessageEx, tempDir);
- string? htmlFile = Path.Combine(tempDir, "index.html");
+ string? htmlFile = Path.Combine(tempDir, "index.html");
- this._logger.Verbose("Writing HTML Preview file {HtmlFile}", htmlFile);
+ this._logger.Verbose("Writing HTML Preview file {HtmlFile}", htmlFile);
- File.WriteAllText(htmlFile, htmlPreview, Encoding.Unicode);
+ File.WriteAllText(htmlFile, htmlPreview, Encoding.Unicode);
- return htmlFile;
- }
+ return htmlFile;
+ }
- string CreateUniqueTempDirectory()
+ string CreateUniqueTempDirectory()
+ {
+ string tempDir;
+ do
{
- string tempDir;
- do
- {
- // find unique temp directory
- tempDir = Path.Combine(Path.GetTempPath(), $"{this._appMeta.AppName}-{Guid.NewGuid().ToString().Truncate(6)}");
- }
- while (Directory.Exists(tempDir));
-
- Directory.CreateDirectory(tempDir);
-
- return tempDir;
+ // find unique temp directory
+ tempDir = Path.Combine(Path.GetTempPath(), $"{this._appMeta.AppName}-{Guid.NewGuid().ToString().Truncate(6)}");
}
+ while (Directory.Exists(tempDir));
- #region Begin Static Container Registrations
+ Directory.CreateDirectory(tempDir);
- ///
- /// Called dynamically from the RegisterStaticMethods() call in the container module.
- ///
- ///
- [UsedImplicitly]
- static void Register(ContainerBuilder builder)
- {
- ArgumentNullException.ThrowIfNull(builder);
+ return tempDir;
+ }
- builder.RegisterType().AsImplementedInterfaces()
- .InstancePerLifetimeScope();
- }
+ #region Begin Static Container Registrations
- #endregion
+ ///
+ /// Called dynamically from the RegisterStaticMethods() call in the container module.
+ ///
+ ///
+ [UsedImplicitly]
+ static void Register(ContainerBuilder builder)
+ {
+ ArgumentNullException.ThrowIfNull(builder);
+
+ builder.RegisterType().AsImplementedInterfaces()
+ .InstancePerLifetimeScope();
}
+
+ #endregion
}
\ No newline at end of file
diff --git a/src/Papercut.UI/AppLayer/IpComm/BackendServiceCoordinator.cs b/src/Papercut.UI/AppLayer/IpComm/BackendServiceCoordinator.cs
index 47657feb..f889e67f 100644
--- a/src/Papercut.UI/AppLayer/IpComm/BackendServiceCoordinator.cs
+++ b/src/Papercut.UI/AppLayer/IpComm/BackendServiceCoordinator.cs
@@ -34,227 +34,226 @@
using Papercut.Infrastructure.IPComm;
using Papercut.Infrastructure.IPComm.Network;
-namespace Papercut.AppLayer.IpComm
+namespace Papercut.AppLayer.IpComm;
+
+public class BackendServiceCoordinator : IBackendServiceStatus, IAppLifecycleStarted,
+ IEventHandler,
+ IEventHandler,
+ IEventHandler,
+ IEventHandler,
+ IEventHandler, IOrderable
{
- public class BackendServiceCoordinator : IBackendServiceStatus, IAppLifecycleStarted,
- IEventHandler,
- IEventHandler,
- IEventHandler,
- IEventHandler,
- IEventHandler, IOrderable
- {
- const string BackendServiceFailureMessage =
- "Papercut Backend Service Exception Attempting to Contact";
-
- readonly PapercutIPCommClientFactory _ipCommClientFactory;
+ const string BackendServiceFailureMessage =
+ "Papercut Backend Service Exception Attempting to Contact";
- readonly ILogger _logger;
+ readonly PapercutIPCommClientFactory _ipCommClientFactory;
- readonly IMessageBus _messageBus;
+ readonly ILogger _logger;
- private bool? _isOnline = null;
+ readonly IMessageBus _messageBus;
- Action _nextUpdateEvent;
+ private bool? _isOnline = null;
- public BackendServiceCoordinator(
- ILogger logger,
- IMessageBus messageBus,
- PapercutIPCommClientFactory ipCommClientFactory)
- {
- this._logger = logger;
- this._messageBus = messageBus;
- this._ipCommClientFactory = ipCommClientFactory;
-
- IObservable rulesUpdateObservable = Observable
- .Create(
- o =>
- {
- this._nextUpdateEvent = o.OnNext;
- return Disposable.Empty;
- }).SubscribeOn(TaskPoolScheduler.Default);
-
- // flush rules every 10 seconds
- rulesUpdateObservable.Buffer(TimeSpan.FromSeconds(10))
- .Where(e => e.Any())
- .SubscribeAsync(async events => await this.PublishUpdateEvent(events.Last()));
- }
+ Action _nextUpdateEvent;
- public async Task OnStartedAsync()
- {
- await this.AttemptExchangeAsync();
- }
+ public BackendServiceCoordinator(
+ ILogger logger,
+ IMessageBus messageBus,
+ PapercutIPCommClientFactory ipCommClientFactory)
+ {
+ this._logger = logger;
+ this._messageBus = messageBus;
+ this._ipCommClientFactory = ipCommClientFactory;
- public bool IsOnline
- {
- get => this._isOnline ?? false;
- private set => this._isOnline = value;
- }
+ IObservable rulesUpdateObservable = Observable
+ .Create(
+ o =>
+ {
+ this._nextUpdateEvent = o.OnNext;
+ return Disposable.Empty;
+ }).SubscribeOn(TaskPoolScheduler.Default);
+
+ // flush rules every 10 seconds
+ rulesUpdateObservable.Buffer(TimeSpan.FromSeconds(10))
+ .Where(e => e.Any())
+ .SubscribeAsync(async events => await this.PublishUpdateEvent(events.Last()));
+ }
- public async Task HandleAsync(PapercutServiceExitEvent @event, CancellationToken token)
- {
- await this.SetOnlineStatus(false, token);
- }
+ public async Task OnStartedAsync()
+ {
+ await this.AttemptExchangeAsync();
+ }
- public async Task HandleAsync(PapercutServicePreStartEvent @event, CancellationToken token)
- {
- await this.SetOnlineStatus(true, token);
- }
+ public bool IsOnline
+ {
+ get => this._isOnline ?? false;
+ private set => this._isOnline = value;
+ }
- public async Task HandleAsync(PapercutServiceReadyEvent @event, CancellationToken token)
- {
- await this.SetOnlineStatus(true, token);
- }
+ public async Task HandleAsync(PapercutServiceExitEvent @event, CancellationToken token)
+ {
+ await this.SetOnlineStatus(false, token);
+ }
- public Task HandleAsync(RulesUpdatedEvent @event, CancellationToken token)
- {
- if (this.IsOnline)
- {
- this._nextUpdateEvent(@event);
- }
+ public async Task HandleAsync(PapercutServicePreStartEvent @event, CancellationToken token)
+ {
+ await this.SetOnlineStatus(true, token);
+ }
- return Task.CompletedTask;
- }
+ public async Task HandleAsync(PapercutServiceReadyEvent @event, CancellationToken token)
+ {
+ await this.SetOnlineStatus(true, token);
+ }
- public async Task HandleAsync(SettingsUpdatedEvent @event, CancellationToken token)
+ public Task HandleAsync(RulesUpdatedEvent @event, CancellationToken token)
+ {
+ if (this.IsOnline)
{
- await this.PublishSmtpUpdatedAsync(@event, token);
+ this._nextUpdateEvent(@event);
}
- public int Order => 10;
+ return Task.CompletedTask;
+ }
+
+ public async Task HandleAsync(SettingsUpdatedEvent @event, CancellationToken token)
+ {
+ await this.PublishSmtpUpdatedAsync(@event, token);
+ }
+
+ public int Order => 10;
- private async Task SetOnlineStatus(bool newStatus, CancellationToken token = default)
+ private async Task SetOnlineStatus(bool newStatus, CancellationToken token = default)
+ {
+ if (this._isOnline != newStatus)
{
- if (this._isOnline != newStatus)
- {
- this.IsOnline = newStatus;
-
- await this._messageBus.PublishAsync(
- new PapercutServiceStatusEvent(
- this.IsOnline
- ? PapercutServiceStatusType.Online
- : PapercutServiceStatusType.Offline),
- token);
- }
+ this.IsOnline = newStatus;
+
+ await this._messageBus.PublishAsync(
+ new PapercutServiceStatusEvent(
+ this.IsOnline
+ ? PapercutServiceStatusType.Online
+ : PapercutServiceStatusType.Offline),
+ token);
}
+ }
- public async Task PublishSmtpUpdatedAsync(SettingsUpdatedEvent @event, CancellationToken token)
- {
- if (!this.IsOnline) return;
+ public async Task PublishSmtpUpdatedAsync(SettingsUpdatedEvent @event, CancellationToken token)
+ {
+ if (!this.IsOnline) return;
- // check if the setting changed
- if (@event.PreviousSettings.IP == @event.NewSettings.IP && @event.PreviousSettings.Port == @event.NewSettings.Port) return;
+ // check if the setting changed
+ if (@event.PreviousSettings.IP == @event.NewSettings.IP && @event.PreviousSettings.Port == @event.NewSettings.Port) return;
- try
- {
- var messenger = this.GetClient();
+ try
+ {
+ var messenger = this.GetClient();
- // update the backend service with the new ip/port settings...
- var smtpServerBindEvent = new SmtpServerBindEvent(
- Properties.Settings.Default.IP,
- Properties.Settings.Default.Port);
+ // update the backend service with the new ip/port settings...
+ var smtpServerBindEvent = new SmtpServerBindEvent(
+ Properties.Settings.Default.IP,
+ Properties.Settings.Default.Port);
- bool successfulPublish = await messenger.PublishEventServer(
- smtpServerBindEvent,
- TimeSpan.FromSeconds(1));
+ bool successfulPublish = await messenger.PublishEventServer(
+ smtpServerBindEvent,
+ TimeSpan.FromSeconds(1));
- this._logger.Information(
- successfulPublish
- ? "Successfully pushed new Smtp Server Binding to Backend Service"
- : "Papercut Backend Service Failed to Update. Could be offline.");
- }
- catch (Exception ex) when (ex is TaskCanceledException || ex is ObjectDisposedException)
- {
- // do nothing
- }
- catch (Exception ex)
- {
- this._logger.Warning(ex, BackendServiceFailureMessage);
- }
+ this._logger.Information(
+ successfulPublish
+ ? "Successfully pushed new Smtp Server Binding to Backend Service"
+ : "Papercut Backend Service Failed to Update. Could be offline.");
}
+ catch (Exception ex) when (ex is TaskCanceledException || ex is ObjectDisposedException)
+ {
+ // do nothing
+ }
+ catch (Exception ex)
+ {
+ this._logger.Warning(ex, BackendServiceFailureMessage);
+ }
+ }
- private async Task AttemptExchangeAsync(CancellationToken token = default)
+ private async Task AttemptExchangeAsync(CancellationToken token = default)
+ {
+ try
{
- try
- {
- var sendEvent = new AppProcessExchangeEvent();
+ var sendEvent = new AppProcessExchangeEvent();
- // attempt to connect to the backend server...
- var ipCommClient = this.GetClient();
+ // attempt to connect to the backend server...
+ var ipCommClient = this.GetClient();
- var receivedEvent = await ipCommClient.ExchangeEventServer(sendEvent, TimeSpan.FromSeconds(1));
+ var receivedEvent = await ipCommClient.ExchangeEventServer(sendEvent, TimeSpan.FromSeconds(1));
- if (receivedEvent != null)
+ if (receivedEvent != null)
+ {
+ if (!string.IsNullOrWhiteSpace(receivedEvent.MessageWritePath))
{
- if (!string.IsNullOrWhiteSpace(receivedEvent.MessageWritePath))
- {
- this._logger.Debug(
- "Background Process Returned {@Event} -- Publishing",
- receivedEvent);
+ this._logger.Debug(
+ "Background Process Returned {@Event} -- Publishing",
+ receivedEvent);
- await this._messageBus.PublishAsync(receivedEvent, token);
- }
+ await this._messageBus.PublishAsync(receivedEvent, token);
+ }
- await this.SetOnlineStatus(true, token);
+ await this.SetOnlineStatus(true, token);
- return;
- }
+ return;
}
- catch (Exception ex) when (ex is TaskCanceledException or ObjectDisposedException)
- {
- // do nothing
- }
- catch (Exception ex)
- {
- this._logger.Warning(ex, BackendServiceFailureMessage);
- }
-
- // publish status message regardless
- await this.SetOnlineStatus(false, token);
+ }
+ catch (Exception ex) when (ex is TaskCanceledException or ObjectDisposedException)
+ {
+ // do nothing
+ }
+ catch (Exception ex)
+ {
+ this._logger.Warning(ex, BackendServiceFailureMessage);
}
- async Task PublishUpdateEvent(RulesUpdatedEvent @event)
+ // publish status message regardless
+ await this.SetOnlineStatus(false, token);
+ }
+
+ async Task PublishUpdateEvent(RulesUpdatedEvent @event)
+ {
+ try
{
- try
- {
- var ipCommClient = this.GetClient();
+ var ipCommClient = this.GetClient();
- bool successfulPublish = await ipCommClient.PublishEventServer(@event, TimeSpan.FromSeconds(1));
+ bool successfulPublish = await ipCommClient.PublishEventServer(@event, TimeSpan.FromSeconds(1));
- this._logger.Information(
- successfulPublish
- ? "Successfully Updated Rules on Backend Service"
- : "Papercut Backend Service Failed to Update Rules. Could be offline.");
- }
- catch (Exception ex) when (ex is TaskCanceledException || ex is ObjectDisposedException)
- {
- // do nothing
- }
- catch (Exception ex)
- {
- this._logger.Warning(ex, BackendServiceFailureMessage);
- }
+ this._logger.Information(
+ successfulPublish
+ ? "Successfully Updated Rules on Backend Service"
+ : "Papercut Backend Service Failed to Update Rules. Could be offline.");
}
-
- PapercutIPCommClient GetClient()
+ catch (Exception ex) when (ex is TaskCanceledException || ex is ObjectDisposedException)
+ {
+ // do nothing
+ }
+ catch (Exception ex)
{
- return this._ipCommClientFactory.GetClient(PapercutIPCommClientConnectTo.Service);
+ this._logger.Warning(ex, BackendServiceFailureMessage);
}
+ }
- #region Begin Static Container Registrations
+ PapercutIPCommClient GetClient()
+ {
+ return this._ipCommClientFactory.GetClient(PapercutIPCommClientConnectTo.Service);
+ }
- ///
- /// Called dynamically from the RegisterStaticMethods() call in the container module.
- ///
- ///
- [UsedImplicitly]
- static void Register(ContainerBuilder builder)
- {
- ArgumentNullException.ThrowIfNull(builder);
+ #region Begin Static Container Registrations
- builder.RegisterType().AsImplementedInterfaces()
- .InstancePerLifetimeScope();
- }
+ ///
+ /// Called dynamically from the RegisterStaticMethods() call in the container module.
+ ///
+ ///
+ [UsedImplicitly]
+ static void Register(ContainerBuilder builder)
+ {
+ ArgumentNullException.ThrowIfNull(builder);
- #endregion
+ builder.RegisterType().AsImplementedInterfaces()
+ .InstancePerLifetimeScope();
}
+
+ #endregion
}
\ No newline at end of file
diff --git a/src/Papercut.UI/AppLayer/IpComm/PapercutIpCommManager.cs b/src/Papercut.UI/AppLayer/IpComm/PapercutIpCommManager.cs
index 1b08366a..404c6abf 100644
--- a/src/Papercut.UI/AppLayer/IpComm/PapercutIpCommManager.cs
+++ b/src/Papercut.UI/AppLayer/IpComm/PapercutIpCommManager.cs
@@ -22,67 +22,66 @@
using Papercut.Domain.LifecycleHooks;
using Papercut.Infrastructure.IPComm.Network;
-namespace Papercut.AppLayer.IpComm
+namespace Papercut.AppLayer.IpComm;
+
+public class PapercutIpCommManager : Disposable, IAppLifecycleStarted
{
- public class PapercutIpCommManager : Disposable, IAppLifecycleStarted
- {
- readonly ILogger _logger;
+ readonly ILogger _logger;
+
+ private readonly PapercutIPCommEndpoints _papercutIpCommEndpoints;
- private readonly PapercutIPCommEndpoints _papercutIpCommEndpoints;
+ readonly PapercutIPCommServer _papercutIpCommServer;
+
+ public PapercutIpCommManager(
+ PapercutIPCommEndpoints papercutIpCommEndpoints,
+ PapercutIPCommServer ipCommServer,
+ ILogger logger)
+ {
+ this._papercutIpCommEndpoints = papercutIpCommEndpoints;
+ this._logger = logger;
+ this._papercutIpCommServer = ipCommServer;
+ }
- readonly PapercutIPCommServer _papercutIpCommServer;
+ public async Task OnStartedAsync()
+ {
+ await this._papercutIpCommServer.StopAsync();
- public PapercutIpCommManager(
- PapercutIPCommEndpoints papercutIpCommEndpoints,
- PapercutIPCommServer ipCommServer,
- ILogger logger)
+ try
{
- this._papercutIpCommEndpoints = papercutIpCommEndpoints;
- this._logger = logger;
- this._papercutIpCommServer = ipCommServer;
+ await this._papercutIpCommServer.StartAsync(this._papercutIpCommEndpoints.UI);
}
-
- public async Task OnStartedAsync()
+ catch (Exception ex)
{
- await this._papercutIpCommServer.StopAsync();
-
- try
- {
- await this._papercutIpCommServer.StartAsync(this._papercutIpCommEndpoints.UI);
- }
- catch (Exception ex)
- {
- this._logger.Warning(
- ex,
- "Papercut IPComm Server failed to bind to the {Address} {Port} specified. The port may already be in use by another process.",
- this._papercutIpCommServer.ListenIpAddress,
- this._papercutIpCommServer.ListenPort);
- }
+ this._logger.Warning(
+ ex,
+ "Papercut IPComm Server failed to bind to the {Address} {Port} specified. The port may already be in use by another process.",
+ this._papercutIpCommServer.ListenIpAddress,
+ this._papercutIpCommServer.ListenPort);
}
+ }
- protected override async ValueTask DisposeAsync(bool disposing)
+ protected override async ValueTask DisposeAsync(bool disposing)
+ {
+ if (disposing)
{
- if (disposing)
- {
- await this._papercutIpCommServer.StopAsync();
- }
+ await this._papercutIpCommServer.StopAsync();
}
+ }
- #region Begin Static Container Registrations
+ #region Begin Static Container Registrations
- ///
- /// Called dynamically from the RegisterStaticMethods() call in the container module.
- ///
- ///
- [UsedImplicitly]
- static void Register(ContainerBuilder builder)
- {
- ArgumentNullException.ThrowIfNull(builder);
-
- builder.RegisterType().AsImplementedInterfaces()
- .SingleInstance();
- }
+ ///
+ /// Called dynamically from the RegisterStaticMethods() call in the container module.
+ ///
+ ///
+ [UsedImplicitly]
+ static void Register(ContainerBuilder builder)
+ {
+ ArgumentNullException.ThrowIfNull(builder);
- #endregion
+ builder.RegisterType().AsImplementedInterfaces()
+ .SingleInstance();
}
+
+ #endregion
}
\ No newline at end of file
diff --git a/src/Papercut.UI/AppLayer/Logging/UiLogSinkQueue.cs b/src/Papercut.UI/AppLayer/Logging/UiLogSinkQueue.cs
index f71f5413..a8c83cf7 100644
--- a/src/Papercut.UI/AppLayer/Logging/UiLogSinkQueue.cs
+++ b/src/Papercut.UI/AppLayer/Logging/UiLogSinkQueue.cs
@@ -25,62 +25,61 @@
using Serilog.Configuration;
using Serilog.Events;
-namespace Papercut.AppLayer.LogSinks
+namespace Papercut.AppLayer.LogSinks;
+
+public class UiLogSinkQueue : ILoggerSettings
{
- public class UiLogSinkQueue : ILoggerSettings
- {
- static readonly ConcurrentQueue _logQueue = new ConcurrentQueue();
+ static readonly ConcurrentQueue _logQueue = new ConcurrentQueue();
- public void Configure(LoggerConfiguration loggerConfiguration)
- {
- bool showDebug = false;
+ public void Configure(LoggerConfiguration loggerConfiguration)
+ {
+ bool showDebug = false;
#if DEBUG
- showDebug = true;
+ showDebug = true;
#endif
- loggerConfiguration.WriteTo.Observers(
- b => b.ObserveOn(TaskPoolScheduler.Default).Subscribe(le =>
- {
- _logQueue.Enqueue(le);
- this.OnLogEvent();
- }),
- // ReSharper disable once ConditionIsAlwaysTrueOrFalse
- showDebug ? LogEventLevel.Debug : LogEventLevel.Information);
- }
-
- public LogEvent? GetLastEvent()
- {
- return _logQueue.TryDequeue(out var log) ? log : null;
- }
-
- public IEnumerable GetLastEvents()
- {
- while (this.GetLastEvent() is { } logEvent)
+ loggerConfiguration.WriteTo.Observers(
+ b => b.ObserveOn(TaskPoolScheduler.Default).Subscribe(le =>
{
- yield return logEvent;
- }
- }
+ _logQueue.Enqueue(le);
+ this.OnLogEvent();
+ }),
+ // ReSharper disable once ConditionIsAlwaysTrueOrFalse
+ showDebug ? LogEventLevel.Debug : LogEventLevel.Information);
+ }
- public event EventHandler LogEvent;
+ public LogEvent? GetLastEvent()
+ {
+ return _logQueue.TryDequeue(out var log) ? log : null;
+ }
- protected virtual void OnLogEvent()
+ public IEnumerable GetLastEvents()
+ {
+ while (this.GetLastEvent() is { } logEvent)
{
- this.LogEvent?.Invoke(this, EventArgs.Empty);
+ yield return logEvent;
}
+ }
- #region Begin Static Container Registrations
+ public event EventHandler LogEvent;
- ///
- /// Called dynamically from the RegisterStaticMethods() call in the container module.
- ///
- ///
- [UsedImplicitly]
- static void Register(ContainerBuilder builder)
- {
- ArgumentNullException.ThrowIfNull(builder);
+ protected virtual void OnLogEvent()
+ {
+ this.LogEvent?.Invoke(this, EventArgs.Empty);
+ }
- builder.RegisterType().As().AsSelf();
- }
+ #region Begin Static Container Registrations
- #endregion
+ ///
+ /// Called dynamically from the RegisterStaticMethods() call in the container module.
+ ///
+ ///
+ [UsedImplicitly]
+ static void Register(ContainerBuilder builder)
+ {
+ ArgumentNullException.ThrowIfNull(builder);
+
+ builder.RegisterType().As().AsSelf();
}
+
+ #endregion
}
\ No newline at end of file
diff --git a/src/Papercut.UI/AppLayer/NewVersionCheck/NewVersionCheckHandler.cs b/src/Papercut.UI/AppLayer/NewVersionCheck/NewVersionCheckHandler.cs
index 1b2e6fa3..712e3b0c 100644
--- a/src/Papercut.UI/AppLayer/NewVersionCheck/NewVersionCheckHandler.cs
+++ b/src/Papercut.UI/AppLayer/NewVersionCheck/NewVersionCheckHandler.cs
@@ -19,8 +19,6 @@
using Autofac;
using Autofac.Util;
-using Microsoft.Extensions.Logging;
-
using NuGet.Versioning;
using Papercut.Core.Infrastructure.Container;
@@ -28,9 +26,11 @@
using Velopack;
+using ILogger = Serilog.ILogger;
+
namespace Papercut.AppLayer.NewVersionCheck;
-public class NewVersionCheckHandler(ILogger logger) : Disposable, IAppLifecycleStarted, INewVersionProvider
+public class NewVersionCheckHandler(UpdateManager updateManager, ILogger logger) : Disposable, IAppLifecycleStarted, INewVersionProvider
{
private readonly CancellationTokenSource _cancellationTokenSource = new();
@@ -40,27 +40,27 @@ public class NewVersionCheckHandler(ILogger logger) : Di
public Task OnStartedAsync()
{
- this._backgroundTask = Task.Run(this.RunNewVersionCheck, this._cancellationTokenSource.Token);
+ _backgroundTask = Task.Run(RunNewVersionCheck, _cancellationTokenSource.Token);
- return this._backgroundTask.IsCompleted ? this._backgroundTask : Task.CompletedTask;
+ return _backgroundTask.IsCompleted ? _backgroundTask : Task.CompletedTask;
}
public async Task GetLatestVersionAsync(CancellationToken token = default)
{
- return await this._updateTask.Task;
+ return await _updateTask.Task;
}
protected override async ValueTask DisposeAsync(bool disposing)
{
if (disposing)
{
- if (this._backgroundTask != null)
+ if (_backgroundTask != null)
{
- await this._cancellationTokenSource.CancelAsync();
+ await _cancellationTokenSource.CancelAsync();
try
{
- await this._backgroundTask;
+ await _backgroundTask;
}
catch (Exception)
{
@@ -68,11 +68,11 @@ protected override async ValueTask DisposeAsync(bool disposing)
}
}
- this._updateTask.SetCanceled();
+ _updateTask.SetCanceled();
try
{
- await this._updateTask.Task;
+ await _updateTask.Task;
}
catch (Exception)
{
@@ -83,35 +83,33 @@ protected override async ValueTask DisposeAsync(bool disposing)
private async Task RunNewVersionCheck()
{
- var mgr = new UpdateManager("https://github.com/ChangemakerStudios/Papercut-SMTP", logger: logger);
-
- if (mgr.IsInstalled)
+ if (updateManager.IsInstalled)
{
try
{
// check for new version
- var newVersion = await mgr.CheckForUpdatesAsync();
+ var newVersion = await updateManager.CheckForUpdatesAsync();
if (newVersion != null)
{
- logger.LogInformation("New Version of Papercut SMTP is Available {@NewVersion}", newVersion);
+ logger.Information("New Version of Papercut SMTP is Available {@NewVersion}", newVersion);
- this._updateTask.SetResult(newVersion);
+ _updateTask.SetResult(newVersion);
}
}
catch (Exception ex)
{
- this._updateTask.SetException(ex);
+ _updateTask.SetException(ex);
}
}
else
{
- logger.LogDebug("Papercut was not installed via Velopack. Cannot check for new versions.");
+ logger.Debug("Papercut was not installed via Velopack. Cannot check for new versions.");
}
this._updateTask.SetResult(null);
// for testing
- //this._updateTask.SetResult(new UpdateInfo(new VelopackAsset() { Version = new SemanticVersion(10, 0, 0) }, false));
+ //_updateTask.SetResult(new UpdateInfo(new VelopackAsset() { Version = new SemanticVersion(10, 0, 0) }, false));
}
#region Begin Static Container Registrations
diff --git a/src/Papercut.UI/AppLayer/Notifications/NotificationMenuCoordinator.cs b/src/Papercut.UI/AppLayer/Notifications/NotificationMenuCoordinator.cs
index 3ddd5050..0d2a3922 100644
--- a/src/Papercut.UI/AppLayer/Notifications/NotificationMenuCoordinator.cs
+++ b/src/Papercut.UI/AppLayer/Notifications/NotificationMenuCoordinator.cs
@@ -32,133 +32,132 @@
using Papercut.Domain.UiCommands;
using Papercut.Infrastructure.Resources;
-namespace Papercut.AppLayer.Notifications
+namespace Papercut.AppLayer.Notifications;
+
+[UsedImplicitly]
+public class NotificationMenuCoordinator : Disposable, IAppLifecyclePreExit, IEventHandler
{
- [UsedImplicitly]
- public class NotificationMenuCoordinator : Disposable, IAppLifecyclePreExit, IEventHandler
- {
- private readonly IAppCommandHub _appCommandHub;
+ private readonly IAppCommandHub _appCommandHub;
- readonly AppResourceLocator _resourceLocator;
+ readonly AppResourceLocator _resourceLocator;
- private readonly IUiCommandHub _uiCommandHub;
+ private readonly IUiCommandHub _uiCommandHub;
- NotifyIcon? _notification;
+ NotifyIcon? _notification;
- public NotificationMenuCoordinator(
- IAppCommandHub appCommandHub,
- IUiCommandHub uiCommandHub,
- AppResourceLocator resourceLocator)
- {
- this._appCommandHub = appCommandHub;
- this._uiCommandHub = uiCommandHub;
- this._resourceLocator = resourceLocator;
+ public NotificationMenuCoordinator(
+ IAppCommandHub appCommandHub,
+ IUiCommandHub uiCommandHub,
+ AppResourceLocator resourceLocator)
+ {
+ this._appCommandHub = appCommandHub;
+ this._uiCommandHub = uiCommandHub;
+ this._resourceLocator = resourceLocator;
- this.InitObservables();
- }
+ this.InitObservables();
+ }
- public Task OnPreExit()
- {
- this.Reset();
+ public Task OnPreExit()
+ {
+ this.Reset();
- return Task.FromResult(AppLifecycleActionResultType.Continue);
- }
+ return Task.FromResult(AppLifecycleActionResultType.Continue);
+ }
- public Task HandleAsync(PapercutClientReadyEvent @event, CancellationToken token)
- {
- if (this._notification == null) this.SetupNotification();
+ public Task HandleAsync(PapercutClientReadyEvent @event, CancellationToken token)
+ {
+ if (this._notification == null) this.SetupNotification();
- return Task.CompletedTask;
- }
+ return Task.CompletedTask;
+ }
- void InitObservables()
- {
- this._uiCommandHub.OnShowBalloonTip
- .Sample(TimeSpan.FromSeconds(1), TaskPoolScheduler.Default)
- .ObserveOn(TaskPoolScheduler.Default)
- .Subscribe(
- @event =>
- {
- this._notification?.ShowBalloonTip(
- @event.Timeout,
- @event.TipTitle,
- @event.TipText,
- @event.ToolTipIcon);
- });
- }
+ void InitObservables()
+ {
+ this._uiCommandHub.OnShowBalloonTip
+ .Sample(TimeSpan.FromSeconds(1), TaskPoolScheduler.Default)
+ .ObserveOn(TaskPoolScheduler.Default)
+ .Subscribe(
+ @event =>
+ {
+ this._notification?.ShowBalloonTip(
+ @event.Timeout,
+ @event.TipTitle,
+ @event.TipText,
+ @event.ToolTipIcon);
+ });
+ }
- protected override void Dispose(bool disposing)
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing)
{
- if (disposing)
- {
- this.Reset();
- }
+ this.Reset();
}
+ }
- private void Reset()
- {
- this._notification?.Dispose();
- this._notification = null;
- }
+ private void Reset()
+ {
+ this._notification?.Dispose();
+ this._notification = null;
+ }
- void SetupNotification()
+ void SetupNotification()
+ {
+ // Set up the notification icon
+ this._notification = new NotifyIcon
{
- // Set up the notification icon
- this._notification = new NotifyIcon
- {
- Icon = new Icon(this._resourceLocator.GetResource("App.ico").Stream),
- Text = AppConstants.ApplicationName,
- Visible = true
- };
-
- this._notification.Click += Notification_OnClick;
- this._notification.BalloonTipClicked +=
- (_, _) =>
- {
- this._uiCommandHub.ShowMainWindow(true);
- };
-
- this._notification.ContextMenuStrip = new ContextMenuStrip();
-
- this._notification.ContextMenuStrip.Items.Add("Show", null, (_, _) =>
- {
- this._uiCommandHub.ShowMainWindow();
- });
- this._notification.ContextMenuStrip.Items.Add("Options", null, (_, _) =>
+ Icon = new Icon(this._resourceLocator.GetResource("App.ico").Stream),
+ Text = AppConstants.ApplicationName,
+ Visible = true
+ };
+
+ this._notification.Click += Notification_OnClick;
+ this._notification.BalloonTipClicked +=
+ (_, _) =>
{
- this._uiCommandHub.ShowOptionWindow();
- });
- this._notification.ContextMenuStrip.Items.Add("Exit", null, (_, _) =>
- {
- this._appCommandHub.Shutdown();
- });
- }
+ this._uiCommandHub.ShowMainWindow(true);
+ };
+
+ this._notification.ContextMenuStrip = new ContextMenuStrip();
- private void Notification_OnClick(object? sender, EventArgs e)
+ this._notification.ContextMenuStrip.Items.Add("Show", null, (_, _) =>
{
- if (e is MouseEventArgs { Button: MouseButtons.Left })
- {
- this._uiCommandHub.ShowMainWindow();
- }
+ this._uiCommandHub.ShowMainWindow();
+ });
+ this._notification.ContextMenuStrip.Items.Add("Options", null, (_, _) =>
+ {
+ this._uiCommandHub.ShowOptionWindow();
+ });
+ this._notification.ContextMenuStrip.Items.Add("Exit", null, (_, _) =>
+ {
+ this._appCommandHub.Shutdown();
+ });
+ }
- this._notification?.ContextMenuStrip?.Show();
+ private void Notification_OnClick(object? sender, EventArgs e)
+ {
+ if (e is MouseEventArgs { Button: MouseButtons.Left })
+ {
+ this._uiCommandHub.ShowMainWindow();
}
- #region Begin Static Container Registrations
+ this._notification?.ContextMenuStrip?.Show();
+ }
- ///
- /// Called dynamically from the RegisterStaticMethods() call in the container module.
- ///
- ///
- [UsedImplicitly]
- static void Register(ContainerBuilder builder)
- {
- ArgumentNullException.ThrowIfNull(builder);
+ #region Begin Static Container Registrations
- builder.RegisterType().AsImplementedInterfaces()
- .SingleInstance();
- }
+ ///
+ /// Called dynamically from the RegisterStaticMethods() call in the container module.
+ ///
+ ///
+ [UsedImplicitly]
+ static void Register(ContainerBuilder builder)
+ {
+ ArgumentNullException.ThrowIfNull(builder);
- #endregion
+ builder.RegisterType().AsImplementedInterfaces()
+ .SingleInstance();
}
+
+ #endregion
}
\ No newline at end of file
diff --git a/src/Papercut.UI/AppLayer/Settings/AppRunOnStartupService.cs b/src/Papercut.UI/AppLayer/Settings/AppRunOnStartupService.cs
index 277d15d8..c1aff45d 100644
--- a/src/Papercut.UI/AppLayer/Settings/AppRunOnStartupService.cs
+++ b/src/Papercut.UI/AppLayer/Settings/AppRunOnStartupService.cs
@@ -28,82 +28,81 @@
using Papercut.Domain.Events;
using Papercut.Domain.UiCommands;
-namespace Papercut.AppLayer.Settings
+namespace Papercut.AppLayer.Settings;
+
+public class AppRunOnStartupService(ILogger logger, IUiCommandHub uiCommandHub) : IEventHandler
{
- public class AppRunOnStartupService(ILogger logger, IUiCommandHub uiCommandHub) : IEventHandler