aboutsummaryrefslogtreecommitdiff
path: root/Transactional Emails
diff options
context:
space:
mode:
authorLibravatar vnugent <public@vaughnnugent.com>2023-01-12 17:47:40 -0500
committerLibravatar vnugent <public@vaughnnugent.com>2023-01-12 17:47:40 -0500
commitd797953c74798252d7153a20e788ed034c71b0ae (patch)
treebb295b619a4c0ed13d18691063ddaebd3961faf5 /Transactional Emails
parentd8ef5d21416c4a9deaa5cae7d3c8a11fae6a15f7 (diff)
Large project reorder and consolidation
Diffstat (limited to 'Transactional Emails')
-rw-r--r--Transactional Emails/Api Endpoints/SendEndpoint.cs273
-rw-r--r--Transactional Emails/EmailDbCtx.cs40
-rw-r--r--Transactional Emails/EmailTransaction.cs162
-rw-r--r--Transactional Emails/SmtpProvider.cs118
-rw-r--r--Transactional Emails/TEmailEntryPoint.cs88
-rw-r--r--Transactional Emails/Transactional Emails.csproj62
-rw-r--r--Transactional Emails/Transactions/EmailTransactionValidator.cs133
-rw-r--r--Transactional Emails/Transactions/TransactionResult.cs44
-rw-r--r--Transactional Emails/Transactions/TransactionStore.cs74
9 files changed, 0 insertions, 994 deletions
diff --git a/Transactional Emails/Api Endpoints/SendEndpoint.cs b/Transactional Emails/Api Endpoints/SendEndpoint.cs
deleted file mode 100644
index c464f6b..0000000
--- a/Transactional Emails/Api Endpoints/SendEndpoint.cs
+++ /dev/null
@@ -1,273 +0,0 @@
-/*
-* Copyright (c) 2022 Vaughn Nugent
-*
-* Library: VNLib
-* Package: Transactional Emails
-* File: SendEndpoint.cs
-*
-* SendEndpoint.cs is part of Transactional Emails which is part of the larger
-* VNLib collection of libraries and utilities.
-*
-* Transactional Emails is free software: you can redistribute it and/or modify
-* it under the terms of the GNU General Public License as published
-* by the Free Software Foundation, either version 2 of the License,
-* or (at your option) any later version.
-*
-* Transactional Emails is distributed in the hope that it will be useful,
-* but WITHOUT ANY WARRANTY; without even the implied warranty of
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-* General Public License for more details.
-*
-* You should have received a copy of the GNU General Public License
-* along with Transactional Emails. If not, see http://www.gnu.org/licenses/.
-*/
-
-using System;
-using System.IO;
-using System.Net;
-using System.Text;
-using System.Text.Json;
-using System.Threading.Tasks;
-using System.Collections.Generic;
-
-using Fluid;
-
-using Minio;
-using Minio.DataModel;
-using Minio.Exceptions;
-
-using MimeKit.Text;
-
-using VNLib.Utils.Extensions;
-using VNLib.Utils.Logging;
-using VNLib.Utils.Memory.Caching;
-using VNLib.Plugins;
-using VNLib.Plugins.Essentials;
-using VNLib.Plugins.Essentials.Extensions;
-using VNLib.Plugins.Essentials.Oauth;
-using VNLib.Plugins.Extensions.Validation;
-using VNLib.Plugins.Extensions.Loading;
-using VNLib.Plugins.Extensions.Loading.Sql;
-
-using Emails.Transactional.Transactions;
-using static Emails.Transactional.TEmailEntryPoint;
-
-#nullable enable
-
-namespace Emails.Transactional.Endpoints
-{
- [ConfigurationName("transaction_endpoint")]
- internal class SendEndpoint : O2EndpointBase
- {
- public const string OAUTH2_USER_SCOPE_PERMISSION = "email:send";
-
- private class FluidCache : ICacheable
- {
- public FluidCache(IFluidTemplate temp)
- {
- this.Template = temp;
- }
-
- public IFluidTemplate Template { get; }
-
- DateTime ICacheable.Expires { get; set; }
-
- bool IEquatable<ICacheable>.Equals(ICacheable? other)
- {
- return ReferenceEquals(Template, (other as FluidCache)?.Template);
- }
-
- void ICacheable.Evicted()
- {}
- }
-
- private static readonly EmailTransactionValidator Validator = new();
- private static readonly FluidParser Parser = new();
-
- private readonly string FromAddress;
- private readonly string? BaseObjectPath;
- private readonly SmtpProvider EmailService;
- private readonly TransactionStore Transactions;
-
- private readonly Dictionary<string, FluidCache> TemplateCache;
- private readonly TimeSpan TemplateCacheTimeout;
- private readonly MinioClient Client;
- private readonly S3Config S3Config;
-
- public SendEndpoint(PluginBase plugin, IReadOnlyDictionary<string, JsonElement> config)
- {
- string? path = config["path"].GetString();
- FromAddress = config["from_address"].GetString() ?? throw new KeyNotFoundException("Missing required key 'from_address' in 'transaction_endpoint'");
- TemplateCacheTimeout = config["cache_valid_sec"].GetTimeSpan(TimeParseType.Seconds);
- BaseObjectPath = config["base_object_path"].GetString();
-
- InitPathAndLog(path, plugin.Log);
-
- //Smtp setup
- {
- IReadOnlyDictionary<string, JsonElement> smtp = plugin.GetConfig("smtp");
- Uri serverUri = new(smtp["server_address"].GetString()!);
- string username = smtp["username"].GetString() ?? throw new KeyNotFoundException("Missing required key 'usename' in 'smtp' config");
- string password = plugin.TryGetSecretAsync("smtp_password").Result ?? throw new KeyNotFoundException("Missing required 'smtp_password' in secrets");
- TimeSpan timeout = smtp["timeout_sec"].GetTimeSpan(TimeParseType.Seconds);
- NetworkCredential cred = new(username, password);
- //Init email service
- EmailService = new(serverUri, cred, timeout);
- }
- //S3 minio setup
- {
- //Init minio s3 client
- S3Config = plugin.TryGetS3Config() ?? throw new KeyNotFoundException("Missing required 's3_config' configuration variable");
- //Init minio from config
- Client = new();
- Client.WithEndpoint(S3Config.ServerAddress)
- .WithCredentials(S3Config.ClientId, S3Config.ClientSecret);
-
- if (S3Config.UseSsl == true)
- {
- Client.WithSSL();
- }
- //Accept optional region
- if (!string.IsNullOrWhiteSpace(S3Config.Region))
- {
- Client.WithRegion(S3Config.Region);
- }
- //Build client
- Client.Build();
- //If the plugin is in debug mode, log requests to logger
- if (plugin.IsDebug())
- {
- Client.SetTraceOn(new ReqLogger(Log));
- }
- }
-
- //Load transactions
- Transactions = new(plugin.GetContextOptions());
-
- TemplateCache = new(20, StringComparer.OrdinalIgnoreCase);
- }
-
- protected override async ValueTask<VfReturnType> PostAsync(HttpEntity entity)
- {
- //Make sure the user has the required scope to send an email
- if (!entity.Session.HasScope(OAUTH2_USER_SCOPE_PERMISSION))
- {
- entity.CloseResponseError(HttpStatusCode.Forbidden, ErrorType.InvalidScope, "Your account does not have the required permissions scope");
- return VfReturnType.VirtualSkip;
- }
-
- //Get the transaction request from the client
- EmailTransaction? transaction = await entity.GetJsonFromFileAsync<EmailTransaction>();
- if(transaction == null)
- {
- entity.CloseResponseError(HttpStatusCode.BadRequest, ErrorType.InvalidRequest, "No request body was received");
- return VfReturnType.VirtualSkip;
- }
-
- TransactionResult webm = new()
- {
- Success = false
- };
- //Set a default from name/address if not set by user
- transaction.From ??= FromAddress;
- transaction.FromName ??= FromAddress;
- //Specify user-id
- transaction.UserId = entity.Session.UserID;
- //validate the form
- if (!Validator.Validate(transaction, webm))
- {
- //return errors
- entity.CloseResponseJson(HttpStatusCode.UnprocessableEntity, webm);
- return VfReturnType.VirtualSkip;
- }
-
- IFluidTemplate fTemp;
-
- //See if the template is in cache
- if (TemplateCache.TryGetOrEvictRecord(transaction.TemplateId!, out FluidCache? cache) == 1)
- {
- fTemp = cache.Template;
- }
- //Record was evicted or not found
- else
- {
- string? templateData = null;
- try
- {
- //Combine base obj path
- string objPath = string.Concat(BaseObjectPath, transaction.TemplateId);
- //Recover the template from the store
- GetObjectArgs args = new();
- args.WithBucket(S3Config.BaseBucket)
- .WithObject(objPath)
- .WithCallbackStream((stream) =>
- {
- //Read template from stream
- using StreamReader reader = new(stream, Encoding.UTF8, detectEncodingFromByteOrderMarks: false, leaveOpen: true);
- templateData = reader.ReadToEnd();
- });
- //get the template object
- ObjectStat status = await Client.GetObjectAsync(args, entity.EventCancellation);
-
-
- }
- catch(ObjectNotFoundException)
- {
- entity.CloseResponseError(HttpStatusCode.NotFound, ErrorType.InvalidRequest, "The requested template does not exist");
- return VfReturnType.VirtualSkip;
- }
- catch(MinioException me)
- {
- Log.Error(me);
- return VfReturnType.Error;
- }
-
- //Make sure template data was found
- if (string.IsNullOrWhiteSpace(templateData))
- {
- entity.CloseResponseError(HttpStatusCode.NotFound, ErrorType.InvalidRequest, "The requested template does not exist");
- return VfReturnType.VirtualSkip;
- }
-
- //Try to parse the template
- if (!Parser.TryParse(templateData, out fTemp, out string error))
- {
- Log.Error(error, "Liquid template parsing error");
- return VfReturnType.Error;
- }
-
- //Cache the new template
- TemplateCache.StoreRecord(transaction.TemplateId!, new FluidCache(fTemp), TemplateCacheTimeout);
- }
-
- //Allways add the template name to the model
- transaction.Variables!["template_name"] = transaction.TemplateId!;
-
- //Create a template model
- TemplateContext ctx = new(transaction.Variables);
- //render the template
- string rendered = fTemp.Render(ctx);
- try
- {
- //Send the template
- _ = await EmailService.SendAsync(transaction, rendered, TextFormat.Html, entity.EventCancellation);
- //Set success flag
- webm.Success = true;
- }
- catch (Exception ex)
- {
- Log.Error(ex);
- //Write an error status message to the transaction store
- transaction.Result = ex.Message;
- }
- //Write transaction to db (we need to return the transaction id)
- _ = await Transactions.AddOrUpdateAsync(transaction);
- //Store the results object
- webm.SmtpStatus = transaction.Result;
- webm.TransactionId = transaction.Id;
- //Return the response object
- entity.CloseResponse(webm);
- return VfReturnType.VirtualSkip;
- }
- }
-}
diff --git a/Transactional Emails/EmailDbCtx.cs b/Transactional Emails/EmailDbCtx.cs
deleted file mode 100644
index cd865d8..0000000
--- a/Transactional Emails/EmailDbCtx.cs
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
-* Copyright (c) 2022 Vaughn Nugent
-*
-* Library: VNLib
-* Package: Transactional Emails
-* File: EmailDbCtx.cs
-*
-* EmailDbCtx.cs is part of Transactional Emails which is part of the larger
-* VNLib collection of libraries and utilities.
-*
-* Transactional Emails is free software: you can redistribute it and/or modify
-* it under the terms of the GNU General Public License as published
-* by the Free Software Foundation, either version 2 of the License,
-* or (at your option) any later version.
-*
-* Transactional Emails is distributed in the hope that it will be useful,
-* but WITHOUT ANY WARRANTY; without even the implied warranty of
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-* General Public License for more details.
-*
-* You should have received a copy of the GNU General Public License
-* along with Transactional Emails. If not, see http://www.gnu.org/licenses/.
-*/
-
-using System;
-
-using Microsoft.EntityFrameworkCore;
-
-using VNLib.Plugins.Extensions.Data;
-
-namespace Emails.Transactional
-{
- internal class EmailDbCtx : TransactionalDbContext
- {
- public DbSet<EmailTransaction> EmailTransactions { get; set; }
-
- public EmailDbCtx(DbContextOptions options) : base(options)
- {}
- }
-}
diff --git a/Transactional Emails/EmailTransaction.cs b/Transactional Emails/EmailTransaction.cs
deleted file mode 100644
index bb7b4a8..0000000
--- a/Transactional Emails/EmailTransaction.cs
+++ /dev/null
@@ -1,162 +0,0 @@
-/*
-* Copyright (c) 2022 Vaughn Nugent
-*
-* Library: VNLib
-* Package: Transactional Emails
-* File: EmailTransaction.cs
-*
-* EmailTransaction.cs is part of Transactional Emails which is part of the larger
-* VNLib collection of libraries and utilities.
-*
-* Transactional Emails is free software: you can redistribute it and/or modify
-* it under the terms of the GNU General Public License as published
-* by the Free Software Foundation, either version 2 of the License,
-* or (at your option) any later version.
-*
-* Transactional Emails is distributed in the hope that it will be useful,
-* but WITHOUT ANY WARRANTY; without even the implied warranty of
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-* General Public License for more details.
-*
-* You should have received a copy of the GNU General Public License
-* along with Transactional Emails. If not, see http://www.gnu.org/licenses/.
-*/
-
-using System;
-using System.Text.Json;
-using System.Text.Json.Serialization;
-using System.Collections.Generic;
-using System.ComponentModel.DataAnnotations;
-using System.ComponentModel.DataAnnotations.Schema;
-
-using VNLib.Plugins.Essentials;
-using VNLib.Plugins.Extensions.Data;
-using VNLib.Plugins.Extensions.Data.Abstractions;
-
-#nullable enable
-
-namespace Emails.Transactional
-{
- /// <summary>
- /// Represents an email transaction request and its status reflected
- /// in the database
- /// </summary>
- internal class EmailTransaction : DbModelBase, IUserEntity
- {
- ///<inheritdoc/>
- [Key]
- [JsonPropertyName("id")]
- public override string Id { get; set; }
- ///<inheritdoc/>
- [JsonIgnore]
- public override DateTime Created { get; set; }
- ///<inheritdoc/>
- [JsonIgnore]
- public override DateTime LastModified { get; set; }
-
- /// <summary>
- /// The sever side user-id that send the email
- /// </summary>
- [JsonIgnore]
- public string? UserId { get; set; }
-
- //To address
- [JsonIgnore]
- public string? To
- {
- //Use json to serialize/deserialze the to addresses
- get => JsonSerializer.Serialize(ToAddresses, Statics.SR_OPTIONS);
- set => ToAddresses = JsonSerializer.Deserialize<Dictionary<string, string>>(value!, Statics.SR_OPTIONS);
- }
-
- /// <summary>
- /// A dictionary of to email address and name pairs
- /// </summary>
- [NotMapped]
- [JsonPropertyName("to")]
- public Dictionary<string, string>? ToAddresses { get; set; }
-
- //From
- /// <summary>
- /// The from email address
- /// </summary>
- [JsonPropertyName("from")]
- public string? From { get; set; }
-
- /// <summary>
- /// The optional from name (not mapped in db)
- /// </summary>
- [NotMapped]
- [JsonPropertyName("from_name")]
- public string? FromName { get; set; }
- /// <summary>
- /// The email subject
- /// </summary>
- [JsonPropertyName("subject")]
- public string? Subject { get; set; }
-
- //CC names
- //ccs are stored in the db as a json serialized string
- [JsonIgnore]
- public string Ccs
- {
- //Use json to serialize/deserialze the to addresses
- get => JsonSerializer.Serialize(CcAddresses, Statics.SR_OPTIONS);
- set => CcAddresses = JsonSerializer.Deserialize<Dictionary<string, string>>(value, Statics.SR_OPTIONS);
- }
-
- [JsonPropertyName("cc")]
- [NotMapped]
- public Dictionary<string, string>? CcAddresses { get; set; }
-
- //BCC Names
-
- //bccs are stored in the db as a json serialized string
- [JsonIgnore]
- public string Bccs
- {
- //Store bccs as a comma separated list of addresses
- get => JsonSerializer.Serialize(BccAddresses, Statics.SR_OPTIONS);
- set => BccAddresses = JsonSerializer.Deserialize<Dictionary<string, string>>(value, Statics.SR_OPTIONS);
- }
-
- /// <summary>
- /// A dictionary of bcc addresses and names
- /// </summary>
- [JsonPropertyName("bcc")]
- [NotMapped]
- public Dictionary<string, string>? BccAddresses { get; set; }
-
- /// <summary>
- /// The replyto email address
- /// </summary>
- [JsonPropertyName("reply_to")]
- public string? ReplyTo { get; set; }
- /// <summary>
- /// Optional reply-to name for the address (not stored in db)
- /// </summary>
- [NotMapped]
- [JsonPropertyName("reply_to_name")]
- public string? ReplyToName { get; set; }
-
- /// <summary>
- /// The object id of the template to send
- /// </summary>
- [JsonPropertyName("template_id")]
- public string? TemplateId { get; set; }
-
- /// <summary>
- /// The result of the STMP transaction
- /// </summary>
- [JsonIgnore]
- public string? Result { get; set; }
-
- /// <summary>
- /// Variables requested from the client to embed in the template
- /// during processing. These are not stored in the database
- /// </summary>
- [NotMapped]
- [JsonPropertyName("variables")]
- public Dictionary<string, string>? Variables { get; set; }
- }
-}
diff --git a/Transactional Emails/SmtpProvider.cs b/Transactional Emails/SmtpProvider.cs
deleted file mode 100644
index e864a9d..0000000
--- a/Transactional Emails/SmtpProvider.cs
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
-* Copyright (c) 2022 Vaughn Nugent
-*
-* Library: VNLib
-* Package: Transactional Emails
-* File: SmtpProvider.cs
-*
-* SmtpProvider.cs is part of Transactional Emails which is part of the larger
-* VNLib collection of libraries and utilities.
-*
-* Transactional Emails is free software: you can redistribute it and/or modify
-* it under the terms of the GNU General Public License as published
-* by the Free Software Foundation, either version 2 of the License,
-* or (at your option) any later version.
-*
-* Transactional Emails is distributed in the hope that it will be useful,
-* but WITHOUT ANY WARRANTY; without even the implied warranty of
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-* General Public License for more details.
-*
-* You should have received a copy of the GNU General Public License
-* along with Transactional Emails. If not, see http://www.gnu.org/licenses/.
-*/
-
-using System;
-using System.Net;
-using System.Threading;
-using System.Threading.Tasks;
-using System.Collections.Generic;
-
-using MailKit.Net.Smtp;
-
-using MimeKit;
-using MimeKit.Text;
-
-namespace Emails.Transactional
-{
- internal class SmtpProvider
- {
- private readonly Uri ServerAddress;
- private readonly ICredentials ServerCreds;
- private readonly TimeSpan Timeout;
-
- public SmtpProvider(Uri ServerAddress, ICredentials ServerCreds, TimeSpan Timeout)
- {
- this.ServerCreds = ServerCreds;
- this.ServerAddress = ServerAddress;
- this.Timeout = Timeout;
- }
-
- /// <summary>
- /// Opens a connection to the configured SMTP server and sends the specified email
- /// request transaction on the server. When the operation completes, the transaction's
- /// result property is populated with the result of the operation.
- /// </summary>
- /// <param name="transaction">The transaction data</param>
- /// <param name="messageBody">The content of the message to send</param>
- /// <param name="dataFromat">The format of the body content</param>
- /// <returns>A task that resolves the status of the operation</returns>
- public async Task<string> SendAsync(EmailTransaction transaction, string messageBody, TextFormat dataFromat, CancellationToken cancellation)
- {
- //Configured a new message
- using MimeMessage message = new()
- {
- Date = DateTime.UtcNow,
- Subject = transaction.Subject
- };
- //From address is the stored from address
- message.From.Add(new MailboxAddress(transaction.FromName, transaction.From));
-
- //Add to email addresses
- foreach (KeyValuePair<string, string> tos in transaction.ToAddresses)
- {
- message.To.Add(new MailboxAddress(tos.Value, tos.Key));
- }
- //Add ccs
- if (transaction.CcAddresses != null)
- {
- foreach (KeyValuePair<string, string> ccs in transaction.CcAddresses)
- {
- message.Cc.Add(new MailboxAddress(ccs.Value, ccs.Key));
- }
- }
- //Add bccs
- if (transaction.BccAddresses != null)
- {
- foreach (KeyValuePair<string, string> bccs in transaction.BccAddresses)
- {
- message.Bcc.Add(new MailboxAddress(bccs.Value, bccs.Key));
- }
- }
-
- //Use html format since we expect to be reading html templates
- using TextPart body = new(dataFromat)
- {
- IsAttachment = false,
- Text = messageBody
- };
- //Set message body
- message.Body = body;
- //Open a new mail client
- using SmtpClient client = new();
- //Set timeout for senting messages
- client.Timeout = (int)Timeout.TotalMilliseconds;
- //Connect to server
- await client.ConnectAsync(ServerAddress, cancellation);
- //Aithenticate
- await client.AuthenticateAsync(ServerCreds, cancellation);
- //Send the email
- string result = await client.SendAsync(message, cancellation);
- //Disconnect from the server
- await client.DisconnectAsync(true, CancellationToken.None);
- //Update the transaction
- transaction.Result = result;
- return result;
- }
- }
-}
diff --git a/Transactional Emails/TEmailEntryPoint.cs b/Transactional Emails/TEmailEntryPoint.cs
deleted file mode 100644
index c95ca08..0000000
--- a/Transactional Emails/TEmailEntryPoint.cs
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
-* Copyright (c) 2022 Vaughn Nugent
-*
-* Library: VNLib
-* Package: Transactional Emails
-* File: TEmailEntryPoint.cs
-*
-* TEmailEntryPoint.cs is part of Transactional Emails which is part of the larger
-* VNLib collection of libraries and utilities.
-*
-* Transactional Emails is free software: you can redistribute it and/or modify
-* it under the terms of the GNU General Public License as published
-* by the Free Software Foundation, either version 2 of the License,
-* or (at your option) any later version.
-*
-* Transactional Emails is distributed in the hope that it will be useful,
-* but WITHOUT ANY WARRANTY; without even the implied warranty of
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-* General Public License for more details.
-*
-* You should have received a copy of the GNU General Public License
-* along with Transactional Emails. If not, see http://www.gnu.org/licenses/.
-*/
-
-using System;
-using System.Threading.Tasks;
-using System.Collections.Generic;
-
-using Emails.Transactional.Endpoints;
-
-using Minio;
-using Minio.DataModel.Tracing;
-
-using VNLib.Utils.Logging;
-using VNLib.Plugins;
-using VNLib.Plugins.Extensions.Loading.Routing;
-
-namespace Emails.Transactional
-{
- public class TEmailEntryPoint : PluginBase
- {
- public override string PluginName => "Emails.Transactional";
-
- internal class ReqLogger : IRequestLogger
- {
- private readonly ILogProvider logProvider;
-
- public ReqLogger(ILogProvider log)
- {
- logProvider = log;
- }
-
- public void LogRequest(RequestToLog requestToLog, ResponseToLog responseToLog, double durationMs)
- {
- logProvider.Debug("S3 result\n{method} {uri} HTTP {ms}ms\nHTTP {status} {message}\n{content}",
- requestToLog.method, requestToLog.resource, durationMs,
- responseToLog.statusCode, responseToLog.errorMessage, responseToLog.content
- );
- }
- }
-
- protected override void OnLoad()
- {
- try
- {
- //Route send oauth endpoint
- this.Route<SendEndpoint>();
-
- Log.Information("Plugin loaded");
- }
- catch (KeyNotFoundException kne)
- {
- Log.Warn("Missing required configuration keys {err}", kne.Message);
- }
- }
-
-
- protected override void OnUnLoad()
- {
- Log.Information("Plugin unloaded");
- }
-
- protected override void ProcessHostCommand(string cmd)
- {
- throw new NotImplementedException();
- }
- }
-}
diff --git a/Transactional Emails/Transactional Emails.csproj b/Transactional Emails/Transactional Emails.csproj
deleted file mode 100644
index 4c1a745..0000000
--- a/Transactional Emails/Transactional Emails.csproj
+++ /dev/null
@@ -1,62 +0,0 @@
-<Project Sdk="Microsoft.NET.Sdk">
-
- <PropertyGroup>
- <TargetFramework>net6.0</TargetFramework>
- <RootNamespace>Emails.Transactional</RootNamespace>
- <AssemblyName>TransactionalEmails</AssemblyName>
-
- <Nullable>enable</Nullable>
- <Authors>Vaughn Nugent</Authors>
- <Copyright>Copyright © 2022 Vaughn Nugent</Copyright>
- <Version>1.0.0.1</Version>
- <PackageProjectUrl>https://www.vaughnnugent.com/resources</PackageProjectUrl>
- </PropertyGroup>
-
- <!-- Resolve nuget dll files and store them in the output dir -->
- <PropertyGroup>
- <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
- </PropertyGroup>
-
- <ItemGroup>
- <PackageReference Include="ErrorProne.NET.CoreAnalyzers" Version="0.1.2">
- <PrivateAssets>all</PrivateAssets>
- <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
- </PackageReference>
- <PackageReference Include="ErrorProne.NET.Structs" Version="0.1.2">
- <PrivateAssets>all</PrivateAssets>
- <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
- </PackageReference>
- <PackageReference Include="FluentValidation" Version="11.3.0" />
- <PackageReference Include="Fluid.Core" Version="2.2.16" />
- <PackageReference Include="MailKit" Version="3.4.2" />
- <PackageReference Include="Minio" Version="4.0.6" />
- </ItemGroup>
-
- <ItemGroup>
- <None Update="TransactionalEmails.json">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </None>
- </ItemGroup>
-
- <ItemGroup>
- <Service Include="{508349b6-6b84-4df5-91f0-309beebad82d}" />
- </ItemGroup>
-
- <ItemGroup>
- <Folder Include="Endpoints\" />
- <Folder Include="Meta\" />
- </ItemGroup>
-
- <ItemGroup>
- <ProjectReference Include="..\..\Extensions\VNLib.Plugins.Extensions.Data\VNLib.Plugins.Extensions.Data.csproj" />
- <ProjectReference Include="..\..\Extensions\VNLib.Plugins.Extensions.Loading.Sql\VNLib.Plugins.Extensions.Loading.Sql.csproj" />
- <ProjectReference Include="..\..\Extensions\VNLib.Plugins.Extensions.Loading\VNLib.Plugins.Extensions.Loading.csproj" />
- <ProjectReference Include="..\..\Extensions\VNLib.Plugins.Extensions.Validation\VNLib.Plugins.Extensions.Validation.csproj" />
- <ProjectReference Include="..\..\PluginBase\VNLib.Plugins.PluginBase.csproj" />
- </ItemGroup>
-
- <Target Name="PostBuild" AfterTargets="PostBuildEvent">
- <Exec Command="start xcopy &quot;$(TargetDir)&quot; &quot;F:\Programming\Web Plugins\DevPlugins\$(TargetName)&quot; /E /Y /R" />
- </Target>
-
-</Project>
diff --git a/Transactional Emails/Transactions/EmailTransactionValidator.cs b/Transactional Emails/Transactions/EmailTransactionValidator.cs
deleted file mode 100644
index 5786efb..0000000
--- a/Transactional Emails/Transactions/EmailTransactionValidator.cs
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
-* Copyright (c) 2022 Vaughn Nugent
-*
-* Library: VNLib
-* Package: Transactional Emails
-* File: EmailTransactionValidator.cs
-*
-* EmailTransactionValidator.cs is part of Transactional Emails which is part of the larger
-* VNLib collection of libraries and utilities.
-*
-* Transactional Emails is free software: you can redistribute it and/or modify
-* it under the terms of the GNU General Public License as published
-* by the Free Software Foundation, either version 2 of the License,
-* or (at your option) any later version.
-*
-* Transactional Emails is distributed in the hope that it will be useful,
-* but WITHOUT ANY WARRANTY; without even the implied warranty of
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-* General Public License for more details.
-*
-* You should have received a copy of the GNU General Public License
-* along with Transactional Emails. If not, see http://www.gnu.org/licenses/.
-*/
-
-using FluentValidation;
-
-using VNLib.Plugins.Extensions.Validation;
-
-
-namespace Emails.Transactional.Transactions
-{
- internal class EmailTransactionValidator : AbstractValidator<EmailTransaction>
- {
- public const int MAX_SUBJECT_LEN = 50;
- public EmailTransactionValidator()
- {
- //Catch to make sure user-id is set
- RuleFor(static t => t.UserId)
- .NotEmpty();
- //validate from addres/name
- RuleFor(static t => t.FromName)
- .NotEmpty()
- .WithName("from_name")
- .AlphaNumericOnly()
- .WithName("from_name");
- RuleFor(static t => t.From)
- .NotEmpty()
- .EmailAddress()
- .WithName("from");
-
- //Rule names must be their json propery name, so clients can properly log errors
- //must include a template id
- RuleFor(static t => t.TemplateId)
- .NotEmpty()
- .MaximumLength(200)
- .WithName("template_id");
-
- //From address must not be empty
- RuleFor(static t => t.From)
- .NotEmpty()
- .EmailAddress();
- //Subject is required alpha num
- RuleFor(static t => t.Subject)
- .NotEmpty()
- .AlphaNumericOnly()
- .MaximumLength(MAX_SUBJECT_LEN)
- .WithName("subject");
-
- //To address must not be empty, and must be valid email addresses
- RuleFor(static t => t.ToAddresses)
- .NotEmpty()
- .ChildRules(static to => {
- //Check keys (address)
- to.RuleForEach(static b => b.Keys)
- .NotEmpty()
- .EmailAddress()
- .WithName("to");
-
- //Check values (names)
- to.RuleForEach(static b => b.Values)
- .NotEmpty()
- .AlphaNumericOnly()
- .WithName("to_name");
- })
- .WithName("to");
- //Check bcc addresses, allowed to be empty
- RuleFor(static t => t.BccAddresses)
- .ChildRules(static bcc => {
- //Check keys (address)
- bcc.RuleForEach(static b => b.Keys)
- .NotEmpty()
- .EmailAddress()
- .WithName("bcc");
-
- //Check values (names)
- bcc.RuleForEach(static b => b.Values)
- .NotEmpty()
- .AlphaNumericOnly()
- .WithName("bcc_names");
- })
- .When(static t => t.BccAddresses != null)
- .WithName("bcc");
- //Check cc, also allowed to be empty
- RuleFor(static t => t.CcAddresses)
- .ChildRules(static cc => {
- //Check keys (names)
- cc.RuleForEach(static b => b.Keys)
- .NotEmpty()
- .EmailAddress()
- .WithName("cc");
-
- //Check values (addresses)
- cc.RuleForEach(static b => b.Values)
- .NotEmpty()
- .AlphaNumericOnly()
- .WithName("cc_names");
- })
- .When(static t => t.BccAddresses != null)
- .WithName("cc");
-
- RuleFor(static t => t.ReplyTo)
- //Allow the reply to email to be empty, if its not, then validate the address
- .EmailAddress()
- .When(static t => t.BccAddresses != null)
- .WithName("reply_to");
-
- //Make sure a variable table is defined
- RuleFor(static t => t.Variables)
- .NotNull()
- .WithName("variables");
- }
- }
-}
diff --git a/Transactional Emails/Transactions/TransactionResult.cs b/Transactional Emails/Transactions/TransactionResult.cs
deleted file mode 100644
index 8506a56..0000000
--- a/Transactional Emails/Transactions/TransactionResult.cs
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
-* Copyright (c) 2022 Vaughn Nugent
-*
-* Library: VNLib
-* Package: Transactional Emails
-* File: TransactionResult.cs
-*
-* TransactionResult.cs is part of Transactional Emails which is part of the larger
-* VNLib collection of libraries and utilities.
-*
-* Transactional Emails is free software: you can redistribute it and/or modify
-* it under the terms of the GNU General Public License as published
-* by the Free Software Foundation, either version 2 of the License,
-* or (at your option) any later version.
-*
-* Transactional Emails is distributed in the hope that it will be useful,
-* but WITHOUT ANY WARRANTY; without even the implied warranty of
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-* General Public License for more details.
-*
-* You should have received a copy of the GNU General Public License
-* along with Transactional Emails. If not, see http://www.gnu.org/licenses/.
-*/
-
-using System.Text.Json.Serialization;
-
-using VNLib.Plugins.Extensions.Validation;
-
-namespace Emails.Transactional.Transactions
-{
- public class TransactionResult : ValErrWebMessage
- {
- [JsonPropertyName("transaction_id")]
- public string TransactionId { get; set; }
- [JsonPropertyName("smtp_status")]
- public string SmtpStatus { get; set; }
-
- [JsonPropertyName("error_code")]
- public string ErrorCode { get; set; }
-
- [JsonPropertyName("error_description")]
- public string ErrorDescription { get; set; }
- }
-}
diff --git a/Transactional Emails/Transactions/TransactionStore.cs b/Transactional Emails/Transactions/TransactionStore.cs
deleted file mode 100644
index a6bd4b9..0000000
--- a/Transactional Emails/Transactions/TransactionStore.cs
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
-* Copyright (c) 2022 Vaughn Nugent
-*
-* Library: VNLib
-* Package: Transactional Emails
-* File: TransactionStore.cs
-*
-* TransactionStore.cs is part of Transactional Emails which is part of the larger
-* VNLib collection of libraries and utilities.
-*
-* Transactional Emails is free software: you can redistribute it and/or modify
-* it under the terms of the GNU General Public License as published
-* by the Free Software Foundation, either version 2 of the License,
-* or (at your option) any later version.
-*
-* Transactional Emails is distributed in the hope that it will be useful,
-* but WITHOUT ANY WARRANTY; without even the implied warranty of
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-* General Public License for more details.
-*
-* You should have received a copy of the GNU General Public License
-* along with Transactional Emails. If not, see http://www.gnu.org/licenses/.
-*/
-
-using System;
-using System.Linq;
-
-using Microsoft.EntityFrameworkCore;
-
-using VNLib.Plugins.Extensions.Data;
-
-namespace Emails.Transactional.Transactions
-{
-
- internal class TransactionStore : DbStore<EmailTransaction>
- {
- private readonly DbContextOptions Options;
-
- public TransactionStore(DbContextOptions options)
- {
- Options = options;
- }
-
- public override TransactionalDbContext NewContext() => new EmailDbCtx(Options);
-
- public override string RecordIdBuilder => Guid.NewGuid().ToString("N");
-
- protected override void OnRecordUpdate(EmailTransaction newRecord, EmailTransaction oldRecord)
- {
- oldRecord.LastModified = DateTime.UtcNow;
- }
-
- protected override IQueryable<EmailTransaction> GetCollectionQueryBuilder(TransactionalDbContext context, params string[] constraints)
- {
- string userId = constraints[0];
- //Get the last transactions for the specifed user
- EmailDbCtx ctx = context as EmailDbCtx;
- return from trans in ctx.EmailTransactions
- where trans.UserId == userId
- orderby trans.LastModified descending
- select trans;
- }
-
- protected override IQueryable<EmailTransaction> GetSingleQueryBuilder(TransactionalDbContext context, params string[] constraints)
- {
- string transactionid = constraints[0];
- EmailDbCtx ctx = context as EmailDbCtx;
- //Selet the exact transaction from its id
- return from trans in ctx.EmailTransactions
- where trans.Id == transactionid
- select trans;
- }
- }
-}