1
0
mirror of https://github.com/bitwarden/server synced 2025-12-14 23:33:41 +00:00

[PM-25427] Allow reading mail templates from disk (#6123)

* Allow reading mail templates from self host disk

* Update src/Core/Services/Implementations/HandlebarsMailService.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update src/Core/Services/Implementations/HandlebarsMailService.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* tweak logic

* some error handling reading templates from disk

* fix: broken test

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Andreas Coroiu <andreas.coroiu@gmail.com>
This commit is contained in:
Kyle Spearrin
2025-10-06 04:13:56 -04:00
committed by GitHub
parent d2577f670e
commit 60d701c945
3 changed files with 59 additions and 3 deletions

View File

@@ -26,6 +26,7 @@ using Bit.Core.Vault.Models.Data;
using Core.Auth.Enums;
using HandlebarsDotNet;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Logging;
namespace Bit.Core.Services;
@@ -39,6 +40,7 @@ public class HandlebarsMailService : IMailService
private readonly IMailDeliveryService _mailDeliveryService;
private readonly IMailEnqueuingService _mailEnqueuingService;
private readonly IDistributedCache _distributedCache;
private readonly ILogger<HandlebarsMailService> _logger;
private readonly Dictionary<string, HandlebarsTemplate<object, object>> _templateCache = new();
private bool _registeredHelpersAndPartials = false;
@@ -47,12 +49,14 @@ public class HandlebarsMailService : IMailService
GlobalSettings globalSettings,
IMailDeliveryService mailDeliveryService,
IMailEnqueuingService mailEnqueuingService,
IDistributedCache distributedCache)
IDistributedCache distributedCache,
ILogger<HandlebarsMailService> logger)
{
_globalSettings = globalSettings;
_mailDeliveryService = mailDeliveryService;
_mailEnqueuingService = mailEnqueuingService;
_distributedCache = distributedCache;
_logger = logger;
}
public async Task SendVerifyEmailEmailAsync(string email, Guid userId, string token)
@@ -708,6 +712,12 @@ public class HandlebarsMailService : IMailService
private async Task<string?> ReadSourceAsync(string templateName)
{
var diskSource = await ReadSourceFromDiskAsync(templateName);
if (!string.IsNullOrWhiteSpace(diskSource))
{
return diskSource;
}
var assembly = typeof(HandlebarsMailService).GetTypeInfo().Assembly;
var fullTemplateName = $"{Namespace}.{templateName}.hbs";
if (!assembly.GetManifestResourceNames().Any(f => f == fullTemplateName))
@@ -721,6 +731,42 @@ public class HandlebarsMailService : IMailService
}
}
private async Task<string?> ReadSourceFromDiskAsync(string templateName)
{
if (!_globalSettings.SelfHosted)
{
return null;
}
try
{
var templateFileSuffix = ".html";
if (templateName.EndsWith(".txt"))
{
templateFileSuffix = ".txt";
}
else if (!templateName.EndsWith(".html"))
{
// unexpected suffix
return null;
}
var suffixPosition = templateName.LastIndexOf(templateFileSuffix);
var templateNameNoSuffix = templateName.Substring(0, suffixPosition);
var templatePathNoSuffix = templateNameNoSuffix.Replace(".", "/");
var diskPath = $"{_globalSettings.MailTemplateDirectory}/{templatePathNoSuffix}{templateFileSuffix}.hbs";
var directory = Path.GetDirectoryName(diskPath);
if (Directory.Exists(directory) && File.Exists(diskPath))
{
var fileContents = await File.ReadAllTextAsync(diskPath);
return fileContents;
}
}
catch (Exception e)
{
_logger.LogError(e, "Failed to read mail template from disk.");
}
return null;
}
private async Task RegisterHelpersAndPartialsAsync()
{
if (_registeredHelpersAndPartials)