Readme Change (#2190)

* Implemented the ability to login to the app by passing apiKey to the login. This is for an upcoming feature (but currently blocked by another story)

* Added a comment

* Ensure locales are sorted

* Added a new status badge that shows how many active installs we have via users that use stats.

* Bump all GA to latest versions

* Bumped dependencies

* Bumped backend notifications

* Updated ngx-pdf-reader to upcoming beta which fixes some PDFs taking time to load. PDF reader will use browser locale to load localization rather than Kavita locale for now.

* Downgraded pdf viewer as beta has lots of bugs.
This commit is contained in:
Joe Milazzo 2023-08-08 14:06:53 -05:00 committed by GitHub
parent d4e1e08b4f
commit 9f17f5daa7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 93 additions and 76 deletions

View File

@ -35,13 +35,13 @@ jobs:
distribution: 'zulu'
java-version: '17'
- uses: actions/upload-artifact@v2
- uses: actions/upload-artifact@v3
with:
name: csproj
path: Kavita.Common/Kavita.Common.csproj
- name: Cache SonarCloud packages
uses: actions/cache@v1
uses: actions/cache@v3
with:
path: ~\sonar\cache
key: ${{ runner.os }}-sonar
@ -49,7 +49,7 @@ jobs:
- name: Cache SonarCloud scanner
id: cache-sonar-scanner
uses: actions/cache@v1
uses: actions/cache@v3
with:
path: .\.sonar\scanner
key: ${{ runner.os }}-sonar-scanner
@ -107,7 +107,7 @@ jobs:
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/develop' }}
steps:
- name: Find Current Pull Request
uses: jwalton/gh-find-current-pr@v1.0.2
uses: jwalton/gh-find-current-pr@v1.3.2
id: findPr
with:
github-token: ${{ secrets.GITHUB_TOKEN }}

View File

@ -10,8 +10,8 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.13.6" />
<PackageReference Include="BenchmarkDotNet.Annotations" Version="0.13.6" />
<PackageReference Include="BenchmarkDotNet" Version="0.13.7" />
<PackageReference Include="BenchmarkDotNet.Annotations" Version="0.13.7" />
<PackageReference Include="NSubstitute" Version="5.0.0" />
</ItemGroup>

View File

@ -6,12 +6,12 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="7.0.9" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.3" />
<PackageReference Include="Moq" Version="4.18.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="7.0.10" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.0" />
<PackageReference Include="Moq" Version="4.20.1" />
<PackageReference Include="NSubstitute" Version="5.0.0" />
<PackageReference Include="System.IO.Abstractions.TestingHelpers" Version="19.2.29" />
<PackageReference Include="TestableIO.System.IO.Abstractions.Wrappers" Version="19.2.29" />
<PackageReference Include="System.IO.Abstractions.TestingHelpers" Version="19.2.51" />
<PackageReference Include="TestableIO.System.IO.Abstractions.Wrappers" Version="19.2.51" />
<PackageReference Include="xunit" Version="2.5.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.0">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>

View File

@ -60,23 +60,23 @@
<PackageReference Include="ExCSS" Version="4.2.1" />
<PackageReference Include="Flurl" Version="3.0.7" />
<PackageReference Include="Flurl.Http" Version="3.2.4" />
<PackageReference Include="Hangfire" Version="1.8.3" />
<PackageReference Include="Hangfire.AspNetCore" Version="1.8.3" />
<PackageReference Include="Hangfire" Version="1.8.4" />
<PackageReference Include="Hangfire.AspNetCore" Version="1.8.4" />
<PackageReference Include="Hangfire.InMemory" Version="0.5.1" />
<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.4" />
<PackageReference Include="HtmlAgilityPack" Version="1.11.50" />
<PackageReference Include="HtmlAgilityPack" Version="1.11.51" />
<PackageReference Include="MarkdownDeep.NET.Core" Version="1.5.0.4" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="7.0.9" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="7.0.9" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="7.0.9" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="7.0.10" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="7.0.10" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="7.0.10" />
<PackageReference Include="Microsoft.AspNetCore.SignalR" Version="1.1.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.9">
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.10">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.9" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.10" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" />
<PackageReference Include="Microsoft.IO.RecyclableMemoryStream" Version="2.3.2" />
<PackageReference Include="MimeTypeMapOfficial" Version="1.0.17" />
@ -95,14 +95,14 @@
<PackageReference Include="Serilog.Sinks.SignalR.Core" Version="0.1.2" />
<PackageReference Include="SharpCompress" Version="0.33.0" />
<PackageReference Include="SixLabors.ImageSharp" Version="3.0.1" />
<PackageReference Include="SonarAnalyzer.CSharp" Version="9.5.0.73987">
<PackageReference Include="SonarAnalyzer.CSharp" Version="9.7.0.75501">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
<PackageReference Include="Swashbuckle.AspNetCore.Filters" Version="7.0.8" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.32.0" />
<PackageReference Include="System.IO.Abstractions" Version="19.2.29" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.32.1" />
<PackageReference Include="System.IO.Abstractions" Version="19.2.51" />
<PackageReference Include="System.Drawing.Common" Version="7.0.0" />
<PackageReference Include="VersOne.Epub" Version="3.3.1" />
</ItemGroup>

View File

@ -177,26 +177,40 @@ public class AccountController : BaseApiController
[HttpPost("login")]
public async Task<ActionResult<UserDto>> Login(LoginDto loginDto)
{
var user = await _userManager.Users
.Include(u => u.UserPreferences)
.SingleOrDefaultAsync(x => x.NormalizedUserName == loginDto.Username.ToUpper());
AppUser? user;
if (!string.IsNullOrEmpty(loginDto.ApiKey))
{
user = await _userManager.Users
.Include(u => u.UserPreferences)
.SingleOrDefaultAsync(x => x.ApiKey == loginDto.ApiKey);
}
else
{
user = await _userManager.Users
.Include(u => u.UserPreferences)
.SingleOrDefaultAsync(x => x.NormalizedUserName == loginDto.Username.ToUpper());
}
if (user == null) return Unauthorized(await _localizationService.Get("en", "bad-credentials"));
var roles = await _userManager.GetRolesAsync(user);
if (!roles.Contains(PolicyConstants.LoginRole)) return Unauthorized(await _localizationService.Translate(user.Id, "disabled-account"));
var result = await _signInManager
.CheckPasswordSignInAsync(user, loginDto.Password, true);
if (result.IsLockedOut)
if (string.IsNullOrEmpty(loginDto.ApiKey))
{
await _userManager.UpdateSecurityStampAsync(user);
return Unauthorized(await _localizationService.Translate(user.Id, "locked-out"));
}
var result = await _signInManager
.CheckPasswordSignInAsync(user, loginDto.Password, true);
if (!result.Succeeded)
{
return Unauthorized(await _localizationService.Translate(user.Id, result.IsNotAllowed ? "confirm-email" : "bad-credentials"));
if (result.IsLockedOut)
{
await _userManager.UpdateSecurityStampAsync(user);
return Unauthorized(await _localizationService.Translate(user.Id, "locked-out"));
}
if (!result.Succeeded)
{
return Unauthorized(await _localizationService.Translate(user.Id, result.IsNotAllowed ? "confirm-email" : "bad-credentials"));
}
}
// Update LastActive on account

View File

@ -24,7 +24,9 @@ public class LocaleController : BaseApiController
{
Title = c.DisplayName,
IsoCode = c.IetfLanguageTag
}).Where(l => !string.IsNullOrEmpty(l.IsoCode));
})
.Where(l => !string.IsNullOrEmpty(l.IsoCode))
.OrderBy(d => d.Title);
return Ok(languages);
}
}

