diff --git a/API/Controllers/AccountController.cs b/API/Controllers/AccountController.cs index 4959fc5f4..876cecf84 100644 --- a/API/Controllers/AccountController.cs +++ b/API/Controllers/AccountController.cs @@ -83,42 +83,55 @@ namespace API.Controllers [HttpPost("register")] public async Task> Register(RegisterDto registerDto) { - if (await _userManager.Users.AnyAsync(x => x.NormalizedUserName == registerDto.Username.ToUpper())) + try { - return BadRequest("Username is taken."); - } - - var user = _mapper.Map(registerDto); - user.UserPreferences ??= new AppUserPreferences(); - - var result = await _userManager.CreateAsync(user, registerDto.Password); - - if (!result.Succeeded) return BadRequest(result.Errors); - - var role = registerDto.IsAdmin ? PolicyConstants.AdminRole : PolicyConstants.PlebRole; - var roleResult = await _userManager.AddToRoleAsync(user, role); - - if (!roleResult.Succeeded) return BadRequest(result.Errors); - - // When we register an admin, we need to grant them access to all Libraries. - if (registerDto.IsAdmin) - { - _logger.LogInformation("{UserName} is being registered as admin. Granting access to all libraries", user.UserName); - var libraries = (await _unitOfWork.LibraryRepository.GetLibrariesAsync()).ToList(); - foreach (var lib in libraries) + if (await _userManager.Users.AnyAsync(x => x.NormalizedUserName == registerDto.Username.ToUpper())) { - lib.AppUsers ??= new List(); - lib.AppUsers.Add(user); + return BadRequest("Username is taken."); } - if (libraries.Any() && !await _unitOfWork.Complete()) _logger.LogError("There was an issue granting library access. Please do this manually"); + + var user = _mapper.Map(registerDto); + user.UserPreferences ??= new AppUserPreferences(); + + var result = await _userManager.CreateAsync(user, registerDto.Password); + + if (!result.Succeeded) return BadRequest(result.Errors); + + var role = registerDto.IsAdmin ? PolicyConstants.AdminRole : PolicyConstants.PlebRole; + var roleResult = await _userManager.AddToRoleAsync(user, role); + + if (!roleResult.Succeeded) return BadRequest(result.Errors); + + // When we register an admin, we need to grant them access to all Libraries. + if (registerDto.IsAdmin) + { + _logger.LogInformation("{UserName} is being registered as admin. Granting access to all libraries", + user.UserName); + var libraries = (await _unitOfWork.LibraryRepository.GetLibrariesAsync()).ToList(); + foreach (var lib in libraries) + { + lib.AppUsers ??= new List(); + lib.AppUsers.Add(user); + } + + if (libraries.Any() && !await _unitOfWork.CommitAsync()) + _logger.LogError("There was an issue granting library access. Please do this manually"); + } + + return new UserDto + { + Username = user.UserName, + Token = await _tokenService.CreateToken(user), + Preferences = _mapper.Map(user.UserPreferences) + }; + } + catch (Exception ex) + { + _logger.LogError(ex, "Something went wrong when registering user"); + await _unitOfWork.RollbackAsync(); } - return new UserDto - { - Username = user.UserName, - Token = await _tokenService.CreateToken(user), - Preferences = _mapper.Map(user.UserPreferences) - }; + return BadRequest("Something went wrong when registering user"); } [HttpPost("login")] @@ -140,7 +153,7 @@ namespace API.Controllers user.UserPreferences ??= new AppUserPreferences(); _unitOfWork.UserRepository.Update(user); - await _unitOfWork.Complete(); + await _unitOfWork.CommitAsync(); _logger.LogInformation("{UserName} logged in at {Time}", user.UserName, user.LastActive); @@ -167,7 +180,6 @@ namespace API.Controllers { var user = await _userManager.Users .Include(u => u.UserPreferences) - //.Include(u => u.UserRoles) .SingleOrDefaultAsync(x => x.NormalizedUserName == updateRbsDto.Username.ToUpper()); if (updateRbsDto.Roles.Contains(PolicyConstants.AdminRole) || updateRbsDto.Roles.Contains(PolicyConstants.PlebRole)) @@ -178,16 +190,22 @@ namespace API.Controllers var existingRoles = (await _userManager.GetRolesAsync(user)) .Where(s => s != PolicyConstants.AdminRole && s != PolicyConstants.PlebRole) .ToList(); - + // Find what needs to be added and what needs to be removed var rolesToRemove = existingRoles.Except(updateRbsDto.Roles); var result = await _userManager.AddToRolesAsync(user, updateRbsDto.Roles); - if (!result.Succeeded) return BadRequest("Something went wrong, unable to update user's roles"); + if (!result.Succeeded) + { + await _unitOfWork.RollbackAsync(); + return BadRequest("Something went wrong, unable to update user's roles"); + } if ((await _userManager.RemoveFromRolesAsync(user, rolesToRemove)).Succeeded) { return Ok(); } + + await _unitOfWork.RollbackAsync(); return BadRequest("Something went wrong, unable to update user's roles"); } diff --git a/API/Controllers/CollectionController.cs b/API/Controllers/CollectionController.cs index 27455a283..6ad5fdbaf 100644 --- a/API/Controllers/CollectionController.cs +++ b/API/Controllers/CollectionController.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using API.Constants; @@ -33,11 +34,7 @@ namespace API.Controllers { return await _unitOfWork.CollectionTagRepository.GetAllTagDtosAsync(); } - else - { - return await _unitOfWork.CollectionTagRepository.GetAllPromotedTagDtosAsync(); - } - + return await _unitOfWork.CollectionTagRepository.GetAllPromotedTagDtosAsync(); } [Authorize(Policy = "RequireAdminRole")] @@ -64,7 +61,7 @@ namespace API.Controllers if (_unitOfWork.HasChanges()) { - if (await _unitOfWork.Complete()) + if (await _unitOfWork.CommitAsync()) { return Ok("Tag updated successfully"); } @@ -81,38 +78,42 @@ namespace API.Controllers [HttpPost("update-series")] public async Task UpdateSeriesForTag(UpdateSeriesForTagDto updateSeriesForTagDto) { - var tag = await _unitOfWork.CollectionTagRepository.GetFullTagAsync(updateSeriesForTagDto.Tag.Id); - if (tag == null) return BadRequest("Not a valid Tag"); - tag.SeriesMetadatas ??= new List(); - - // Check if Tag has updated (Summary) - if (tag.Summary == null || !tag.Summary.Equals(updateSeriesForTagDto.Tag.Summary)) + try { - tag.Summary = updateSeriesForTagDto.Tag.Summary; - _unitOfWork.CollectionTagRepository.Update(tag); - } + var tag = await _unitOfWork.CollectionTagRepository.GetFullTagAsync(updateSeriesForTagDto.Tag.Id); + if (tag == null) return BadRequest("Not a valid Tag"); + tag.SeriesMetadatas ??= new List(); - foreach (var seriesIdToRemove in updateSeriesForTagDto.SeriesIdsToRemove) - { - tag.SeriesMetadatas.Remove(tag.SeriesMetadatas.Single(sm => sm.SeriesId == seriesIdToRemove)); - } - + // Check if Tag has updated (Summary) + if (tag.Summary == null || !tag.Summary.Equals(updateSeriesForTagDto.Tag.Summary)) + { + tag.Summary = updateSeriesForTagDto.Tag.Summary; + _unitOfWork.CollectionTagRepository.Update(tag); + } - if (tag.SeriesMetadatas.Count == 0) - { - _unitOfWork.CollectionTagRepository.Remove(tag); - } + foreach (var seriesIdToRemove in updateSeriesForTagDto.SeriesIdsToRemove) + { + tag.SeriesMetadatas.Remove(tag.SeriesMetadatas.Single(sm => sm.SeriesId == seriesIdToRemove)); + } - if (_unitOfWork.HasChanges() && await _unitOfWork.Complete()) + + if (tag.SeriesMetadatas.Count == 0) + { + _unitOfWork.CollectionTagRepository.Remove(tag); + } + + if (_unitOfWork.HasChanges() && await _unitOfWork.CommitAsync()) + { + return Ok("Tag updated"); + } + } + catch (Exception) { - return Ok("Tag updated"); + await _unitOfWork.RollbackAsync(); } return BadRequest("Something went wrong. Please try again."); } - - - } } \ No newline at end of file diff --git a/API/Controllers/LibraryController.cs b/API/Controllers/LibraryController.cs index 72a91f1fb..352b9f873 100644 --- a/API/Controllers/LibraryController.cs +++ b/API/Controllers/LibraryController.cs @@ -67,7 +67,7 @@ namespace API.Controllers } - if (!await _unitOfWork.Complete()) return BadRequest("There was a critical issue. Please try again."); + if (!await _unitOfWork.CommitAsync()) return BadRequest("There was a critical issue. Please try again."); _logger.LogInformation("Created a new library: {LibraryName}", library.Name); _taskScheduler.ScanLibrary(library.Id); @@ -133,7 +133,7 @@ namespace API.Controllers return Ok(_mapper.Map(user)); } - if (await _unitOfWork.Complete()) + if (await _unitOfWork.CommitAsync()) { _logger.LogInformation("Added: {SelectedLibraries} to {Username}",libraryString, updateLibraryForUserDto.Username); return Ok(_mapper.Map(user)); @@ -199,7 +199,7 @@ namespace API.Controllers _unitOfWork.LibraryRepository.Update(library); - if (!await _unitOfWork.Complete()) return BadRequest("There was a critical issue updating the library."); + if (!await _unitOfWork.CommitAsync()) return BadRequest("There was a critical issue updating the library."); if (differenceBetweenFolders.Any()) { _taskScheduler.ScanLibrary(library.Id, true); diff --git a/API/Controllers/ReaderController.cs b/API/Controllers/ReaderController.cs index c364fac48..2ac3d51fe 100644 --- a/API/Controllers/ReaderController.cs +++ b/API/Controllers/ReaderController.cs @@ -116,7 +116,7 @@ namespace API.Controllers _unitOfWork.UserRepository.Update(user); - if (await _unitOfWork.Complete()) + if (await _unitOfWork.CommitAsync()) { return Ok(); } @@ -157,7 +157,7 @@ namespace API.Controllers _unitOfWork.UserRepository.Update(user); - if (await _unitOfWork.Complete()) + if (await _unitOfWork.CommitAsync()) { return Ok(); } @@ -198,7 +198,7 @@ namespace API.Controllers _unitOfWork.UserRepository.Update(user); - if (await _unitOfWork.Complete()) + if (await _unitOfWork.CommitAsync()) { return Ok(); } @@ -251,7 +251,7 @@ namespace API.Controllers _unitOfWork.UserRepository.Update(user); - if (await _unitOfWork.Complete()) + if (await _unitOfWork.CommitAsync()) { return Ok(); } diff --git a/API/Controllers/SeriesController.cs b/API/Controllers/SeriesController.cs index caa55b229..3780538ad 100644 --- a/API/Controllers/SeriesController.cs +++ b/API/Controllers/SeriesController.cs @@ -114,7 +114,7 @@ namespace API.Controllers _unitOfWork.UserRepository.Update(user); - if (!await _unitOfWork.Complete()) return BadRequest("There was a critical error."); + if (!await _unitOfWork.CommitAsync()) return BadRequest("There was a critical error."); return Ok(); } @@ -139,7 +139,7 @@ namespace API.Controllers _unitOfWork.SeriesRepository.Update(series); - if (await _unitOfWork.Complete()) + if (await _unitOfWork.CommitAsync()) { return Ok(); } @@ -190,61 +190,68 @@ namespace API.Controllers [HttpPost("metadata")] public async Task UpdateSeriesMetadata(UpdateSeriesMetadataDto updateSeriesMetadataDto) { - var seriesId = updateSeriesMetadataDto.SeriesMetadata.SeriesId; - var series = await _unitOfWork.SeriesRepository.GetSeriesByIdAsync(seriesId); - if (series.Metadata == null) + try { - series.Metadata = DbFactory.SeriesMetadata(updateSeriesMetadataDto.Tags - .Select(dto => DbFactory.CollectionTag(dto.Id, dto.Title, dto.Summary, dto.Promoted)).ToList()); - } - else - { - series.Metadata.CollectionTags ??= new List(); - var newTags = new List(); - - // I want a union of these 2 lists. Return only elements that are in both lists, but the list types are different - var existingTags = series.Metadata.CollectionTags.ToList(); - foreach (var existing in existingTags) + var seriesId = updateSeriesMetadataDto.SeriesMetadata.SeriesId; + var series = await _unitOfWork.SeriesRepository.GetSeriesByIdAsync(seriesId); + if (series.Metadata == null) { - if (updateSeriesMetadataDto.Tags.SingleOrDefault(t => t.Id == existing.Id) == null) + series.Metadata = DbFactory.SeriesMetadata(updateSeriesMetadataDto.Tags + .Select(dto => DbFactory.CollectionTag(dto.Id, dto.Title, dto.Summary, dto.Promoted)).ToList()); + } + else + { + series.Metadata.CollectionTags ??= new List(); + var newTags = new List(); + + // I want a union of these 2 lists. Return only elements that are in both lists, but the list types are different + var existingTags = series.Metadata.CollectionTags.ToList(); + foreach (var existing in existingTags) { - // Remove tag - series.Metadata.CollectionTags.Remove(existing); + if (updateSeriesMetadataDto.Tags.SingleOrDefault(t => t.Id == existing.Id) == null) + { + // Remove tag + series.Metadata.CollectionTags.Remove(existing); + } + } + + // At this point, all tags that aren't in dto have been removed. + foreach (var tag in updateSeriesMetadataDto.Tags) + { + var existingTag = series.Metadata.CollectionTags.SingleOrDefault(t => t.Title == tag.Title); + if (existingTag != null) + { + // Update existingTag + existingTag.Promoted = tag.Promoted; + existingTag.Title = tag.Title; + existingTag.NormalizedTitle = Parser.Parser.Normalize(tag.Title).ToUpper(); + } + else + { + // Add new tag + newTags.Add(DbFactory.CollectionTag(tag.Id, tag.Title, tag.Summary, tag.Promoted)); + } + } + + foreach (var tag in newTags) + { + series.Metadata.CollectionTags.Add(tag); } } - // At this point, all tags that aren't in dto have been removed. - foreach (var tag in updateSeriesMetadataDto.Tags) + if (!_unitOfWork.HasChanges()) { - var existingTag = series.Metadata.CollectionTags.SingleOrDefault(t => t.Title == tag.Title); - if (existingTag != null) - { - // Update existingTag - existingTag.Promoted = tag.Promoted; - existingTag.Title = tag.Title; - existingTag.NormalizedTitle = Parser.Parser.Normalize(tag.Title).ToUpper(); - } - else - { - // Add new tag - newTags.Add(DbFactory.CollectionTag(tag.Id, tag.Title, tag.Summary, tag.Promoted)); - } + return Ok("No changes to save"); } - foreach (var tag in newTags) + if (await _unitOfWork.CommitAsync()) { - series.Metadata.CollectionTags.Add(tag); + return Ok("Successfully updated"); } } - - if (!_unitOfWork.HasChanges()) + catch (Exception) { - return Ok("No changes to save"); - } - - if (await _unitOfWork.Complete()) - { - return Ok("Successfully updated"); + await _unitOfWork.RollbackAsync(); } return BadRequest("Could not update metadata"); diff --git a/API/Controllers/SettingsController.cs b/API/Controllers/SettingsController.cs index b30d7fdd3..ce68e4e5a 100644 --- a/API/Controllers/SettingsController.cs +++ b/API/Controllers/SettingsController.cs @@ -95,7 +95,7 @@ namespace API.Controllers _configuration.GetSection("Logging:LogLevel:Default").Value = updateSettingsDto.LoggingLevel + ""; if (!_unitOfWork.HasChanges()) return Ok("Nothing was updated"); - if (!_unitOfWork.HasChanges() || !await _unitOfWork.Complete()) + if (!_unitOfWork.HasChanges() || !await _unitOfWork.CommitAsync()) return BadRequest("There was a critical issue. Please try again."); _logger.LogInformation("Server Settings updated"); diff --git a/API/Controllers/UsersController.cs b/API/Controllers/UsersController.cs index 10d6d3e07..3a9a44d6c 100644 --- a/API/Controllers/UsersController.cs +++ b/API/Controllers/UsersController.cs @@ -26,7 +26,7 @@ namespace API.Controllers var user = await _unitOfWork.UserRepository.GetUserByUsernameAsync(username); _unitOfWork.UserRepository.Delete(user); - if (await _unitOfWork.Complete()) return Ok(); + if (await _unitOfWork.CommitAsync()) return Ok(); return BadRequest("Could not delete the user."); } @@ -71,7 +71,7 @@ namespace API.Controllers _unitOfWork.UserRepository.Update(existingPreferences); - if (await _unitOfWork.Complete()) + if (await _unitOfWork.CommitAsync()) { return Ok(preferencesDto); } diff --git a/API/Data/UnitOfWork.cs b/API/Data/UnitOfWork.cs index 178136e3a..ba89d0612 100644 --- a/API/Data/UnitOfWork.cs +++ b/API/Data/UnitOfWork.cs @@ -30,7 +30,11 @@ namespace API.Data public IAppUserProgressRepository AppUserProgressRepository => new AppUserProgressRepository(_context); public ICollectionTagRepository CollectionTagRepository => new CollectionTagRepository(_context, _mapper); - public async Task Complete() + public bool Commit() + { + return _context.SaveChanges() > 0; + } + public async Task CommitAsync() { return await _context.SaveChangesAsync() > 0; } @@ -39,5 +43,16 @@ namespace API.Data { return _context.ChangeTracker.HasChanges(); } + + public async Task RollbackAsync() + { + await _context.DisposeAsync(); + return true; + } + public bool Rollback() + { + _context.Dispose(); + return true; + } } } \ No newline at end of file diff --git a/API/Interfaces/IUnitOfWork.cs b/API/Interfaces/IUnitOfWork.cs index 8f4b53c8f..df326c3e2 100644 --- a/API/Interfaces/IUnitOfWork.cs +++ b/API/Interfaces/IUnitOfWork.cs @@ -11,7 +11,10 @@ namespace API.Interfaces ISettingsRepository SettingsRepository { get; } IAppUserProgressRepository AppUserProgressRepository { get; } ICollectionTagRepository CollectionTagRepository { get; } - Task Complete(); + bool Commit(); + Task CommitAsync(); bool HasChanges(); + bool Rollback(); + Task RollbackAsync(); } } \ No newline at end of file diff --git a/API/Services/MetadataService.cs b/API/Services/MetadataService.cs index 122fc90c6..6ee2856ab 100644 --- a/API/Services/MetadataService.cs +++ b/API/Services/MetadataService.cs @@ -158,7 +158,7 @@ namespace API.Services } - if (_unitOfWork.HasChanges() && Task.Run(() => _unitOfWork.Complete()).Result) + if (_unitOfWork.HasChanges() && Task.Run(() => _unitOfWork.CommitAsync()).Result) { _logger.LogInformation("Updated metadata for {LibraryName} in {ElapsedMilliseconds} milliseconds", library.Name, sw.ElapsedMilliseconds); } @@ -191,7 +191,7 @@ namespace API.Services _unitOfWork.SeriesRepository.Update(series); - if (_unitOfWork.HasChanges() && Task.Run(() => _unitOfWork.Complete()).Result) + if (_unitOfWork.HasChanges() && Task.Run(() => _unitOfWork.CommitAsync()).Result) { _logger.LogInformation("Updated metadata for {SeriesName} in {ElapsedMilliseconds} milliseconds", series.Name, sw.ElapsedMilliseconds); } diff --git a/API/Services/Tasks/ScannerService.cs b/API/Services/Tasks/ScannerService.cs index 91e873d13..232e8fce0 100644 --- a/API/Services/Tasks/ScannerService.cs +++ b/API/Services/Tasks/ScannerService.cs @@ -89,7 +89,7 @@ namespace API.Services.Tasks UpdateLibrary(library, series); _unitOfWork.LibraryRepository.Update(library); - if (Task.Run(() => _unitOfWork.Complete()).Result) + if (Task.Run(() => _unitOfWork.CommitAsync()).Result) { _logger.LogInformation("Processed {TotalFiles} files and {ParsedSeriesCount} series in {ElapsedScanTime} milliseconds for {LibraryName}", totalFiles, series.Keys.Count, sw.ElapsedMilliseconds + scanElapsedTime, library.Name); }