mirror of
https://github.com/Kareadita/Kavita.git
synced 2025-07-09 03:04:19 -04:00
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:
parent
d4e1e08b4f
commit
9f17f5daa7
8
.github/workflows/build-and-test.yml
vendored
8
.github/workflows/build-and-test.yml
vendored
@ -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 }}
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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!;
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -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)
|
||||
|
16
UI/Web/package-lock.json
generated
16
UI/Web/package-lock.json
generated
@ -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"
|
||||
},
|
||||
|
@ -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",
|
||||
|
@ -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'])
|
||||
|
@ -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"
|
||||
|
@ -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(() => {
|
||||
|
@ -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`);
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user