View File

@ -4,4 +4,8 @@ public class LoginDto
{
public string Username { get; init; } = default!;
public string Password { get; set; } = default!;
/// <summary>
/// If ApiKey is passed, will ignore username/password for validation
/// </summary>
public string? ApiKey { get; set; } = default!;
}

View File

@ -14,7 +14,7 @@
<PackageReference Include="Flurl.Http" Version="3.2.4" />
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.1" />
<PackageReference Include="SonarAnalyzer.CSharp" Version="9.5.0.73987">
<PackageReference Include="SonarAnalyzer.CSharp" Version="9.7.0.75501">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>

View File

@ -17,8 +17,10 @@ your reading collection with your friends and family!
<a href="https://hosted.weblate.org/engage/kavita/">
<img src="https://hosted.weblate.org/widgets/kavita/-/ui/svg-badge.svg" alt="Translation status" />
</a>
<img src="https://img.shields.io/endpoint?url=https://stats.kavitareader.com/api/ui/shield-badge"/>
</div>
## What Kavita Provides
- Serve up Manga/Webtoons/Comics (cbr, cbz, zip/rar/rar5, 7zip, raw images) and Books (epub, pdf)
- First class responsive readers that work great on any device (phone, tablet, desktop)

View File

@ -21,8 +21,8 @@
"@fortawesome/fontawesome-free": "^6.4.2",
"@iharbeck/ngx-virtual-scroller": "^16.0.0",
"@iplab/ngx-file-upload": "^16.0.1",
"@microsoft/signalr": "^7.0.9",
"@ng-bootstrap/ng-bootstrap": "^15.1.0",
"@microsoft/signalr": "^7.0.10",
"@ng-bootstrap/ng-bootstrap": "^15.1.1",
"@ngneat/transloco": "^5.0.6",
"@ngneat/transloco-locale": "^5.1.1",
"@ngneat/transloco-persist-lang": "^5.0.0",
@ -3141,9 +3141,9 @@
"dev": true
},
"node_modules/@microsoft/signalr": {
"version": "7.0.9",
"resolved": "https://registry.npmjs.org/@microsoft/signalr/-/signalr-7.0.9.tgz",
"integrity": "sha512-aGfBLAYTh+6ydYvLXV/jcocWr8KKmTOgWyl/mDx5Hzrii1aAfrn+bpBNzrl5sto5ehsHCdTIzTCuOCT3baIjOw==",
"version": "7.0.10",
"resolved": "https://registry.npmjs.org/@microsoft/signalr/-/signalr-7.0.10.tgz",
"integrity": "sha512-tOEn32i5EatAx4sZbzmLgcBc2VbKQmx+F4rI2/Ioq2MnBaYcFxbDzOoZgISIS4IR9H1ij/sKoU8zQOAFC8GJKg==",
"dependencies": {
"abort-controller": "^3.0.0",
"eventsource": "^2.0.2",
@ -3153,9 +3153,9 @@
}
},
"node_modules/@ng-bootstrap/ng-bootstrap": {
"version": "15.1.0",
"resolved": "https://registry.npmjs.org/@ng-bootstrap/ng-bootstrap/-/ng-bootstrap-15.1.0.tgz",
"integrity": "sha512-4Z/sXYcAq22D15jtlnZV7qztuSnlSlOgO7EVp6rJ8dyGi3CPzX9PqMfetoM6K5sKQTiSW8IfsbdXCWN7rnzxWQ==",
"version": "15.1.1",
"resolved": "https://registry.npmjs.org/@ng-bootstrap/ng-bootstrap/-/ng-bootstrap-15.1.1.tgz",
"integrity": "sha512-nZlIMMggtI3IHkGs0XPrUIUdpeEzQvfGV9M4I9IvCqiS2n4RwWoUvWK1ICo4csZqFNBDlCQx956gO6ZZUSL2mw==",
"dependencies": {
"tslib": "^2.3.0"
},

View File

@ -26,8 +26,8 @@
"@fortawesome/fontawesome-free": "^6.4.2",
"@iharbeck/ngx-virtual-scroller": "^16.0.0",
"@iplab/ngx-file-upload": "^16.0.1",
"@microsoft/signalr": "^7.0.9",
"@ng-bootstrap/ng-bootstrap": "^15.1.0",
"@microsoft/signalr": "^7.0.10",
"@ng-bootstrap/ng-bootstrap": "^15.1.1",
"@ngneat/transloco": "^5.0.6",
"@ngneat/transloco-locale": "^5.1.1",
"@ngneat/transloco-persist-lang": "^5.0.0",

View File

@ -50,14 +50,6 @@ export class AppComponent implements OnInit {
if (!user) return false;
return user.preferences.noTransitions;
}), takeUntilDestroyed(this.destroyRef));
this.translocoService.events$.subscribe(event => {
if (event.type === 'translationLoadSuccess') {
console.log('Language has fully loaded!', translate('login.title'));
}
console.log('language event: ', event.type, translate('login.title'));
});
}
@HostListener('window:resize', ['$event'])

View File

@ -19,7 +19,7 @@
height="100vh"
[(page)]="currentPage"
[textLayer]="true"
[useBrowserLocale]="false"
[useBrowserLocale]="true"
[showHandToolButton]="true"
[showOpenFileButton]="false"
[showPrintButton]="false"
@ -30,7 +30,6 @@
[showSecondaryToolbarButton]="true"
[showBorders]="true"
[theme]="theme"
[formTheme]="theme"
[backgroundColor]="backgroundColor"
[customToolbar]="multiToolbar"
[language]="user.preferences.locale"

View File

@ -1,6 +1,6 @@
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit } from '@angular/core';
import {AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit} from '@angular/core';
import { FormGroup, FormControl, Validators, ReactiveFormsModule } from '@angular/forms';
import { Router, RouterLink } from '@angular/router';
import {ActivatedRoute, Router, RouterLink} from '@angular/router';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
import { ToastrService } from 'ngx-toastr';
import { take } from 'rxjs/operators';
@ -18,17 +18,10 @@ import {TRANSLOCO_SCOPE, TranslocoDirective} from "@ngneat/transloco";
styleUrls: ['./user-login.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
standalone: true,
imports: [SplashContainerComponent, NgIf, ReactiveFormsModule, RouterLink, TranslocoDirective],
providers: [
{
provide: TRANSLOCO_SCOPE,
useValue: 'login'
}
]
imports: [SplashContainerComponent, NgIf, ReactiveFormsModule, RouterLink, TranslocoDirective]
})
export class UserLoginComponent implements OnInit {
//model: any = {username: '', password: ''};
loginForm: FormGroup = new FormGroup({
username: new FormControl('', [Validators.required]),
password: new FormControl('', [Validators.required, Validators.maxLength(32), Validators.minLength(6), Validators.pattern("^.{6,32}$")])
@ -45,8 +38,8 @@ export class UserLoginComponent implements OnInit {
isSubmitting = false;
constructor(private accountService: AccountService, private router: Router, private memberService: MemberService,
private toastr: ToastrService, private navService: NavService, private modalService: NgbModal,
private readonly cdRef: ChangeDetectorRef) {
private toastr: ToastrService, private navService: NavService,
private readonly cdRef: ChangeDetectorRef, private route: ActivatedRoute) {
this.navService.showNavBar();
this.navService.hideSideNav();
}
@ -76,11 +69,21 @@ export class UserLoginComponent implements OnInit {
this.cdRef.markForCheck();
});
this.route.queryParamMap.subscribe(params => {
const val = params.get('apiKey');
console.log('key: ', val);
if (val != null && val.length > 0) {
this.login(val);
}
});
}
login() {
login(apiKey: string = '') {
const model = this.loginForm.getRawValue();
model.apiKey = apiKey;
this.isSubmitting = true;
this.cdRef.markForCheck();
this.accountService.login(model).subscribe(() => {

View File

@ -1,7 +1,6 @@
import {Injectable} from "@angular/core";
import {HttpClient} from "@angular/common/http";
import {Translation, TranslocoLoader} from "@ngneat/transloco";
import {tap} from "rxjs/operators";
@Injectable({ providedIn: 'root' })
@ -10,7 +9,6 @@ export class HttpLoader implements TranslocoLoader {
getTranslation(langPath: string) {
const tokens = langPath.split('/');
return this.http.get<Translation>(`assets/langs/${tokens[tokens.length - 1]}.json`)
.pipe(tap(d => console.log('translations: ', d)));
return this.http.get<Translation>(`assets/langs/${tokens[tokens.length - 1]}.json`);
}
}

View File

@ -34,14 +34,12 @@ export function preloadUser(userService: AccountService, transloco: TranslocoSer
return function() {
return userService.currentUser$.pipe(switchMap((user) => {
if (user && user.preferences.locale) {
console.log('preloaded locale: ', user.preferences.locale)
transloco.setActiveLang(user.preferences.locale);
return transloco.load(user.preferences.locale)
}
// If no user or locale is available, fallback to the default language ('en')
const localStorageLocale = localStorage.getItem(AccountService.localeKey) || 'en';
console.log('preloaded locale: ', localStorageLocale)
transloco.setActiveLang(localStorageLocale);
return transloco.load(localStorageLocale)
})).subscribe();

View File

@ -7,7 +7,7 @@
"name": "GPL-3.0",
"url": "https://github.com/Kareadita/Kavita/blob/develop/LICENSE"
},
"version": "0.7.6.11"
"version": "0.7.6.13"
},
"servers": [
{
@ -13625,6 +13625,11 @@
"password": {
"type": "string",
"nullable": true
},
"apiKey": {
"type": "string",
"description": "If ApiKey is passed, will ignore username/password for validation",
"nullable": true
}
},
"additionalProperties": false