From b921a14e12071893a7c338c0f1dac4dbe68bafc9 Mon Sep 17 00:00:00 2001 From: Joseph Milazzo Date: Tue, 8 Mar 2022 17:33:58 -0600 Subject: [PATCH] On Deck tweaks + Bugfixes (#1141) * Tweaked the On deck to only look for series that have progress in past 30 days. This number is just to test it out, it will be configurable later. Tweaked the layout of the dashboard to remove a redundant section. * Fixed a bug where archives with __MACOSX/ inside would break the reader during flattening. * Fixed a bug where confirm service rejection should have resolved as false. * Fixed an issue with checking if server is accessible with loopback and local ips --- API.Tests/Parser/ParserTest.cs | 3 ++ API.Tests/Services/DirectoryServiceTests.cs | 18 ++++++- API/Data/Repositories/SeriesRepository.cs | 8 ++-- API/Parser/Parser.cs | 2 +- API/Services/DirectoryService.cs | 5 +- API/Services/EmailService.cs | 47 +++++++++++++++++-- UI/Web/src/app/library/library.component.html | 8 ++-- UI/Web/src/app/library/library.component.ts | 2 +- UI/Web/src/app/shared/confirm.service.ts | 4 +- 9 files changed, 81 insertions(+), 16 deletions(-) diff --git a/API.Tests/Parser/ParserTest.cs b/API.Tests/Parser/ParserTest.cs index 5b7900e86..84dee1b8b 100644 --- a/API.Tests/Parser/ParserTest.cs +++ b/API.Tests/Parser/ParserTest.cs @@ -208,6 +208,9 @@ namespace API.Tests.Parser [InlineData("MACOSX/Love Hina/", false)] [InlineData("._Love Hina/Love Hina/", true)] [InlineData("@Recently-Snapshot/Love Hina/", true)] + [InlineData("@recycle/Love Hina/", true)] + [InlineData("@recycle/Love Hina/", true)] + [InlineData("E:/Test/__MACOSX/Love Hina/", true)] public void HasBlacklistedFolderInPathTest(string inputPath, bool expected) { Assert.Equal(expected, HasBlacklistedFolderInPath(inputPath)); diff --git a/API.Tests/Services/DirectoryServiceTests.cs b/API.Tests/Services/DirectoryServiceTests.cs index 391b4eac4..c0d49820b 100644 --- a/API.Tests/Services/DirectoryServiceTests.cs +++ b/API.Tests/Services/DirectoryServiceTests.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.IO; using System.IO.Abstractions.TestingHelpers; @@ -755,6 +755,22 @@ namespace API.Tests.Services Assert.True(fileSystem.Directory.Exists($"{testDirectory}subdir/")); } + [Fact] + public void Flatten_ShouldFlatten_WithoutMacosx() + { + const string testDirectory = "/manga/"; + var fileSystem = new MockFileSystem(); + fileSystem.AddDirectory(testDirectory); + fileSystem.AddFile($"{testDirectory}data-1.jpg", new MockFileData("abc")); + fileSystem.AddFile($"{testDirectory}subdir/data-3.webp", new MockFileData("abc")); + fileSystem.AddFile($"{testDirectory}__MACOSX/data-4.webp", new MockFileData("abc")); + + var ds = new DirectoryService(Substitute.For>(), fileSystem); + ds.Flatten($"{testDirectory}"); + Assert.Equal(2, ds.GetFiles(testDirectory).Count()); + Assert.False(fileSystem.FileExists($"{testDirectory}data-4.webp")); + } + #endregion #region CheckWriteAccess diff --git a/API/Data/Repositories/SeriesRepository.cs b/API/Data/Repositories/SeriesRepository.cs index 1d72040d9..efe2f1a27 100644 --- a/API/Data/Repositories/SeriesRepository.cs +++ b/API/Data/Repositories/SeriesRepository.cs @@ -597,7 +597,7 @@ public class SeriesRepository : ISeriesRepository { //var allSeriesWithProgress = await _context.AppUserProgresses.Select(p => p.SeriesId).ToListAsync(); //var allChapters = await GetChapterIdsForSeriesAsync(allSeriesWithProgress); - + var cuttoffProgressPoint = DateTime.Now - TimeSpan.FromDays(30); var query = (await CreateFilteredSearchQueryable(userId, libraryId, filter)) .Join(_context.AppUserProgresses, s => s.Id, progress => progress.SeriesId, (s, progress) => new @@ -606,12 +606,14 @@ public class SeriesRepository : ISeriesRepository PagesRead = _context.AppUserProgresses.Where(s1 => s1.SeriesId == s.Id && s1.AppUserId == userId) .Sum(s1 => s1.PagesRead), progress.AppUserId, - LastReadingProgress = _context.AppUserProgresses.Where(p => p.Id == progress.Id && p.AppUserId == userId) + LastReadingProgress = _context.AppUserProgresses + .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 => allChapters.Contains(c.Id)).Max(c => c.Created) - }); + }) + .Where(d => d.LastReadingProgress >= cuttoffProgressPoint); // I think I need another Join statement. The problem is the chapters are still limited to progress diff --git a/API/Parser/Parser.cs b/API/Parser/Parser.cs index 5cbb816e1..1b8557f73 100644 --- a/API/Parser/Parser.cs +++ b/API/Parser/Parser.cs @@ -1003,7 +1003,7 @@ namespace API.Parser public static bool HasBlacklistedFolderInPath(string path) { - return path.Contains("__MACOSX") || path.StartsWith("@Recently-Snapshot") || path.StartsWith("._"); + return path.Contains("__MACOSX") || path.StartsWith("@Recently-Snapshot") || path.StartsWith("@recycle") || path.StartsWith("._"); } diff --git a/API/Services/DirectoryService.cs b/API/Services/DirectoryService.cs index da9e4698b..6e5b335d1 100644 --- a/API/Services/DirectoryService.cs +++ b/API/Services/DirectoryService.cs @@ -703,7 +703,7 @@ namespace API.Services } - private void FlattenDirectory(IDirectoryInfo root, IDirectoryInfo directory, ref int directoryIndex) + private static void FlattenDirectory(IFileSystemInfo root, IDirectoryInfo directory, ref int directoryIndex) { if (!root.FullName.Equals(directory.FullName)) { @@ -725,6 +725,9 @@ namespace API.Services foreach (var subDirectory in directory.EnumerateDirectories().OrderByNatural(d => d.FullName)) { + // We need to check if the directory is not a blacklisted (ie __MACOSX) + if (Parser.Parser.HasBlacklistedFolderInPath(subDirectory.FullName)) continue; + FlattenDirectory(root, subDirectory, ref directoryIndex); } } diff --git a/API/Services/EmailService.cs b/API/Services/EmailService.cs index 08d00d29d..ab2c52a2c 100644 --- a/API/Services/EmailService.cs +++ b/API/Services/EmailService.cs @@ -1,4 +1,6 @@ using System; +using System.Linq; +using System.Net; using System.Threading.Tasks; using API.Data; using API.DTOs.Email; @@ -40,14 +42,22 @@ public class EmailService : IEmailService cli.Settings.HttpClientFactory = new UntrustedCertClientFactory()); } + /// + /// Test if this instance is accessible outside the network + /// + /// This will do some basic filtering to auto return false if the emailUrl is a LAN ip + /// + /// public async Task TestConnectivity(string emailUrl) { - // FlurlHttp.ConfigureClient(emailUrl, cli => - // cli.Settings.HttpClientFactory = new UntrustedCertClientFactory()); - var result = new EmailTestResultDto(); try { + if (IsLocalIpAddress(emailUrl)) + { + result.Successful = false; + result.ErrorMessage = "This is a local IP address"; + } result.Successful = await SendEmailWithGet(emailUrl + "/api/email/test"); } catch (KavitaException ex) @@ -72,6 +82,7 @@ public class EmailService : IEmailService public async Task CheckIfAccessible(string host) { // This is the only exception for using the default because we need an external service to check if the server is accessible for emails + if (IsLocalIpAddress(host)) return false; return await SendEmailWithGet(DefaultApiUrl + "/api/email/reachable?host=" + host); } @@ -138,4 +149,34 @@ public class EmailService : IEmailService return true; } + private static bool IsLocalIpAddress(string url) + { + var host = url.Split(':')[0]; + try + { + // get host IP addresses + var hostIPs = Dns.GetHostAddresses(host); + // get local IP addresses + var localIPs = Dns.GetHostAddresses(Dns.GetHostName()); + + // test if any host IP equals to any local IP or to localhost + foreach (var hostIp in hostIPs) + { + // is localhost + if (IPAddress.IsLoopback(hostIp)) return true; + // is local address + if (localIPs.Contains(hostIp)) + { + return true; + } + } + } + catch + { + // ignored + } + + return false; + } + } diff --git a/UI/Web/src/app/library/library.component.html b/UI/Web/src/app/library/library.component.html index 0eb8f7aa9..4add40fa0 100644 --- a/UI/Web/src/app/library/library.component.html +++ b/UI/Web/src/app/library/library.component.html @@ -12,25 +12,25 @@ - + - + - + diff --git a/UI/Web/src/app/library/library.component.ts b/UI/Web/src/app/library/library.component.ts index 19462c71b..dade32f8b 100644 --- a/UI/Web/src/app/library/library.component.ts +++ b/UI/Web/src/app/library/library.component.ts @@ -138,7 +138,7 @@ export class LibraryComponent implements OnInit, OnDestroy { handleSectionClick(sectionTitle: string) { if (sectionTitle.toLowerCase() === 'collections') { this.router.navigate(['collections']); - } else if (sectionTitle.toLowerCase() === 'recently added') { + } else if (sectionTitle.toLowerCase() === 'recently updated series') { this.router.navigate(['recently-added']); } else if (sectionTitle.toLowerCase() === 'on deck') { this.router.navigate(['on-deck']); diff --git a/UI/Web/src/app/shared/confirm.service.ts b/UI/Web/src/app/shared/confirm.service.ts index 72344ca08..f1cbbb881 100644 --- a/UI/Web/src/app/shared/confirm.service.ts +++ b/UI/Web/src/app/shared/confirm.service.ts @@ -41,7 +41,7 @@ export class ConfirmService { return resolve(result); }); modalRef.dismissed.pipe(take(1)).subscribe(() => { - return reject(false); + return resolve(false); }); }); @@ -65,7 +65,7 @@ export class ConfirmService { return resolve(result); }); modalRef.dismissed.pipe(take(1)).subscribe(() => { - return reject(false); + return resolve(false); }); }) }