.NET 7 + Spring Cleaning (#1677)
* Updated to net7.0 * Updated GA to .net 7 * Updated System.IO.Abstractions to use New factory. * Converted Regex into SourceGenerator in Parser. * Updated more regex to source generators. * Enabled Nullability and more regex changes throughout codebase. * Parser is 100% GeneratedRegexified * Lots of nullability code * Enabled nullability for all repositories. * Fixed another unit test * Refactored some code around and took care of some todos. * Updating code for nullability and cleaning up methods that aren't used anymore. Refctored all uses of Parser.Normalize() to use new extension * More nullability exercises. 500 warnings to go. * Fixed a bug where custom file uploads for entities wouldn't save in webP. * Nullability is done for all DTOs * Fixed all unit tests and nullability for the project. Only OPDS is left which will be done with an upcoming OPDS enhancement. * Use localization in book service after validating * Code smells * Switched to preview build of swashbuckle for .net7 support * Fixed up merge issues * Disable emulate comic book when on single page reader * Fixed a regression where double page renderer wouldn't layout the images correctly * Updated to swashbuckle which support .net 7 * Fixed a bad GA action * Some code cleanup * More code smells * Took care of most of nullable issues * Fixed a broken test due to having more than one test run in parallel * I'm really not sure why the unit tests are failing or are so extremely slow on .net 7 * Updated all dependencies * Fixed up build and removed hardcoded framework from build scripts. (this merge removes Regex Source generators). Unit tests are completely busted. * Unit tests and code cleanup. Needs shakeout now. * Adjusted Series model since a few fields are not-nullable. Removed dead imports on the project. * Refactored to use Builder pattern for all unit tests. * Switched nullability down to warnings. It wasn't possible to switch due to constraint issues in DB Migration.
This commit is contained in:
parent
76fe3fd64a
commit
5d1dd7b3f0
283 changed files with 4221 additions and 4593 deletions
|
@ -2,12 +2,14 @@
|
|||
|
||||
<PropertyGroup>
|
||||
<AnalysisMode>Default</AnalysisMode>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
|
||||
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||
<TieredPGO>true</TieredPGO>
|
||||
<TieredCompilation>true</TieredCompilation>
|
||||
<ApplicationIcon>../favicon.ico</ApplicationIcon>
|
||||
<Nullable>warnings</Nullable>
|
||||
<LangVersion>latestmajor</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<Target Name="PostBuild" AfterTargets="Build" Condition=" '$(Configuration)' == 'Debug' ">
|
||||
|
@ -55,32 +57,32 @@
|
|||
<PackageReference Include="CsvHelper" Version="30.0.1" />
|
||||
<PackageReference Include="Docnet.Core" Version="2.4.0-alpha.4" />
|
||||
<PackageReference Include="ExCSS" Version="4.1.0" />
|
||||
<PackageReference Include="Flurl" Version="3.0.6" />
|
||||
<PackageReference Include="Flurl" Version="3.0.7" />
|
||||
<PackageReference Include="Flurl.Http" Version="3.2.4" />
|
||||
<PackageReference Include="Hangfire" Version="1.7.31" />
|
||||
<PackageReference Include="Hangfire.AspNetCore" Version="1.7.31" />
|
||||
<PackageReference Include="Hangfire.InMemory" Version="0.3.4" />
|
||||
<PackageReference Include="Hangfire" Version="1.7.33" />
|
||||
<PackageReference Include="Hangfire.AspNetCore" Version="1.7.33" />
|
||||
<PackageReference Include="Hangfire.InMemory" Version="0.3.6" />
|
||||
<PackageReference Include="Hangfire.MaximumConcurrentExecutions" Version="1.1.0" />
|
||||
<PackageReference Include="Hangfire.MemoryStorage.Core" Version="1.4.0" />
|
||||
<PackageReference Include="Hangfire.Storage.SQLite" Version="0.3.2" />
|
||||
<PackageReference Include="Hangfire.Storage.SQLite" Version="0.3.3" />
|
||||
<PackageReference Include="HtmlAgilityPack" Version="1.11.46" />
|
||||
<PackageReference Include="MarkdownDeep.NET.Core" Version="1.5.0.4" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="6.0.10" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="6.0.10" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="6.0.10" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="7.0.3" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="7.0.3" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="7.0.3" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.SignalR" Version="1.1.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.10">
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.3">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="6.0.10" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.1" />
|
||||
<PackageReference Include="Microsoft.IO.RecyclableMemoryStream" Version="2.2.1" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.3" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" />
|
||||
<PackageReference Include="Microsoft.IO.RecyclableMemoryStream" Version="2.3.2" />
|
||||
<PackageReference Include="NetVips" Version="2.2.0" />
|
||||
<PackageReference Include="NetVips.Native" Version="8.13.1" />
|
||||
<PackageReference Include="NReco.Logging.File" Version="1.1.5" />
|
||||
<PackageReference Include="NetVips.Native" Version="8.13.2" />
|
||||
<PackageReference Include="NReco.Logging.File" Version="1.1.6" />
|
||||
<PackageReference Include="Serilog" Version="2.12.0" />
|
||||
<PackageReference Include="Serilog.AspNetCore" Version="6.0.1" />
|
||||
<PackageReference Include="Serilog.AspNetCore" Version="6.1.0" />
|
||||
<PackageReference Include="Serilog.Enrichers.Thread" Version="3.2.0-dev-00752" />
|
||||
<PackageReference Include="Serilog.Extensions.Hosting" Version="5.0.1" />
|
||||
<PackageReference Include="Serilog.Settings.Configuration" Version="3.4.0" />
|
||||
|
@ -89,16 +91,16 @@
|
|||
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
|
||||
<PackageReference Include="Serilog.Sinks.SignalR.Core" Version="0.1.2" />
|
||||
<PackageReference Include="SharpCompress" Version="0.32.2" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="2.1.3" />
|
||||
<PackageReference Include="SonarAnalyzer.CSharp" Version="8.47.0.55603">
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="3.0.0" />
|
||||
<PackageReference Include="SonarAnalyzer.CSharp" Version="8.53.0.62665">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.Filters" Version="7.0.6" />
|
||||
<PackageReference Include="System.Drawing.Common" Version="6.0.0" />
|
||||
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.24.0" />
|
||||
<PackageReference Include="System.IO.Abstractions" Version="17.2.3" />
|
||||
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.27.0" />
|
||||
<PackageReference Include="System.IO.Abstractions" Version="19.2.1" />
|
||||
<PackageReference Include="System.Drawing.Common" Version="7.0.0" />
|
||||
<PackageReference Include="VersOne.Epub" Version="3.3.0-alpha1" />
|
||||
</ItemGroup>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ namespace API.Comparators;
|
|||
public class NumericComparer : IComparer
|
||||
{
|
||||
|
||||
public int Compare(object x, object y)
|
||||
public int Compare(object? x, object? y)
|
||||
{
|
||||
if((x is string xs) && (y is string ys))
|
||||
{
|
||||
|
|
|
@ -3,7 +3,6 @@ using System.Collections.Generic;
|
|||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
using API.Constants;
|
||||
using API.Data;
|
||||
using API.Data.Repositories;
|
||||
|
@ -23,7 +22,6 @@ using Microsoft.AspNetCore.Authorization;
|
|||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace API.Controllers;
|
||||
|
@ -213,10 +211,13 @@ public class AccountController : BaseApiController
|
|||
var dto = _mapper.Map<UserDto>(user);
|
||||
dto.Token = await _tokenService.CreateToken(user);
|
||||
dto.RefreshToken = await _tokenService.CreateRefreshToken(user);
|
||||
var pref = await _unitOfWork.UserRepository.GetPreferencesAsync(user.UserName);
|
||||
var pref = await _unitOfWork.UserRepository.GetPreferencesAsync(user.UserName!);
|
||||
if (pref == null) return Ok(dto);
|
||||
|
||||
pref.Theme ??= await _unitOfWork.SiteThemeRepository.GetDefaultTheme();
|
||||
dto.Preferences = _mapper.Map<UserPreferencesDto>(pref);
|
||||
return dto;
|
||||
|
||||
return Ok(dto);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -248,7 +249,7 @@ public class AccountController : BaseApiController
|
|||
.GetFields(BindingFlags.Public | BindingFlags.Static)
|
||||
.Where(f => f.FieldType == typeof(string))
|
||||
.ToDictionary(f => f.Name,
|
||||
f => (string) f.GetValue(null)).Values.ToList();
|
||||
f => (string) f.GetValue(null)!).Values.ToList();
|
||||
}
|
||||
|
||||
|
||||
|
@ -260,6 +261,7 @@ public class AccountController : BaseApiController
|
|||
public async Task<ActionResult<string>> ResetApiKey()
|
||||
{
|
||||
var user = await _unitOfWork.UserRepository.GetUserByUsernameAsync(User.GetUsername());
|
||||
if (user == null) return Unauthorized();
|
||||
|
||||
user.ApiKey = HashUtil.ApiKey();
|
||||
|
||||
|
@ -281,7 +283,7 @@ public class AccountController : BaseApiController
|
|||
/// <param name="dto"></param>
|
||||
/// <returns>Returns just if the email was sent or server isn't reachable</returns>
|
||||
[HttpPost("update/email")]
|
||||
public async Task<ActionResult> UpdateEmail(UpdateEmailDto dto)
|
||||
public async Task<ActionResult> UpdateEmail(UpdateEmailDto? dto)
|
||||
{
|
||||
var user = await _unitOfWork.UserRepository.GetUserByUsernameAsync(User.GetUsername());
|
||||
if (user == null) return Unauthorized("You do not have permission");
|
||||
|
@ -297,7 +299,7 @@ public class AccountController : BaseApiController
|
|||
}
|
||||
|
||||
// Validate no other users exist with this email
|
||||
if (user.Email.Equals(dto.Email)) return Ok("Nothing to do");
|
||||
if (user.Email!.Equals(dto.Email)) return Ok("Nothing to do");
|
||||
|
||||
// Check if email is used by another user
|
||||
var existingUserEmail = await _unitOfWork.UserRepository.GetUserByEmailAsync(dto.Email);
|
||||
|
@ -335,7 +337,7 @@ public class AccountController : BaseApiController
|
|||
{
|
||||
EmailAddress = string.IsNullOrEmpty(user.Email) ? dto.Email : user.Email,
|
||||
InstallId = BuildInfo.Version.ToString(),
|
||||
InvitingUser = (await _unitOfWork.UserRepository.GetAdminUsersAsync()).First().UserName,
|
||||
InvitingUser = (await _unitOfWork.UserRepository.GetAdminUsersAsync()).First().UserName!,
|
||||
ServerConfirmationLink = emailLink
|
||||
});
|
||||
}
|
||||
|
@ -357,7 +359,7 @@ public class AccountController : BaseApiController
|
|||
}
|
||||
|
||||
|
||||
await _eventHub.SendMessageToAsync(MessageFactory.UserUpdate, MessageFactory.UserUpdateEvent(user.Id, user.UserName), user.Id);
|
||||
await _eventHub.SendMessageToAsync(MessageFactory.UserUpdate, MessageFactory.UserUpdateEvent(user.Id, user.UserName!), user.Id);
|
||||
|
||||
return Ok();
|
||||
}
|
||||
|
@ -367,9 +369,9 @@ public class AccountController : BaseApiController
|
|||
{
|
||||
var user = await _unitOfWork.UserRepository.GetUserByUsernameAsync(User.GetUsername());
|
||||
if (user == null) return Unauthorized("You do not have permission");
|
||||
if (dto == null) return BadRequest("Invalid payload");
|
||||
|
||||
var isAdmin = await _unitOfWork.UserRepository.IsUserAdminAsync(user);
|
||||
if (!await _accountService.HasChangeRestrictionRole(user)) return BadRequest("You do not have permission");
|
||||
|
||||
user.AgeRestriction = isAdmin ? AgeRating.NotApplicable : dto.AgeRating;
|
||||
user.AgeRestrictionIncludeUnknowns = isAdmin || dto.IncludeUnknowns;
|
||||
|
@ -387,7 +389,7 @@ public class AccountController : BaseApiController
|
|||
return BadRequest("There was an error updating the age restriction");
|
||||
}
|
||||
|
||||
await _eventHub.SendMessageToAsync(MessageFactory.UserUpdate, MessageFactory.UserUpdateEvent(user.Id, user.UserName), user.Id);
|
||||
await _eventHub.SendMessageToAsync(MessageFactory.UserUpdate, MessageFactory.UserUpdateEvent(user.Id, user.UserName!), user.Id);
|
||||
|
||||
return Ok();
|
||||
}
|
||||
|
@ -402,13 +404,14 @@ public class AccountController : BaseApiController
|
|||
public async Task<ActionResult> UpdateAccount(UpdateUserDto dto)
|
||||
{
|
||||
var adminUser = await _unitOfWork.UserRepository.GetUserByUsernameAsync(User.GetUsername());
|
||||
if (adminUser == null) return Unauthorized();
|
||||
if (!await _unitOfWork.UserRepository.IsUserAdminAsync(adminUser)) return Unauthorized("You do not have permission");
|
||||
|
||||
var user = await _unitOfWork.UserRepository.GetUserByIdAsync(dto.UserId);
|
||||
if (user == null) return BadRequest("User does not exist");
|
||||
|
||||
// Check if username is changing
|
||||
if (!user.UserName.Equals(dto.Username))
|
||||
if (!user.UserName!.Equals(dto.Username))
|
||||
{
|
||||
// Validate username change
|
||||
var errors = await _accountService.ValidateUsername(dto.Username);
|
||||
|
@ -488,12 +491,13 @@ public class AccountController : BaseApiController
|
|||
public async Task<ActionResult<string>> GetInviteUrl(int userId, bool withBaseUrl)
|
||||
{
|
||||
var user = await _unitOfWork.UserRepository.GetUserByIdAsync(userId);
|
||||
if (user == null) return Unauthorized();
|
||||
if (user.EmailConfirmed)
|
||||
return BadRequest("User is already confirmed");
|
||||
if (string.IsNullOrEmpty(user.ConfirmationToken))
|
||||
return BadRequest("Manual setup is unable to be completed. Please cancel and recreate the invite.");
|
||||
|
||||
return await _accountService.GenerateEmailLink(Request, user.ConfirmationToken, "confirm-email", user.Email, withBaseUrl);
|
||||
return await _accountService.GenerateEmailLink(Request, user.ConfirmationToken, "confirm-email", user.Email!, withBaseUrl);
|
||||
}
|
||||
|
||||
|
||||
|
@ -520,23 +524,14 @@ public class AccountController : BaseApiController
|
|||
if (emailValidationErrors.Any())
|
||||
{
|
||||
var invitedUser = await _unitOfWork.UserRepository.GetUserByEmailAsync(dto.Email);
|
||||
if (await _userManager.IsEmailConfirmedAsync(invitedUser))
|
||||
return BadRequest($"User is already registered as {invitedUser.UserName}");
|
||||
if (await _userManager.IsEmailConfirmedAsync(invitedUser!))
|
||||
return BadRequest($"User is already registered as {invitedUser!.UserName}");
|
||||
return BadRequest("User is already invited under this email and has yet to accepted invite.");
|
||||
}
|
||||
}
|
||||
|
||||
// Create a new user
|
||||
var user = new AppUser()
|
||||
{
|
||||
UserName = dto.Email,
|
||||
Email = dto.Email,
|
||||
ApiKey = HashUtil.ApiKey(),
|
||||
UserPreferences = new AppUserPreferences
|
||||
{
|
||||
Theme = await _unitOfWork.SiteThemeRepository.GetDefaultTheme()
|
||||
}
|
||||
};
|
||||
var user = DbFactory.AppUser(dto.Email, dto.Email, await _unitOfWork.SiteThemeRepository.GetDefaultTheme());
|
||||
|
||||
try
|
||||
{
|
||||
|
@ -612,7 +607,7 @@ public class AccountController : BaseApiController
|
|||
await _emailService.SendConfirmationEmail(new ConfirmationEmailDto()
|
||||
{
|
||||
EmailAddress = dto.Email,
|
||||
InvitingUser = adminUser.UserName,
|
||||
InvitingUser = adminUser.UserName!,
|
||||
ServerConfirmationLink = emailLink
|
||||
});
|
||||
}
|
||||
|
@ -680,14 +675,14 @@ public class AccountController : BaseApiController
|
|||
await _unitOfWork.CommitAsync();
|
||||
|
||||
|
||||
user = await _unitOfWork.UserRepository.GetUserByUsernameAsync(user.UserName,
|
||||
AppUserIncludes.UserPreferences);
|
||||
user = (await _unitOfWork.UserRepository.GetUserByUsernameAsync(user.UserName,
|
||||
AppUserIncludes.UserPreferences))!;
|
||||
|
||||
// Perform Login code
|
||||
return new UserDto
|
||||
{
|
||||
Username = user.UserName,
|
||||
Email = user.Email,
|
||||
Username = user.UserName!,
|
||||
Email = user.Email!,
|
||||
Token = await _tokenService.CreateToken(user),
|
||||
RefreshToken = await _tokenService.CreateRefreshToken(user),
|
||||
ApiKey = user.ApiKey,
|
||||
|
@ -731,7 +726,7 @@ public class AccountController : BaseApiController
|
|||
|
||||
// For the user's connected devices to pull the new information in
|
||||
await _eventHub.SendMessageToAsync(MessageFactory.UserUpdate,
|
||||
MessageFactory.UserUpdateEvent(user.Id, user.UserName), user.Id);
|
||||
MessageFactory.UserUpdateEvent(user.Id, user.UserName!), user.Id);
|
||||
|
||||
// Perform Login code
|
||||
return Ok();
|
||||
|
@ -832,14 +827,14 @@ public class AccountController : BaseApiController
|
|||
|
||||
await _unitOfWork.CommitAsync();
|
||||
|
||||
user = await _unitOfWork.UserRepository.GetUserByUsernameAsync(user.UserName,
|
||||
user = await _unitOfWork.UserRepository.GetUserByUsernameAsync(user.UserName!,
|
||||
AppUserIncludes.UserPreferences);
|
||||
|
||||
// Perform Login code
|
||||
return new UserDto
|
||||
{
|
||||
Username = user.UserName,
|
||||
Email = user.Email,
|
||||
Username = user!.UserName!,
|
||||
Email = user.Email!,
|
||||
Token = await _tokenService.CreateToken(user),
|
||||
RefreshToken = await _tokenService.CreateRefreshToken(user),
|
||||
ApiKey = user.ApiKey,
|
||||
|
@ -873,8 +868,8 @@ public class AccountController : BaseApiController
|
|||
{
|
||||
await _emailService.SendMigrationEmail(new EmailMigrationDto()
|
||||
{
|
||||
EmailAddress = user.Email,
|
||||
Username = user.UserName,
|
||||
EmailAddress = user.Email!,
|
||||
Username = user.UserName!,
|
||||
ServerConfirmationLink = emailLink,
|
||||
InstallId = (await _unitOfWork.SettingsRepository.GetSettingAsync(ServerSettingKey.InstallId)).Value
|
||||
});
|
||||
|
@ -908,8 +903,8 @@ public class AccountController : BaseApiController
|
|||
if (emailValidationErrors.Any())
|
||||
{
|
||||
var invitedUser = await _unitOfWork.UserRepository.GetUserByEmailAsync(dto.Email);
|
||||
if (await _userManager.IsEmailConfirmedAsync(invitedUser))
|
||||
return BadRequest($"User is already registered as {invitedUser.UserName}");
|
||||
if (await _userManager.IsEmailConfirmedAsync(invitedUser!))
|
||||
return BadRequest($"User is already registered as {invitedUser!.UserName}");
|
||||
|
||||
_logger.LogInformation("A user is attempting to login, but hasn't accepted email invite");
|
||||
return BadRequest("User is already invited under this email and has yet to accepted invite.");
|
||||
|
|
|
@ -37,6 +37,7 @@ public class BookController : BaseApiController
|
|||
public async Task<ActionResult<BookInfoDto>> GetBookInfo(int chapterId)
|
||||
{
|
||||
var dto = await _unitOfWork.ChapterRepository.GetChapterInfoDtoAsync(chapterId);
|
||||
if (dto == null) return BadRequest("Chapter does not exist");
|
||||
var bookTitle = string.Empty;
|
||||
switch (dto.SeriesFormat)
|
||||
{
|
||||
|
@ -93,6 +94,7 @@ public class BookController : BaseApiController
|
|||
{
|
||||
if (chapterId <= 0) return BadRequest("Chapter is not valid");
|
||||
var chapter = await _unitOfWork.ChapterRepository.GetChapterAsync(chapterId);
|
||||
if (chapter == null) return BadRequest("Chapter is not valid");
|
||||
using var book = await EpubReader.OpenBookAsync(chapter.Files.ElementAt(0).FilePath, BookService.BookReaderOptions);
|
||||
|
||||
var key = BookService.CoalesceKeyForAnyFile(book, file);
|
||||
|
@ -116,8 +118,9 @@ public class BookController : BaseApiController
|
|||
public async Task<ActionResult<ICollection<BookChapterItem>>> GetBookChapters(int chapterId)
|
||||
{
|
||||
if (chapterId <= 0) return BadRequest("Chapter is not valid");
|
||||
|
||||
var chapter = await _unitOfWork.ChapterRepository.GetChapterAsync(chapterId);
|
||||
if (chapter == null) return BadRequest("Chapter is not valid");
|
||||
|
||||
try
|
||||
{
|
||||
return Ok(await _bookService.GenerateTableOfContents(chapter));
|
||||
|
@ -140,6 +143,7 @@ public class BookController : BaseApiController
|
|||
public async Task<ActionResult<string>> GetBookPage(int chapterId, [FromQuery] int page)
|
||||
{
|
||||
var chapter = await _cacheService.Ensure(chapterId);
|
||||
if (chapter == null) return BadRequest("Could not find Chapter");
|
||||
var path = _cacheService.GetCachedFile(chapter);
|
||||
|
||||
var baseUrl = "//" + Request.Host + Request.PathBase + "/api/";
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using API.DTOs.ReadingLists;
|
||||
using API.DTOs.ReadingLists.CBL;
|
||||
using API.Extensions;
|
||||
using API.Services;
|
||||
|
|
|
@ -1,14 +1,12 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using API.Data;
|
||||
using API.Data.Repositories;
|
||||
using API.DTOs.CollectionTags;
|
||||
using API.Entities.Metadata;
|
||||
using API.Extensions;
|
||||
using API.Services;
|
||||
using API.Services.Tasks.Metadata;
|
||||
using API.SignalR;
|
||||
using Kavita.Common;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
@ -35,16 +33,17 @@ public class CollectionController : BaseApiController
|
|||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpGet]
|
||||
public async Task<IEnumerable<CollectionTagDto>> GetAllTags()
|
||||
public async Task<ActionResult<IEnumerable<CollectionTagDto>>> GetAllTags()
|
||||
{
|
||||
var user = await _unitOfWork.UserRepository.GetUserByUsernameAsync(User.GetUsername());
|
||||
if (user == null) return Unauthorized();
|
||||
var isAdmin = await _unitOfWork.UserRepository.IsUserAdminAsync(user);
|
||||
if (isAdmin)
|
||||
{
|
||||
return await _unitOfWork.CollectionTagRepository.GetAllTagDtosAsync();
|
||||
return Ok(await _unitOfWork.CollectionTagRepository.GetAllTagDtosAsync());
|
||||
}
|
||||
|
||||
return await _unitOfWork.CollectionTagRepository.GetAllPromotedTagDtosAsync(user.Id);
|
||||
return Ok(await _unitOfWork.CollectionTagRepository.GetAllPromotedTagDtosAsync(user.Id));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -55,13 +54,13 @@ public class CollectionController : BaseApiController
|
|||
/// <returns></returns>
|
||||
[Authorize(Policy = "RequireAdminRole")]
|
||||
[HttpGet("search")]
|
||||
public async Task<IEnumerable<CollectionTagDto>> SearchTags(string queryString)
|
||||
public async Task<ActionResult<IEnumerable<CollectionTagDto>>> SearchTags(string queryString)
|
||||
{
|
||||
queryString ??= string.Empty;
|
||||
queryString = queryString.Replace(@"%", string.Empty);
|
||||
if (queryString.Length == 0) return await GetAllTags();
|
||||
|
||||
return await _unitOfWork.CollectionTagRepository.SearchTagDtosAsync(queryString, User.GetUserId());
|
||||
return Ok(await _unitOfWork.CollectionTagRepository.SearchTagDtosAsync(queryString, User.GetUserId()));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -126,7 +125,7 @@ public class CollectionController : BaseApiController
|
|||
{
|
||||
try
|
||||
{
|
||||
var tag = await _unitOfWork.CollectionTagRepository.GetFullTagAsync(updateSeriesForTagDto.Tag.Id);
|
||||
var tag = await _unitOfWork.CollectionTagRepository.GetTagAsync(updateSeriesForTagDto.Tag.Id, CollectionTagIncludes.SeriesMetadata);
|
||||
if (tag == null) return BadRequest("Not a valid Tag");
|
||||
tag.SeriesMetadatas ??= new List<SeriesMetadata>();
|
||||
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using API.Data;
|
||||
using API.Data.Repositories;
|
||||
|
@ -9,9 +7,7 @@ using API.DTOs.Device;
|
|||
using API.Extensions;
|
||||
using API.Services;
|
||||
using API.SignalR;
|
||||
using ExCSS;
|
||||
using Kavita.Common;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace API.Controllers;
|
||||
|
@ -39,6 +35,7 @@ public class DeviceController : BaseApiController
|
|||
public async Task<ActionResult> CreateOrUpdateDevice(CreateDeviceDto dto)
|
||||
{
|
||||
var user = await _unitOfWork.UserRepository.GetUserByUsernameAsync(User.GetUsername(), AppUserIncludes.Devices);
|
||||
if (user == null) return Unauthorized();
|
||||
var device = await _deviceService.Create(dto, user);
|
||||
|
||||
if (device == null) return BadRequest("There was an error when creating the device");
|
||||
|
@ -50,6 +47,7 @@ public class DeviceController : BaseApiController
|
|||
public async Task<ActionResult> UpdateDevice(UpdateDeviceDto dto)
|
||||
{
|
||||
var user = await _unitOfWork.UserRepository.GetUserByUsernameAsync(User.GetUsername(), AppUserIncludes.Devices);
|
||||
if (user == null) return Unauthorized();
|
||||
var device = await _deviceService.Update(dto, user);
|
||||
|
||||
if (device == null) return BadRequest("There was an error when updating the device");
|
||||
|
@ -67,6 +65,7 @@ public class DeviceController : BaseApiController
|
|||
{
|
||||
if (deviceId <= 0) return BadRequest("Not a valid deviceId");
|
||||
var user = await _unitOfWork.UserRepository.GetUserByUsernameAsync(User.GetUsername(), AppUserIncludes.Devices);
|
||||
if (user == null) return Unauthorized();
|
||||
if (await _deviceService.Delete(user, deviceId)) return Ok();
|
||||
|
||||
return BadRequest("Could not delete device");
|
||||
|
|
|
@ -93,13 +93,13 @@ public class DownloadController : BaseApiController
|
|||
public async Task<ActionResult> DownloadVolume(int volumeId)
|
||||
{
|
||||
if (!await HasDownloadPermission()) return BadRequest("You do not have permission");
|
||||
|
||||
var files = await _unitOfWork.VolumeRepository.GetFilesForVolume(volumeId);
|
||||
var volume = await _unitOfWork.VolumeRepository.GetVolumeByIdAsync(volumeId);
|
||||
if (volume == null) return BadRequest("Volume doesn't exist");
|
||||
var files = await _unitOfWork.VolumeRepository.GetFilesForVolume(volumeId);
|
||||
var series = await _unitOfWork.SeriesRepository.GetSeriesByIdAsync(volume.SeriesId);
|
||||
try
|
||||
{
|
||||
return await DownloadFiles(files, $"download_{User.GetUsername()}_v{volumeId}", $"{series.Name} - Volume {volume.Number}.zip");
|
||||
return await DownloadFiles(files, $"download_{User.GetUsername()}_v{volumeId}", $"{series!.Name} - Volume {volume.Number}.zip");
|
||||
}
|
||||
catch (KavitaException ex)
|
||||
{
|
||||
|
@ -110,6 +110,7 @@ public class DownloadController : BaseApiController
|
|||
private async Task<bool> HasDownloadPermission()
|
||||
{
|
||||
var user = await _unitOfWork.UserRepository.GetUserByUsernameAsync(User.GetUsername());
|
||||
if (user == null) return false;
|
||||
return await _accountService.HasDownloadPermission(user);
|
||||
}
|
||||
|
||||
|
@ -130,11 +131,12 @@ public class DownloadController : BaseApiController
|
|||
if (!await HasDownloadPermission()) return BadRequest("You do not have permission");
|
||||
var files = await _unitOfWork.ChapterRepository.GetFilesForChapterAsync(chapterId);
|
||||
var chapter = await _unitOfWork.ChapterRepository.GetChapterAsync(chapterId);
|
||||
if (chapter == null) return BadRequest("Invalid chapter");
|
||||
var volume = await _unitOfWork.VolumeRepository.GetVolumeByIdAsync(chapter.VolumeId);
|
||||
var series = await _unitOfWork.SeriesRepository.GetSeriesByIdAsync(volume.SeriesId);
|
||||
var series = await _unitOfWork.SeriesRepository.GetSeriesByIdAsync(volume!.SeriesId);
|
||||
try
|
||||
{
|
||||
return await DownloadFiles(files, $"download_{User.GetUsername()}_c{chapterId}", $"{series.Name} - Chapter {chapter.Number}.zip");
|
||||
return await DownloadFiles(files, $"download_{User.GetUsername()}_c{chapterId}", $"{series!.Name} - Chapter {chapter.Number}.zip");
|
||||
}
|
||||
catch (KavitaException ex)
|
||||
{
|
||||
|
@ -177,8 +179,9 @@ public class DownloadController : BaseApiController
|
|||
public async Task<ActionResult> DownloadSeries(int seriesId)
|
||||
{
|
||||
if (!await HasDownloadPermission()) return BadRequest("You do not have permission");
|
||||
var files = await _unitOfWork.SeriesRepository.GetFilesForSeries(seriesId);
|
||||
var series = await _unitOfWork.SeriesRepository.GetSeriesByIdAsync(seriesId);
|
||||
if (series == null) return BadRequest("Invalid Series");
|
||||
var files = await _unitOfWork.SeriesRepository.GetFilesForSeries(seriesId);
|
||||
try
|
||||
{
|
||||
return await DownloadFiles(files, $"download_{User.GetUsername()}_s{seriesId}", $"{series.Name}.zip");
|
||||
|
@ -201,13 +204,13 @@ public class DownloadController : BaseApiController
|
|||
if (!downloadBookmarkDto.Bookmarks.Any()) return BadRequest("Bookmarks cannot be empty");
|
||||
|
||||
// We know that all bookmarks will be for one single seriesId
|
||||
var userId = User.GetUserId();
|
||||
var username = User.GetUsername();
|
||||
var userId = User.GetUserId()!;
|
||||
var username = User.GetUsername()!;
|
||||
var series = await _unitOfWork.SeriesRepository.GetSeriesByIdAsync(downloadBookmarkDto.Bookmarks.First().SeriesId);
|
||||
|
||||
var files = await _bookmarkService.GetBookmarkFilesById(downloadBookmarkDto.Bookmarks.Select(b => b.Id));
|
||||
|
||||
var filename = $"{series.Name} - Bookmarks.zip";
|
||||
var filename = $"{series!.Name} - Bookmarks.zip";
|
||||
await _eventHub.SendMessageAsync(MessageFactory.NotificationProgress,
|
||||
MessageFactory.DownloadProgressEvent(username, Path.GetFileNameWithoutExtension(filename), 0F));
|
||||
var seriesIds = string.Join("_", downloadBookmarkDto.Bookmarks.Select(b => b.SeriesId).Distinct());
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.IO;
|
||||
using API.Services;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace API.Controllers;
|
||||
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
|
|
|
@ -7,7 +7,6 @@ using API.Data;
|
|||
using API.Data.Repositories;
|
||||
using API.DTOs;
|
||||
using API.DTOs.JumpBar;
|
||||
using API.DTOs.Search;
|
||||
using API.DTOs.System;
|
||||
using API.Entities;
|
||||
using API.Entities.Enums;
|
||||
|
@ -17,7 +16,6 @@ using API.Services;
|
|||
using API.Services.Tasks.Scanner;
|
||||
using API.SignalR;
|
||||
using AutoMapper;
|
||||
using Kavita.Common;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
@ -209,6 +207,7 @@ public class LibraryController : BaseApiController
|
|||
{
|
||||
var userId = await _unitOfWork.UserRepository.GetUserIdByApiKeyAsync(dto.ApiKey);
|
||||
var user = await _unitOfWork.UserRepository.GetUserByIdAsync(userId);
|
||||
if (user == null) return Unauthorized();
|
||||
|
||||
// Validate user has Admin privileges
|
||||
var isAdmin = await _unitOfWork.UserRepository.IsUserAdminAsync(user);
|
||||
|
@ -244,7 +243,6 @@ public class LibraryController : BaseApiController
|
|||
|
||||
try
|
||||
{
|
||||
var library = await _unitOfWork.LibraryRepository.GetLibraryForIdAsync(libraryId, LibraryIncludes.None);
|
||||
if (TaskScheduler.HasScanTaskRunningForLibrary(libraryId))
|
||||
{
|
||||
_logger.LogInformation("User is attempting to delete a library while a scan is in progress");
|
||||
|
@ -252,6 +250,9 @@ public class LibraryController : BaseApiController
|
|||
"You cannot delete a library while a scan is in progress. Please wait for scan to complete or restart Kavita then try to delete");
|
||||
}
|
||||
|
||||
var library = await _unitOfWork.LibraryRepository.GetLibraryForIdAsync(libraryId);
|
||||
if (library == null) return BadRequest("Library no longer exists");
|
||||
|
||||
// Due to a bad schema that I can't figure out how to fix, we need to erase all RelatedSeries before we delete the library
|
||||
// Aka SeriesRelation has an invalid foreign key
|
||||
foreach (var s in await _unitOfWork.SeriesRepository.GetSeriesForLibraryIdAsync(library.Id,
|
||||
|
@ -317,8 +318,10 @@ public class LibraryController : BaseApiController
|
|||
[HttpPost("update")]
|
||||
public async Task<ActionResult> UpdateLibrary(UpdateLibraryDto dto)
|
||||
{
|
||||
var newName = dto.Name.Trim();
|
||||
var library = await _unitOfWork.LibraryRepository.GetLibraryForIdAsync(dto.Id, LibraryIncludes.Folders);
|
||||
if (library == null) return BadRequest("Library doesn't exist");
|
||||
|
||||
var newName = dto.Name.Trim();
|
||||
if (await _unitOfWork.LibraryRepository.LibraryExists(newName) && !library.Name.Equals(newName))
|
||||
return BadRequest("Library name already exists");
|
||||
|
||||
|
|
|
@ -194,6 +194,7 @@ public class OpdsController : BaseApiController
|
|||
return BadRequest("OPDS is not enabled on this server");
|
||||
var userId = await GetUser(apiKey);
|
||||
var user = await _unitOfWork.UserRepository.GetUserByIdAsync(userId);
|
||||
if (user == null) return Unauthorized();
|
||||
var isAdmin = await _unitOfWork.UserRepository.IsUserAdminAsync(user);
|
||||
|
||||
IEnumerable<CollectionTagDto> tags = isAdmin ? (await _unitOfWork.CollectionTagRepository.GetAllTagDtosAsync())
|
||||
|
@ -230,6 +231,7 @@ public class OpdsController : BaseApiController
|
|||
return BadRequest("OPDS is not enabled on this server");
|
||||
var userId = await GetUser(apiKey);
|
||||
var user = await _unitOfWork.UserRepository.GetUserByIdAsync(userId);
|
||||
if (user == null) return Unauthorized();
|
||||
var isAdmin = await _unitOfWork.UserRepository.IsUserAdminAsync(user);
|
||||
|
||||
IEnumerable <CollectionTagDto> tags;
|
||||
|
@ -310,7 +312,8 @@ public class OpdsController : BaseApiController
|
|||
var userId = await GetUser(apiKey);
|
||||
var user = await _unitOfWork.UserRepository.GetUserByIdAsync(userId);
|
||||
|
||||
var userWithLists = await _unitOfWork.UserRepository.GetUserByUsernameAsync(user.UserName, AppUserIncludes.ReadingListsWithItems);
|
||||
var userWithLists = await _unitOfWork.UserRepository.GetUserByUsernameAsync(user!.UserName!, AppUserIncludes.ReadingListsWithItems);
|
||||
if (userWithLists == null) return Unauthorized();
|
||||
var readingList = userWithLists.ReadingLists.SingleOrDefault(t => t.Id == readingListId);
|
||||
if (readingList == null)
|
||||
{
|
||||
|
@ -432,7 +435,6 @@ public class OpdsController : BaseApiController
|
|||
query = query.Replace(@"%", string.Empty);
|
||||
// Get libraries user has access to
|
||||
var libraries = (await _unitOfWork.LibraryRepository.GetLibrariesForUserIdAsync(userId)).ToList();
|
||||
|
||||
if (!libraries.Any()) return BadRequest("User does not have access to any libraries");
|
||||
|
||||
var isAdmin = await _unitOfWork.UserRepository.IsUserAdminAsync(user);
|
||||
|
@ -569,7 +571,7 @@ public class OpdsController : BaseApiController
|
|||
(await _unitOfWork.ChapterRepository.GetChaptersAsync(volumeId)).OrderBy(x => double.Parse(x.Number),
|
||||
_chapterSortComparer);
|
||||
|
||||
var feed = CreateFeed(series.Name + " - Volume " + volume.Name + $" - {SeriesService.FormatChapterName(libraryType)}s ", $"{apiKey}/series/{seriesId}/volume/{volumeId}", apiKey);
|
||||
var feed = CreateFeed(series.Name + " - Volume " + volume!.Name + $" - {SeriesService.FormatChapterName(libraryType)}s ", $"{apiKey}/series/{seriesId}/volume/{volumeId}", apiKey);
|
||||
SetFeedId(feed, $"series-{series.Id}-volume-{volume.Id}-{SeriesService.FormatChapterName(libraryType)}s");
|
||||
foreach (var chapter in chapters)
|
||||
{
|
||||
|
@ -597,11 +599,12 @@ public class OpdsController : BaseApiController
|
|||
var userId = await GetUser(apiKey);
|
||||
var series = await _unitOfWork.SeriesRepository.GetSeriesDtoByIdAsync(seriesId, userId);
|
||||
var libraryType = await _unitOfWork.LibraryRepository.GetLibraryTypeAsync(series.LibraryId);
|
||||
var volume = await _unitOfWork.VolumeRepository.GetVolumeAsync(volumeId);
|
||||
var chapter = await _unitOfWork.ChapterRepository.GetChapterDtoAsync(chapterId);
|
||||
if (chapter == null) return BadRequest("Chapter doesn't exist");
|
||||
var volume = await _unitOfWork.VolumeRepository.GetVolumeAsync(volumeId);
|
||||
var files = await _unitOfWork.ChapterRepository.GetFilesForChapterAsync(chapterId);
|
||||
|
||||
var feed = CreateFeed(series.Name + " - Volume " + volume.Name + $" - {SeriesService.FormatChapterName(libraryType)}s", $"{apiKey}/series/{seriesId}/volume/{volumeId}/chapter/{chapterId}", apiKey);
|
||||
var feed = CreateFeed(series.Name + " - Volume " + volume!.Name + $" - {SeriesService.FormatChapterName(libraryType)}s", $"{apiKey}/series/{seriesId}/volume/{volumeId}/chapter/{chapterId}", apiKey);
|
||||
SetFeedId(feed, $"series-{series.Id}-volume-{volumeId}-{SeriesService.FormatChapterName(libraryType)}-{chapterId}-files");
|
||||
foreach (var mangaFile in files)
|
||||
{
|
||||
|
@ -768,7 +771,7 @@ public class OpdsController : BaseApiController
|
|||
DirectoryService.GetHumanReadableBytes(_directoryService.GetTotalSize(new List<string>()
|
||||
{mangaFile.FilePath}));
|
||||
var fileType = _downloadService.GetContentTypeFromFile(mangaFile.FilePath);
|
||||
var filename = Uri.EscapeDataString(Path.GetFileName(mangaFile.FilePath) ?? string.Empty);
|
||||
var filename = Uri.EscapeDataString(Path.GetFileName(mangaFile.FilePath));
|
||||
var libraryType = await _unitOfWork.LibraryRepository.GetLibraryTypeAsync(series.LibraryId);
|
||||
var volume = await _unitOfWork.VolumeRepository.GetVolumeDtoAsync(volumeId, await GetUser(apiKey));
|
||||
|
||||
|
@ -890,7 +893,7 @@ public class OpdsController : BaseApiController
|
|||
return link;
|
||||
}
|
||||
|
||||
private static FeedLink CreateLink(string rel, string type, string href, string title = null)
|
||||
private static FeedLink CreateLink(string rel, string type, string href, string? title = null)
|
||||
{
|
||||
return new FeedLink()
|
||||
{
|
||||
|
|
|
@ -38,10 +38,10 @@ public class PluginController : BaseApiController
|
|||
var userId = await _unitOfWork.UserRepository.GetUserIdByApiKeyAsync(apiKey);
|
||||
if (userId <= 0) return Unauthorized();
|
||||
var user = await _unitOfWork.UserRepository.GetUserByIdAsync(userId);
|
||||
_logger.LogInformation("Plugin {PluginName} has authenticated with {UserName} ({UserId})'s API Key", pluginName, user.UserName, userId);
|
||||
_logger.LogInformation("Plugin {PluginName} has authenticated with {UserName} ({UserId})'s API Key", pluginName, user!.UserName, userId);
|
||||
return new UserDto
|
||||
{
|
||||
Username = user.UserName,
|
||||
Username = user.UserName!,
|
||||
Token = await _tokenService.CreateToken(user),
|
||||
ApiKey = user.ApiKey,
|
||||
};
|
||||
|
|
|
@ -13,7 +13,6 @@ using API.Entities;
|
|||
using API.Entities.Enums;
|
||||
using API.Extensions;
|
||||
using API.Services;
|
||||
using API.Services.Tasks;
|
||||
using API.SignalR;
|
||||
using Hangfire;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
|
@ -164,7 +163,7 @@ public class ReaderController : BaseApiController
|
|||
[ResponseCache(CacheProfileName = ResponseCacheProfiles.Hour, VaryByQueryKeys = new []{"chapterId", "extractPdf"})]
|
||||
public async Task<ActionResult<IEnumerable<FileDimensionDto>>> GetFileDimensions(int chapterId, bool extractPdf = false)
|
||||
{
|
||||
if (chapterId <= 0) return null;
|
||||
if (chapterId <= 0) return ArraySegment<FileDimensionDto>.Empty;
|
||||
var chapter = await _cacheService.Ensure(chapterId, extractPdf);
|
||||
if (chapter == null) return BadRequest("Could not find Chapter");
|
||||
return Ok(_cacheService.GetCachedFileDimensions(chapterId));
|
||||
|
@ -179,9 +178,9 @@ public class ReaderController : BaseApiController
|
|||
/// <returns></returns>
|
||||
[HttpGet("chapter-info")]
|
||||
[ResponseCache(CacheProfileName = ResponseCacheProfiles.Hour, VaryByQueryKeys = new []{"chapterId", "extractPdf", "includeDimensions"})]
|
||||
public async Task<ActionResult<ChapterInfoDto>> GetChapterInfo(int chapterId, bool extractPdf = false, bool includeDimensions = false)
|
||||
public async Task<ActionResult<ChapterInfoDto?>> GetChapterInfo(int chapterId, bool extractPdf = false, bool includeDimensions = false)
|
||||
{
|
||||
if (chapterId <= 0) return null; // This can happen occasionally from UI, we should just ignore
|
||||
if (chapterId <= 0) return Ok(null); // This can happen occasionally from UI, we should just ignore
|
||||
var chapter = await _cacheService.Ensure(chapterId, extractPdf);
|
||||
if (chapter == null) return BadRequest("Could not find Chapter");
|
||||
|
||||
|
@ -249,7 +248,7 @@ public class ReaderController : BaseApiController
|
|||
|
||||
return Ok(new BookmarkInfoDto()
|
||||
{
|
||||
SeriesName = series.Name,
|
||||
SeriesName = series!.Name,
|
||||
SeriesFormat = series.Format,
|
||||
SeriesId = series.Id,
|
||||
LibraryId = series.LibraryId,
|
||||
|
@ -267,6 +266,7 @@ public class ReaderController : BaseApiController
|
|||
public async Task<ActionResult> MarkRead(MarkReadDto markReadDto)
|
||||
{
|
||||
var user = await _unitOfWork.UserRepository.GetUserByUsernameAsync(User.GetUsername(), AppUserIncludes.Progress);
|
||||
if (user == null) return Unauthorized();
|
||||
await _readerService.MarkSeriesAsRead(user, markReadDto.SeriesId);
|
||||
|
||||
if (!await _unitOfWork.CommitAsync()) return BadRequest("There was an issue saving progress");
|
||||
|
@ -284,6 +284,7 @@ public class ReaderController : BaseApiController
|
|||
public async Task<ActionResult> MarkUnread(MarkReadDto markReadDto)
|
||||
{
|
||||
var user = await _unitOfWork.UserRepository.GetUserByUsernameAsync(User.GetUsername(), AppUserIncludes.Progress);
|
||||
if (user == null) return Unauthorized();
|
||||
await _readerService.MarkSeriesAsUnread(user, markReadDto.SeriesId);
|
||||
|
||||
if (!await _unitOfWork.CommitAsync()) return BadRequest("There was an issue saving progress");
|
||||
|
@ -300,6 +301,7 @@ public class ReaderController : BaseApiController
|
|||
public async Task<ActionResult> MarkVolumeAsUnread(MarkVolumeReadDto markVolumeReadDto)
|
||||
{
|
||||
var user = await _unitOfWork.UserRepository.GetUserByUsernameAsync(User.GetUsername(), AppUserIncludes.Progress);
|
||||
if (user == null) return Unauthorized();
|
||||
|
||||
var chapters = await _unitOfWork.ChapterRepository.GetChaptersAsync(markVolumeReadDto.VolumeId);
|
||||
await _readerService.MarkChaptersAsUnread(user, markVolumeReadDto.SeriesId, chapters);
|
||||
|
@ -323,9 +325,10 @@ public class ReaderController : BaseApiController
|
|||
var user = await _unitOfWork.UserRepository.GetUserByUsernameAsync(User.GetUsername(), AppUserIncludes.Progress);
|
||||
|
||||
var chapters = await _unitOfWork.ChapterRepository.GetChaptersAsync(markVolumeReadDto.VolumeId);
|
||||
if (user == null) return Unauthorized();
|
||||
await _readerService.MarkChaptersAsRead(user, markVolumeReadDto.SeriesId, chapters);
|
||||
await _eventHub.SendMessageAsync(MessageFactory.UserProgressUpdate,
|
||||
MessageFactory.UserProgressUpdateEvent(user.Id, user.UserName, markVolumeReadDto.SeriesId,
|
||||
MessageFactory.UserProgressUpdateEvent(user.Id, user.UserName!, markVolumeReadDto.SeriesId,
|
||||
markVolumeReadDto.VolumeId, 0, chapters.Sum(c => c.Pages)));
|
||||
|
||||
if (await _unitOfWork.CommitAsync())
|
||||
|
@ -346,6 +349,7 @@ public class ReaderController : BaseApiController
|
|||
public async Task<ActionResult> MarkMultipleAsRead(MarkVolumesReadDto dto)
|
||||
{
|
||||
var user = await _unitOfWork.UserRepository.GetUserByUsernameAsync(User.GetUsername(), AppUserIncludes.Progress);
|
||||
if (user == null) return Unauthorized();
|
||||
user.Progresses ??= new List<AppUserProgress>();
|
||||
|
||||
var chapterIds = await _unitOfWork.VolumeRepository.GetChapterIdsByVolumeIds(dto.VolumeIds);
|
||||
|
@ -374,6 +378,7 @@ public class ReaderController : BaseApiController
|
|||
public async Task<ActionResult> MarkMultipleAsUnread(MarkVolumesReadDto dto)
|
||||
{
|
||||
var user = await _unitOfWork.UserRepository.GetUserByUsernameAsync(User.GetUsername(), AppUserIncludes.Progress);
|
||||
if (user == null) return Unauthorized();
|
||||
user.Progresses ??= new List<AppUserProgress>();
|
||||
|
||||
var chapterIds = await _unitOfWork.VolumeRepository.GetChapterIdsByVolumeIds(dto.VolumeIds);
|
||||
|
@ -401,6 +406,7 @@ public class ReaderController : BaseApiController
|
|||
public async Task<ActionResult> MarkMultipleSeriesAsRead(MarkMultipleSeriesAsReadDto dto)
|
||||
{
|
||||
var user = await _unitOfWork.UserRepository.GetUserByUsernameAsync(User.GetUsername(), AppUserIncludes.Progress);
|
||||
if (user == null) return Unauthorized();
|
||||
user.Progresses ??= new List<AppUserProgress>();
|
||||
|
||||
var volumes = await _unitOfWork.VolumeRepository.GetVolumesForSeriesAsync(dto.SeriesIds.ToArray(), true);
|
||||
|
@ -426,6 +432,7 @@ public class ReaderController : BaseApiController
|
|||
public async Task<ActionResult> MarkMultipleSeriesAsUnread(MarkMultipleSeriesAsReadDto dto)
|
||||
{
|
||||
var user = await _unitOfWork.UserRepository.GetUserByUsernameAsync(User.GetUsername(), AppUserIncludes.Progress);
|
||||
if (user == null) return Unauthorized();
|
||||
user.Progresses ??= new List<AppUserProgress>();
|
||||
|
||||
var volumes = await _unitOfWork.VolumeRepository.GetVolumesForSeriesAsync(dto.SeriesIds.ToArray(), true);
|
||||
|
@ -509,6 +516,7 @@ public class ReaderController : BaseApiController
|
|||
public async Task<ActionResult<bool>> MarkChaptersUntilAsRead(int seriesId, float chapterNumber)
|
||||
{
|
||||
var user = await _unitOfWork.UserRepository.GetUserByUsernameAsync(User.GetUsername(), AppUserIncludes.Progress);
|
||||
if (user == null) return Unauthorized();
|
||||
user.Progresses ??= new List<AppUserProgress>();
|
||||
|
||||
// Tachiyomi sends chapter 0.0f when there's no chapters read.
|
||||
|
@ -546,6 +554,7 @@ public class ReaderController : BaseApiController
|
|||
public async Task<ActionResult<IEnumerable<BookmarkDto>>> GetBookmarks(int chapterId)
|
||||
{
|
||||
var user = await _unitOfWork.UserRepository.GetUserByUsernameAsync(User.GetUsername(), AppUserIncludes.Bookmarks);
|
||||
if (user == null) return Unauthorized();
|
||||
if (user.Bookmarks == null) return Ok(Array.Empty<BookmarkDto>());
|
||||
return Ok(await _unitOfWork.UserRepository.GetBookmarkDtosForChapter(user.Id, chapterId));
|
||||
}
|
||||
|
@ -559,6 +568,7 @@ public class ReaderController : BaseApiController
|
|||
public async Task<ActionResult<IEnumerable<BookmarkDto>>> GetAllBookmarks(FilterDto filterDto)
|
||||
{
|
||||
var user = await _unitOfWork.UserRepository.GetUserByUsernameAsync(User.GetUsername(), AppUserIncludes.Bookmarks);
|
||||
if (user == null) return Unauthorized();
|
||||
if (user.Bookmarks == null) return Ok(Array.Empty<BookmarkDto>());
|
||||
|
||||
return Ok(await _unitOfWork.UserRepository.GetAllBookmarkDtos(user.Id, filterDto));
|
||||
|
@ -573,6 +583,7 @@ public class ReaderController : BaseApiController
|
|||
public async Task<ActionResult> RemoveBookmarks(RemoveBookmarkForSeriesDto dto)
|
||||
{
|
||||
var user = await _unitOfWork.UserRepository.GetUserByUsernameAsync(User.GetUsername(), AppUserIncludes.Bookmarks);
|
||||
if (user == null) return Unauthorized();
|
||||
if (user.Bookmarks == null) return Ok("Nothing to remove");
|
||||
|
||||
try
|
||||
|
@ -612,7 +623,8 @@ public class ReaderController : BaseApiController
|
|||
public async Task<ActionResult> BulkRemoveBookmarks(BulkRemoveBookmarkForSeriesDto dto)
|
||||
{
|
||||
var user = await _unitOfWork.UserRepository.GetUserByUsernameAsync(User.GetUsername(), AppUserIncludes.Bookmarks);
|
||||
if (user.Bookmarks == null) return Ok("Nothing to remove");
|
||||
if (user == null) return Unauthorized();
|
||||
if (user?.Bookmarks == null) return Ok("Nothing to remove");
|
||||
|
||||
try
|
||||
{
|
||||
|
@ -648,7 +660,8 @@ public class ReaderController : BaseApiController
|
|||
public async Task<ActionResult<IEnumerable<BookmarkDto>>> GetBookmarksForVolume(int volumeId)
|
||||
{
|
||||
var user = await _unitOfWork.UserRepository.GetUserByUsernameAsync(User.GetUsername(), AppUserIncludes.Bookmarks);
|
||||
if (user.Bookmarks == null) return Ok(Array.Empty<BookmarkDto>());
|
||||
if (user == null) return Unauthorized();
|
||||
if (user?.Bookmarks == null) return Ok(Array.Empty<BookmarkDto>());
|
||||
return Ok(await _unitOfWork.UserRepository.GetBookmarkDtosForVolume(user.Id, volumeId));
|
||||
}
|
||||
|
||||
|
@ -661,7 +674,8 @@ public class ReaderController : BaseApiController
|
|||
public async Task<ActionResult<IEnumerable<BookmarkDto>>> GetBookmarksForSeries(int seriesId)
|
||||
{
|
||||
var user = await _unitOfWork.UserRepository.GetUserByUsernameAsync(User.GetUsername(), AppUserIncludes.Bookmarks);
|
||||
if (user.Bookmarks == null) return Ok(Array.Empty<BookmarkDto>());
|
||||
if (user == null) return Unauthorized();
|
||||
if (user?.Bookmarks == null) return Ok(Array.Empty<BookmarkDto>());
|
||||
|
||||
return Ok(await _unitOfWork.UserRepository.GetBookmarkDtosForSeries(user.Id, seriesId));
|
||||
}
|
||||
|
|
|
@ -1,22 +1,15 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.IO.Abstractions;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using API.Comparators;
|
||||
using API.Data;
|
||||
using API.Data.Repositories;
|
||||
using API.DTOs.ReadingLists;
|
||||
using API.DTOs.ReadingLists.CBL;
|
||||
using API.Entities;
|
||||
using API.Extensions;
|
||||
using API.Helpers;
|
||||
using API.Services;
|
||||
using API.SignalR;
|
||||
using Kavita.Common;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace API.Controllers;
|
||||
|
@ -186,8 +179,8 @@ public class ReadingListController : BaseApiController
|
|||
[HttpPost("create")]
|
||||
public async Task<ActionResult<ReadingListDto>> CreateList(CreateReadingListDto dto)
|
||||
{
|
||||
|
||||
var user = await _unitOfWork.UserRepository.GetUserByUsernameAsync(User.GetUsername(), AppUserIncludes.ReadingLists);
|
||||
if (user == null) return Unauthorized();
|
||||
|
||||
try
|
||||
{
|
||||
|
|
|
@ -58,7 +58,7 @@ public class RecommendedController : BaseApiController
|
|||
[HttpGet("highly-rated")]
|
||||
public async Task<ActionResult<PagedList<SeriesDto>>> GetHighlyRated(int libraryId, [FromQuery] UserParams userParams)
|
||||
{
|
||||
var userId = User.GetUserId();
|
||||
var userId = User.GetUserId()!;
|
||||
userParams ??= new UserParams();
|
||||
var series = await _unitOfWork.SeriesRepository.GetHighlyRated(userId, libraryId, userParams);
|
||||
await _unitOfWork.SeriesRepository.AddSeriesModifiers(userId, series);
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using API.Data;
|
||||
using API.Data.Repositories;
|
||||
|
@ -54,6 +53,7 @@ public class SearchController : BaseApiController
|
|||
queryString = Services.Tasks.Scanner.Parser.Parser.CleanQuery(queryString);
|
||||
|
||||
var user = await _unitOfWork.UserRepository.GetUserByUsernameAsync(User.GetUsername());
|
||||
if (user == null) return Unauthorized();
|
||||
var libraries = _unitOfWork.LibraryRepository.GetLibraryIdsForUserIdAsync(user.Id, QueryContext.Search).ToList();
|
||||
if (!libraries.Any()) return BadRequest("User does not have access to any libraries");
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using API.Constants;
|
||||
using API.Data;
|
||||
|
@ -11,7 +10,6 @@ using API.DTOs.Metadata;
|
|||
using API.DTOs.SeriesDetail;
|
||||
using API.Entities;
|
||||
using API.Entities.Enums;
|
||||
using API.Entities.Metadata;
|
||||
using API.Extensions;
|
||||
using API.Helpers;
|
||||
using API.Services;
|
||||
|
@ -137,7 +135,7 @@ public class SeriesController : BaseApiController
|
|||
public async Task<ActionResult> UpdateSeriesRating(UpdateSeriesRatingDto updateSeriesRatingDto)
|
||||
{
|
||||
var user = await _unitOfWork.UserRepository.GetUserByUsernameAsync(User.GetUsername(), AppUserIncludes.Ratings);
|
||||
if (!await _seriesService.UpdateRating(user, updateSeriesRatingDto)) return BadRequest("There was a critical error.");
|
||||
if (!await _seriesService.UpdateRating(user!, updateSeriesRatingDto)) return BadRequest("There was a critical error.");
|
||||
return Ok();
|
||||
}
|
||||
|
||||
|
@ -159,14 +157,14 @@ public class SeriesController : BaseApiController
|
|||
}
|
||||
|
||||
series.Name = updateSeries.Name.Trim();
|
||||
series.NormalizedName = Services.Tasks.Scanner.Parser.Parser.Normalize(series.Name);
|
||||
if (!string.IsNullOrEmpty(updateSeries.SortName.Trim()))
|
||||
series.NormalizedName = series.Name.ToNormalized();
|
||||
if (!string.IsNullOrEmpty(updateSeries.SortName?.Trim()))
|
||||
{
|
||||
series.SortName = updateSeries.SortName.Trim();
|
||||
}
|
||||
|
||||
series.LocalizedName = updateSeries.LocalizedName.Trim();
|
||||
series.NormalizedLocalizedName = Services.Tasks.Scanner.Parser.Parser.Normalize(series.LocalizedName);
|
||||
series.LocalizedName = updateSeries.LocalizedName?.Trim();
|
||||
series.NormalizedLocalizedName = series.LocalizedName?.ToNormalized();
|
||||
|
||||
series.NameLocked = updateSeries.NameLocked;
|
||||
series.SortNameLocked = updateSeries.SortNameLocked;
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
@ -8,7 +7,6 @@ using API.DTOs.Jobs;
|
|||
using API.DTOs.Stats;
|
||||
using API.DTOs.Update;
|
||||
using API.Extensions;
|
||||
using API.Logging;
|
||||
using API.Services;
|
||||
using API.Services.Tasks;
|
||||
using Hangfire;
|
||||
|
@ -16,7 +14,6 @@ using Hangfire.Storage;
|
|||
using Kavita.Common;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using TaskScheduler = API.Services.TaskScheduler;
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
|
|
|
@ -32,7 +32,7 @@ public class StatsController : BaseApiController
|
|||
public async Task<ActionResult<UserReadStatistics>> GetUserReadStatistics(int userId)
|
||||
{
|
||||
var user = await _unitOfWork.UserRepository.GetUserByUsernameAsync(User.GetUsername());
|
||||
if (user.Id != userId && !await _userManager.IsInRoleAsync(user, PolicyConstants.AdminRole))
|
||||
if (user!.Id != userId && !await _userManager.IsInRoleAsync(user, PolicyConstants.AdminRole))
|
||||
return Unauthorized("You are not authorized to view another user's statistics");
|
||||
|
||||
return Ok(await _statService.GetUserReadStatistics(userId, new List<int>()));
|
||||
|
@ -116,7 +116,7 @@ public class StatsController : BaseApiController
|
|||
{
|
||||
var user = await _unitOfWork.UserRepository.GetUserByUsernameAsync(User.GetUsername());
|
||||
var isAdmin = User.IsInRole(PolicyConstants.AdminRole);
|
||||
if (!isAdmin && userId != user.Id) return BadRequest();
|
||||
if (!isAdmin && userId != user!.Id) return BadRequest();
|
||||
|
||||
return Ok(await _statService.ReadCountByDay(userId, days));
|
||||
}
|
||||
|
@ -136,7 +136,7 @@ public class StatsController : BaseApiController
|
|||
{
|
||||
var user = await _unitOfWork.UserRepository.GetUserByUsernameAsync(User.GetUsername());
|
||||
var isAdmin = User.IsInRole(PolicyConstants.AdminRole);
|
||||
if (!isAdmin && userId != user.Id) return BadRequest();
|
||||
if (!isAdmin && userId != user!.Id) return BadRequest();
|
||||
|
||||
return Ok(await _statService.GetReadingHistory(userId));
|
||||
}
|
||||
|
|
|
@ -43,7 +43,8 @@ public class TachiyomiController : BaseApiController
|
|||
[HttpPost("mark-chapter-until-as-read")]
|
||||
public async Task<ActionResult<bool>> MarkChaptersUntilAsRead(int seriesId, float chapterNumber)
|
||||
{
|
||||
var user = await _unitOfWork.UserRepository.GetUserByUsernameAsync(User.GetUsername(), AppUserIncludes.Progress);
|
||||
var user = (await _unitOfWork.UserRepository.GetUserByUsernameAsync(User.GetUsername(),
|
||||
AppUserIncludes.Progress))!;
|
||||
return Ok(await _tachiyomiService.MarkChaptersUntilAsRead(user, seriesId, chapterNumber));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
using System.Threading.Tasks;
|
||||
using API.Data;
|
||||
using API.DTOs.Theme;
|
||||
using API.Extensions;
|
||||
using API.Services;
|
||||
using API.Services.Tasks;
|
||||
using Kavita.Common;
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using API.Data;
|
||||
using API.DTOs.Uploads;
|
||||
|
@ -10,7 +9,6 @@ using Flurl.Http;
|
|||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using NetVips;
|
||||
|
||||
namespace API.Controllers;
|
||||
|
||||
|
@ -93,8 +91,10 @@ public class UploadController : BaseApiController
|
|||
|
||||
try
|
||||
{
|
||||
var filePath = _imageService.CreateThumbnailFromBase64(uploadFileDto.Url, ImageService.GetSeriesFormat(uploadFileDto.Id));
|
||||
var series = await _unitOfWork.SeriesRepository.GetSeriesByIdAsync(uploadFileDto.Id);
|
||||
if (series == null) return BadRequest("Invalid Series");
|
||||
var convertToWebP = (await _unitOfWork.SettingsRepository.GetSettingsDtoAsync()).ConvertCoverToWebP;
|
||||
var filePath = _imageService.CreateThumbnailFromBase64(uploadFileDto.Url, ImageService.GetSeriesFormat(uploadFileDto.Id), convertToWebP);
|
||||
|
||||
if (!string.IsNullOrEmpty(filePath))
|
||||
{
|
||||
|
@ -140,8 +140,10 @@ public class UploadController : BaseApiController
|
|||
|
||||
try
|
||||
{
|
||||
var filePath = _imageService.CreateThumbnailFromBase64(uploadFileDto.Url, $"{ImageService.GetCollectionTagFormat(uploadFileDto.Id)}");
|
||||
var tag = await _unitOfWork.CollectionTagRepository.GetTagAsync(uploadFileDto.Id);
|
||||
if (tag == null) return BadRequest("Invalid Tag id");
|
||||
var convertToWebP = (await _unitOfWork.SettingsRepository.GetSettingsDtoAsync()).ConvertCoverToWebP;
|
||||
var filePath = _imageService.CreateThumbnailFromBase64(uploadFileDto.Url, $"{ImageService.GetCollectionTagFormat(uploadFileDto.Id)}", convertToWebP);
|
||||
|
||||
if (!string.IsNullOrEmpty(filePath))
|
||||
{
|
||||
|
@ -190,8 +192,10 @@ public class UploadController : BaseApiController
|
|||
|
||||
try
|
||||
{
|
||||
var filePath = _imageService.CreateThumbnailFromBase64(uploadFileDto.Url, $"{ImageService.GetReadingListFormat(uploadFileDto.Id)}");
|
||||
var readingList = await _unitOfWork.ReadingListRepository.GetReadingListByIdAsync(uploadFileDto.Id);
|
||||
if (readingList == null) return BadRequest("Reading list is not valid");
|
||||
var convertToWebP = (await _unitOfWork.SettingsRepository.GetSettingsDtoAsync()).ConvertCoverToWebP;
|
||||
var filePath = _imageService.CreateThumbnailFromBase64(uploadFileDto.Url, $"{ImageService.GetReadingListFormat(uploadFileDto.Id)}", convertToWebP);
|
||||
|
||||
if (!string.IsNullOrEmpty(filePath))
|
||||
{
|
||||
|
@ -238,7 +242,9 @@ public class UploadController : BaseApiController
|
|||
try
|
||||
{
|
||||
var chapter = await _unitOfWork.ChapterRepository.GetChapterAsync(uploadFileDto.Id);
|
||||
var filePath = _imageService.CreateThumbnailFromBase64(uploadFileDto.Url, $"{ImageService.GetChapterFormat(uploadFileDto.Id, chapter.VolumeId)}");
|
||||
if (chapter == null) return BadRequest("Invalid Chapter");
|
||||
var convertToWebP = (await _unitOfWork.SettingsRepository.GetSettingsDtoAsync()).ConvertCoverToWebP;
|
||||
var filePath = _imageService.CreateThumbnailFromBase64(uploadFileDto.Url, $"{ImageService.GetChapterFormat(uploadFileDto.Id, chapter.VolumeId)}", convertToWebP);
|
||||
|
||||
if (!string.IsNullOrEmpty(filePath))
|
||||
{
|
||||
|
@ -246,8 +252,11 @@ public class UploadController : BaseApiController
|
|||
chapter.CoverImageLocked = true;
|
||||
_unitOfWork.ChapterRepository.Update(chapter);
|
||||
var volume = await _unitOfWork.VolumeRepository.GetVolumeAsync(chapter.VolumeId);
|
||||
volume.CoverImage = chapter.CoverImage;
|
||||
_unitOfWork.VolumeRepository.Update(volume);
|
||||
if (volume != null)
|
||||
{
|
||||
volume.CoverImage = chapter.CoverImage;
|
||||
_unitOfWork.VolumeRepository.Update(volume);
|
||||
}
|
||||
}
|
||||
|
||||
if (_unitOfWork.HasChanges())
|
||||
|
@ -301,8 +310,9 @@ public class UploadController : BaseApiController
|
|||
|
||||
try
|
||||
{
|
||||
var convertToWebP = (await _unitOfWork.SettingsRepository.GetSettingsDtoAsync()).ConvertCoverToWebP;
|
||||
var filePath = _imageService.CreateThumbnailFromBase64(uploadFileDto.Url,
|
||||
$"{ImageService.GetLibraryFormat(uploadFileDto.Id)}", ImageService.LibraryThumbnailWidth);
|
||||
$"{ImageService.GetLibraryFormat(uploadFileDto.Id)}", convertToWebP, ImageService.LibraryThumbnailWidth);
|
||||
|
||||
if (!string.IsNullOrEmpty(filePath))
|
||||
{
|
||||
|
@ -340,19 +350,20 @@ public class UploadController : BaseApiController
|
|||
try
|
||||
{
|
||||
var chapter = await _unitOfWork.ChapterRepository.GetChapterAsync(uploadFileDto.Id);
|
||||
if (chapter == null) return BadRequest("Chapter no longer exists");
|
||||
var originalFile = chapter.CoverImage;
|
||||
chapter.CoverImage = string.Empty;
|
||||
chapter.CoverImageLocked = false;
|
||||
_unitOfWork.ChapterRepository.Update(chapter);
|
||||
var volume = await _unitOfWork.VolumeRepository.GetVolumeAsync(chapter.VolumeId);
|
||||
var volume = (await _unitOfWork.VolumeRepository.GetVolumeAsync(chapter.VolumeId))!;
|
||||
volume.CoverImage = chapter.CoverImage;
|
||||
_unitOfWork.VolumeRepository.Update(volume);
|
||||
var series = await _unitOfWork.SeriesRepository.GetSeriesByIdAsync(volume.SeriesId);
|
||||
var series = (await _unitOfWork.SeriesRepository.GetSeriesByIdAsync(volume.SeriesId))!;
|
||||
|
||||
if (_unitOfWork.HasChanges())
|
||||
{
|
||||
await _unitOfWork.CommitAsync();
|
||||
System.IO.File.Delete(originalFile);
|
||||
if (originalFile != null) System.IO.File.Delete(originalFile);
|
||||
_taskScheduler.RefreshSeriesMetadata(series.LibraryId, series.Id, true);
|
||||
return Ok();
|
||||
}
|
||||
|
|
|
@ -4,10 +4,7 @@ using System.Threading.Tasks;
|
|||
using API.Data;
|
||||
using API.Data.Repositories;
|
||||
using API.DTOs;
|
||||
using API.DTOs.Filtering;
|
||||
using API.Entities.Enums;
|
||||
using API.Extensions;
|
||||
using API.Helpers;
|
||||
using API.SignalR;
|
||||
using AutoMapper;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
|
@ -68,6 +65,7 @@ public class UsersController : BaseApiController
|
|||
{
|
||||
var userId = await _unitOfWork.UserRepository.GetUserIdByUsernameAsync(User.GetUsername());
|
||||
var library = await _unitOfWork.LibraryRepository.GetLibraryForIdAsync(libraryId);
|
||||
if (library == null) return BadRequest("Library does not exist");
|
||||
return Ok(await _unitOfWork.AppUserProgressRepository.UserHasProgress(library.Type, userId));
|
||||
}
|
||||
|
||||
|
@ -83,9 +81,8 @@ public class UsersController : BaseApiController
|
|||
{
|
||||
var user = await _unitOfWork.UserRepository.GetUserByUsernameAsync(User.GetUsername(),
|
||||
AppUserIncludes.UserPreferences);
|
||||
var existingPreferences = user.UserPreferences;
|
||||
|
||||
preferencesDto.Theme ??= await _unitOfWork.SiteThemeRepository.GetDefaultTheme();
|
||||
if (user == null) return Unauthorized();
|
||||
var existingPreferences = user!.UserPreferences;
|
||||
|
||||
existingPreferences.ReadingDirection = preferencesDto.ReadingDirection;
|
||||
existingPreferences.ScalingOption = preferencesDto.ScalingOption;
|
||||
|
@ -107,8 +104,8 @@ public class UsersController : BaseApiController
|
|||
existingPreferences.BookReaderImmersiveMode = preferencesDto.BookReaderImmersiveMode;
|
||||
existingPreferences.GlobalPageLayoutMode = preferencesDto.GlobalPageLayoutMode;
|
||||
existingPreferences.BlurUnreadSummaries = preferencesDto.BlurUnreadSummaries;
|
||||
existingPreferences.Theme = await _unitOfWork.SiteThemeRepository.GetThemeById(preferencesDto.Theme.Id);
|
||||
existingPreferences.LayoutMode = preferencesDto.LayoutMode;
|
||||
existingPreferences.Theme = preferencesDto.Theme ?? await _unitOfWork.SiteThemeRepository.GetDefaultTheme();
|
||||
existingPreferences.PromptForDownloadSize = preferencesDto.PromptForDownloadSize;
|
||||
existingPreferences.NoTransitions = preferencesDto.NoTransitions;
|
||||
existingPreferences.SwipeToPaginate = preferencesDto.SwipeToPaginate;
|
||||
|
@ -117,7 +114,7 @@ public class UsersController : BaseApiController
|
|||
|
||||
if (await _unitOfWork.CommitAsync())
|
||||
{
|
||||
await _eventHub.SendMessageToAsync(MessageFactory.UserUpdate, MessageFactory.UserUpdateEvent(user.Id, user.UserName), user.Id);
|
||||
await _eventHub.SendMessageToAsync(MessageFactory.UserUpdate, MessageFactory.UserUpdateEvent(user.Id, user.UserName!), user.Id);
|
||||
return Ok(preferencesDto);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using API.Data;
|
||||
using API.Data.Repositories;
|
||||
|
@ -56,6 +55,7 @@ public class WantToReadController : BaseApiController
|
|||
{
|
||||
var user = await _unitOfWork.UserRepository.GetUserByUsernameAsync(User.GetUsername(),
|
||||
AppUserIncludes.WantToRead);
|
||||
if (user == null) return Unauthorized();
|
||||
|
||||
var existingIds = user.WantToRead.Select(s => s.Id).ToList();
|
||||
existingIds.AddRange(dto.SeriesIds);
|
||||
|
@ -84,6 +84,7 @@ public class WantToReadController : BaseApiController
|
|||
{
|
||||
var user = await _unitOfWork.UserRepository.GetUserByUsernameAsync(User.GetUsername(),
|
||||
AppUserIncludes.WantToRead);
|
||||
if (user == null) return Unauthorized();
|
||||
|
||||
user.WantToRead = user.WantToRead.Where(s => !dto.SeriesIds.Contains(s.Id)).ToList();
|
||||
|
||||
|
|
|
@ -5,12 +5,12 @@ namespace API.DTOs.Account;
|
|||
public class ConfirmEmailDto
|
||||
{
|
||||
[Required]
|
||||
public string Email { get; set; }
|
||||
public string Email { get; set; } = default!;
|
||||
[Required]
|
||||
public string Token { get; set; }
|
||||
public string Token { get; set; } = default!;
|
||||
[Required]
|
||||
[StringLength(32, MinimumLength = 6)]
|
||||
public string Password { get; set; }
|
||||
public string Password { get; set; } = default!;
|
||||
[Required]
|
||||
public string Username { get; set; }
|
||||
public string Username { get; set; } = default!;
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ namespace API.DTOs.Account;
|
|||
public class ConfirmEmailUpdateDto
|
||||
{
|
||||
[Required]
|
||||
public string Email { get; set; }
|
||||
public string Email { get; set; } = default!;
|
||||
[Required]
|
||||
public string Token { get; set; }
|
||||
public string Token { get; set; } = default!;
|
||||
}
|
||||
|
|
|
@ -2,6 +2,6 @@
|
|||
|
||||
public class ConfirmMigrationEmailDto
|
||||
{
|
||||
public string Email { get; set; }
|
||||
public string Token { get; set; }
|
||||
public string Email { get; set; } = default!;
|
||||
public string Token { get; set; } = default!;
|
||||
}
|
||||
|
|
|
@ -5,10 +5,10 @@ namespace API.DTOs.Account;
|
|||
public class ConfirmPasswordResetDto
|
||||
{
|
||||
[Required]
|
||||
public string Email { get; set; }
|
||||
public string Email { get; set; } = default!;
|
||||
[Required]
|
||||
public string Token { get; set; }
|
||||
public string Token { get; set; } = default!;
|
||||
[Required]
|
||||
[StringLength(32, MinimumLength = 6)]
|
||||
public string Password { get; set; }
|
||||
public string Password { get; set; } = default!;
|
||||
}
|
||||
|
|
|
@ -1,24 +1,23 @@
|
|||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using API.Entities.Enums;
|
||||
|
||||
namespace API.DTOs.Account;
|
||||
|
||||
public class InviteUserDto
|
||||
{
|
||||
[Required]
|
||||
public string Email { get; set; }
|
||||
public string Email { get; set; } = default!;
|
||||
/// <summary>
|
||||
/// List of Roles to assign to user. If admin not present, Pleb will be applied.
|
||||
/// If admin present, all libraries will be granted access and will ignore those from DTO.
|
||||
/// </summary>
|
||||
public ICollection<string> Roles { get; init; }
|
||||
public ICollection<string> Roles { get; init; } = default!;
|
||||
/// <summary>
|
||||
/// A list of libraries to grant access to
|
||||
/// </summary>
|
||||
public IList<int> Libraries { get; init; }
|
||||
public IList<int> Libraries { get; init; } = default!;
|
||||
/// <summary>
|
||||
/// An Age Rating which will limit the account to seeing everything equal to or below said rating.
|
||||
/// </summary>
|
||||
public AgeRestrictionDto AgeRestriction { get; set; }
|
||||
public AgeRestrictionDto AgeRestriction { get; set; } = default!;
|
||||
}
|
||||
|
|
|
@ -5,9 +5,9 @@ public class InviteUserResponse
|
|||
/// <summary>
|
||||
/// Email link used to setup the user account
|
||||
/// </summary>
|
||||
public string EmailLink { get; set; }
|
||||
public string EmailLink { get; set; } = default!;
|
||||
/// <summary>
|
||||
/// Was an email sent (ie is this server accessible)
|
||||
/// </summary>
|
||||
public bool EmailSent { get; set; }
|
||||
public bool EmailSent { get; set; } = default!;
|
||||
}
|
||||
|
|
|
@ -2,6 +2,6 @@
|
|||
|
||||
public class LoginDto
|
||||
{
|
||||
public string Username { get; init; }
|
||||
public string Password { get; set; }
|
||||
public string Username { get; init; } = default!;
|
||||
public string Password { get; set; } = default!;
|
||||
}
|
||||
|
|
|
@ -2,8 +2,7 @@
|
|||
|
||||
public class MigrateUserEmailDto
|
||||
{
|
||||
public string Email { get; set; }
|
||||
public string Username { get; set; }
|
||||
public string Password { get; set; }
|
||||
public bool SendEmail { get; set; }
|
||||
public string Email { get; set; } = default!;
|
||||
public string Username { get; set; } = default!;
|
||||
public string Password { get; set; } = default!;
|
||||
}
|
||||
|
|
|
@ -8,15 +8,15 @@ public class ResetPasswordDto
|
|||
/// The Username of the User
|
||||
/// </summary>
|
||||
[Required]
|
||||
public string UserName { get; init; }
|
||||
public string UserName { get; init; } = default!;
|
||||
/// <summary>
|
||||
/// The new password
|
||||
/// </summary>
|
||||
[Required]
|
||||
[StringLength(32, MinimumLength = 6)]
|
||||
public string Password { get; init; }
|
||||
public string Password { get; init; } = default!;
|
||||
/// <summary>
|
||||
/// The old, existing password. If an admin is performing the change, this is not required. Otherwise, it is.
|
||||
/// </summary>
|
||||
public string OldPassword { get; init; }
|
||||
public string OldPassword { get; init; } = default!;
|
||||
}
|
||||
|
|
|
@ -2,6 +2,6 @@
|
|||
|
||||
public class TokenRequestDto
|
||||
{
|
||||
public string Token { get; init; }
|
||||
public string RefreshToken { get; init; }
|
||||
public string Token { get; init; } = default!;
|
||||
public string RefreshToken { get; init; } = default!;
|
||||
}
|
||||
|
|
|
@ -2,6 +2,6 @@
|
|||
|
||||
public class UpdateEmailDto
|
||||
{
|
||||
public string Email { get; set; }
|
||||
public string Password { get; set; }
|
||||
public string Email { get; set; } = default!;
|
||||
public string Password { get; set; } = default!;
|
||||
}
|
||||
|
|
|
@ -1,24 +1,21 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Text.Json.Serialization;
|
||||
using API.Entities.Enums;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion.Internal;
|
||||
|
||||
namespace API.DTOs.Account;
|
||||
|
||||
public record UpdateUserDto
|
||||
{
|
||||
public int UserId { get; set; }
|
||||
public string Username { get; set; }
|
||||
public string Username { get; set; } = default!;
|
||||
/// List of Roles to assign to user. If admin not present, Pleb will be applied.
|
||||
/// If admin present, all libraries will be granted access and will ignore those from DTO.
|
||||
public IList<string> Roles { get; init; }
|
||||
public IList<string> Roles { get; init; } = default!;
|
||||
/// <summary>
|
||||
/// A list of libraries to grant access to
|
||||
/// </summary>
|
||||
public IList<int> Libraries { get; init; }
|
||||
public IList<int> Libraries { get; init; } = default!;
|
||||
/// <summary>
|
||||
/// An Age Rating which will limit the account to seeing everything equal to or below said rating.
|
||||
/// </summary>
|
||||
public AgeRestrictionDto AgeRestriction { get; init; }
|
||||
public AgeRestrictionDto AgeRestriction { get; init; } = default!;
|
||||
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
using System.Collections.Generic;
|
||||
using API.Entities.Enums;
|
||||
using API.Entities.Interfaces;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
|
||||
namespace API.DTOs;
|
||||
|
||||
|
@ -16,11 +15,11 @@ public class ChapterDto : IHasReadTimeEstimate, IEntityDate
|
|||
/// <summary>
|
||||
/// Range of chapters. Chapter 2-4 -> "2-4". Chapter 2 -> "2".
|
||||
/// </summary>
|
||||
public string Range { get; init; }
|
||||
public string Range { get; init; } = default!;
|
||||
/// <summary>
|
||||
/// Smallest number of the Range.
|
||||
/// </summary>
|
||||
public string Number { get; init; }
|
||||
public string Number { get; init; } = default!;
|
||||
/// <summary>
|
||||
/// Total number of pages in all MangaFiles
|
||||
/// </summary>
|
||||
|
@ -32,11 +31,11 @@ public class ChapterDto : IHasReadTimeEstimate, IEntityDate
|
|||
/// <summary>
|
||||
/// Used for books/specials to display custom title. For non-specials/books, will be set to <see cref="Range"/>
|
||||
/// </summary>
|
||||
public string Title { get; set; }
|
||||
public string Title { get; set; } = default!;
|
||||
/// <summary>
|
||||
/// The files that represent this Chapter
|
||||
/// </summary>
|
||||
public ICollection<MangaFileDto> Files { get; init; }
|
||||
public ICollection<MangaFileDto> Files { get; init; } = default!;
|
||||
/// <summary>
|
||||
/// Calculated at API time. Number of pages read for this Chapter for logged in user.
|
||||
/// </summary>
|
||||
|
@ -69,12 +68,12 @@ public class ChapterDto : IHasReadTimeEstimate, IEntityDate
|
|||
/// Title of the Chapter/Issue
|
||||
/// </summary>
|
||||
/// <remarks>Metadata field</remarks>
|
||||
public string TitleName { get; set; }
|
||||
public string TitleName { get; set; } = default!;
|
||||
/// <summary>
|
||||
/// Summary of the Chapter
|
||||
/// </summary>
|
||||
/// <remarks>This is not set normally, only for Series Detail</remarks>
|
||||
public string Summary { get; init; }
|
||||
public string Summary { get; init; } = default!;
|
||||
/// <summary>
|
||||
/// Age Rating for the issue/chapter
|
||||
/// </summary>
|
||||
|
|
|
@ -9,9 +9,9 @@ public class CollectionTagBulkAddDto
|
|||
/// </summary>
|
||||
/// <remarks>Can be 0 which then will use Title to create a tag</remarks>
|
||||
public int CollectionTagId { get; init; }
|
||||
public string CollectionTagTitle { get; init; }
|
||||
public string CollectionTagTitle { get; init; } = default!;
|
||||
/// <summary>
|
||||
/// Series Ids to add onto Collection Tag
|
||||
/// </summary>
|
||||
public IEnumerable<int> SeriesIds { get; init; }
|
||||
public IEnumerable<int> SeriesIds { get; init; } = default!;
|
||||
}
|
||||
|
|
|
@ -3,12 +3,12 @@
|
|||
public class CollectionTagDto
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Title { get; set; }
|
||||
public string Summary { get; set; }
|
||||
public string Title { get; set; } = default!;
|
||||
public string Summary { get; set; } = default!;
|
||||
public bool Promoted { get; set; }
|
||||
/// <summary>
|
||||
/// The cover image string. This is used on Frontend to show or hide the Cover Image
|
||||
/// </summary>
|
||||
public string CoverImage { get; set; }
|
||||
public string CoverImage { get; set; } = default!;
|
||||
public bool CoverImageLocked { get; set; }
|
||||
}
|
||||
|
|
|
@ -4,6 +4,6 @@ namespace API.DTOs.CollectionTags;
|
|||
|
||||
public class UpdateSeriesForTagDto
|
||||
{
|
||||
public CollectionTagDto Tag { get; init; }
|
||||
public IEnumerable<int> SeriesIdsToRemove { get; init; }
|
||||
public CollectionTagDto Tag { get; init; } = default!;
|
||||
public IEnumerable<int> SeriesIdsToRemove { get; init; } = default!;
|
||||
}
|
||||
|
|
|
@ -7,10 +7,10 @@ namespace API.DTOs;
|
|||
public class CreateLibraryDto
|
||||
{
|
||||
[Required]
|
||||
public string Name { get; init; }
|
||||
public string Name { get; init; } = default!;
|
||||
[Required]
|
||||
public LibraryType Type { get; init; }
|
||||
[Required]
|
||||
[MinLength(1)]
|
||||
public IEnumerable<string> Folders { get; init; }
|
||||
public IEnumerable<string> Folders { get; init; } = default!;
|
||||
}
|
||||
|
|
|
@ -4,5 +4,5 @@ namespace API.DTOs;
|
|||
|
||||
public class DeleteSeriesDto
|
||||
{
|
||||
public IList<int> SeriesIds { get; set; }
|
||||
public IList<int> SeriesIds { get; set; } = default!;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Runtime.InteropServices;
|
||||
using API.Entities.Enums.Device;
|
||||
|
||||
namespace API.DTOs.Device;
|
||||
|
@ -7,14 +6,14 @@ namespace API.DTOs.Device;
|
|||
public class CreateDeviceDto
|
||||
{
|
||||
[Required]
|
||||
public string Name { get; set; }
|
||||
public string Name { get; set; } = default!;
|
||||
/// <summary>
|
||||
/// Platform of the device. If not know, defaults to "Custom"
|
||||
/// </summary>
|
||||
[Required]
|
||||
public DevicePlatform Platform { get; set; }
|
||||
[Required]
|
||||
public string EmailAddress { get; set; }
|
||||
public string EmailAddress { get; set; } = default!;
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -17,11 +17,11 @@ public class DeviceDto
|
|||
/// </summary>
|
||||
/// <remarks>If this device is web, this will be the browser name</remarks>
|
||||
/// <example>Pixel 3a, John's Kindle</example>
|
||||
public string Name { get; set; }
|
||||
public string Name { get; set; } = default!;
|
||||
/// <summary>
|
||||
/// An email address associated with the device (ie Kindle). Will be used with Send to functionality
|
||||
/// </summary>
|
||||
public string EmailAddress { get; set; }
|
||||
public string EmailAddress { get; set; } = default!;
|
||||
/// <summary>
|
||||
/// Platform (ie) Windows 10
|
||||
/// </summary>
|
||||
|
|
|
@ -5,5 +5,5 @@ namespace API.DTOs.Device;
|
|||
public class SendToDeviceDto
|
||||
{
|
||||
public int DeviceId { get; set; }
|
||||
public IReadOnlyList<int> ChapterIds { get; set; }
|
||||
public IReadOnlyList<int> ChapterIds { get; set; } = default!;
|
||||
}
|
||||
|
|
|
@ -8,12 +8,12 @@ public class UpdateDeviceDto
|
|||
[Required]
|
||||
public int Id { get; set; }
|
||||
[Required]
|
||||
public string Name { get; set; }
|
||||
public string Name { get; set; } = default!;
|
||||
/// <summary>
|
||||
/// Platform of the device. If not know, defaults to "Custom"
|
||||
/// </summary>
|
||||
[Required]
|
||||
public DevicePlatform Platform { get; set; }
|
||||
[Required]
|
||||
public string EmailAddress { get; set; }
|
||||
public string EmailAddress { get; set; } = default!;
|
||||
}
|
||||
|
|
|
@ -7,5 +7,5 @@ namespace API.DTOs.Downloads;
|
|||
public class DownloadBookmarkDto
|
||||
{
|
||||
[Required]
|
||||
public IEnumerable<BookmarkDto> Bookmarks { get; set; }
|
||||
public IEnumerable<BookmarkDto> Bookmarks { get; set; } = default!;
|
||||
}
|
||||
|
|
|
@ -2,11 +2,11 @@
|
|||
|
||||
public class ConfirmationEmailDto
|
||||
{
|
||||
public string InvitingUser { get; init; }
|
||||
public string EmailAddress { get; init; }
|
||||
public string ServerConfirmationLink { get; init; }
|
||||
public string InvitingUser { get; init; } = default!;
|
||||
public string EmailAddress { get; init; } = default!;
|
||||
public string ServerConfirmationLink { get; init; } = default!;
|
||||
/// <summary>
|
||||
/// InstallId of this Kavita Instance
|
||||
/// </summary>
|
||||
public string InstallId { get; init; }
|
||||
public string InstallId { get; init; } = default!;
|
||||
}
|
||||
|
|
|
@ -2,11 +2,11 @@
|
|||
|
||||
public class EmailMigrationDto
|
||||
{
|
||||
public string EmailAddress { get; init; }
|
||||
public string Username { get; init; }
|
||||
public string ServerConfirmationLink { get; init; }
|
||||
public string EmailAddress { get; init; } = default!;
|
||||
public string Username { get; init; } = default!;
|
||||
public string ServerConfirmationLink { get; init; } = default!;
|
||||
/// <summary>
|
||||
/// InstallId of this Kavita Instance
|
||||
/// </summary>
|
||||
public string InstallId { get; init; }
|
||||
public string InstallId { get; init; } = default!;
|
||||
}
|
||||
|
|
|
@ -6,5 +6,5 @@
|
|||
public class EmailTestResultDto
|
||||
{
|
||||
public bool Successful { get; set; }
|
||||
public string ErrorMessage { get; set; }
|
||||
public string ErrorMessage { get; set; } = default!;
|
||||
}
|
||||
|
|
|
@ -2,10 +2,10 @@
|
|||
|
||||
public class PasswordResetEmailDto
|
||||
{
|
||||
public string EmailAddress { get; init; }
|
||||
public string ServerConfirmationLink { get; init; }
|
||||
public string EmailAddress { get; init; } = default!;
|
||||
public string ServerConfirmationLink { get; init; } = default!;
|
||||
/// <summary>
|
||||
/// InstallId of this Kavita Instance
|
||||
/// </summary>
|
||||
public string InstallId { get; init; }
|
||||
public string InstallId { get; init; } = default!;
|
||||
}
|
||||
|
|
|
@ -4,6 +4,6 @@ namespace API.DTOs.Email;
|
|||
|
||||
public class SendToDto
|
||||
{
|
||||
public string DestinationEmail { get; set; }
|
||||
public IEnumerable<string> FilePaths { get; set; }
|
||||
public string DestinationEmail { get; set; } = default!;
|
||||
public IEnumerable<string> FilePaths { get; set; } = default!;
|
||||
}
|
||||
|
|
|
@ -2,5 +2,5 @@
|
|||
|
||||
public class TestEmailDto
|
||||
{
|
||||
public string Url { get; set; }
|
||||
public string Url { get; set; } = default!;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using API.Entities;
|
||||
using API.Entities.Enums;
|
||||
|
||||
|
@ -81,7 +80,7 @@ public class FilterDto
|
|||
/// <summary>
|
||||
/// Sorting Options for a query. Defaults to null, which uses the queries natural sorting order
|
||||
/// </summary>
|
||||
public SortOptions SortOptions { get; set; } = null;
|
||||
public SortOptions? SortOptions { get; set; } = null;
|
||||
/// <summary>
|
||||
/// Age Ratings. Empty list will return everything back
|
||||
/// </summary>
|
||||
|
@ -99,10 +98,8 @@ public class FilterDto
|
|||
/// An optional name string to filter by. Empty string will ignore.
|
||||
/// </summary>
|
||||
public string SeriesNameQuery { get; init; } = string.Empty;
|
||||
#nullable enable
|
||||
/// <summary>
|
||||
/// An optional release year to filter by. Null will ignore. You can pass 0 for an individual field to ignore it.
|
||||
/// </summary>
|
||||
public Range<int>? ReleaseYearRange { get; init; } = null;
|
||||
#nullable disable
|
||||
}
|
||||
|
|
|
@ -2,6 +2,6 @@
|
|||
|
||||
public class LanguageDto
|
||||
{
|
||||
public string IsoCode { get; set; }
|
||||
public string Title { get; set; }
|
||||
public required string IsoCode { get; set; }
|
||||
public required string Title { get; set; }
|
||||
}
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
/// </summary>
|
||||
public class Range<T>
|
||||
{
|
||||
public T Min { get; set; }
|
||||
public T Max { get; set; }
|
||||
public T? Min { get; init; }
|
||||
public T? Max { get; init; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
|
|
|
@ -7,7 +7,7 @@ namespace API.DTOs;
|
|||
/// </summary>
|
||||
public class GroupedSeriesDto
|
||||
{
|
||||
public string SeriesName { get; set; }
|
||||
public string SeriesName { get; set; } = default!;
|
||||
public int SeriesId { get; set; }
|
||||
public int LibraryId { get; set; }
|
||||
public LibraryType LibraryType { get; set; }
|
||||
|
|
|
@ -7,11 +7,11 @@ public class JobDto
|
|||
/// <summary>
|
||||
/// Job Id
|
||||
/// </summary>
|
||||
public string Id { get; set; }
|
||||
public string Id { get; set; } = default!;
|
||||
/// <summary>
|
||||
/// Human Readable title for the Job
|
||||
/// </summary>
|
||||
public string Title { get; set; }
|
||||
public string Title { get; set; } = default!;
|
||||
/// <summary>
|
||||
/// When the job was created
|
||||
/// </summary>
|
||||
|
@ -28,5 +28,5 @@ public class JobDto
|
|||
/// Last time the job was run
|
||||
/// </summary>
|
||||
public DateTime? LastExecutionUtc { get; set; }
|
||||
public string Cron { get; set; }
|
||||
public string Cron { get; set; } = default!;
|
||||
}
|
||||
|
|
|
@ -9,12 +9,13 @@ public class JumpKeyDto
|
|||
/// Number of items in this Key
|
||||
/// </summary>
|
||||
public int Size { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Code to use in URL (url encoded)
|
||||
/// </summary>
|
||||
public string Key { get; set; }
|
||||
public string Key { get; set; } = default!;
|
||||
/// <summary>
|
||||
/// What is visible to user
|
||||
/// </summary>
|
||||
public string Title { get; set; }
|
||||
public string Title { get; set; } = default!;
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ namespace API.DTOs;
|
|||
public class LibraryDto
|
||||
{
|
||||
public int Id { get; init; }
|
||||
public string Name { get; init; }
|
||||
public string? Name { get; init; }
|
||||
/// <summary>
|
||||
/// Last time Library was scanned
|
||||
/// </summary>
|
||||
|
@ -16,7 +16,7 @@ public class LibraryDto
|
|||
/// <summary>
|
||||
/// An optional Cover Image or null
|
||||
/// </summary>
|
||||
public string CoverImage { get; init; }
|
||||
public string? CoverImage { get; init; }
|
||||
/// <summary>
|
||||
/// If Folder Watching is enabled for this library
|
||||
/// </summary>
|
||||
|
@ -37,9 +37,9 @@ public class LibraryDto
|
|||
/// Include library series in Search
|
||||
/// </summary>
|
||||
public bool IncludeInSearch { get; set; } = true;
|
||||
public ICollection<string> Folders { get; init; } = new List<string>();
|
||||
/// <summary>
|
||||
/// When showing series, only parent series or series with no relationships will be returned
|
||||
/// </summary>
|
||||
public bool CollapseSeriesRelationships { get; set; } = false;
|
||||
public ICollection<string> Folders { get; init; }
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ namespace API.DTOs;
|
|||
public class MangaFileDto
|
||||
{
|
||||
public int Id { get; init; }
|
||||
public string FilePath { get; init; }
|
||||
public string FilePath { get; init; } = default!;
|
||||
public int Pages { get; init; }
|
||||
public long Bytes { get; init; }
|
||||
public MangaFormat Format { get; init; }
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using API.Data.Misc;
|
||||
using API.DTOs.Account;
|
||||
using API.Entities.Enums;
|
||||
|
||||
namespace API.DTOs;
|
||||
|
||||
|
@ -12,11 +10,11 @@ namespace API.DTOs;
|
|||
public class MemberDto
|
||||
{
|
||||
public int Id { get; init; }
|
||||
public string Username { get; init; }
|
||||
public string Email { get; init; }
|
||||
public AgeRestrictionDto AgeRestriction { get; init; }
|
||||
public string? Username { get; init; }
|
||||
public string? Email { get; init; }
|
||||
public AgeRestrictionDto? AgeRestriction { get; init; }
|
||||
public DateTime Created { get; init; }
|
||||
public DateTime LastActive { get; init; }
|
||||
public IEnumerable<LibraryDto> Libraries { get; init; }
|
||||
public IEnumerable<string> Roles { get; init; }
|
||||
public IEnumerable<LibraryDto>? Libraries { get; init; }
|
||||
public IEnumerable<string>? Roles { get; init; }
|
||||
}
|
||||
|
|
|
@ -5,5 +5,5 @@ namespace API.DTOs.Metadata;
|
|||
public class AgeRatingDto
|
||||
{
|
||||
public AgeRating Value { get; set; }
|
||||
public string Title { get; set; }
|
||||
public required string Title { get; set; }
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ public class ChapterMetadataDto
|
|||
{
|
||||
public int Id { get; set; }
|
||||
public int ChapterId { get; set; }
|
||||
public string Title { get; set; }
|
||||
public string Title { get; set; } = default!;
|
||||
public ICollection<PersonDto> Writers { get; set; } = new List<PersonDto>();
|
||||
public ICollection<PersonDto> CoverArtists { get; set; } = new List<PersonDto>();
|
||||
public ICollection<PersonDto> Publishers { get; set; } = new List<PersonDto>();
|
||||
|
@ -29,16 +29,16 @@ public class ChapterMetadataDto
|
|||
/// </summary>
|
||||
public ICollection<TagDto> Tags { get; set; } = new List<TagDto>();
|
||||
public AgeRating AgeRating { get; set; }
|
||||
public string ReleaseDate { get; set; }
|
||||
public string? ReleaseDate { get; set; }
|
||||
public PublicationStatus PublicationStatus { get; set; }
|
||||
/// <summary>
|
||||
/// Summary for the Chapter/Issue
|
||||
/// </summary>
|
||||
public string Summary { get; set; }
|
||||
public string? Summary { get; set; }
|
||||
/// <summary>
|
||||
/// Language for the Chapter/Issue
|
||||
/// </summary>
|
||||
public string Language { get; set; }
|
||||
public string? Language { get; set; }
|
||||
/// <summary>
|
||||
/// Number in the TotalCount of issues
|
||||
/// </summary>
|
||||
|
|
|
@ -3,5 +3,5 @@
|
|||
public class GenreTagDto
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Title { get; set; }
|
||||
public required string Title { get; set; }
|
||||
}
|
||||
|
|
|
@ -5,5 +5,5 @@ namespace API.DTOs.Metadata;
|
|||
public class PublicationStatusDto
|
||||
{
|
||||
public PublicationStatus Value { get; set; }
|
||||
public string Title { get; set; }
|
||||
public required string Title { get; set; }
|
||||
}
|
||||
|
|
|
@ -3,5 +3,5 @@
|
|||
public class TagDto
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Title { get; set; }
|
||||
public required string Title { get; set; }
|
||||
}
|
||||
|
|
|
@ -8,11 +8,11 @@ public class FeedCategory
|
|||
public string Scheme { get; } = "http://www.bisg.org/standards/bisac_subject/index.html";
|
||||
|
||||
[XmlAttribute("term")]
|
||||
public string Term { get; set; }
|
||||
public string Term { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// The actual genre
|
||||
/// </summary>
|
||||
[XmlAttribute("label")]
|
||||
public string Label { get; set; }
|
||||
public string Label { get; set; } = default!;
|
||||
}
|
||||
|
|
|
@ -10,13 +10,13 @@ public class FeedEntry
|
|||
public string Updated { get; init; } = DateTime.UtcNow.ToString("s");
|
||||
|
||||
[XmlElement("id")]
|
||||
public string Id { get; set; }
|
||||
public required string Id { get; set; }
|
||||
|
||||
[XmlElement("title")]
|
||||
public string Title { get; set; }
|
||||
public required string Title { get; set; }
|
||||
|
||||
[XmlElement("summary")]
|
||||
public string Summary { get; set; }
|
||||
public string? Summary { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Represents Size of the Entry
|
||||
|
@ -24,20 +24,20 @@ public class FeedEntry
|
|||
/// <example>2 MB</example>
|
||||
/// </summary>
|
||||
[XmlElement("extent", Namespace = "http://purl.org/dc/terms/")]
|
||||
public string Extent { get; set; }
|
||||
public string? Extent { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Format of the file
|
||||
/// https://dublincore.org/specifications/dublin-core/dcmi-terms/
|
||||
/// </summary>
|
||||
[XmlElement("format", Namespace = "http://purl.org/dc/terms/format")]
|
||||
public string Format { get; set; }
|
||||
public string? Format { get; set; }
|
||||
|
||||
[XmlElement("language", Namespace = "http://purl.org/dc/terms/")]
|
||||
public string Language { get; set; }
|
||||
public string? Language { get; set; }
|
||||
|
||||
[XmlElement("content")]
|
||||
public FeedEntryContent Content { get; set; }
|
||||
public FeedEntryContent? Content { get; set; }
|
||||
|
||||
[XmlElement("link")]
|
||||
public List<FeedLink> Links { get; set; } = new List<FeedLink>();
|
||||
|
|
|
@ -8,29 +8,29 @@ public class OpenSearchDescription
|
|||
/// <summary>
|
||||
/// Contains a brief human-readable title that identifies this search engine.
|
||||
/// </summary>
|
||||
public string ShortName { get; set; }
|
||||
public string ShortName { get; set; } = default!;
|
||||
/// <summary>
|
||||
/// Contains an extended human-readable title that identifies this search engine.
|
||||
/// </summary>
|
||||
public string LongName { get; set; }
|
||||
public string LongName { get; set; } = default!;
|
||||
/// <summary>
|
||||
/// Contains a human-readable text description of the search engine.
|
||||
/// </summary>
|
||||
public string Description { get; set; }
|
||||
public string Description { get; set; } = default!;
|
||||
/// <summary>
|
||||
/// https://github.com/dewitt/opensearch/blob/master/opensearch-1-1-draft-6.md#the-url-element
|
||||
/// </summary>
|
||||
public SearchLink Url { get; set; }
|
||||
public SearchLink Url { get; set; } = default!;
|
||||
/// <summary>
|
||||
/// Contains a set of words that are used as keywords to identify and categorize this search content.
|
||||
/// Tags must be a single word and are delimited by the space character (' ').
|
||||
/// </summary>
|
||||
public string Tags { get; set; }
|
||||
public string Tags { get; set; } = string.Empty;
|
||||
/// <summary>
|
||||
/// Contains a URL that identifies the location of an image that can be used in association with this search content.
|
||||
/// <example><Image height="64" width="64" type="image/png">http://example.com/websearch.png</Image></example>
|
||||
/// </summary>
|
||||
public string Image { get; set; }
|
||||
public string Image { get; set; } = default!;
|
||||
public string InputEncoding { get; set; } = "UTF-8";
|
||||
public string OutputEncoding { get; set; } = "UTF-8";
|
||||
/// <summary>
|
||||
|
|
|
@ -5,11 +5,11 @@ namespace API.DTOs.OPDS;
|
|||
public class SearchLink
|
||||
{
|
||||
[XmlAttribute("type")]
|
||||
public string Type { get; set; }
|
||||
public string Type { get; set; } = default!;
|
||||
|
||||
[XmlAttribute("rel")]
|
||||
public string Rel { get; set; } = "results";
|
||||
|
||||
[XmlAttribute("template")]
|
||||
public string Template { get; set; }
|
||||
public string Template { get; set; } = default!;
|
||||
}
|
||||
|
|
|
@ -5,6 +5,6 @@ namespace API.DTOs;
|
|||
public class PersonDto
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
public required string Name { get; set; }
|
||||
public PersonRole Role { get; set; }
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace API.DTOs;
|
||||
|
||||
|
@ -19,5 +18,5 @@ public class ProgressDto
|
|||
/// For EPUB reader, this can be an optional string of the id of a part marker, to help resume reading position
|
||||
/// on pages that combine multiple "chapters".
|
||||
/// </summary>
|
||||
public string BookScrollId { get; set; }
|
||||
public string? BookScrollId { get; set; }
|
||||
}
|
||||
|
|
|
@ -7,14 +7,14 @@ public class BookChapterItem
|
|||
/// <summary>
|
||||
/// Name of the Chapter
|
||||
/// </summary>
|
||||
public string Title { get; set; }
|
||||
public string Title { get; set; } = default!;
|
||||
/// <summary>
|
||||
/// A part represents the id of the anchor so we can scroll to it. 01_values.xhtml#h_sVZPaxUSy/
|
||||
/// </summary>
|
||||
public string Part { get; set; }
|
||||
public string Part { get; set; } = default!;
|
||||
/// <summary>
|
||||
/// Page Number to load for the chapter
|
||||
/// </summary>
|
||||
public int Page { get; set; }
|
||||
public ICollection<BookChapterItem> Children { get; set; }
|
||||
public ICollection<BookChapterItem> Children { get; set; } = default!;
|
||||
}
|
||||
|
|
|
@ -4,15 +4,15 @@ namespace API.DTOs.Reader;
|
|||
|
||||
public class BookInfoDto : IChapterInfoDto
|
||||
{
|
||||
public string BookTitle { get; set; }
|
||||
public string BookTitle { get; set; } = default! ;
|
||||
public int SeriesId { get; set; }
|
||||
public int VolumeId { get; set; }
|
||||
public MangaFormat SeriesFormat { get; set; }
|
||||
public string SeriesName { get; set; }
|
||||
public string ChapterNumber { get; set; }
|
||||
public string VolumeNumber { get; set; }
|
||||
public string SeriesName { get; set; } = default! ;
|
||||
public string ChapterNumber { get; set; } = default! ;
|
||||
public string VolumeNumber { get; set; } = default! ;
|
||||
public int LibraryId { get; set; }
|
||||
public int Pages { get; set; }
|
||||
public bool IsSpecial { get; set; }
|
||||
public string ChapterTitle { get; set; }
|
||||
public string ChapterTitle { get; set; } = default! ;
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ namespace API.DTOs.Reader;
|
|||
|
||||
public class BookmarkInfoDto
|
||||
{
|
||||
public string SeriesName { get; set; }
|
||||
public string SeriesName { get; set; } = default!;
|
||||
public MangaFormat SeriesFormat { get; set; }
|
||||
public int SeriesId { get; set; }
|
||||
public int LibraryId { get; set; }
|
||||
|
|
|
@ -4,5 +4,5 @@ namespace API.DTOs.Reader;
|
|||
|
||||
public class BulkRemoveBookmarkForSeriesDto
|
||||
{
|
||||
public ICollection<int> SeriesIds { get; init; }
|
||||
public ICollection<int> SeriesIds { get; init; } = default!;
|
||||
}
|
||||
|
|
|
@ -11,11 +11,11 @@ public class ChapterInfoDto : IChapterInfoDto
|
|||
/// <summary>
|
||||
/// The Chapter Number
|
||||
/// </summary>
|
||||
public string ChapterNumber { get; set; }
|
||||
public string ChapterNumber { get; set; } = default! ;
|
||||
/// <summary>
|
||||
/// The Volume Number
|
||||
/// </summary>
|
||||
public string VolumeNumber { get; set; }
|
||||
public string VolumeNumber { get; set; } = default! ;
|
||||
/// <summary>
|
||||
/// Volume entity Id
|
||||
/// </summary>
|
||||
|
@ -23,7 +23,7 @@ public class ChapterInfoDto : IChapterInfoDto
|
|||
/// <summary>
|
||||
/// Series Name
|
||||
/// </summary>
|
||||
public string SeriesName { get; set; }
|
||||
public string SeriesName { get; set; } = null!;
|
||||
/// <summary>
|
||||
/// Series Format
|
||||
/// </summary>
|
||||
|
@ -51,7 +51,7 @@ public class ChapterInfoDto : IChapterInfoDto
|
|||
/// <summary>
|
||||
/// File name of the chapter
|
||||
/// </summary>
|
||||
public string FileName { get; set; }
|
||||
public string? FileName { get; set; }
|
||||
/// <summary>
|
||||
/// If this is marked as a special in Kavita
|
||||
/// </summary>
|
||||
|
@ -59,21 +59,22 @@ public class ChapterInfoDto : IChapterInfoDto
|
|||
/// <summary>
|
||||
/// The subtitle to render on the reader
|
||||
/// </summary>
|
||||
public string Subtitle { get; set; }
|
||||
public string? Subtitle { get; set; }
|
||||
/// <summary>
|
||||
/// Series Title
|
||||
/// </summary>
|
||||
/// <remarks>Usually just series name, but can include chapter title</remarks>
|
||||
public string Title { get; set; }
|
||||
public string Title { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// List of all files with their inner archive structure maintained in filename and dimensions
|
||||
/// </summary>
|
||||
/// <remarks>This is optionally returned by includeDimensions</remarks>
|
||||
public IEnumerable<FileDimensionDto> PageDimensions { get; set; }
|
||||
public IEnumerable<FileDimensionDto>? PageDimensions { get; set; }
|
||||
/// <summary>
|
||||
/// For Double Page reader, this will contain snap points to ensure the reader always resumes on correct page
|
||||
/// </summary>
|
||||
/// <remarks>This is optionally returned by includeDimensions</remarks>
|
||||
public IDictionary<int, int> DoublePairs { get; set; }
|
||||
public IDictionary<int, int>? DoublePairs { get; set; }
|
||||
|
||||
}
|
||||
|
|
|
@ -4,5 +4,5 @@ namespace API.DTOs.Reader;
|
|||
|
||||
public class MarkMultipleSeriesAsReadDto
|
||||
{
|
||||
public IReadOnlyList<int> SeriesIds { get; init; }
|
||||
public IReadOnlyList<int> SeriesIds { get; init; } = default!;
|
||||
}
|
||||
|
|
|
@ -11,9 +11,9 @@ public class MarkVolumesReadDto
|
|||
/// <summary>
|
||||
/// A list of Volumes to mark read
|
||||
/// </summary>
|
||||
public IReadOnlyList<int> VolumeIds { get; set; }
|
||||
public IReadOnlyList<int> VolumeIds { get; set; } = default!;
|
||||
/// <summary>
|
||||
/// A list of additional Chapters to mark as read
|
||||
/// </summary>
|
||||
public IReadOnlyList<int> ChapterIds { get; set; }
|
||||
public IReadOnlyList<int> ChapterIds { get; set; } = default!;
|
||||
}
|
||||
|
|
|
@ -2,5 +2,5 @@
|
|||
|
||||
public class CreateReadingListDto
|
||||
{
|
||||
public string Title { get; init; }
|
||||
public string Title { get; init; } = default!;
|
||||
}
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
public class ReadingListDto
|
||||
{
|
||||
public int Id { get; init; }
|
||||
public string Title { get; set; }
|
||||
public string Summary { get; set; }
|
||||
public string Title { get; set; } = default!;
|
||||
public string Summary { get; set; } = default!;
|
||||
/// <summary>
|
||||
/// Reading lists that are promoted are only done by admins
|
||||
/// </summary>
|
||||
|
|
|
@ -9,18 +9,18 @@ public class ReadingListItemDto
|
|||
public int Order { get; init; }
|
||||
public int ChapterId { get; init; }
|
||||
public int SeriesId { get; init; }
|
||||
public string SeriesName { get; set; }
|
||||
public string? SeriesName { get; set; }
|
||||
public MangaFormat SeriesFormat { get; set; }
|
||||
public int PagesRead { get; set; }
|
||||
public int PagesTotal { get; set; }
|
||||
public string ChapterNumber { get; set; }
|
||||
public string ChapterTitleName { get; set; }
|
||||
public string VolumeNumber { get; set; }
|
||||
public string? ChapterNumber { get; set; }
|
||||
public string? VolumeNumber { get; set; }
|
||||
public string? ChapterTitleName { get; set; }
|
||||
public int VolumeId { get; set; }
|
||||
public int LibraryId { get; set; }
|
||||
public string? Title { get; set; }
|
||||
public LibraryType LibraryType { get; set; }
|
||||
public string LibraryName { get; set; }
|
||||
public string Title { get; set; }
|
||||
public string? LibraryName { get; set; }
|
||||
/// <summary>
|
||||
/// Release Date from Chapter
|
||||
/// </summary>
|
||||
|
|
|
@ -6,6 +6,6 @@ public class UpdateReadingListByMultipleDto
|
|||
{
|
||||
public int SeriesId { get; init; }
|
||||
public int ReadingListId { get; init; }
|
||||
public IReadOnlyList<int> VolumeIds { get; init; }
|
||||
public IReadOnlyList<int> ChapterIds { get; init; }
|
||||
public IReadOnlyList<int> VolumeIds { get; init; } = default!;
|
||||
public IReadOnlyList<int> ChapterIds { get; init; } = default!;
|
||||
}
|
||||
|
|
|
@ -5,5 +5,5 @@ namespace API.DTOs.ReadingLists;
|
|||
public class UpdateReadingListByMultipleSeriesDto
|
||||
{
|
||||
public int ReadingListId { get; init; }
|
||||
public IReadOnlyList<int> SeriesIds { get; init; }
|
||||
public IReadOnlyList<int> SeriesIds { get; init; } = default!;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace API.DTOs.ReadingLists;
|
||||
|
||||
|
|
|
@ -8,14 +8,14 @@ namespace API.DTOs;
|
|||
/// </summary>
|
||||
public class RecentlyAddedItemDto
|
||||
{
|
||||
public string SeriesName { get; set; }
|
||||
public string SeriesName { get; set; } = default!;
|
||||
public int SeriesId { get; set; }
|
||||
public int LibraryId { get; set; }
|
||||
public LibraryType LibraryType { get; set; }
|
||||
/// <summary>
|
||||
/// This will automatically map to Volume X, Chapter Y, etc.
|
||||
/// </summary>
|
||||
public string Title { get; set; }
|
||||
public string Title { get; set; } = default!;
|
||||
public DateTime Created { get; set; }
|
||||
/// <summary>
|
||||
/// Chapter Id if this is a chapter. Not guaranteed to be set.
|
||||
|
|
|
@ -5,12 +5,12 @@ namespace API.DTOs;
|
|||
public class RegisterDto
|
||||
{
|
||||
[Required]
|
||||
public string Username { get; init; }
|
||||
public string Username { get; init; } = default!;
|
||||
/// <summary>
|
||||
/// An email to register with. Optional. Provides Forgot Password functionality
|
||||
/// </summary>
|
||||
public string Email { get; init; }
|
||||
public string Email { get; init; } = default!;
|
||||
[Required]
|
||||
[StringLength(32, MinimumLength = 6)]
|
||||
public string Password { get; set; }
|
||||
public string Password { get; set; } = default!;
|
||||
}
|
||||
|
|
|
@ -8,10 +8,10 @@ public class ScanFolderDto
|
|||
/// <summary>
|
||||
/// Api key for a user with Admin permissions
|
||||
/// </summary>
|
||||
public string ApiKey { get; set; }
|
||||
public string ApiKey { get; set; } = default!;
|
||||
/// <summary>
|
||||
/// Folder Path to Scan
|
||||
/// </summary>
|
||||
/// <remarks>JSON cannot accept /, so you may need to use // escaping on paths</remarks>
|
||||
public string FolderPath { get; set; }
|
||||
public string FolderPath { get; set; } = default!;
|
||||
}
|
||||
|
|
|
@ -5,13 +5,13 @@ namespace API.DTOs.Search;
|
|||
public class SearchResultDto
|
||||
{
|
||||
public int SeriesId { get; init; }
|
||||
public string Name { get; init; }
|
||||
public string OriginalName { get; init; }
|
||||
public string SortName { get; init; }
|
||||
public string LocalizedName { get; init; }
|
||||
public string Name { get; init; } = default!;
|
||||
public string OriginalName { get; init; } = default!;
|
||||
public string SortName { get; init; } = default!;
|
||||
public string LocalizedName { get; init; } = default!;
|
||||
public MangaFormat Format { get; init; }
|
||||
|
||||
// Grouping information
|
||||
public string LibraryName { get; set; }
|
||||
public string LibraryName { get; set; } = default!;
|
||||
public int LibraryId { get; set; }
|
||||
}
|
||||
|
|
|
@ -10,15 +10,15 @@ namespace API.DTOs.Search;
|
|||
/// </summary>
|
||||
public class SearchResultGroupDto
|
||||
{
|
||||
public IEnumerable<LibraryDto> Libraries { get; set; }
|
||||
public IEnumerable<SearchResultDto> Series { get; set; }
|
||||
public IEnumerable<CollectionTagDto> Collections { get; set; }
|
||||
public IEnumerable<ReadingListDto> ReadingLists { get; set; }
|
||||
public IEnumerable<PersonDto> Persons { get; set; }
|
||||
public IEnumerable<GenreTagDto> Genres { get; set; }
|
||||
public IEnumerable<TagDto> Tags { get; set; }
|
||||
public IEnumerable<MangaFileDto> Files { get; set; }
|
||||
public IEnumerable<ChapterDto> Chapters { get; set; }
|
||||
public IEnumerable<LibraryDto> Libraries { get; set; } = default!;
|
||||
public IEnumerable<SearchResultDto> Series { get; set; } = default!;
|
||||
public IEnumerable<CollectionTagDto> Collections { get; set; } = default!;
|
||||
public IEnumerable<ReadingListDto> ReadingLists { get; set; } = default!;
|
||||
public IEnumerable<PersonDto> Persons { get; set; } = default!;
|
||||
public IEnumerable<GenreTagDto> Genres { get; set; } = default!;
|
||||
public IEnumerable<TagDto> Tags { get; set; } = default!;
|
||||
public IEnumerable<MangaFileDto> Files { get; set; } = default!;
|
||||
public IEnumerable<ChapterDto> Chapters { get; set; } = default!;
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -2,5 +2,5 @@
|
|||
|
||||
public class SeriesByIdsDto
|
||||
{
|
||||
public int[] SeriesIds { get; init; }
|
||||
public int[] SeriesIds { get; init; } = default!;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
using System.Collections.Generic;
|
||||
using API.Entities.Enums;
|
||||
|
||||
namespace API.DTOs.SeriesDetail;
|
||||
|
||||
|
@ -10,17 +9,17 @@ public class RelatedSeriesDto
|
|||
/// </summary>
|
||||
public int SourceSeriesId { get; set; }
|
||||
|
||||
public IEnumerable<SeriesDto> Sequels { get; set; }
|
||||
public IEnumerable<SeriesDto> Prequels { get; set; }
|
||||
public IEnumerable<SeriesDto> SpinOffs { get; set; }
|
||||
public IEnumerable<SeriesDto> Adaptations { get; set; }
|
||||
public IEnumerable<SeriesDto> SideStories { get; set; }
|
||||
public IEnumerable<SeriesDto> Characters { get; set; }
|
||||
public IEnumerable<SeriesDto> Contains { get; set; }
|
||||
public IEnumerable<SeriesDto> Others { get; set; }
|
||||
public IEnumerable<SeriesDto> AlternativeSettings { get; set; }
|
||||
public IEnumerable<SeriesDto> AlternativeVersions { get; set; }
|
||||
public IEnumerable<SeriesDto> Doujinshis { get; set; }
|
||||
public IEnumerable<SeriesDto> Parent { get; set; }
|
||||
public IEnumerable<SeriesDto> Editions { get; set; }
|
||||
public IEnumerable<SeriesDto> Sequels { get; set; } = default!;
|
||||
public IEnumerable<SeriesDto> Prequels { get; set; } = default!;
|
||||
public IEnumerable<SeriesDto> SpinOffs { get; set; } = default!;
|
||||
public IEnumerable<SeriesDto> Adaptations { get; set; } = default!;
|
||||
public IEnumerable<SeriesDto> SideStories { get; set; } = default!;
|
||||
public IEnumerable<SeriesDto> Characters { get; set; } = default!;
|
||||
public IEnumerable<SeriesDto> Contains { get; set; } = default!;
|
||||
public IEnumerable<SeriesDto> Others { get; set; } = default!;
|
||||
public IEnumerable<SeriesDto> AlternativeSettings { get; set; } = default!;
|
||||
public IEnumerable<SeriesDto> AlternativeVersions { get; set; } = default!;
|
||||
public IEnumerable<SeriesDto> Doujinshis { get; set; } = default!;
|
||||
public IEnumerable<SeriesDto> Parent { get; set; } = default!;
|
||||
public IEnumerable<SeriesDto> Editions { get; set; } = default!;
|
||||
}
|
||||
|
|
|
@ -11,19 +11,19 @@ public class SeriesDetailDto
|
|||
/// <summary>
|
||||
/// Specials for the Series. These will have their title and range cleaned to remove the special marker and prepare
|
||||
/// </summary>
|
||||
public IEnumerable<ChapterDto> Specials { get; set; }
|
||||
public IEnumerable<ChapterDto> Specials { get; set; } = default!;
|
||||
/// <summary>
|
||||
/// All Chapters, excluding Specials and single chapters (0 chapter) for a volume
|
||||
/// </summary>
|
||||
public IEnumerable<ChapterDto> Chapters { get; set; }
|
||||
public IEnumerable<ChapterDto> Chapters { get; set; } = default!;
|
||||
/// <summary>
|
||||
/// Just the Volumes for the Series (Excludes Volume 0)
|
||||
/// </summary>
|
||||
public IEnumerable<VolumeDto> Volumes { get; set; }
|
||||
public IEnumerable<VolumeDto> Volumes { get; set; } = default!;
|
||||
/// <summary>
|
||||
/// These are chapters that are in Volume 0 and should be read AFTER the volumes
|
||||
/// </summary>
|
||||
public IEnumerable<ChapterDto> StorylineChapters { get; set; }
|
||||
public IEnumerable<ChapterDto> StorylineChapters { get; set; } = default!;
|
||||
/// <summary>
|
||||
/// How many chapters are unread
|
||||
/// </summary>
|
||||
|
|
|
@ -5,16 +5,16 @@ namespace API.DTOs.SeriesDetail;
|
|||
public class UpdateRelatedSeriesDto
|
||||
{
|
||||
public int SeriesId { get; set; }
|
||||
public IList<int> Adaptations { get; set; }
|
||||
public IList<int> Characters { get; set; }
|
||||
public IList<int> Contains { get; set; }
|
||||
public IList<int> Others { get; set; }
|
||||
public IList<int> Prequels { get; set; }
|
||||
public IList<int> Sequels { get; set; }
|
||||
public IList<int> SideStories { get; set; }
|
||||
public IList<int> SpinOffs { get; set; }
|
||||
public IList<int> AlternativeSettings { get; set; }
|
||||
public IList<int> AlternativeVersions { get; set; }
|
||||
public IList<int> Doujinshis { get; set; }
|
||||
public IList<int> Editions { get; set; }
|
||||
public IList<int> Adaptations { get; set; } = default!;
|
||||
public IList<int> Characters { get; set; } = default!;
|
||||
public IList<int> Contains { get; set; } = default!;
|
||||
public IList<int> Others { get; set; } = default!;
|
||||
public IList<int> Prequels { get; set; } = default!;
|
||||
public IList<int> Sequels { get; set; } = default!;
|
||||
public IList<int> SideStories { get; set; } = default!;
|
||||
public IList<int> SpinOffs { get; set; } = default!;
|
||||
public IList<int> AlternativeSettings { get; set; } = default!;
|
||||
public IList<int> AlternativeVersions { get; set; } = default!;
|
||||
public IList<int> Doujinshis { get; set; } = default!;
|
||||
public IList<int> Editions { get; set; } = default!;
|
||||
}
|
||||
|
|
|
@ -7,11 +7,11 @@ namespace API.DTOs;
|
|||
public class SeriesDto : IHasReadTimeEstimate
|
||||
{
|
||||
public int Id { get; init; }
|
||||
public string Name { get; init; }
|
||||
public string OriginalName { get; init; }
|
||||
public string LocalizedName { get; init; }
|
||||
public string SortName { get; init; }
|
||||
public string Summary { get; init; }
|
||||
public string? Name { get; init; }
|
||||
public string? OriginalName { get; init; }
|
||||
public string? LocalizedName { get; init; }
|
||||
public string? SortName { get; init; }
|
||||
public string? Summary { get; init; }
|
||||
public int Pages { get; init; }
|
||||
public bool CoverImageLocked { get; set; }
|
||||
/// <summary>
|
||||
|
@ -33,7 +33,7 @@ public class SeriesDto : IHasReadTimeEstimate
|
|||
/// <summary>
|
||||
/// Review from logged in user. Calculated at API-time.
|
||||
/// </summary>
|
||||
public string UserReview { get; set; }
|
||||
public string? UserReview { get; set; }
|
||||
public MangaFormat Format { get; set; }
|
||||
|
||||
public DateTime Created { get; set; }
|
||||
|
@ -47,7 +47,7 @@ public class SeriesDto : IHasReadTimeEstimate
|
|||
public long WordCount { get; set; }
|
||||
|
||||
public int LibraryId { get; set; }
|
||||
public string LibraryName { get; set; }
|
||||
public string LibraryName { get; set; } = default!;
|
||||
/// <inheritdoc cref="IHasReadTimeEstimate.MinHoursToRead"/>
|
||||
public int MinHoursToRead { get; set; }
|
||||
/// <inheritdoc cref="IHasReadTimeEstimate.MaxHoursToRead"/>
|
||||
|
@ -57,7 +57,7 @@ public class SeriesDto : IHasReadTimeEstimate
|
|||
/// <summary>
|
||||
/// The highest level folder for this Series
|
||||
/// </summary>
|
||||
public string FolderPath { get; set; }
|
||||
public string FolderPath { get; set; } = default!;
|
||||
/// <summary>
|
||||
/// The last time the folder for this series was scanned
|
||||
/// </summary>
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using API.DTOs.CollectionTags;
|
||||
using API.DTOs.Metadata;
|
||||
using API.Entities.Enums;
|
||||
|
@ -10,18 +9,21 @@ public class SeriesMetadataDto
|
|||
{
|
||||
public int Id { get; set; }
|
||||
public string Summary { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Collections the Series belongs to
|
||||
/// </summary>
|
||||
public ICollection<CollectionTagDto> CollectionTags { get; set; }
|
||||
public ICollection<CollectionTagDto> CollectionTags { get; set; } = new List<CollectionTagDto>();
|
||||
|
||||
/// <summary>
|
||||
/// Genres for the Series
|
||||
/// </summary>
|
||||
public ICollection<GenreTagDto> Genres { get; set; }
|
||||
public ICollection<GenreTagDto> Genres { get; set; } = new List<GenreTagDto>();
|
||||
|
||||
/// <summary>
|
||||
/// Collection of all Tags from underlying chapters for a Series
|
||||
/// </summary>
|
||||
public ICollection<TagDto> Tags { get; set; }
|
||||
public ICollection<TagDto> Tags { get; set; } = new List<TagDto>();
|
||||
public ICollection<PersonDto> Writers { get; set; } = new List<PersonDto>();
|
||||
public ICollection<PersonDto> CoverArtists { get; set; } = new List<PersonDto>();
|
||||
public ICollection<PersonDto> Publishers { get; set; } = new List<PersonDto>();
|
||||
|
|
|
@ -1,18 +1,16 @@
|
|||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using API.Services;
|
||||
using API.Services;
|
||||
|
||||
namespace API.DTOs.Settings;
|
||||
|
||||
public class ServerSettingDto
|
||||
{
|
||||
public string CacheDirectory { get; set; }
|
||||
public string TaskScan { get; set; }
|
||||
public string CacheDirectory { get; set; } = default!;
|
||||
public string TaskScan { get; set; } = default!;
|
||||
/// <summary>
|
||||
/// Logging level for server. Managed in appsettings.json.
|
||||
/// </summary>
|
||||
public string LoggingLevel { get; set; }
|
||||
public string TaskBackup { get; set; }
|
||||
public string LoggingLevel { get; set; } = default!;
|
||||
public string TaskBackup { get; set; } = default!;
|
||||
/// <summary>
|
||||
/// Port the server listens on. Managed in appsettings.json.
|
||||
/// </summary>
|
||||
|
@ -32,22 +30,22 @@ public class ServerSettingDto
|
|||
/// <summary>
|
||||
/// Base Url for the kavita. Requires restart to take effect.
|
||||
/// </summary>
|
||||
public string BaseUrl { get; set; }
|
||||
public string BaseUrl { get; set; } = default!;
|
||||
/// <summary>
|
||||
/// Where Bookmarks are stored.
|
||||
/// </summary>
|
||||
/// <remarks>If null or empty string, will default back to default install setting aka <see cref="DirectoryService.BookmarkDirectory"/></remarks>
|
||||
public string BookmarksDirectory { get; set; }
|
||||
public string BookmarksDirectory { get; set; } = default!;
|
||||
/// <summary>
|
||||
/// Email service to use for the invite user flow, forgot password, etc.
|
||||
/// </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; }
|
||||
public string EmailServiceUrl { get; set; } = default!;
|
||||
public string InstallVersion { get; set; } = default!;
|
||||
/// <summary>
|
||||
/// Represents a unique Id to this Kavita installation. Only used in Stats to identify unique installs.
|
||||
/// </summary>
|
||||
public string InstallId { get; set; }
|
||||
public string InstallId { get; set; } = default!;
|
||||
/// <summary>
|
||||
/// If the server should save bookmarks as WebP encoding
|
||||
/// </summary>
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue