From 9f17f5daa768d4728de90170e0cf842ccbe61dcb Mon Sep 17 00:00:00 2001 From: Joe Milazzo Date: Tue, 8 Aug 2023 14:06:53 -0500 Subject: [PATCH] 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. --- .github/workflows/build-and-test.yml | 8 ++-- API.Benchmark/API.Benchmark.csproj | 4 +- API.Tests/API.Tests.csproj | 10 ++--- API/API.csproj | 22 +++++----- API/Controllers/AccountController.cs | 40 +++++++++++++------ API/Controllers/LocaleController.cs | 4 +- API/DTOs/Account/LoginDto.cs | 4 ++ Kavita.Common/Kavita.Common.csproj | 2 +- README.md | 2 + UI/Web/package-lock.json | 16 ++++---- UI/Web/package.json | 4 +- UI/Web/src/app/app.component.ts | 8 ---- .../pdf-reader/pdf-reader.component.html | 3 +- .../user-login/user-login.component.ts | 29 ++++++++------ UI/Web/src/httpLoader.ts | 4 +- UI/Web/src/main.ts | 2 - openapi.json | 7 +++- 17 files changed, 93 insertions(+), 76 deletions(-) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 26d79d619..2f2a91e8b 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -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 }} diff --git a/API.Benchmark/API.Benchmark.csproj b/API.Benchmark/API.Benchmark.csproj index 02faebe82..daf3b461e 100644 --- a/API.Benchmark/API.Benchmark.csproj +++ b/API.Benchmark/API.Benchmark.csproj @@ -10,8 +10,8 @@ - - + + diff --git a/API.Tests/API.Tests.csproj b/API.Tests/API.Tests.csproj index 88844ad4b..36bb142fe 100644 --- a/API.Tests/API.Tests.csproj +++ b/API.Tests/API.Tests.csproj @@ -6,12 +6,12 @@ - - - + + + - - + + runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/API/API.csproj b/API/API.csproj index 513ab182b..829f3fde9 100644 --- a/API/API.csproj +++ b/API/API.csproj @@ -60,23 +60,23 @@ - - + + - + - - - + + + - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + @@ -95,14 +95,14 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive - - + + diff --git a/API/Controllers/AccountController.cs b/API/Controllers/AccountController.cs index a8abbd501..b14bac8c1 100644 --- a/API/Controllers/AccountController.cs +++ b/API/Controllers/AccountController.cs @@ -177,26 +177,40 @@ public class AccountController : BaseApiController [HttpPost("login")] public async Task> 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 diff --git a/API/Controllers/LocaleController.cs b/API/Controllers/LocaleController.cs index a9ca35ce9..dde8b0d03 100644 --- a/API/Controllers/LocaleController.cs +++ b/API/Controllers/LocaleController.cs @@ -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); } } diff --git a/API/DTOs/Account/LoginDto.cs b/API/DTOs/Account/LoginDto.cs index 111db06d3..a5376e1fc 100644 --- a/API/DTOs/Account/LoginDto.cs +++ b/API/DTOs/Account/LoginDto.cs @@ -4,4 +4,8 @@ public class LoginDto { public string Username { get; init; } = default!; public string Password { get; set; } = default!; + /// + /// If ApiKey is passed, will ignore username/password for validation + /// + public string? ApiKey { get; set; } = default!; } diff --git a/Kavita.Common/Kavita.Common.csproj b/Kavita.Common/Kavita.Common.csproj index 8c6ba601c..c57325df6 100644 --- a/Kavita.Common/Kavita.Common.csproj +++ b/Kavita.Common/Kavita.Common.csproj @@ -14,7 +14,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/README.md b/README.md index a57bfaafa..79d7779b9 100644 --- a/README.md +++ b/README.md @@ -17,8 +17,10 @@ your reading collection with your friends and family! Translation status + + ## 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) diff --git a/UI/Web/package-lock.json b/UI/Web/package-lock.json index bbe9f4b64..5d4594bb7 100644 --- a/UI/Web/package-lock.json +++ b/UI/Web/package-lock.json @@ -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" }, diff --git a/UI/Web/package.json b/UI/Web/package.json index d6da207c2..1e273421f 100644 --- a/UI/Web/package.json +++ b/UI/Web/package.json @@ -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", diff --git a/UI/Web/src/app/app.component.ts b/UI/Web/src/app/app.component.ts index 18ad91ebe..c87d1773f 100644 --- a/UI/Web/src/app/app.component.ts +++ b/UI/Web/src/app/app.component.ts @@ -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']) diff --git a/UI/Web/src/app/pdf-reader/_components/pdf-reader/pdf-reader.component.html b/UI/Web/src/app/pdf-reader/_components/pdf-reader/pdf-reader.component.html index baec678c9..913315a74 100644 --- a/UI/Web/src/app/pdf-reader/_components/pdf-reader/pdf-reader.component.html +++ b/UI/Web/src/app/pdf-reader/_components/pdf-reader/pdf-reader.component.html @@ -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" diff --git a/UI/Web/src/app/registration/user-login/user-login.component.ts b/UI/Web/src/app/registration/user-login/user-login.component.ts index cab985ebd..7f2f5bb92 100644 --- a/UI/Web/src/app/registration/user-login/user-login.component.ts +++ b/UI/Web/src/app/registration/user-login/user-login.component.ts @@ -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(() => { diff --git a/UI/Web/src/httpLoader.ts b/UI/Web/src/httpLoader.ts index 7529a0ed3..d3c2de6b7 100644 --- a/UI/Web/src/httpLoader.ts +++ b/UI/Web/src/httpLoader.ts @@ -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(`assets/langs/${tokens[tokens.length - 1]}.json`) - .pipe(tap(d => console.log('translations: ', d))); + return this.http.get(`assets/langs/${tokens[tokens.length - 1]}.json`); } } diff --git a/UI/Web/src/main.ts b/UI/Web/src/main.ts index 4d7684332..3b14fd93a 100644 --- a/UI/Web/src/main.ts +++ b/UI/Web/src/main.ts @@ -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(); diff --git a/openapi.json b/openapi.json index 84c86013b..f0ca9cf28 100644 --- a/openapi.json +++ b/openapi.json @@ -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