Email is now Built-in! (#2635)
This commit is contained in:
parent
2a539da24c
commit
a85644fb6b
55 changed files with 5129 additions and 1047 deletions
|
@ -334,7 +334,9 @@ public class AccountController : BaseApiController
|
|||
|
||||
|
||||
/// <summary>
|
||||
/// Initiates the flow to update a user's email address. The email address is not changed in this API. A confirmation link is sent/dumped which will
|
||||
/// Initiates the flow to update a user's email address.
|
||||
///
|
||||
/// If email is not setup, then the email address is not changed in this API. A confirmation link is sent/dumped which will
|
||||
/// validate the email. It must be confirmed for the email to update.
|
||||
/// </summary>
|
||||
/// <param name="dto"></param>
|
||||
|
@ -374,10 +376,22 @@ public class AccountController : BaseApiController
|
|||
return BadRequest(await _localizationService.Translate(User.GetUserId(), "generate-token"));
|
||||
}
|
||||
|
||||
user.EmailConfirmed = false;
|
||||
var serverSettings = await _unitOfWork.SettingsRepository.GetSettingsDtoAsync();
|
||||
var shouldEmailUser = serverSettings.IsEmailSetup() || !_emailService.IsValidEmail(user.Email);
|
||||
user.EmailConfirmed = !shouldEmailUser;
|
||||
user.ConfirmationToken = token;
|
||||
await _userManager.UpdateAsync(user);
|
||||
|
||||
if (!shouldEmailUser)
|
||||
{
|
||||
return Ok(new InviteUserResponse
|
||||
{
|
||||
EmailLink = string.Empty,
|
||||
EmailSent = false
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// Send a confirmation email
|
||||
try
|
||||
{
|
||||
|
@ -396,30 +410,27 @@ public class AccountController : BaseApiController
|
|||
}
|
||||
|
||||
|
||||
var accessible = await _accountService.CheckIfAccessible(Request);
|
||||
if (accessible)
|
||||
try
|
||||
{
|
||||
try
|
||||
var invitingUser = (await _unitOfWork.UserRepository.GetAdminUsersAsync()).First().UserName!;
|
||||
// Email the old address of the update change
|
||||
BackgroundJob.Enqueue(() => _emailService.SendEmailChangeEmail(new ConfirmationEmailDto()
|
||||
{
|
||||
// Email the old address of the update change
|
||||
await _emailService.SendEmailChangeEmail(new ConfirmationEmailDto()
|
||||
{
|
||||
EmailAddress = string.IsNullOrEmpty(user.Email) ? dto.Email : user.Email,
|
||||
InstallId = BuildInfo.Version.ToString(),
|
||||
InvitingUser = (await _unitOfWork.UserRepository.GetAdminUsersAsync()).First().UserName!,
|
||||
ServerConfirmationLink = emailLink
|
||||
});
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
/* Swallow exception */
|
||||
}
|
||||
EmailAddress = string.IsNullOrEmpty(user.Email) ? dto.Email : user.Email,
|
||||
InstallId = BuildInfo.Version.ToString(),
|
||||
InvitingUser = invitingUser,
|
||||
ServerConfirmationLink = emailLink
|
||||
}));
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
/* Swallow exception */
|
||||
}
|
||||
|
||||
return Ok(new InviteUserResponse
|
||||
{
|
||||
EmailLink = string.Empty,
|
||||
EmailSent = accessible
|
||||
EmailSent = true
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
@ -579,8 +590,7 @@ public class AccountController : BaseApiController
|
|||
|
||||
|
||||
/// <summary>
|
||||
/// Invites a user to the server. Will generate a setup link for continuing setup. If the server is not accessible, no
|
||||
/// email will be sent.
|
||||
/// Invites a user to the server. Will generate a setup link for continuing setup. If email is not setup, a link will be presented to user to continue setup.
|
||||
/// </summary>
|
||||
/// <param name="dto"></param>
|
||||
/// <returns></returns>
|
||||
|
@ -679,15 +689,15 @@ public class AccountController : BaseApiController
|
|||
return BadRequest(await _localizationService.Translate(User.GetUserId(), "generic-invite-user"));
|
||||
}
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
var emailLink = await _accountService.GenerateEmailLink(Request, user.ConfirmationToken, "confirm-email", dto.Email);
|
||||
_logger.LogCritical("[Invite User]: Email Link for {UserName}: {Link}", user.UserName, emailLink);
|
||||
|
||||
if (!_emailService.IsValidEmail(dto.Email))
|
||||
var settings = await _unitOfWork.SettingsRepository.GetSettingsDtoAsync();
|
||||
if (!_emailService.IsValidEmail(dto.Email) || !settings.IsEmailSetup())
|
||||
{
|
||||
_logger.LogInformation("[Invite User] {Email} doesn't appear to be an email, so will not send an email to address", dto.Email.Replace(Environment.NewLine, string.Empty));
|
||||
_logger.LogInformation("[Invite User] {Email} doesn't appear to be an email or email is not setup", dto.Email.Replace(Environment.NewLine, string.Empty));
|
||||
return Ok(new InviteUserResponse
|
||||
{
|
||||
EmailLink = emailLink,
|
||||
|
@ -696,22 +706,17 @@ public class AccountController : BaseApiController
|
|||
});
|
||||
}
|
||||
|
||||
var accessible = await _accountService.CheckIfAccessible(Request);
|
||||
if (accessible)
|
||||
BackgroundJob.Enqueue(() => _emailService.SendInviteEmail(new ConfirmationEmailDto()
|
||||
{
|
||||
// Do the email send on a background thread to ensure UI can move forward without having to wait for a timeout when users use fake emails
|
||||
BackgroundJob.Enqueue(() => _emailService.SendConfirmationEmail(new ConfirmationEmailDto()
|
||||
{
|
||||
EmailAddress = dto.Email,
|
||||
InvitingUser = adminUser.UserName,
|
||||
ServerConfirmationLink = emailLink
|
||||
}));
|
||||
}
|
||||
EmailAddress = dto.Email,
|
||||
InvitingUser = adminUser.UserName,
|
||||
ServerConfirmationLink = emailLink
|
||||
}));
|
||||
|
||||
return Ok(new InviteUserResponse
|
||||
{
|
||||
EmailLink = emailLink,
|
||||
EmailSent = accessible
|
||||
EmailSent = true
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
@ -837,7 +842,6 @@ public class AccountController : BaseApiController
|
|||
await _eventHub.SendMessageToAsync(MessageFactory.UserUpdate,
|
||||
MessageFactory.UserUpdateEvent(user.Id, user.UserName!), user.Id);
|
||||
|
||||
// Perform Login code
|
||||
return Ok();
|
||||
}
|
||||
|
||||
|
@ -882,6 +886,10 @@ public class AccountController : BaseApiController
|
|||
[EnableRateLimiting("Authentication")]
|
||||
public async Task<ActionResult<string>> ForgotPassword([FromQuery] string email)
|
||||
{
|
||||
|
||||
var settings = await _unitOfWork.SettingsRepository.GetSettingsDtoAsync();
|
||||
if (!settings.IsEmailSetup()) return Ok(await _localizationService.Get("en", "email-not-enabled"));
|
||||
|
||||
var user = await _unitOfWork.UserRepository.GetUserByEmailAsync(email);
|
||||
if (user == null)
|
||||
{
|
||||
|
@ -896,26 +904,28 @@ public class AccountController : BaseApiController
|
|||
if (string.IsNullOrEmpty(user.Email) || !user.EmailConfirmed)
|
||||
return BadRequest(await _localizationService.Translate(user.Id, "confirm-email"));
|
||||
|
||||
var token = await _userManager.GeneratePasswordResetTokenAsync(user);
|
||||
var emailLink = await _accountService.GenerateEmailLink(Request, token, "confirm-reset-password", user.Email);
|
||||
_logger.LogCritical("[Forgot Password]: Email Link for {UserName}: {Link}", user.UserName, emailLink);
|
||||
if (!_emailService.IsValidEmail(user.Email))
|
||||
{
|
||||
_logger.LogCritical("[Forgot Password]: User is trying to do a forgot password flow, but their email ({Email}) isn't valid. No email will be send", user.Email);
|
||||
_logger.LogCritical("[Forgot Password]: User is trying to do a forgot password flow, but their email ({Email}) isn't valid. No email will be send. Admin must change it in UI", user.Email);
|
||||
return Ok(await _localizationService.Translate(user.Id, "invalid-email"));
|
||||
}
|
||||
if (await _accountService.CheckIfAccessible(Request))
|
||||
{
|
||||
await _emailService.SendPasswordResetEmail(new PasswordResetEmailDto()
|
||||
{
|
||||
EmailAddress = user.Email,
|
||||
ServerConfirmationLink = emailLink,
|
||||
InstallId = (await _unitOfWork.SettingsRepository.GetSettingAsync(ServerSettingKey.InstallId)).Value
|
||||
});
|
||||
return Ok(await _localizationService.Translate(user.Id, "email-sent"));
|
||||
}
|
||||
|
||||
return Ok(await _localizationService.Translate(user.Id, "not-accessible-password"));
|
||||
var token = await _userManager.GeneratePasswordResetTokenAsync(user);
|
||||
var emailLink = await _accountService.GenerateEmailLink(Request, token, "confirm-reset-password", user.Email);
|
||||
user.ConfirmationToken = token;
|
||||
_unitOfWork.UserRepository.Update(user);
|
||||
await _unitOfWork.CommitAsync();
|
||||
_logger.LogCritical("[Forgot Password]: Email Link for {UserName}: {Link}", user.UserName, emailLink);
|
||||
|
||||
var installId = (await _unitOfWork.SettingsRepository.GetSettingAsync(ServerSettingKey.InstallId)).Value;
|
||||
BackgroundJob.Enqueue(() => _emailService.SendForgotPasswordEmail(new PasswordResetEmailDto()
|
||||
{
|
||||
EmailAddress = user.Email,
|
||||
ServerConfirmationLink = emailLink,
|
||||
InstallId = installId
|
||||
}));
|
||||
|
||||
return Ok(await _localizationService.Translate(user.Id, "email-sent"));
|
||||
}
|
||||
|
||||
[HttpGet("email-confirmed")]
|
||||
|
@ -965,7 +975,7 @@ public class AccountController : BaseApiController
|
|||
/// <returns></returns>
|
||||
[HttpPost("resend-confirmation-email")]
|
||||
[EnableRateLimiting("Authentication")]
|
||||
public async Task<ActionResult<string>> ResendConfirmationSendEmail([FromQuery] int userId)
|
||||
public async Task<ActionResult<InviteUserResponse>> ResendConfirmationSendEmail([FromQuery] int userId)
|
||||
{
|
||||
var user = await _unitOfWork.UserRepository.GetUserByIdAsync(userId);
|
||||
if (user == null) return BadRequest(await _localizationService.Get("en", "no-user"));
|
||||
|
@ -976,96 +986,47 @@ public class AccountController : BaseApiController
|
|||
if (user.EmailConfirmed) return BadRequest(await _localizationService.Translate(user.Id, "user-already-confirmed"));
|
||||
|
||||
var token = await _userManager.GenerateEmailConfirmationTokenAsync(user);
|
||||
var emailLink = await _accountService.GenerateEmailLink(Request, token, "confirm-email", user.Email);
|
||||
user.ConfirmationToken = token;
|
||||
_unitOfWork.UserRepository.Update(user);
|
||||
await _unitOfWork.CommitAsync();
|
||||
var emailLink = await _accountService.GenerateEmailLink(Request, token, "confirm-email-update", user.Email);
|
||||
_logger.LogCritical("[Email Migration]: Email Link for {UserName}: {Link}", user.UserName, emailLink);
|
||||
|
||||
if (!_emailService.IsValidEmail(user.Email))
|
||||
{
|
||||
_logger.LogCritical("[Email Migration]: User {UserName} is trying to resend an invite flow, but their email ({Email}) isn't valid. No email will be send", user.UserName, user.Email);
|
||||
return BadRequest(await _localizationService.Translate(user.Id, "invalid-email"));
|
||||
}
|
||||
|
||||
if (await _accountService.CheckIfAccessible(Request))
|
||||
|
||||
var serverSettings = await _unitOfWork.SettingsRepository.GetSettingsDtoAsync();
|
||||
var shouldEmailUser = serverSettings.IsEmailSetup() || !_emailService.IsValidEmail(user.Email);
|
||||
|
||||
if (!shouldEmailUser)
|
||||
{
|
||||
try
|
||||
return Ok(new InviteUserResponse()
|
||||
{
|
||||
await _emailService.SendMigrationEmail(new EmailMigrationDto()
|
||||
{
|
||||
EmailAddress = user.Email!,
|
||||
Username = user.UserName!,
|
||||
ServerConfirmationLink = emailLink,
|
||||
InstallId = (await _unitOfWork.SettingsRepository.GetSettingAsync(ServerSettingKey.InstallId)).Value
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "There was an issue resending invite email");
|
||||
return BadRequest(await _localizationService.Translate(user.Id, "generic-invite-email"));
|
||||
}
|
||||
return Ok(emailLink);
|
||||
EmailLink = emailLink,
|
||||
EmailSent = false,
|
||||
InvalidEmail = !_emailService.IsValidEmail(user.Email)
|
||||
});
|
||||
}
|
||||
|
||||
return BadRequest(await _localizationService.Translate(user.Id, "not-accessible"));
|
||||
BackgroundJob.Enqueue(() => _emailService.SendInviteEmail(new ConfirmationEmailDto()
|
||||
{
|
||||
EmailAddress = user.Email!,
|
||||
InvitingUser = User.GetUsername(),
|
||||
ServerConfirmationLink = emailLink,
|
||||
InstallId = serverSettings.InstallId
|
||||
}));
|
||||
|
||||
return Ok(new InviteUserResponse()
|
||||
{
|
||||
EmailLink = emailLink,
|
||||
EmailSent = true,
|
||||
InvalidEmail = !_emailService.IsValidEmail(user.Email)
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is similar to invite. Essentially we authenticate the user's password then go through invite email flow
|
||||
/// </summary>
|
||||
/// <param name="dto"></param>
|
||||
/// <returns></returns>
|
||||
[AllowAnonymous]
|
||||
[HttpPost("migrate-email")]
|
||||
public async Task<ActionResult<string>> MigrateEmail(MigrateUserEmailDto dto)
|
||||
{
|
||||
// If there is an admin account already, return
|
||||
var users = await _unitOfWork.UserRepository.GetAdminUsersAsync();
|
||||
if (users.Any()) return BadRequest(await _localizationService.Get("en", "admin-already-exists"));
|
||||
|
||||
// Check if there is an existing invite
|
||||
var emailValidationErrors = await _accountService.ValidateEmail(dto.Email);
|
||||
if (emailValidationErrors.Any())
|
||||
{
|
||||
var invitedUser = await _unitOfWork.UserRepository.GetUserByEmailAsync(dto.Email);
|
||||
if (await _userManager.IsEmailConfirmedAsync(invitedUser!))
|
||||
return BadRequest(await _localizationService.Get("en", "user-already-registered", invitedUser!.UserName));
|
||||
|
||||
_logger.LogInformation("A user is attempting to login, but hasn't accepted email invite");
|
||||
return BadRequest(await _localizationService.Get("en", "user-already-invited"));
|
||||
}
|
||||
|
||||
|
||||
var user = await _userManager.Users
|
||||
.Include(u => u.UserPreferences)
|
||||
.SingleOrDefaultAsync(x => x.NormalizedUserName == dto.Username.ToUpper());
|
||||
if (user == null) return BadRequest(await _localizationService.Get("en", "invalid-username"));
|
||||
|
||||
var validPassword = await _signInManager.UserManager.CheckPasswordAsync(user, dto.Password);
|
||||
if (!validPassword) return BadRequest(await _localizationService.Get("en", "bad-credentials"));
|
||||
|
||||
try
|
||||
{
|
||||
var token = await _userManager.GenerateEmailConfirmationTokenAsync(user);
|
||||
|
||||
user.Email = dto.Email;
|
||||
if (!await ConfirmEmailToken(token, user)) return BadRequest(await _localizationService.Get("en", "critical-email-migration"));
|
||||
_unitOfWork.UserRepository.Update(user);
|
||||
|
||||
await _unitOfWork.CommitAsync();
|
||||
|
||||
return Ok();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "There was an issue during email migration. Contact support");
|
||||
_unitOfWork.UserRepository.Delete(user);
|
||||
await _unitOfWork.CommitAsync();
|
||||
}
|
||||
|
||||
return BadRequest(await _localizationService.Get("en", "critical-email-migration"));
|
||||
}
|
||||
|
||||
|
||||
|
||||
private async Task<bool> ConfirmEmailToken(string token, AppUser user)
|
||||
{
|
||||
var result = await _userManager.ConfirmEmailAsync(user, token);
|
||||
|
|
|
@ -92,18 +92,28 @@ public class DeviceController : BaseApiController
|
|||
return Ok(await _unitOfWork.DeviceRepository.GetDevicesForUserAsync(User.GetUserId()));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends a collection of chapters to the user's device
|
||||
/// </summary>
|
||||
/// <param name="dto"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost("send-to")]
|
||||
public async Task<ActionResult> SendToDevice(SendToDeviceDto dto)
|
||||
{
|
||||
if (dto.ChapterIds.Any(i => i < 0)) return BadRequest(await _localizationService.Translate(User.GetUserId(), "greater-0", "ChapterIds"));
|
||||
if (dto.DeviceId < 0) return BadRequest(await _localizationService.Translate(User.GetUserId(), "greater-0", "DeviceId"));
|
||||
|
||||
if (await _emailService.IsDefaultEmailService())
|
||||
var isEmailSetup = (await _unitOfWork.SettingsRepository.GetSettingsDtoAsync()).IsEmailSetup();
|
||||
if (!isEmailSetup)
|
||||
return BadRequest(await _localizationService.Translate(User.GetUserId(), "send-to-kavita-email"));
|
||||
|
||||
// // Validate that the device belongs to the user
|
||||
var user = await _unitOfWork.UserRepository.GetUserByIdAsync(User.GetUserId(), AppUserIncludes.Devices);
|
||||
if (user == null || user.Devices.All(d => d.Id != dto.DeviceId)) return BadRequest(await _localizationService.Translate(User.GetUserId(), "send-to-unallowed"));
|
||||
|
||||
var userId = User.GetUserId();
|
||||
await _eventHub.SendMessageToAsync(MessageFactory.NotificationProgress,
|
||||
MessageFactory.SendingToDeviceEvent(await _localizationService.Translate(User.GetUserId(), "send-to-device-status"),
|
||||
MessageFactory.SendingToDeviceEvent(await _localizationService.Translate(userId, "send-to-device-status"),
|
||||
"started"), userId);
|
||||
try
|
||||
{
|
||||
|
@ -112,16 +122,16 @@ public class DeviceController : BaseApiController
|
|||
}
|
||||
catch (KavitaException ex)
|
||||
{
|
||||
return BadRequest(await _localizationService.Translate(User.GetUserId(), ex.Message));
|
||||
return BadRequest(await _localizationService.Translate(userId, ex.Message));
|
||||
}
|
||||
finally
|
||||
{
|
||||
await _eventHub.SendMessageToAsync(MessageFactory.SendingToDevice,
|
||||
MessageFactory.SendingToDeviceEvent(await _localizationService.Translate(User.GetUserId(), "send-to-device-status"),
|
||||
MessageFactory.SendingToDeviceEvent(await _localizationService.Translate(userId, "send-to-device-status"),
|
||||
"ended"), userId);
|
||||
}
|
||||
|
||||
return BadRequest(await _localizationService.Translate(User.GetUserId(), "generic-send-to"));
|
||||
return BadRequest(await _localizationService.Translate(userId, "generic-send-to"));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -14,19 +14,9 @@ namespace API.Controllers;
|
|||
|
||||
#nullable enable
|
||||
|
||||
public class PluginController : BaseApiController
|
||||
public class PluginController(IUnitOfWork unitOfWork, ITokenService tokenService, ILogger<PluginController> logger)
|
||||
: BaseApiController
|
||||
{
|
||||
private readonly IUnitOfWork _unitOfWork;
|
||||
private readonly ITokenService _tokenService;
|
||||
private readonly ILogger<PluginController> _logger;
|
||||
|
||||
public PluginController(IUnitOfWork unitOfWork, ITokenService tokenService, ILogger<PluginController> logger)
|
||||
{
|
||||
_unitOfWork = unitOfWork;
|
||||
_tokenService = tokenService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Authenticate with the Server given an apiKey. This will log you in by returning the user object and the JWT token.
|
||||
/// </summary>
|
||||
|
@ -42,11 +32,11 @@ public class PluginController : BaseApiController
|
|||
// NOTE: In order to log information about plugins, we need some Plugin Description information for each request
|
||||
// Should log into access table so we can tell the user
|
||||
var ipAddress = HttpContext.Connection.RemoteIpAddress?.ToString();
|
||||
var userAgent = HttpContext.Request.Headers["User-Agent"];
|
||||
var userId = await _unitOfWork.UserRepository.GetUserIdByApiKeyAsync(apiKey);
|
||||
var userAgent = HttpContext.Request.Headers.UserAgent;
|
||||
var userId = await unitOfWork.UserRepository.GetUserIdByApiKeyAsync(apiKey);
|
||||
if (userId <= 0)
|
||||
{
|
||||
_logger.LogInformation("A Plugin ({PluginName}) tried to authenticate with an apiKey that doesn't match. Information {@Information}", pluginName.Replace(Environment.NewLine, string.Empty), new
|
||||
logger.LogInformation("A Plugin ({PluginName}) tried to authenticate with an apiKey that doesn't match. Information {@Information}", pluginName.Replace(Environment.NewLine, string.Empty), new
|
||||
{
|
||||
IpAddress = ipAddress,
|
||||
UserAgent = userAgent,
|
||||
|
@ -54,15 +44,15 @@ public class PluginController : BaseApiController
|
|||
});
|
||||
throw new KavitaUnauthenticatedUserException();
|
||||
}
|
||||
var user = await _unitOfWork.UserRepository.GetUserByIdAsync(userId);
|
||||
_logger.LogInformation("Plugin {PluginName} has authenticated with {UserName} ({UserId})'s API Key", pluginName.Replace(Environment.NewLine, string.Empty), user!.UserName, userId);
|
||||
var user = await unitOfWork.UserRepository.GetUserByIdAsync(userId);
|
||||
logger.LogInformation("Plugin {PluginName} has authenticated with {UserName} ({UserId})'s API Key", pluginName.Replace(Environment.NewLine, string.Empty), user!.UserName, userId);
|
||||
return new UserDto
|
||||
{
|
||||
Username = user.UserName!,
|
||||
Token = await _tokenService.CreateToken(user),
|
||||
RefreshToken = await _tokenService.CreateRefreshToken(user),
|
||||
Token = await tokenService.CreateToken(user),
|
||||
RefreshToken = await tokenService.CreateRefreshToken(user),
|
||||
ApiKey = user.ApiKey,
|
||||
KavitaVersion = (await _unitOfWork.SettingsRepository.GetSettingAsync(ServerSettingKey.InstallVersion)).Value
|
||||
KavitaVersion = (await unitOfWork.SettingsRepository.GetSettingAsync(ServerSettingKey.InstallVersion)).Value
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -76,8 +66,8 @@ public class PluginController : BaseApiController
|
|||
[HttpGet("version")]
|
||||
public async Task<ActionResult<string>> GetVersion([Required] string apiKey)
|
||||
{
|
||||
var userId = await _unitOfWork.UserRepository.GetUserIdByApiKeyAsync(apiKey);
|
||||
var userId = await unitOfWork.UserRepository.GetUserIdByApiKeyAsync(apiKey);
|
||||
if (userId <= 0) throw new KavitaUnauthenticatedUserException();
|
||||
return Ok((await _unitOfWork.SettingsRepository.GetSettingAsync(ServerSettingKey.InstallVersion)).Value);
|
||||
return Ok((await unitOfWork.SettingsRepository.GetSettingAsync(ServerSettingKey.InstallVersion)).Value);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -275,22 +275,6 @@ public class ServerController : BaseApiController
|
|||
return Ok();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the KavitaEmail version for non-default instances
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[Authorize("RequireAdminRole")]
|
||||
[HttpGet("email-version")]
|
||||
public async Task<ActionResult<string?>> GetEmailVersion()
|
||||
{
|
||||
var emailServiceUrl = (await _unitOfWork.SettingsRepository.GetSettingAsync(ServerSettingKey.EmailServiceUrl))
|
||||
.Value;
|
||||
|
||||
if (emailServiceUrl.Equals(EmailService.DefaultApiUrl)) return Ok(null);
|
||||
|
||||
return Ok(await _emailService.GetVersion(emailServiceUrl));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks for updates and pushes an event to the UI
|
||||
/// </summary>
|
||||
|
@ -301,5 +285,4 @@ public class ServerController : BaseApiController
|
|||
await _taskScheduler.CheckForUpdate();
|
||||
return Ok();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ using System.Threading.Tasks;
|
|||
using API.Data;
|
||||
using API.DTOs.Email;
|
||||
using API.DTOs.Settings;
|
||||
using API.Entities;
|
||||
using API.Entities.Enums;
|
||||
using API.Extensions;
|
||||
using API.Helpers.Converters;
|
||||
|
@ -119,28 +120,7 @@ public class SettingsController : BaseApiController
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets the email service url
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[Authorize(Policy = "RequireAdminRole")]
|
||||
[HttpPost("reset-email-url")]
|
||||
public async Task<ActionResult<ServerSettingDto>> ResetEmailServiceUrlSettings()
|
||||
{
|
||||
_logger.LogInformation("{UserName} is resetting Email Service Url Setting", User.GetUsername());
|
||||
var emailSetting = await _unitOfWork.SettingsRepository.GetSettingAsync(ServerSettingKey.EmailServiceUrl);
|
||||
emailSetting.Value = EmailService.DefaultApiUrl;
|
||||
_unitOfWork.SettingsRepository.Update(emailSetting);
|
||||
|
||||
if (!await _unitOfWork.CommitAsync())
|
||||
{
|
||||
await _unitOfWork.RollbackAsync();
|
||||
}
|
||||
|
||||
return Ok(await _unitOfWork.SettingsRepository.GetSettingsDtoAsync());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends a test email from the Email Service. Will not send if email service is the Default Provider
|
||||
/// Sends a test email from the Email Service.
|
||||
/// </summary>
|
||||
/// <param name="dto"></param>
|
||||
/// <returns></returns>
|
||||
|
@ -149,8 +129,19 @@ public class SettingsController : BaseApiController
|
|||
public async Task<ActionResult<EmailTestResultDto>> TestEmailServiceUrl(TestEmailDto dto)
|
||||
{
|
||||
var user = await _unitOfWork.UserRepository.GetUserByIdAsync(User.GetUserId());
|
||||
var emailService = (await _unitOfWork.SettingsRepository.GetSettingAsync(ServerSettingKey.EmailServiceUrl)).Value;
|
||||
return Ok(await _emailService.TestConnectivity(dto.Url, user!.Email, !emailService.Equals(EmailService.DefaultApiUrl)));
|
||||
return Ok(await _emailService.SendTestEmail(user!.Email));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Is the minimum information setup for Email to work
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[Authorize]
|
||||
[HttpGet("is-email-setup")]
|
||||
public async Task<ActionResult<bool>> IsEmailSetup()
|
||||
{
|
||||
var settings = await _unitOfWork.SettingsRepository.GetSettingsDtoAsync();
|
||||
return Ok(settings.IsEmailSetup());
|
||||
}
|
||||
|
||||
|
||||
|
@ -233,6 +224,10 @@ public class SettingsController : BaseApiController
|
|||
_unitOfWork.SettingsRepository.Update(setting);
|
||||
}
|
||||
|
||||
UpdateEmailSettings(setting, updateSettingsDto);
|
||||
|
||||
|
||||
|
||||
if (setting.Key == ServerSettingKey.IpAddresses && updateSettingsDto.IpAddresses != setting.Value)
|
||||
{
|
||||
if (OsInfo.IsDocker) continue;
|
||||
|
@ -289,17 +284,6 @@ public class SettingsController : BaseApiController
|
|||
_unitOfWork.SettingsRepository.Update(setting);
|
||||
}
|
||||
|
||||
if (setting.Key == ServerSettingKey.EmailServiceUrl && updateSettingsDto.EmailServiceUrl + string.Empty != setting.Value)
|
||||
{
|
||||
setting.Value = string.IsNullOrEmpty(updateSettingsDto.EmailServiceUrl) ? EmailService.DefaultApiUrl : updateSettingsDto.EmailServiceUrl;
|
||||
setting.Value = UrlHelper.RemoveEndingSlash(setting.Value);
|
||||
FlurlHttp.ConfigureClient(setting.Value, cli =>
|
||||
cli.Settings.HttpClientFactory = new UntrustedCertClientFactory());
|
||||
|
||||
_unitOfWork.SettingsRepository.Update(setting);
|
||||
}
|
||||
|
||||
|
||||
if (setting.Key == ServerSettingKey.BookmarkDirectory && bookmarkDirectory != setting.Value)
|
||||
{
|
||||
// Validate new directory can be used
|
||||
|
@ -392,6 +376,63 @@ public class SettingsController : BaseApiController
|
|||
return Ok(updateSettingsDto);
|
||||
}
|
||||
|
||||
private void UpdateEmailSettings(ServerSetting setting, ServerSettingDto updateSettingsDto)
|
||||
{
|
||||
if (setting.Key == ServerSettingKey.EmailHost && updateSettingsDto.SmtpConfig.Host + string.Empty != setting.Value)
|
||||
{
|
||||
setting.Value = updateSettingsDto.SmtpConfig.Host + string.Empty;
|
||||
_unitOfWork.SettingsRepository.Update(setting);
|
||||
}
|
||||
|
||||
if (setting.Key == ServerSettingKey.EmailPort && updateSettingsDto.SmtpConfig.Port + string.Empty != setting.Value)
|
||||
{
|
||||
setting.Value = updateSettingsDto.SmtpConfig.Port + string.Empty;
|
||||
_unitOfWork.SettingsRepository.Update(setting);
|
||||
}
|
||||
|
||||
if (setting.Key == ServerSettingKey.EmailAuthPassword && updateSettingsDto.SmtpConfig.Password + string.Empty != setting.Value)
|
||||
{
|
||||
setting.Value = updateSettingsDto.SmtpConfig.Password + string.Empty;
|
||||
_unitOfWork.SettingsRepository.Update(setting);
|
||||
}
|
||||
|
||||
if (setting.Key == ServerSettingKey.EmailAuthUserName && updateSettingsDto.SmtpConfig.UserName + string.Empty != setting.Value)
|
||||
{
|
||||
setting.Value = updateSettingsDto.SmtpConfig.UserName + string.Empty;
|
||||
_unitOfWork.SettingsRepository.Update(setting);
|
||||
}
|
||||
|
||||
if (setting.Key == ServerSettingKey.EmailSenderAddress && updateSettingsDto.SmtpConfig.SenderAddress + string.Empty != setting.Value)
|
||||
{
|
||||
setting.Value = updateSettingsDto.SmtpConfig.SenderAddress + string.Empty;
|
||||
_unitOfWork.SettingsRepository.Update(setting);
|
||||
}
|
||||
|
||||
if (setting.Key == ServerSettingKey.EmailSenderDisplayName && updateSettingsDto.SmtpConfig.SenderDisplayName + string.Empty != setting.Value)
|
||||
{
|
||||
setting.Value = updateSettingsDto.SmtpConfig.SenderDisplayName + string.Empty;
|
||||
_unitOfWork.SettingsRepository.Update(setting);
|
||||
}
|
||||
|
||||
if (setting.Key == ServerSettingKey.EmailSizeLimit && updateSettingsDto.SmtpConfig.SizeLimit + string.Empty != setting.Value)
|
||||
{
|
||||
setting.Value = updateSettingsDto.SmtpConfig.SizeLimit + string.Empty;
|
||||
_unitOfWork.SettingsRepository.Update(setting);
|
||||
}
|
||||
|
||||
if (setting.Key == ServerSettingKey.EmailEnableSsl && updateSettingsDto.SmtpConfig.EnableSsl + string.Empty != setting.Value)
|
||||
{
|
||||
setting.Value = updateSettingsDto.SmtpConfig.EnableSsl + string.Empty;
|
||||
_unitOfWork.SettingsRepository.Update(setting);
|
||||
}
|
||||
|
||||
if (setting.Key == ServerSettingKey.EmailCustomizedTemplates && updateSettingsDto.SmtpConfig.CustomizedTemplates + string.Empty != setting.Value)
|
||||
{
|
||||
setting.Value = updateSettingsDto.SmtpConfig.CustomizedTemplates + string.Empty;
|
||||
_unitOfWork.SettingsRepository.Update(setting);
|
||||
}
|
||||
}
|
||||
|
||||
[Authorize(Policy = "RequireAdminRole")]
|
||||
[HttpGet("task-frequencies")]
|
||||
public ActionResult<IEnumerable<string>> GetTaskFrequencies()
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue