Bugfixes (#1177)
* Fixed an underline on hover of pagination link * Ensure title of companion bar eats full width if there is no filter * If a user doesn't have the Download role, they will not be able to download over OPDS. * Fixed a bug where after going into webtoon reader mode then leaving, the bookmark effect would continue using the webtoon mode styling * Fixed a bug where continuous reader wasn't being triggered due to moving scrollbar to body and a floating point percision error on scroll top * Fixed how continuous trigger is shown so that we properly adjust scroll on the top (for prev chapter) * Fixed a bad merge that broke saving any edits to series metadata * When a epub key is not correct, even after we correct it, ignore the inlining of the style so the book is at least still readable. * Disabled double rendering (this feature is being postponed to a later release) * Disabled user setting and forced it to Single on any save * Removed cache directory from UpdateSettings validation as we don't allow changing it. * Fix security issue with url parse * After all migrations run, update the installed version in the Database. Send that installed version on the stat service. * Dependency bot to update some security stuff * Some misc code cleanup and fixes on the typeahead (still broken)
This commit is contained in:
parent
1a011e30c2
commit
242d8b106d
27 changed files with 165 additions and 118 deletions
|
@ -89,8 +89,7 @@ namespace API.Controllers
|
|||
private async Task<bool> HasDownloadPermission()
|
||||
{
|
||||
var user = await _unitOfWork.UserRepository.GetUserByUsernameAsync(User.GetUsername());
|
||||
var roles = await _userManager.GetRolesAsync(user);
|
||||
return roles.Contains(PolicyConstants.DownloadRole) || roles.Contains(PolicyConstants.AdminRole);
|
||||
return await _downloadService.HasDownloadPermission(user);
|
||||
}
|
||||
|
||||
private async Task<ActionResult> GetFirstFileDownload(IEnumerable<MangaFile> files)
|
||||
|
|
|
@ -765,7 +765,7 @@ public class OpdsController : BaseApiController
|
|||
filename);
|
||||
accLink.TotalPages = chapter.Pages;
|
||||
|
||||
return new FeedEntry()
|
||||
var entry = new FeedEntry()
|
||||
{
|
||||
Id = mangaFile.Id.ToString(),
|
||||
Title = title,
|
||||
|
@ -776,7 +776,6 @@ public class OpdsController : BaseApiController
|
|||
{
|
||||
CreateLink(FeedLinkRelation.Image, FeedLinkType.Image, $"/api/image/chapter-cover?chapterId={chapterId}"),
|
||||
CreateLink(FeedLinkRelation.Thumbnail, FeedLinkType.Image, $"/api/image/chapter-cover?chapterId={chapterId}"),
|
||||
accLink,
|
||||
CreatePageStreamLink(seriesId, volumeId, chapterId, mangaFile, apiKey)
|
||||
},
|
||||
Content = new FeedEntryContent()
|
||||
|
@ -785,6 +784,15 @@ public class OpdsController : BaseApiController
|
|||
Type = "text"
|
||||
}
|
||||
};
|
||||
|
||||
var user = await _unitOfWork.UserRepository.GetUserByIdAsync(await GetUser(apiKey));
|
||||
if (await _downloadService.HasDownloadPermission(user))
|
||||
{
|
||||
entry.Links.Add(accLink);
|
||||
}
|
||||
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
[HttpGet("{apiKey}/image")]
|
||||
|
|
|
@ -105,16 +105,6 @@ namespace API.Controllers
|
|||
{
|
||||
_logger.LogInformation("{UserName} is updating Server Settings", User.GetUsername());
|
||||
|
||||
if (updateSettingsDto.CacheDirectory.Equals(string.Empty))
|
||||
{
|
||||
return BadRequest("Cache Directory cannot be empty");
|
||||
}
|
||||
|
||||
if (!Directory.Exists(updateSettingsDto.CacheDirectory))
|
||||
{
|
||||
return BadRequest("Directory does not exist or is not accessible.");
|
||||
}
|
||||
|
||||
// We do not allow CacheDirectory changes, so we will ignore.
|
||||
var currentSettings = await _unitOfWork.SettingsRepository.GetSettingsAsync();
|
||||
var updateBookmarks = false;
|
||||
|
|
|
@ -4,6 +4,7 @@ using System.Threading.Tasks;
|
|||
using API.Data;
|
||||
using API.Data.Repositories;
|
||||
using API.DTOs;
|
||||
using API.Entities.Enums;
|
||||
using API.Extensions;
|
||||
using AutoMapper;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
|
@ -87,6 +88,9 @@ namespace API.Controllers
|
|||
existingPreferences.BookReaderReadingDirection = preferencesDto.BookReaderReadingDirection;
|
||||
existingPreferences.Theme = await _unitOfWork.SiteThemeRepository.GetThemeById(preferencesDto.Theme.Id);
|
||||
|
||||
// TODO: Remove this code - this overrides layout mode to be single until the mode is released
|
||||
existingPreferences.LayoutMode = LayoutMode.Single;
|
||||
|
||||
_unitOfWork.UserRepository.Update(existingPreferences);
|
||||
|
||||
if (await _unitOfWork.CommitAsync())
|
||||
|
|
|
@ -37,5 +37,6 @@ namespace API.DTOs.Settings
|
|||
/// </summary>
|
||||
/// <remarks>If null or empty string, will default back to default install setting aka <see cref="EmailService.DefaultApiUrl"/></remarks>
|
||||
public string EmailServiceUrl { get; set; }
|
||||
public string InstallVersion { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -610,8 +610,6 @@ public class SeriesRepository : ISeriesRepository
|
|||
/// <returns></returns>
|
||||
public async Task<IEnumerable<SeriesDto>> GetOnDeck(int userId, int libraryId, UserParams userParams, FilterDto filter, bool cutoffOnDate = true)
|
||||
{
|
||||
//var allSeriesWithProgress = await _context.AppUserProgresses.Select(p => p.SeriesId).ToListAsync();
|
||||
//var allChapters = await GetChapterIdsForSeriesAsync(allSeriesWithProgress);
|
||||
var cutoffProgressPoint = DateTime.Now - TimeSpan.FromDays(30);
|
||||
var query = (await CreateFilteredSearchQueryable(userId, libraryId, filter))
|
||||
.Join(_context.AppUserProgresses, s => s.Id, progress => progress.SeriesId, (s, progress) =>
|
||||
|
@ -625,7 +623,7 @@ public class SeriesRepository : ISeriesRepository
|
|||
.Where(p => p.Id == progress.Id && p.AppUserId == userId)
|
||||
.Max(p => p.LastModified),
|
||||
// This is only taking into account chapters that have progress on them, not all chapters in said series
|
||||
LastChapterCreated = _context.Chapter.Where(c => progress.ChapterId == c.Id).Max(c => c.Created)
|
||||
LastChapterCreated = _context.Chapter.Where(c => progress.ChapterId == c.Id).Max(c => c.Created),
|
||||
//LastChapterCreated = _context.Chapter.Where(c => allChapters.Contains(c.Id)).Max(c => c.Created)
|
||||
});
|
||||
if (cutoffOnDate)
|
||||
|
|
|
@ -45,6 +45,9 @@ namespace API.Helpers.Converters
|
|||
case ServerSettingKey.EmailServiceUrl:
|
||||
destination.EmailServiceUrl = row.Value;
|
||||
break;
|
||||
case ServerSettingKey.InstallVersion:
|
||||
destination.InstallVersion = row.Value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -36,7 +36,6 @@ namespace API
|
|||
|
||||
|
||||
var directoryService = new DirectoryService(null, new FileSystem());
|
||||
//MigrateConfigFiles.Migrate(isDocker, directoryService);
|
||||
|
||||
// Before anything, check if JWT has been generated properly or if user still has default
|
||||
if (!Configuration.CheckIfJwtTokenSet() &&
|
||||
|
|
|
@ -150,7 +150,7 @@ namespace API.Services
|
|||
{
|
||||
// @Import statements will be handled by browser, so we must inline the css into the original file that request it, so they can be
|
||||
// Scoped
|
||||
var prepend = filename.Length > 0 ? filename.Replace(Path.GetFileName(filename), "") : string.Empty;
|
||||
var prepend = filename.Length > 0 ? filename.Replace(Path.GetFileName(filename), string.Empty) : string.Empty;
|
||||
var importBuilder = new StringBuilder();
|
||||
foreach (Match match in Parser.Parser.CssImportUrlRegex.Matches(stylesheetHtml))
|
||||
{
|
||||
|
@ -343,7 +343,7 @@ namespace API.Services
|
|||
{
|
||||
foreach (var styleLinks in styleNodes)
|
||||
{
|
||||
var key = BookService.CleanContentKeys(styleLinks.Attributes["href"].Value);
|
||||
var key = CleanContentKeys(styleLinks.Attributes["href"].Value);
|
||||
// Some epubs are malformed the key in content.opf might be: content/resources/filelist_0_0.xml but the actual html links to resources/filelist_0_0.xml
|
||||
// In this case, we will do a search for the key that ends with
|
||||
if (!book.Content.Css.ContainsKey(key))
|
||||
|
@ -358,11 +358,20 @@ namespace API.Services
|
|||
key = correctedKey;
|
||||
}
|
||||
|
||||
var styleContent = await ScopeStyles(await book.Content.Css[key].ReadContentAsync(), apiBase,
|
||||
book.Content.Css[key].FileName, book);
|
||||
if (styleContent != null)
|
||||
try
|
||||
{
|
||||
body.PrependChild(HtmlNode.CreateNode($"<style>{styleContent}</style>"));
|
||||
var cssFile = book.Content.Css[key];
|
||||
|
||||
var styleContent = await ScopeStyles(await cssFile.ReadContentAsync(), apiBase,
|
||||
cssFile.FileName, book);
|
||||
if (styleContent != null)
|
||||
{
|
||||
body.PrependChild(HtmlNode.CreateNode($"<style>{styleContent}</style>"));
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "There was an error reading css file for inlining likely due to a key mismatch in metadata");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -460,7 +469,7 @@ namespace API.Services
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the leading ../
|
||||
/// Removes all leading ../
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <returns></returns>
|
||||
|
|
|
@ -84,6 +84,8 @@ namespace API.Services
|
|||
ConfigDirectory = FileSystem.Path.Join(FileSystem.Directory.GetCurrentDirectory(), "config");
|
||||
BookmarkDirectory = FileSystem.Path.Join(FileSystem.Directory.GetCurrentDirectory(), "config", "bookmarks");
|
||||
SiteThemeDirectory = FileSystem.Path.Join(FileSystem.Directory.GetCurrentDirectory(), "config", "themes");
|
||||
|
||||
ExistOrCreate(SiteThemeDirectory);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -2,7 +2,9 @@
|
|||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using API.Constants;
|
||||
using API.Entities;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.StaticFiles;
|
||||
|
||||
namespace API.Services;
|
||||
|
@ -11,15 +13,18 @@ public interface IDownloadService
|
|||
{
|
||||
Task<(byte[], string, string)> GetFirstFileDownload(IEnumerable<MangaFile> files);
|
||||
string GetContentTypeFromFile(string filepath);
|
||||
Task<bool> HasDownloadPermission(AppUser user);
|
||||
}
|
||||
public class DownloadService : IDownloadService
|
||||
{
|
||||
private readonly IDirectoryService _directoryService;
|
||||
private readonly UserManager<AppUser> _userManager;
|
||||
private readonly FileExtensionContentTypeProvider _fileTypeProvider = new FileExtensionContentTypeProvider();
|
||||
|
||||
public DownloadService(IDirectoryService directoryService)
|
||||
public DownloadService(IDirectoryService directoryService, UserManager<AppUser> userManager)
|
||||
{
|
||||
_directoryService = directoryService;
|
||||
_userManager = userManager;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -53,4 +58,10 @@ public class DownloadService : IDownloadService
|
|||
|
||||
return contentType;
|
||||
}
|
||||
|
||||
public async Task<bool> HasDownloadPermission(AppUser user)
|
||||
{
|
||||
var roles = await _userManager.GetRolesAsync(user);
|
||||
return roles.Contains(PolicyConstants.DownloadRole) || roles.Contains(PolicyConstants.AdminRole);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -99,11 +99,12 @@ public class StatsService : IStatsService
|
|||
public async Task<ServerInfoDto> GetServerInfo()
|
||||
{
|
||||
var installId = await _unitOfWork.SettingsRepository.GetSettingAsync(ServerSettingKey.InstallId);
|
||||
var installVersion = await _unitOfWork.SettingsRepository.GetSettingAsync(ServerSettingKey.InstallVersion);
|
||||
var serverInfo = new ServerInfoDto
|
||||
{
|
||||
InstallId = installId.Value,
|
||||
Os = RuntimeInformation.OSDescription,
|
||||
KavitaVersion = BuildInfo.Version.ToString(),
|
||||
KavitaVersion = installVersion.Value,
|
||||
DotnetVersion = Environment.Version.ToString(),
|
||||
IsDocker = new OsInfo(Array.Empty<IOsVersionAdapter>()).IsDocker,
|
||||
NumOfCores = Math.Max(Environment.ProcessorCount, 1),
|
||||
|
|
|
@ -8,6 +8,7 @@ using System.Threading.Tasks;
|
|||
using API.Constants;
|
||||
using API.Data;
|
||||
using API.Entities;
|
||||
using API.Entities.Enums;
|
||||
using API.Extensions;
|
||||
using API.Middleware;
|
||||
using API.Services;
|
||||
|
@ -148,13 +149,19 @@ namespace API
|
|||
// Apply all migrations on startup
|
||||
var logger = serviceProvider.GetRequiredService<ILogger<Program>>();
|
||||
var userManager = serviceProvider.GetRequiredService<UserManager<AppUser>>();
|
||||
|
||||
var context = serviceProvider.GetRequiredService<DataContext>();
|
||||
|
||||
await MigrateBookmarks.Migrate(directoryService, unitOfWork,
|
||||
logger, cacheService);
|
||||
|
||||
// Only run this if we are upgrading
|
||||
await MigrateChangePasswordRoles.Migrate(unitOfWork, userManager);
|
||||
|
||||
// Update the version in the DB after all migrations are run
|
||||
var installVersion = await unitOfWork.SettingsRepository.GetSettingAsync(ServerSettingKey.InstallVersion);
|
||||
installVersion.Value = BuildInfo.Version.ToString();
|
||||
unitOfWork.SettingsRepository.Update(installVersion);
|
||||
await unitOfWork.CommitAsync();
|
||||
}).GetAwaiter()
|
||||
.GetResult();
|
||||
}
|
||||
|
@ -206,21 +213,6 @@ namespace API
|
|||
|
||||
app.UseDefaultFiles();
|
||||
|
||||
// This is not implemented completely. Commenting out until implemented
|
||||
// var service = serviceProvider.GetRequiredService<IUnitOfWork>();
|
||||
// var settings = service.SettingsRepository.GetSettingsDto();
|
||||
// if (!string.IsNullOrEmpty(settings.BaseUrl) && !settings.BaseUrl.Equals("/"))
|
||||
// {
|
||||
// var path = !settings.BaseUrl.StartsWith("/")
|
||||
// ? $"/{settings.BaseUrl}"
|
||||
// : settings.BaseUrl;
|
||||
// path = !path.EndsWith("/")
|
||||
// ? $"{path}/"
|
||||
// : path;
|
||||
// app.UsePathBase(path);
|
||||
// Console.WriteLine("Starting with base url as " + path);
|
||||
// }
|
||||
|
||||
app.UseStaticFiles(new StaticFileOptions
|
||||
{
|
||||
ContentTypeProvider = new FileExtensionContentTypeProvider()
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue