mirror of
https://github.com/Kareadita/Kavita.git
synced 2025-07-09 03:04:19 -04:00
Polish Round 2 (#2845)
This commit is contained in:
parent
c47fec4648
commit
5195f08c2f
31
API.Tests/Services/TokenServiceTests.cs
Normal file
31
API.Tests/Services/TokenServiceTests.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
@ -30,9 +30,9 @@ public enum LibraryType
|
|||||||
[Description("Light Novel")]
|
[Description("Light Novel")]
|
||||||
LightNovel = 4,
|
LightNovel = 4,
|
||||||
/// <summary>
|
/// <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>
|
/// </summary>
|
||||||
[Description("Comic (ComicVine)")]
|
[Description("Comic (Comic Vine)")]
|
||||||
ComicVine = 5,
|
ComicVine = 5,
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -108,7 +108,7 @@ public class DeviceService : IDeviceService
|
|||||||
public async Task<bool> SendTo(IReadOnlyList<int> chapterIds, int deviceId)
|
public async Task<bool> SendTo(IReadOnlyList<int> chapterIds, int deviceId)
|
||||||
{
|
{
|
||||||
var settings = await _unitOfWork.SettingsRepository.GetSettingsDtoAsync();
|
var settings = await _unitOfWork.SettingsRepository.GetSettingsDtoAsync();
|
||||||
if (!settings.IsEmailSetup())
|
if (!settings.IsEmailSetupForSendToDevice())
|
||||||
throw new KavitaException("send-to-kavita-email");
|
throw new KavitaException("send-to-kavita-email");
|
||||||
|
|
||||||
var device = await _unitOfWork.DeviceRepository.GetDeviceById(deviceId);
|
var device = await _unitOfWork.DeviceRepository.GetDeviceById(deviceId);
|
||||||
@ -123,9 +123,16 @@ public class DeviceService : IDeviceService
|
|||||||
throw new KavitaException("send-to-size-limit");
|
throw new KavitaException("send-to-size-limit");
|
||||||
|
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
device.UpdateLastUsed();
|
device.UpdateLastUsed();
|
||||||
_unitOfWork.DeviceRepository.Update(device);
|
_unitOfWork.DeviceRepository.Update(device);
|
||||||
await _unitOfWork.CommitAsync();
|
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()
|
var success = await _emailService.SendFilesToEmail(new SendToDto()
|
||||||
{
|
{
|
||||||
|
@ -126,7 +126,7 @@ public class ScrobblingService : IScrobblingService
|
|||||||
var users = await _unitOfWork.UserRepository.GetAllUsersAsync();
|
var users = await _unitOfWork.UserRepository.GetAllUsersAsync();
|
||||||
foreach (var user in users)
|
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);
|
_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,
|
await _eventHub.SendMessageToAsync(MessageFactory.ScrobblingKeyExpired,
|
||||||
MessageFactory.ScrobblingKeyExpiredEvent(ScrobbleProvider.AniList), user.Id);
|
MessageFactory.ScrobblingKeyExpiredEvent(ScrobbleProvider.AniList), user.Id);
|
||||||
@ -151,7 +151,7 @@ public class ScrobblingService : IScrobblingService
|
|||||||
private async Task<bool> HasTokenExpired(string token, ScrobbleProvider provider)
|
private async Task<bool> HasTokenExpired(string token, ScrobbleProvider provider)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(token) ||
|
if (string.IsNullOrEmpty(token) ||
|
||||||
!_tokenService.HasTokenExpired(token)) return false;
|
!TokenService.HasTokenExpired(token)) return false;
|
||||||
|
|
||||||
var license = await _unitOfWork.SettingsRepository.GetSettingAsync(ServerSettingKey.LicenseKey);
|
var license = await _unitOfWork.SettingsRepository.GetSettingAsync(ServerSettingKey.LicenseKey);
|
||||||
if (string.IsNullOrEmpty(license.Value)) return true;
|
if (string.IsNullOrEmpty(license.Value)) return true;
|
||||||
@ -778,7 +778,7 @@ public class ScrobblingService : IScrobblingService
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_tokenService.HasTokenExpired(evt.AppUser.AniListAccessToken))
|
if (TokenService.HasTokenExpired(evt.AppUser.AniListAccessToken))
|
||||||
{
|
{
|
||||||
_unitOfWork.ScrobbleRepository.Attach(new ScrobbleError()
|
_unitOfWork.ScrobbleRepository.Attach(new ScrobbleError()
|
||||||
{
|
{
|
||||||
|
@ -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
|
// 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
|
// Report that no series exist in the reading list
|
||||||
importSummary.Results.Add(new CblBookResult
|
importSummary.Results.Add(new CblBookResult
|
||||||
|
@ -24,8 +24,7 @@ public interface ITokenService
|
|||||||
Task<string> CreateToken(AppUser user);
|
Task<string> CreateToken(AppUser user);
|
||||||
Task<TokenRequestDto?> ValidateRefreshToken(TokenRequestDto request);
|
Task<TokenRequestDto?> ValidateRefreshToken(TokenRequestDto request);
|
||||||
Task<string> CreateRefreshToken(AppUser user);
|
Task<string> CreateRefreshToken(AppUser user);
|
||||||
Task<string> GetJwtFromUser(AppUser user);
|
Task<string?> GetJwtFromUser(AppUser user);
|
||||||
bool HasTokenExpired(string token);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -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 userClaims = await _userManager.GetClaimsAsync(user);
|
||||||
var jwtClaim = userClaims.FirstOrDefault(claim => claim.Type == "jwt");
|
var jwtClaim = userClaims.FirstOrDefault(claim => claim.Type == "jwt");
|
||||||
return jwtClaim?.Value;
|
return jwtClaim?.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool HasTokenExpired(string? token)
|
public static bool HasTokenExpired(string? token)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(token)) return true;
|
if (string.IsNullOrEmpty(token)) return true;
|
||||||
|
|
||||||
var tokenHandler = new JwtSecurityTokenHandler();
|
var tokenHandler = new JwtSecurityTokenHandler();
|
||||||
var tokenContent = tokenHandler.ReadJwtToken(token);
|
var tokenContent = tokenHandler.ReadJwtToken(token);
|
||||||
return tokenContent.ValidTo >= DateTime.UtcNow;
|
var validToUtc = tokenContent.ValidTo.ToUniversalTime();
|
||||||
|
|
||||||
|
return validToUtc < DateTime.UtcNow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -70,6 +70,7 @@ export class ManageEmailSettingsComponent implements OnInit {
|
|||||||
this.settingsForm.get('port')?.setValue(587);
|
this.settingsForm.get('port')?.setValue(587);
|
||||||
this.settingsForm.get('sizeLimit')?.setValue(26214400);
|
this.settingsForm.get('sizeLimit')?.setValue(26214400);
|
||||||
this.settingsForm.get('enableSsl')?.setValue(true);
|
this.settingsForm.get('enableSsl')?.setValue(true);
|
||||||
|
this.settingsForm.markAsDirty();
|
||||||
this.cdRef.markForCheck();
|
this.cdRef.markForCheck();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,6 +79,7 @@ export class ManageEmailSettingsComponent implements OnInit {
|
|||||||
this.settingsForm.get('port')?.setValue(587 );
|
this.settingsForm.get('port')?.setValue(587 );
|
||||||
this.settingsForm.get('sizeLimit')?.setValue(1048576);
|
this.settingsForm.get('sizeLimit')?.setValue(1048576);
|
||||||
this.settingsForm.get('enableSsl')?.setValue(true);
|
this.settingsForm.get('enableSsl')?.setValue(true);
|
||||||
|
this.settingsForm.markAsDirty();
|
||||||
this.cdRef.markForCheck();
|
this.cdRef.markForCheck();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,6 +26,7 @@
|
|||||||
|
|
||||||
<virtual-scroller [ngClass]="{'empty': items.length === 0 && !isLoading}" #scroll [items]="items" [bufferAmount]="bufferAmount" [parentScroll]="parentScroll">
|
<virtual-scroller [ngClass]="{'empty': items.length === 0 && !isLoading}" #scroll [items]="items" [bufferAmount]="bufferAmount" [parentScroll]="parentScroll">
|
||||||
<div class="grid row g-0" #container>
|
<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"
|
<div class="card col-auto mt-2 mb-2"
|
||||||
(click)="tryToSaveJumpKey(item)"
|
(click)="tryToSaveJumpKey(item)"
|
||||||
*ngFor="let item of scroll.viewPortItems; trackBy:trackByIdentity; index as i" id="jumpbar-index--{{i}}"
|
*ngFor="let item of scroll.viewPortItems; trackBy:trackByIdentity; index as i" id="jumpbar-index--{{i}}"
|
||||||
|
@ -76,7 +76,7 @@ export class AllCollectionsComponent implements OnInit {
|
|||||||
jumpbarKeys: Array<JumpKey> = [];
|
jumpbarKeys: Array<JumpKey> = [];
|
||||||
isAdmin$: Observable<boolean> = of(false);
|
isAdmin$: Observable<boolean> = of(false);
|
||||||
filterOpen: EventEmitter<boolean> = new EventEmitter();
|
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;
|
user!: User;
|
||||||
|
|
||||||
@HostListener('document:keydown.shift', ['$event'])
|
@HostListener('document:keydown.shift', ['$event'])
|
||||||
@ -146,13 +146,14 @@ export class AllCollectionsComponent implements OnInit {
|
|||||||
|
|
||||||
switch (action.action) {
|
switch (action.action) {
|
||||||
case Action.Promote:
|
case Action.Promote:
|
||||||
this.collectionService.promoteMultipleCollections([collectionTag.id], true).subscribe();
|
this.collectionService.promoteMultipleCollections([collectionTag.id], true).subscribe(_ => this.loadPage());
|
||||||
break;
|
break;
|
||||||
case Action.UnPromote:
|
case Action.UnPromote:
|
||||||
this.collectionService.promoteMultipleCollections([collectionTag.id], false).subscribe();
|
this.collectionService.promoteMultipleCollections([collectionTag.id], false).subscribe(_ => this.loadPage());
|
||||||
break;
|
break;
|
||||||
case(Action.Delete):
|
case(Action.Delete):
|
||||||
this.collectionService.deleteTag(collectionTag.id).subscribe(res => {
|
this.collectionService.deleteTag(collectionTag.id).subscribe(res => {
|
||||||
|
this.loadPage();
|
||||||
this.toastr.success(res);
|
this.toastr.success(res);
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
@ -114,9 +114,6 @@ export class SeriesMetadataDetailComponent implements OnChanges, OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ngOnChanges(changes: SimpleChanges): void {
|
ngOnChanges(changes: SimpleChanges): void {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
this.hasExtendedProperties = this.seriesMetadata.colorists.length > 0 ||
|
this.hasExtendedProperties = this.seriesMetadata.colorists.length > 0 ||
|
||||||
this.seriesMetadata.editors.length > 0 ||
|
this.seriesMetadata.editors.length > 0 ||
|
||||||
this.seriesMetadata.coverArtists.length > 0 ||
|
this.seriesMetadata.coverArtists.length > 0 ||
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
"name": "GPL-3.0",
|
"name": "GPL-3.0",
|
||||||
"url": "https://github.com/Kareadita/Kavita/blob/develop/LICENSE"
|
"url": "https://github.com/Kareadita/Kavita/blob/develop/LICENSE"
|
||||||
},
|
},
|
||||||
"version": "0.7.14.14"
|
"version": "0.7.14.15"
|
||||||
},
|
},
|
||||||
"servers": [
|
"servers": [
|
||||||
{
|
{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user