diff options
author | vnugent <public@vaughnnugent.com> | 2023-08-28 21:54:26 -0400 |
---|---|---|
committer | vnugent <public@vaughnnugent.com> | 2023-08-28 21:54:26 -0400 |
commit | b153adbd86e226ad805c2edbb90e4032d386a1b0 (patch) | |
tree | 9d3e5d7f2966c66e0264001cb38c67f74d6cf707 /lib/Emails.Transactional.Plugin/src/Endpoints/SendEndpoint.cs | |
parent | 964e81b81cdb430ecee8f67a68e3c616b3f339aa (diff) |
Refactor overhaul, data extensions & Resend.com support
Diffstat (limited to 'lib/Emails.Transactional.Plugin/src/Endpoints/SendEndpoint.cs')
-rw-r--r-- | lib/Emails.Transactional.Plugin/src/Endpoints/SendEndpoint.cs | 160 |
1 files changed, 160 insertions, 0 deletions
diff --git a/lib/Emails.Transactional.Plugin/src/Endpoints/SendEndpoint.cs b/lib/Emails.Transactional.Plugin/src/Endpoints/SendEndpoint.cs new file mode 100644 index 0000000..3e6f940 --- /dev/null +++ b/lib/Emails.Transactional.Plugin/src/Endpoints/SendEndpoint.cs @@ -0,0 +1,160 @@ +/* +* Copyright (c) 2023 Vaughn Nugent +* +* Library: VNLib +* Package: Emails.Transactional +* File: SendEndpoint.cs +* +* SendEndpoint.cs is part of Emails.Transactional which is part of the larger +* VNLib collection of libraries and utilities. +* +* Emails.Transactional 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. +* +* Emails.Transactional 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 Emails.Transactional. If not, see http://www.gnu.org/licenses/. +*/ + + +using System; +using System.Net; +using System.Threading.Tasks; + +using VNLib.Utils.Logging; +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 VNLib.Plugins.Extensions.Data.Extensions; + +using Emails.Transactional.Mta; +using Emails.Transactional.Templates; +using Emails.Transactional.Transactions; + +namespace Emails.Transactional.Endpoints +{ + + [ConfigurationName("send_endpoint")] + internal class SendEndpoint : O2EndpointBase + { + public const string OAUTH2_USER_SCOPE_PERMISSION = "email:send"; + + private static readonly EmailTransactionValidator Validator = new(); + + private readonly string FromAddress; + + private readonly TransactionStore Transactions; + private readonly IMailTransferAgent EmailService; + private readonly ITemplateStorage Templates; + + public SendEndpoint(PluginBase plugin, IConfigScope config) + { + string? path = config["path"].GetString(); + FromAddress = config.GetRequiredProperty("from_address", e => e.GetString()!); + + InitPathAndLog(path, plugin.Log); + + //Load transactions + Transactions = new(plugin.GetContextOptions()); + + //init ail transfer agent + EmailService = plugin.GetOrCreateSingleton<MailTransferAgent>(); + + //Load templates + Templates = plugin.GetOrCreateSingleton<EmailTemplateStore>(); + } + + 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; + } + + //Always add the template name to the model + transaction.Variables!["template_name"] = transaction.TemplateId!; + + try + { + //Get the template + IEmailTemplate template = await Templates.GetTemplateAsync(transaction.TemplateId!, entity.EventCancellation); + + //Render the template with the model + IEmailMessageData message = template.RenderTemplate(transaction.Variables!); + + //Send the template + MtaResult result = await EmailService.SendEmailAsync(transaction, message, entity.EventCancellation); + + //Set success flag + webm.Success = result.Success; + webm.SmtpStatus = result.Message; + } + //Handle a template lookup failure + catch(TemplateLookupFailedException ex) + { + Log.Debug("Failed to fetch template: {err}", transaction.TemplateId, ex.Message); + + entity.CloseResponseError(HttpStatusCode.NotFound, ErrorType.InvalidRequest, "The requested template does not exist"); + return VfReturnType.VirtualSkip; + } + catch (Exception ex) + { + Log.Error(ex); + + //Write an error status message to the transaction store + webm.SmtpStatus = transaction.Result = ex.Message; + } + + //Write transaction to db (we need to return the transaction id) + _ = await Transactions.AddOrUpdateAsync(transaction, entity.EventCancellation); + + //Store the results object + webm.TransactionId = transaction.Id; + + //Return the response object + entity.CloseResponse(webm); + return VfReturnType.VirtualSkip; + } + } +} |