Polish Round 2 (#2845)

This commit is contained in:
Joe Milazzo 2024-04-11 17:01:34 -05:00 committed by GitHub
parent c47fec4648
commit 5195f08c2f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 63 additions and 22 deletions

View File

@ -0,0 +1,31 @@
using API.Services;
using Xunit;
namespace API.Tests.Services;
public class TokenServiceTests
{
[Fact]
public void HasTokenExpired_OldToken()
{
// ValidTo: 1/1/1990
var result = TokenService.HasTokenExpired("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjYzMzgzMDM5OX0.KM_cUKSaCJL3ts0Qim3ZHUeJT7yf-wKoLdKb0rx0VbU");
Assert.True(result);
}
[Fact]
public void HasTokenExpired_ValidInFuture()
{
// ValidTo: 4/11/2200
var result = TokenService.HasTokenExpired("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjcyNjg0ODYzOTl9.nZrN5USbUmMYDKwkPoMtEAhTeYTeaikgAeSzDPj5kZQ");
Assert.False(result);
}
[Fact]
public void HasTokenExpired_NoToken()
{
var result = TokenService.HasTokenExpired("");
Assert.True(result);
}
}

View File

@ -30,9 +30,9 @@ public enum LibraryType
[Description("Light Novel")]
LightNovel = 4,
/// <summary>
/// Uses Comic regex for filename parsing, uses ComicVine type of Parsing. Will replace Comic type in future
/// Uses Comic regex for filename parsing, uses Comic Vine type of Parsing. Will replace Comic type in future
/// </summary>
[Description("Comic (ComicVine)")]
[Description("Comic (Comic Vine)")]
ComicVine = 5,
}

View File

@ -108,7 +108,7 @@ public class DeviceService : IDeviceService
public async Task<bool> SendTo(IReadOnlyList<int> chapterIds, int deviceId)
{
var settings = await _unitOfWork.SettingsRepository.GetSettingsDtoAsync();
if (!settings.IsEmailSetup())
if (!settings.IsEmailSetupForSendToDevice())
throw new KavitaException("send-to-kavita-email");
var device = await _unitOfWork.DeviceRepository.GetDeviceById(deviceId);
@ -123,9 +123,16 @@ public class DeviceService : IDeviceService
throw new KavitaException("send-to-size-limit");
device.UpdateLastUsed();
_unitOfWork.DeviceRepository.Update(device);
await _unitOfWork.CommitAsync();
try
{
device.UpdateLastUsed();
_unitOfWork.DeviceRepository.Update(device);
await _unitOfWork.CommitAsync();
}
catch (Exception ex)
{
_logger.LogError(ex, "There was an issue updating device last used time");
}
var success = await _emailService.SendFilesToEmail(new SendToDto()
{

View File

@ -126,7 +126,7 @@ public class ScrobblingService : IScrobblingService
var users = await _unitOfWork.UserRepository.GetAllUsersAsync();
foreach (var user in users)
{
if (string.IsNullOrEmpty(user.AniListAccessToken) || !_tokenService.HasTokenExpired(user.AniListAccessToken)) continue;
if (string.IsNullOrEmpty(user.AniListAccessToken) || !TokenService.HasTokenExpired(user.AniListAccessToken)) continue;
_logger.LogInformation("User {UserName}'s AniList token has expired! They need to regenerate it for scrobbling to work", user.UserName);
await _eventHub.SendMessageToAsync(MessageFactory.ScrobblingKeyExpired,
MessageFactory.ScrobblingKeyExpiredEvent(ScrobbleProvider.AniList), user.Id);
@ -151,7 +151,7 @@ public class ScrobblingService : IScrobblingService
private async Task<bool> HasTokenExpired(string token, ScrobbleProvider provider)
{
if (string.IsNullOrEmpty(token) ||
!_tokenService.HasTokenExpired(token)) return false;
!TokenService.HasTokenExpired(token)) return false;
var license = await _unitOfWork.SettingsRepository.GetSettingAsync(ServerSettingKey.LicenseKey);
if (string.IsNullOrEmpty(license.Value)) return true;
@ -778,7 +778,7 @@ public class ScrobblingService : IScrobblingService
continue;
}
if (_tokenService.HasTokenExpired(evt.AppUser.AniListAccessToken))
if (TokenService.HasTokenExpired(evt.AppUser.AniListAccessToken))
{
_unitOfWork.ScrobbleRepository.Attach(new ScrobbleError()
{

View File

@ -561,7 +561,7 @@ public class ReadingListService : IReadingListService
// How can we match properly with ComicVine library when year is part of the series unless we do this in 2 passes and see which has a better match
if (!userSeries.Any())
if (userSeries.Count == 0)
{
// Report that no series exist in the reading list
importSummary.Results.Add(new CblBookResult

View File

@ -24,8 +24,7 @@ public interface ITokenService
Task<string> CreateToken(AppUser user);
Task<TokenRequestDto?> ValidateRefreshToken(TokenRequestDto request);
Task<string> CreateRefreshToken(AppUser user);
Task<string> GetJwtFromUser(AppUser user);
bool HasTokenExpired(string token);
Task<string?> GetJwtFromUser(AppUser user);
}
@ -135,18 +134,21 @@ public class TokenService : ITokenService
}
}
public async Task<string> GetJwtFromUser(AppUser user)
public async Task<string?> GetJwtFromUser(AppUser user)
{
var userClaims = await _userManager.GetClaimsAsync(user);
var jwtClaim = userClaims.FirstOrDefault(claim => claim.Type == "jwt");
return jwtClaim?.Value;
}
public bool HasTokenExpired(string? token)
public static bool HasTokenExpired(string? token)
{
if (string.IsNullOrEmpty(token)) return true;
var tokenHandler = new JwtSecurityTokenHandler();
var tokenContent = tokenHandler.ReadJwtToken(token);
return tokenContent.ValidTo >= DateTime.UtcNow;
var validToUtc = tokenContent.ValidTo.ToUniversalTime();
return validToUtc < DateTime.UtcNow;
}
}

View File

@ -70,6 +70,7 @@ export class ManageEmailSettingsComponent implements OnInit {
this.settingsForm.get('port')?.setValue(587);
this.settingsForm.get('sizeLimit')?.setValue(26214400);
this.settingsForm.get('enableSsl')?.setValue(true);
this.settingsForm.markAsDirty();
this.cdRef.markForCheck();
}
@ -78,6 +79,7 @@ export class ManageEmailSettingsComponent implements OnInit {
this.settingsForm.get('port')?.setValue(587 );
this.settingsForm.get('sizeLimit')?.setValue(1048576);
this.settingsForm.get('enableSsl')?.setValue(true);
this.settingsForm.markAsDirty();
this.cdRef.markForCheck();
}

View File

@ -26,6 +26,7 @@
<virtual-scroller [ngClass]="{'empty': items.length === 0 && !isLoading}" #scroll [items]="items" [bufferAmount]="bufferAmount" [parentScroll]="parentScroll">
<div class="grid row g-0" #container>
<!-- TODO: @for (item of scroll.viewPortItems; track trackByIdentity; let i = $index;) { works -->
<div class="card col-auto mt-2 mb-2"
(click)="tryToSaveJumpKey(item)"
*ngFor="let item of scroll.viewPortItems; trackBy:trackByIdentity; index as i" id="jumpbar-index--{{i}}"

View File

@ -76,7 +76,7 @@ export class AllCollectionsComponent implements OnInit {
jumpbarKeys: Array<JumpKey> = [];
isAdmin$: Observable<boolean> = of(false);
filterOpen: EventEmitter<boolean> = new EventEmitter();
trackByIdentity = (index: number, item: UserCollection) => `${item.id}_${item.title}`;
trackByIdentity = (index: number, item: UserCollection) => `${item.id}_${item.title}_${item.owner}_${item.promoted}`;
user!: User;
@HostListener('document:keydown.shift', ['$event'])
@ -146,13 +146,14 @@ export class AllCollectionsComponent implements OnInit {
switch (action.action) {
case Action.Promote:
this.collectionService.promoteMultipleCollections([collectionTag.id], true).subscribe();
this.collectionService.promoteMultipleCollections([collectionTag.id], true).subscribe(_ => this.loadPage());
break;
case Action.UnPromote:
this.collectionService.promoteMultipleCollections([collectionTag.id], false).subscribe();
this.collectionService.promoteMultipleCollections([collectionTag.id], false).subscribe(_ => this.loadPage());
break;
case(Action.Delete):
this.collectionService.deleteTag(collectionTag.id).subscribe(res => {
this.loadPage();
this.toastr.success(res);
});
break;

View File

@ -114,9 +114,6 @@ export class SeriesMetadataDetailComponent implements OnChanges, OnInit {
}
ngOnChanges(changes: SimpleChanges): void {
this.hasExtendedProperties = this.seriesMetadata.colorists.length > 0 ||
this.seriesMetadata.editors.length > 0 ||
this.seriesMetadata.coverArtists.length > 0 ||

View File

@ -7,7 +7,7 @@
"name": "GPL-3.0",
"url": "https://github.com/Kareadita/Kavita/blob/develop/LICENSE"
},
"version": "0.7.14.14"
"version": "0.7.14.15"
},
"servers": [
{