Version Update Modal Rework + A few bugfixes (#3664)

This commit is contained in:
Joe Milazzo 2025-03-22 15:05:48 -05:00 committed by GitHub
parent 9fb3bdd548
commit 43d0d1277f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
65 changed files with 1963 additions and 805 deletions

View file

@ -40,10 +40,11 @@ public class SettingsController : BaseApiController
private readonly IEmailService _emailService;
private readonly ILibraryWatcher _libraryWatcher;
private readonly ILocalizationService _localizationService;
private readonly ISettingsService _settingsService;
public SettingsController(ILogger<SettingsController> logger, IUnitOfWork unitOfWork, ITaskScheduler taskScheduler,
IDirectoryService directoryService, IMapper mapper, IEmailService emailService, ILibraryWatcher libraryWatcher,
ILocalizationService localizationService)
ILocalizationService localizationService, ISettingsService settingsService)
{
_logger = logger;
_unitOfWork = unitOfWork;
@ -53,6 +54,7 @@ public class SettingsController : BaseApiController
_emailService = emailService;
_libraryWatcher = libraryWatcher;
_localizationService = localizationService;
_settingsService = settingsService;
}
[HttpGet("base-url")]
@ -139,346 +141,32 @@ public class SettingsController : BaseApiController
}
/// <summary>
/// Update Server settings
/// </summary>
/// <param name="updateSettingsDto"></param>
/// <returns></returns>
[Authorize(Policy = "RequireAdminRole")]
[HttpPost]
public async Task<ActionResult<ServerSettingDto>> UpdateSettings(ServerSettingDto updateSettingsDto)
{
_logger.LogInformation("{UserName} is updating Server Settings", User.GetUsername());
// We do not allow CacheDirectory changes, so we will ignore.
var currentSettings = await _unitOfWork.SettingsRepository.GetSettingsAsync();
var updateBookmarks = false;
var originalBookmarkDirectory = _directoryService.BookmarkDirectory;
var bookmarkDirectory = updateSettingsDto.BookmarksDirectory;
if (!updateSettingsDto.BookmarksDirectory.EndsWith("bookmarks") &&
!updateSettingsDto.BookmarksDirectory.EndsWith("bookmarks/"))
{
bookmarkDirectory =
_directoryService.FileSystem.Path.Join(updateSettingsDto.BookmarksDirectory, "bookmarks");
}
if (string.IsNullOrEmpty(updateSettingsDto.BookmarksDirectory))
{
bookmarkDirectory = _directoryService.BookmarkDirectory;
}
var updateTask = false;
foreach (var setting in currentSettings)
{
if (setting.Key == ServerSettingKey.OnDeckProgressDays &&
updateSettingsDto.OnDeckProgressDays + string.Empty != setting.Value)
{
setting.Value = updateSettingsDto.OnDeckProgressDays + string.Empty;
_unitOfWork.SettingsRepository.Update(setting);
}
if (setting.Key == ServerSettingKey.OnDeckUpdateDays &&
updateSettingsDto.OnDeckUpdateDays + string.Empty != setting.Value)
{
setting.Value = updateSettingsDto.OnDeckUpdateDays + string.Empty;
_unitOfWork.SettingsRepository.Update(setting);
}
if (setting.Key == ServerSettingKey.Port && updateSettingsDto.Port + string.Empty != setting.Value)
{
if (OsInfo.IsDocker) continue;
setting.Value = updateSettingsDto.Port + string.Empty;
// Port is managed in appSetting.json
Configuration.Port = updateSettingsDto.Port;
_unitOfWork.SettingsRepository.Update(setting);
}
if (setting.Key == ServerSettingKey.CacheSize &&
updateSettingsDto.CacheSize + string.Empty != setting.Value)
{
setting.Value = updateSettingsDto.CacheSize + string.Empty;
// CacheSize is managed in appSetting.json
Configuration.CacheSize = updateSettingsDto.CacheSize;
_unitOfWork.SettingsRepository.Update(setting);
}
updateTask = updateTask || UpdateSchedulingSettings(setting, updateSettingsDto);
UpdateEmailSettings(setting, updateSettingsDto);
if (setting.Key == ServerSettingKey.IpAddresses && updateSettingsDto.IpAddresses != setting.Value)
{
if (OsInfo.IsDocker) continue;
// Validate IP addresses
foreach (var ipAddress in updateSettingsDto.IpAddresses.Split(',',
StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries))
{
if (!IPAddress.TryParse(ipAddress.Trim(), out _))
{
return BadRequest(await _localizationService.Translate(User.GetUserId(), "ip-address-invalid",
ipAddress));
}
}
setting.Value = updateSettingsDto.IpAddresses;
// IpAddresses is managed in appSetting.json
Configuration.IpAddresses = updateSettingsDto.IpAddresses;
_unitOfWork.SettingsRepository.Update(setting);
}
if (setting.Key == ServerSettingKey.BaseUrl && updateSettingsDto.BaseUrl + string.Empty != setting.Value)
{
var path = !updateSettingsDto.BaseUrl.StartsWith('/')
? $"/{updateSettingsDto.BaseUrl}"
: updateSettingsDto.BaseUrl;
path = !path.EndsWith('/')
? $"{path}/"
: path;
setting.Value = path;
Configuration.BaseUrl = updateSettingsDto.BaseUrl;
_unitOfWork.SettingsRepository.Update(setting);
}
if (setting.Key == ServerSettingKey.LoggingLevel &&
updateSettingsDto.LoggingLevel + string.Empty != setting.Value)
{
setting.Value = updateSettingsDto.LoggingLevel + string.Empty;
LogLevelOptions.SwitchLogLevel(updateSettingsDto.LoggingLevel);
_unitOfWork.SettingsRepository.Update(setting);
}
if (setting.Key == ServerSettingKey.EnableOpds &&
updateSettingsDto.EnableOpds + string.Empty != setting.Value)
{
setting.Value = updateSettingsDto.EnableOpds + string.Empty;
_unitOfWork.SettingsRepository.Update(setting);
}
if (setting.Key == ServerSettingKey.EncodeMediaAs &&
((int)updateSettingsDto.EncodeMediaAs).ToString() != setting.Value)
{
setting.Value = ((int)updateSettingsDto.EncodeMediaAs).ToString();
_unitOfWork.SettingsRepository.Update(setting);
}
if (setting.Key == ServerSettingKey.CoverImageSize &&
((int)updateSettingsDto.CoverImageSize).ToString() != setting.Value)
{
setting.Value = ((int)updateSettingsDto.CoverImageSize).ToString();
_unitOfWork.SettingsRepository.Update(setting);
}
if (setting.Key == ServerSettingKey.HostName && updateSettingsDto.HostName + string.Empty != setting.Value)
{
setting.Value = (updateSettingsDto.HostName + string.Empty).Trim();
setting.Value = UrlHelper.RemoveEndingSlash(setting.Value);
_unitOfWork.SettingsRepository.Update(setting);
}
if (setting.Key == ServerSettingKey.BookmarkDirectory && bookmarkDirectory != setting.Value)
{
// Validate new directory can be used
if (!await _directoryService.CheckWriteAccess(bookmarkDirectory))
{
return BadRequest(
await _localizationService.Translate(User.GetUserId(), "bookmark-dir-permissions"));
}
originalBookmarkDirectory = setting.Value;
// Normalize the path deliminators. Just to look nice in DB, no functionality
setting.Value = _directoryService.FileSystem.Path.GetFullPath(bookmarkDirectory);
_unitOfWork.SettingsRepository.Update(setting);
updateBookmarks = true;
}
if (setting.Key == ServerSettingKey.AllowStatCollection &&
updateSettingsDto.AllowStatCollection + string.Empty != setting.Value)
{
setting.Value = updateSettingsDto.AllowStatCollection + string.Empty;
_unitOfWork.SettingsRepository.Update(setting);
}
if (setting.Key == ServerSettingKey.TotalBackups &&
updateSettingsDto.TotalBackups + string.Empty != setting.Value)
{
if (updateSettingsDto.TotalBackups > 30 || updateSettingsDto.TotalBackups < 1)
{
return BadRequest(await _localizationService.Translate(User.GetUserId(), "total-backups"));
}
setting.Value = updateSettingsDto.TotalBackups + string.Empty;
_unitOfWork.SettingsRepository.Update(setting);
}
if (setting.Key == ServerSettingKey.TotalLogs &&
updateSettingsDto.TotalLogs + string.Empty != setting.Value)
{
if (updateSettingsDto.TotalLogs > 30 || updateSettingsDto.TotalLogs < 1)
{
return BadRequest(await _localizationService.Translate(User.GetUserId(), "total-logs"));
}
setting.Value = updateSettingsDto.TotalLogs + string.Empty;
_unitOfWork.SettingsRepository.Update(setting);
}
if (setting.Key == ServerSettingKey.EnableFolderWatching &&
updateSettingsDto.EnableFolderWatching + string.Empty != setting.Value)
{
setting.Value = updateSettingsDto.EnableFolderWatching + string.Empty;
_unitOfWork.SettingsRepository.Update(setting);
}
}
if (!_unitOfWork.HasChanges()) return Ok(updateSettingsDto);
try
{
await _unitOfWork.CommitAsync();
if (!updateSettingsDto.AllowStatCollection)
{
_taskScheduler.CancelStatsTasks();
}
else
{
await _taskScheduler.ScheduleStatsTasks();
}
if (updateBookmarks)
{
UpdateBookmarkDirectory(originalBookmarkDirectory, bookmarkDirectory);
}
if (updateTask)
{
BackgroundJob.Enqueue(() => _taskScheduler.ScheduleTasks());
}
if (updateSettingsDto.EnableFolderWatching)
{
BackgroundJob.Enqueue(() => _libraryWatcher.StartWatching());
}
else
{
BackgroundJob.Enqueue(() => _libraryWatcher.StopWatching());
}
return Ok(await _settingsService.UpdateSettings(updateSettingsDto));
}
catch (KavitaException ex)
{
return BadRequest(await _localizationService.Translate(User.GetUserId(), ex.Message));
}
catch (Exception ex)
{
_logger.LogError(ex, "There was an exception when updating server settings");
await _unitOfWork.RollbackAsync();
return BadRequest(await _localizationService.Translate(User.GetUserId(), "generic-error"));
}
_logger.LogInformation("Server Settings updated");
BackgroundJob.Enqueue(() => _taskScheduler.ScheduleTasks());
return Ok(updateSettingsDto);
}
private void UpdateBookmarkDirectory(string originalBookmarkDirectory, string bookmarkDirectory)
{
_directoryService.ExistOrCreate(bookmarkDirectory);
_directoryService.CopyDirectoryToDirectory(originalBookmarkDirectory, bookmarkDirectory);
_directoryService.ClearAndDeleteDirectory(originalBookmarkDirectory);
}
private bool UpdateSchedulingSettings(ServerSetting setting, ServerSettingDto updateSettingsDto)
{
if (setting.Key == ServerSettingKey.TaskBackup && updateSettingsDto.TaskBackup != setting.Value)
{
setting.Value = updateSettingsDto.TaskBackup;
_unitOfWork.SettingsRepository.Update(setting);
return true;
}
if (setting.Key == ServerSettingKey.TaskScan && updateSettingsDto.TaskScan != setting.Value)
{
setting.Value = updateSettingsDto.TaskScan;
_unitOfWork.SettingsRepository.Update(setting);
return true;
}
if (setting.Key == ServerSettingKey.TaskCleanup && updateSettingsDto.TaskCleanup != setting.Value)
{
setting.Value = updateSettingsDto.TaskCleanup;
_unitOfWork.SettingsRepository.Update(setting);
return true;
}
return false;
}
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);
}
}
/// <summary>
/// All values allowed for Task Scheduling APIs. A custom cron job is not included. Disabled is not applicable for Cleanup.
/// </summary>
@ -549,7 +237,7 @@ public class SettingsController : BaseApiController
}
/// <summary>
/// Update the metadata settings for Kavita+ users
/// Update the metadata settings for Kavita+ Metadata feature
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
@ -557,54 +245,14 @@ public class SettingsController : BaseApiController
[HttpPost("metadata-settings")]
public async Task<ActionResult<MetadataSettingsDto>> UpdateMetadataSettings(MetadataSettingsDto dto)
{
var existingMetadataSetting = await _unitOfWork.SettingsRepository.GetMetadataSettings();
existingMetadataSetting.Enabled = dto.Enabled;
existingMetadataSetting.EnableSummary = dto.EnableSummary;
existingMetadataSetting.EnableLocalizedName = dto.EnableLocalizedName;
existingMetadataSetting.EnablePublicationStatus = dto.EnablePublicationStatus;
existingMetadataSetting.EnableRelationships = dto.EnableRelationships;
existingMetadataSetting.EnablePeople = dto.EnablePeople;
existingMetadataSetting.EnableStartDate = dto.EnableStartDate;
existingMetadataSetting.EnableGenres = dto.EnableGenres;
existingMetadataSetting.EnableTags = dto.EnableTags;
existingMetadataSetting.FirstLastPeopleNaming = dto.FirstLastPeopleNaming;
existingMetadataSetting.EnableCoverImage = dto.EnableCoverImage;
existingMetadataSetting.AgeRatingMappings = dto.AgeRatingMappings ?? [];
existingMetadataSetting.Blacklist = dto.Blacklist.Where(s => !string.IsNullOrWhiteSpace(s)).DistinctBy(d => d.ToNormalized()).ToList() ?? [];
existingMetadataSetting.Whitelist = dto.Whitelist.Where(s => !string.IsNullOrWhiteSpace(s)).DistinctBy(d => d.ToNormalized()).ToList() ?? [];
existingMetadataSetting.Overrides = dto.Overrides.ToList() ?? [];
existingMetadataSetting.PersonRoles = dto.PersonRoles ?? [];
// Handle Field Mappings
if (dto.FieldMappings != null)
try
{
// Clear existing mappings
existingMetadataSetting.FieldMappings ??= [];
_unitOfWork.SettingsRepository.RemoveRange(existingMetadataSetting.FieldMappings);
existingMetadataSetting.FieldMappings.Clear();
// Add new mappings
foreach (var mappingDto in dto.FieldMappings)
{
existingMetadataSetting.FieldMappings.Add(new MetadataFieldMapping
{
SourceType = mappingDto.SourceType,
DestinationType = mappingDto.DestinationType,
SourceValue = mappingDto.SourceValue,
DestinationValue = mappingDto.DestinationValue,
ExcludeFromSource = mappingDto.ExcludeFromSource
});
}
return Ok(await _settingsService.UpdateMetadataSettings(dto));
}
catch (Exception ex)
{
_logger.LogError(ex, "There was an issue when updating metadata settings");
return BadRequest(ex.Message);
}
// Save changes
await _unitOfWork.CommitAsync();
// Return updated settings
return Ok(await _unitOfWork.SettingsRepository.GetMetadataSettingDto());
}
}