Polish before Release 2 (#3723)

Co-authored-by: Amelia <77553571+Fesaa@users.noreply.github.com>
This commit is contained in:
Joe Milazzo 2025-04-11 09:07:17 -06:00 committed by GitHub
parent 67d7d8467e
commit 4453482d93
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
21 changed files with 201 additions and 154 deletions

View file

@ -52,7 +52,7 @@
<ItemGroup>
<PackageReference Include="CsvHelper" Version="33.0.1" />
<PackageReference Include="MailKit" Version="4.11.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.3">
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.4">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
@ -70,11 +70,11 @@
<PackageReference Include="MarkdownDeep.NET.Core" Version="1.5.0.4" />
<PackageReference Include="Hangfire.AspNetCore" Version="1.8.18" />
<PackageReference Include="Microsoft.AspNetCore.SignalR" Version="1.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="9.0.3" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="9.0.3" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="9.0.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.3" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.3" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="9.0.4" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="9.0.4" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="9.0.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.4" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.4" />
<PackageReference Include="Microsoft.IO.RecyclableMemoryStream" Version="3.0.1" />
<PackageReference Include="MimeTypeMapOfficial" Version="1.0.17" />
<PackageReference Include="Nager.ArticleNumber" Version="1.0.7" />
@ -92,15 +92,15 @@
<PackageReference Include="Serilog.Sinks.SignalR.Core" Version="0.1.2" />
<PackageReference Include="SharpCompress" Version="0.39.0" />
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.7" />
<PackageReference Include="SonarAnalyzer.CSharp" Version="10.7.0.110445">
<PackageReference Include="SonarAnalyzer.CSharp" Version="10.8.0.113526">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Swashbuckle.AspNetCore" Version="8.0.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="8.1.1" />
<PackageReference Include="Swashbuckle.AspNetCore.Filters" Version="8.0.2" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.7.0" />
<PackageReference Include="System.IO.Abstractions" Version="22.0.12" />
<PackageReference Include="System.Drawing.Common" Version="9.0.3" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.8.0" />
<PackageReference Include="System.IO.Abstractions" Version="22.0.13" />
<PackageReference Include="System.Drawing.Common" Version="9.0.4" />
<PackageReference Include="VersOne.Epub" Version="3.3.3" />
<PackageReference Include="YamlDotNet" Version="16.3.0" />
</ItemGroup>

View file

@ -138,6 +138,12 @@ public class AccountController : BaseApiController
return BadRequest(usernameValidation);
}
// If Email is empty, default to the username
if (string.IsNullOrEmpty(registerDto.Email))
{
registerDto.Email = registerDto.Username;
}
var user = new AppUserBuilder(registerDto.Username, registerDto.Email,
await _unitOfWork.SiteThemeRepository.GetDefaultTheme()).Build();
@ -352,10 +358,11 @@ 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<InviteUserResponse>> UpdateEmail(UpdateEmailDto? dto)
{
var user = await _unitOfWork.UserRepository.GetUserByUsernameAsync(User.GetUsername());
if (user == null || User.IsInRole(PolicyConstants.ReadOnlyRole)) return Unauthorized(await _localizationService.Translate(User.GetUserId(), "permission-denied"));
if (user == null || User.IsInRole(PolicyConstants.ReadOnlyRole))
return Unauthorized(await _localizationService.Translate(User.GetUserId(), "permission-denied"));
if (dto == null || string.IsNullOrEmpty(dto.Email) || string.IsNullOrEmpty(dto.Password))
return BadRequest(await _localizationService.Translate(User.GetUserId(), "invalid-payload"));
@ -364,12 +371,13 @@ public class AccountController : BaseApiController
// Validate this user's password
if (! await _userManager.CheckPasswordAsync(user, dto.Password))
{
_logger.LogCritical("A user tried to change {UserName}'s email, but password didn't validate", user.UserName);
_logger.LogWarning("A user tried to change {UserName}'s email, but password didn't validate", user.UserName);
return BadRequest(await _localizationService.Translate(User.GetUserId(), "permission-denied"));
}
// Validate no other users exist with this email
if (user.Email!.Equals(dto.Email)) return BadRequest(await _localizationService.Translate(User.GetUserId(), "nothing-to-do"));
if (user.Email!.Equals(dto.Email))
return BadRequest(await _localizationService.Translate(User.GetUserId(), "nothing-to-do"));
// Check if email is used by another user
var existingUserEmail = await _unitOfWork.UserRepository.GetUserByEmailAsync(dto.Email);
@ -386,8 +394,10 @@ public class AccountController : BaseApiController
return BadRequest(await _localizationService.Translate(User.GetUserId(), "generate-token"));
}
var isValidEmailAddress = _emailService.IsValidEmail(user.Email);
var serverSettings = await _unitOfWork.SettingsRepository.GetSettingsDtoAsync();
var shouldEmailUser = serverSettings.IsEmailSetup() || !_emailService.IsValidEmail(user.Email);
var shouldEmailUser = serverSettings.IsEmailSetup() || !isValidEmailAddress;
user.EmailConfirmed = !shouldEmailUser;
user.ConfirmationToken = token;
await _userManager.UpdateAsync(user);
@ -401,7 +411,8 @@ public class AccountController : BaseApiController
return Ok(new InviteUserResponse
{
EmailLink = string.Empty,
EmailSent = false
EmailSent = false,
InvalidEmail = !isValidEmailAddress
});
}
@ -409,7 +420,7 @@ public class AccountController : BaseApiController
// Send a confirmation email
try
{
if (!_emailService.IsValidEmail(user.Email))
if (!isValidEmailAddress)
{
_logger.LogCritical("[Update Email]: User is trying to update their email, but their existing email ({Email}) isn't valid. No email will be send", user.Email);
return Ok(new InviteUserResponse
@ -441,7 +452,8 @@ public class AccountController : BaseApiController
return Ok(new InviteUserResponse
{
EmailLink = string.Empty,
EmailSent = true
EmailSent = true,
InvalidEmail = !isValidEmailAddress
});
}
catch (Exception ex)

View file

@ -1,6 +1,7 @@
using System.ComponentModel.DataAnnotations;
namespace API.DTOs;
#nullable enable
public class RegisterDto
{
@ -9,7 +10,7 @@ public class RegisterDto
/// <summary>
/// An email to register with. Optional. Provides Forgot Password functionality
/// </summary>
public string Email { get; init; } = default!;
public string? Email { get; set; } = default!;
[Required]
[StringLength(256, MinimumLength = 6)]
public string Password { get; set; } = default!;

View file

@ -510,7 +510,7 @@ public static class SeriesFilter
return queries.Aggregate((q1, q2) => q1.Intersect(q2));
case FilterComparison.IsEmpty:
return queryable.Where(s => s.Metadata.Tags == null || s.Metadata.Tags.Count == 0);
return queryable.Where(s => s.Metadata.Tags.Count == 0);
case FilterComparison.GreaterThan:
case FilterComparison.GreaterThanEqual:
case FilterComparison.LessThan:
@ -707,7 +707,7 @@ public static class SeriesFilter
return queries.Aggregate((q1, q2) => q1.Intersect(q2));
case FilterComparison.IsEmpty:
return queryable.Where(s => collectionSeries.All(c => c != s.Id));
return queryable.Where(s => s.Collections.Count == 0);
case FilterComparison.GreaterThan:
case FilterComparison.GreaterThanEqual:
case FilterComparison.LessThan: