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!
+
+
## 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