mirror of
https://github.com/Kareadita/Kavita.git
synced 2025-06-03 05:34:21 -04:00
Email Version availability (#2345)
This commit is contained in:
parent
d1157e90c4
commit
bcb75ed241
@ -42,12 +42,13 @@ public class ServerController : BaseApiController
|
|||||||
private readonly IUnitOfWork _unitOfWork;
|
private readonly IUnitOfWork _unitOfWork;
|
||||||
private readonly IEasyCachingProviderFactory _cachingProviderFactory;
|
private readonly IEasyCachingProviderFactory _cachingProviderFactory;
|
||||||
private readonly ILocalizationService _localizationService;
|
private readonly ILocalizationService _localizationService;
|
||||||
|
private readonly IEmailService _emailService;
|
||||||
|
|
||||||
public ServerController(ILogger<ServerController> logger,
|
public ServerController(ILogger<ServerController> logger,
|
||||||
IBackupService backupService, IArchiveService archiveService, IVersionUpdaterService versionUpdaterService, IStatsService statsService,
|
IBackupService backupService, IArchiveService archiveService, IVersionUpdaterService versionUpdaterService, IStatsService statsService,
|
||||||
ICleanupService cleanupService, IScannerService scannerService, IAccountService accountService,
|
ICleanupService cleanupService, IScannerService scannerService, IAccountService accountService,
|
||||||
ITaskScheduler taskScheduler, IUnitOfWork unitOfWork, IEasyCachingProviderFactory cachingProviderFactory,
|
ITaskScheduler taskScheduler, IUnitOfWork unitOfWork, IEasyCachingProviderFactory cachingProviderFactory,
|
||||||
ILocalizationService localizationService)
|
ILocalizationService localizationService, IEmailService emailService)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_backupService = backupService;
|
_backupService = backupService;
|
||||||
@ -61,6 +62,7 @@ public class ServerController : BaseApiController
|
|||||||
_unitOfWork = unitOfWork;
|
_unitOfWork = unitOfWork;
|
||||||
_cachingProviderFactory = cachingProviderFactory;
|
_cachingProviderFactory = cachingProviderFactory;
|
||||||
_localizationService = localizationService;
|
_localizationService = localizationService;
|
||||||
|
_emailService = emailService;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -270,5 +272,22 @@ public class ServerController : BaseApiController
|
|||||||
return Ok();
|
return Ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the KavitaEmail version for non-default instances
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
[Authorize("RequireAdminRole")]
|
||||||
|
[HttpGet("email-version")]
|
||||||
|
public async Task<ActionResult<string?>> GetEmailVersion()
|
||||||
|
{
|
||||||
|
var emailServiceUrl = (await _unitOfWork.SettingsRepository.GetSettingAsync(ServerSettingKey.EmailServiceUrl))
|
||||||
|
.Value;
|
||||||
|
|
||||||
|
if (emailServiceUrl.Equals(EmailService.DefaultApiUrl)) return Ok(null);
|
||||||
|
|
||||||
|
return Ok(await _emailService.GetVersion(emailServiceUrl));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,7 @@ public interface IEmailService
|
|||||||
Task<EmailTestResultDto> TestConnectivity(string emailUrl, string adminEmail, bool sendEmail);
|
Task<EmailTestResultDto> TestConnectivity(string emailUrl, string adminEmail, bool sendEmail);
|
||||||
Task<bool> IsDefaultEmailService();
|
Task<bool> IsDefaultEmailService();
|
||||||
Task SendEmailChangeEmail(ConfirmationEmailDto data);
|
Task SendEmailChangeEmail(ConfirmationEmailDto data);
|
||||||
|
Task<string?> GetVersion(string emailUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class EmailService : IEmailService
|
public class EmailService : IEmailService
|
||||||
@ -94,6 +95,34 @@ public class EmailService : IEmailService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<string> GetVersion(string emailUrl)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var settings = await _unitOfWork.SettingsRepository.GetSettingsDtoAsync();
|
||||||
|
var response = await $"{emailUrl}/api/about/version"
|
||||||
|
.WithHeader("Accept", "application/json")
|
||||||
|
.WithHeader("User-Agent", "Kavita")
|
||||||
|
.WithHeader("x-api-key", "MsnvA2DfQqxSK5jh")
|
||||||
|
.WithHeader("x-kavita-version", BuildInfo.Version)
|
||||||
|
.WithHeader("x-kavita-installId", settings.InstallId)
|
||||||
|
.WithHeader("Content-Type", "application/json")
|
||||||
|
.WithTimeout(TimeSpan.FromSeconds(10))
|
||||||
|
.GetStringAsync();
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(response))
|
||||||
|
{
|
||||||
|
return response.Replace("\"", string.Empty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public async Task SendConfirmationEmail(ConfirmationEmailDto data)
|
public async Task SendConfirmationEmail(ConfirmationEmailDto data)
|
||||||
{
|
{
|
||||||
var emailLink = (await _unitOfWork.SettingsRepository.GetSettingAsync(ServerSettingKey.EmailServiceUrl)).Value;
|
var emailLink = (await _unitOfWork.SettingsRepository.GetSettingAsync(ServerSettingKey.EmailServiceUrl)).Value;
|
||||||
|
@ -651,9 +651,10 @@ public class SeriesService : ISeriesService
|
|||||||
{
|
{
|
||||||
var series = await _unitOfWork.SeriesRepository.GetSeriesByIdAsync(seriesId, SeriesIncludes.Metadata | SeriesIncludes.Library);
|
var series = await _unitOfWork.SeriesRepository.GetSeriesByIdAsync(seriesId, SeriesIncludes.Metadata | SeriesIncludes.Library);
|
||||||
if (series == null) throw new KavitaException(await _localizationService.Translate(userId, "series-doesnt-exist"));
|
if (series == null) throw new KavitaException(await _localizationService.Translate(userId, "series-doesnt-exist"));
|
||||||
var libraryIds = _unitOfWork.LibraryRepository.GetLibraryIdsForUserIdAsync(userId);
|
if (!(await _unitOfWork.UserRepository.HasAccessToSeries(userId, seriesId)))
|
||||||
if (!libraryIds.Contains(series.LibraryId)) //// TODO: Rewrite this to use a new method which checks permissions all in the DB to be streamlined and less memory
|
{
|
||||||
throw new UnauthorizedAccessException("user-no-access-library-from-series");
|
throw new UnauthorizedAccessException("user-no-access-library-from-series");
|
||||||
|
}
|
||||||
if (series?.Metadata.PublicationStatus is not (PublicationStatus.OnGoing or PublicationStatus.Ended) || series.Library.Type == LibraryType.Book)
|
if (series?.Metadata.PublicationStatus is not (PublicationStatus.OnGoing or PublicationStatus.Ended) || series.Library.Type == LibraryType.Book)
|
||||||
{
|
{
|
||||||
return new NextExpectedChapterDto()
|
return new NextExpectedChapterDto()
|
||||||
@ -670,18 +671,32 @@ public class SeriesService : ISeriesService
|
|||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
// Calculate the time differences between consecutive chapters
|
// Calculate the time differences between consecutive chapters
|
||||||
var timeDifferences = chapters
|
// var timeDifferences = chapters
|
||||||
.Select((chapter, index) => new
|
// .Select((chapter, index) => new
|
||||||
|
// {
|
||||||
|
// ChapterNumber = chapter.Number,
|
||||||
|
// VolumeNumber = chapter.Volume.Number,
|
||||||
|
// TimeDifference = index == 0 ? TimeSpan.Zero : (chapter.CreatedUtc - chapters.ElementAt(index - 1).CreatedUtc)
|
||||||
|
// })
|
||||||
|
// .ToList();
|
||||||
|
// Quantize time differences: Chapters created within an hour from each other will be treated as one time delta
|
||||||
|
var timeDifferences = new List<TimeSpan>();
|
||||||
|
DateTime? previousChapterTime = null;
|
||||||
|
foreach (var chapter in chapters)
|
||||||
|
{
|
||||||
|
if (previousChapterTime.HasValue && (chapter.CreatedUtc - previousChapterTime.Value) <= TimeSpan.FromHours(1))
|
||||||
{
|
{
|
||||||
ChapterNumber = chapter.Number,
|
continue; // Skip this chapter if it's within an hour of the previous one
|
||||||
VolumeNumber = chapter.Volume.Number,
|
}
|
||||||
TimeDifference = index == 0 ? TimeSpan.Zero : (chapter.CreatedUtc - chapters.ElementAt(index - 1).CreatedUtc)
|
timeDifferences.Add(chapter.CreatedUtc - previousChapterTime ?? TimeSpan.Zero);
|
||||||
})
|
previousChapterTime = chapter.CreatedUtc;
|
||||||
.ToList();
|
}
|
||||||
|
|
||||||
// Calculate the average time difference between chapters
|
// Calculate the average time difference between chapters
|
||||||
|
// var averageTimeDifference = timeDifferences
|
||||||
|
// .Average(td => td.TimeDifference.TotalDays);
|
||||||
var averageTimeDifference = timeDifferences
|
var averageTimeDifference = timeDifferences
|
||||||
.Average(td => td.TimeDifference.TotalDays);
|
.Average(td => td.TotalDays);
|
||||||
|
|
||||||
// Calculate the forecast for when the next chapter is expected
|
// Calculate the forecast for when the next chapter is expected
|
||||||
var nextChapterExpected = chapters.Any()
|
var nextChapterExpected = chapters.Any()
|
||||||
@ -693,8 +708,8 @@ public class SeriesService : ISeriesService
|
|||||||
nextChapterExpected = DateTime.UtcNow + TimeSpan.FromDays(averageTimeDifference);
|
nextChapterExpected = DateTime.UtcNow + TimeSpan.FromDays(averageTimeDifference);
|
||||||
}
|
}
|
||||||
|
|
||||||
var lastChapter = timeDifferences.Last();
|
var lastChapter = chapters.Last();
|
||||||
float.TryParse(lastChapter.ChapterNumber, NumberStyles.Number, CultureInfo.InvariantCulture,
|
float.TryParse(lastChapter.Number, NumberStyles.Number, CultureInfo.InvariantCulture,
|
||||||
out var lastChapterNumber);
|
out var lastChapterNumber);
|
||||||
|
|
||||||
var result = new NextExpectedChapterDto()
|
var result = new NextExpectedChapterDto()
|
||||||
@ -708,7 +723,7 @@ public class SeriesService : ISeriesService
|
|||||||
if (lastChapterNumber > 0)
|
if (lastChapterNumber > 0)
|
||||||
{
|
{
|
||||||
result.ChapterNumber = lastChapterNumber + 1;
|
result.ChapterNumber = lastChapterNumber + 1;
|
||||||
result.VolumeNumber = lastChapter.VolumeNumber;
|
result.VolumeNumber = lastChapter.Volume.Number;
|
||||||
result.Title = series.Library.Type switch
|
result.Title = series.Library.Type switch
|
||||||
{
|
{
|
||||||
LibraryType.Manga => await _localizationService.Translate(userId, "chapter-num",
|
LibraryType.Manga => await _localizationService.Translate(userId, "chapter-num",
|
||||||
@ -722,8 +737,8 @@ public class SeriesService : ISeriesService
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
result.VolumeNumber = lastChapter.VolumeNumber + 1;
|
result.VolumeNumber = lastChapter.Volume.Number + 1;
|
||||||
result.Title = await _localizationService.Translate(userId, "vol-num",
|
result.Title = await _localizationService.Translate(userId, "volume-num",
|
||||||
new object[] {result.VolumeNumber});
|
new object[] {result.VolumeNumber});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -238,21 +238,8 @@ public class Startup
|
|||||||
|
|
||||||
logger.LogInformation("Running Migrations");
|
logger.LogInformation("Running Migrations");
|
||||||
|
|
||||||
// v0.7.2
|
|
||||||
await MigrateLoginRoles.Migrate(unitOfWork, userManager, logger);
|
|
||||||
|
|
||||||
// v0.7.3
|
|
||||||
await MigrateRemoveWebPSettingRows.Migrate(unitOfWork, logger);
|
|
||||||
|
|
||||||
// v0.7.4
|
|
||||||
await MigrateDisableScrobblingOnComicLibraries.Migrate(unitOfWork, dataContext, logger);
|
|
||||||
|
|
||||||
// v0.7.6
|
|
||||||
await MigrateExistingRatings.Migrate(dataContext, logger);
|
|
||||||
|
|
||||||
// v0.7.9
|
// v0.7.9
|
||||||
await MigrateUserLibrarySideNavStream.Migrate(unitOfWork, dataContext, logger);
|
await MigrateUserLibrarySideNavStream.Migrate(unitOfWork, dataContext, logger);
|
||||||
await MigrateDashboardStreamNamesToLocaleKeys.Migrate(unitOfWork, dataContext, logger);
|
|
||||||
|
|
||||||
// Update the version in the DB after all migrations are run
|
// Update the version in the DB after all migrations are run
|
||||||
var installVersion = await unitOfWork.SettingsRepository.GetSettingAsync(ServerSettingKey.InstallVersion);
|
var installVersion = await unitOfWork.SettingsRepository.GetSettingAsync(ServerSettingKey.InstallVersion);
|
||||||
|
@ -5,6 +5,7 @@ import {ServerInfoSlim} from '../admin/_models/server-info';
|
|||||||
import { UpdateVersionEvent } from '../_models/events/update-version-event';
|
import { UpdateVersionEvent } from '../_models/events/update-version-event';
|
||||||
import { Job } from '../_models/job/job';
|
import { Job } from '../_models/job/job';
|
||||||
import { KavitaMediaError } from '../admin/_models/media-error';
|
import { KavitaMediaError } from '../admin/_models/media-error';
|
||||||
|
import {TextResonse} from "../_types/text-response";
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
@ -67,4 +68,8 @@ export class ServerService {
|
|||||||
clearMediaAlerts() {
|
clearMediaAlerts() {
|
||||||
return this.httpClient.post(this.baseUrl + 'server/clear-media-alerts', {});
|
return this.httpClient.post(this.baseUrl + 'server/clear-media-alerts', {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getEmailVersion() {
|
||||||
|
return this.httpClient.get<string>(this.baseUrl + 'server/email-version', TextResonse);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
import {ChangeDetectionStrategy, ChangeDetectorRef, Component, inject, OnInit} from '@angular/core';
|
import {ChangeDetectionStrategy, ChangeDetectorRef, Component, inject, OnInit} from '@angular/core';
|
||||||
import {FormControl, FormGroup, ReactiveFormsModule, Validators} from '@angular/forms';
|
import {FormControl, FormGroup, ReactiveFormsModule, Validators} from '@angular/forms';
|
||||||
import {ToastrService} from 'ngx-toastr';
|
import {ToastrService} from 'ngx-toastr';
|
||||||
import {take} from 'rxjs';
|
import {forkJoin, take} from 'rxjs';
|
||||||
import {EmailTestResult, SettingsService} from '../settings.service';
|
import {EmailTestResult, SettingsService} from '../settings.service';
|
||||||
import {ServerSettings} from '../_models/server-settings';
|
import {ServerSettings} from '../_models/server-settings';
|
||||||
import {NgbTooltip} from '@ng-bootstrap/ng-bootstrap';
|
import {NgbTooltip} from '@ng-bootstrap/ng-bootstrap';
|
||||||
import {NgIf, NgTemplateOutlet} from '@angular/common';
|
import {NgIf, NgTemplateOutlet} from '@angular/common';
|
||||||
import {TranslocoModule, TranslocoService} from "@ngneat/transloco";
|
import {translate, TranslocoModule, TranslocoService} from "@ngneat/transloco";
|
||||||
import {SafeHtmlPipe} from "../../pipe/safe-html.pipe";
|
import {SafeHtmlPipe} from "../../pipe/safe-html.pipe";
|
||||||
|
import {ServerService} from "../../_services/server.service";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-manage-email-settings',
|
selector: 'app-manage-email-settings',
|
||||||
@ -22,9 +23,13 @@ export class ManageEmailSettingsComponent implements OnInit {
|
|||||||
serverSettings!: ServerSettings;
|
serverSettings!: ServerSettings;
|
||||||
settingsForm: FormGroup = new FormGroup({});
|
settingsForm: FormGroup = new FormGroup({});
|
||||||
link = '<a href="https://github.com/Kareadita/KavitaEmail" target="_blank" rel="noopener noreferrer">Kavita Email</a>';
|
link = '<a href="https://github.com/Kareadita/KavitaEmail" target="_blank" rel="noopener noreferrer">Kavita Email</a>';
|
||||||
|
emailVersion: string | null = null;
|
||||||
private readonly cdRef = inject(ChangeDetectorRef);
|
private readonly cdRef = inject(ChangeDetectorRef);
|
||||||
|
private readonly serverService = inject(ServerService);
|
||||||
|
private readonly settingsService = inject(SettingsService);
|
||||||
|
private readonly toastr = inject(ToastrService);
|
||||||
|
|
||||||
constructor(private settingsService: SettingsService, private toastr: ToastrService, private translocoService: TranslocoService) { }
|
constructor() { }
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.settingsService.getServerSettings().pipe(take(1)).subscribe((settings: ServerSettings) => {
|
this.settingsService.getServerSettings().pipe(take(1)).subscribe((settings: ServerSettings) => {
|
||||||
@ -33,6 +38,11 @@ export class ManageEmailSettingsComponent implements OnInit {
|
|||||||
this.settingsForm.addControl('hostName', new FormControl(this.serverSettings.hostName, []));
|
this.settingsForm.addControl('hostName', new FormControl(this.serverSettings.hostName, []));
|
||||||
this.cdRef.markForCheck();
|
this.cdRef.markForCheck();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.serverService.getEmailVersion().subscribe(version => {
|
||||||
|
this.emailVersion = version;
|
||||||
|
this.cdRef.markForCheck();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
resetForm() {
|
resetForm() {
|
||||||
@ -51,7 +61,7 @@ export class ManageEmailSettingsComponent implements OnInit {
|
|||||||
this.settingsService.updateServerSettings(modelSettings).pipe(take(1)).subscribe((settings: ServerSettings) => {
|
this.settingsService.updateServerSettings(modelSettings).pipe(take(1)).subscribe((settings: ServerSettings) => {
|
||||||
this.serverSettings = settings;
|
this.serverSettings = settings;
|
||||||
this.resetForm();
|
this.resetForm();
|
||||||
this.toastr.success(this.translocoService.translate('toasts.server-settings-updated'));
|
this.toastr.success(translate('toasts.server-settings-updated'));
|
||||||
}, (err: any) => {
|
}, (err: any) => {
|
||||||
console.error('error: ', err);
|
console.error('error: ', err);
|
||||||
});
|
});
|
||||||
@ -61,7 +71,7 @@ export class ManageEmailSettingsComponent implements OnInit {
|
|||||||
this.settingsService.resetServerSettings().pipe(take(1)).subscribe((settings: ServerSettings) => {
|
this.settingsService.resetServerSettings().pipe(take(1)).subscribe((settings: ServerSettings) => {
|
||||||
this.serverSettings = settings;
|
this.serverSettings = settings;
|
||||||
this.resetForm();
|
this.resetForm();
|
||||||
this.toastr.success(this.translocoService.translate('toasts.server-settings-updated'));
|
this.toastr.success(translate('toasts.server-settings-updated'));
|
||||||
}, (err: any) => {
|
}, (err: any) => {
|
||||||
console.error('error: ', err);
|
console.error('error: ', err);
|
||||||
});
|
});
|
||||||
@ -71,7 +81,7 @@ export class ManageEmailSettingsComponent implements OnInit {
|
|||||||
this.settingsService.resetEmailServerSettings().pipe(take(1)).subscribe((settings: ServerSettings) => {
|
this.settingsService.resetEmailServerSettings().pipe(take(1)).subscribe((settings: ServerSettings) => {
|
||||||
this.serverSettings.emailServiceUrl = settings.emailServiceUrl;
|
this.serverSettings.emailServiceUrl = settings.emailServiceUrl;
|
||||||
this.resetForm();
|
this.resetForm();
|
||||||
this.toastr.success(this.translocoService.translate('toasts.email-service-reset'));
|
this.toastr.success(translate('toasts.email-service-reset'));
|
||||||
}, (err: any) => {
|
}, (err: any) => {
|
||||||
console.error('error: ', err);
|
console.error('error: ', err);
|
||||||
});
|
});
|
||||||
@ -79,11 +89,14 @@ export class ManageEmailSettingsComponent implements OnInit {
|
|||||||
|
|
||||||
testEmailServiceUrl() {
|
testEmailServiceUrl() {
|
||||||
if (this.settingsForm.get('emailServiceUrl')?.value === '') return;
|
if (this.settingsForm.get('emailServiceUrl')?.value === '') return;
|
||||||
this.settingsService.testEmailServerSettings(this.settingsForm.get('emailServiceUrl')?.value).pipe(take(1)).subscribe(async (result: EmailTestResult) => {
|
forkJoin([this.settingsService.testEmailServerSettings(this.settingsForm.get('emailServiceUrl')?.value), this.serverService.getEmailVersion()])
|
||||||
|
.pipe(take(1)).subscribe(async (results) => {
|
||||||
|
const result = results[0] as EmailTestResult;
|
||||||
if (result.successful) {
|
if (result.successful) {
|
||||||
this.toastr.success(this.translocoService.translate('toasts.email-service-reachable'));
|
const version = ('. Kavita Email: ' + results[1] ? 'v' + results[1] : '');
|
||||||
|
this.toastr.success(translate('toasts.email-service-reachable') + version);
|
||||||
} else {
|
} else {
|
||||||
this.toastr.error(this.translocoService.translate('toasts.email-service-unresponsive') + result.errorMessage);
|
this.toastr.error(translate('toasts.email-service-unresponsive') + result.errorMessage.split('(')[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
}, (err: any) => {
|
}, (err: any) => {
|
||||||
|
@ -1907,7 +1907,7 @@
|
|||||||
"reading-list-imported": "Reading List imported",
|
"reading-list-imported": "Reading List imported",
|
||||||
"incognito-off": "Incognito mode is off. Progress will now start being tracked.",
|
"incognito-off": "Incognito mode is off. Progress will now start being tracked.",
|
||||||
"email-service-reset": "Email Service Reset",
|
"email-service-reset": "Email Service Reset",
|
||||||
"email-service-reachable": "Email Service was reachable",
|
"email-service-reachable": "Kavita Email Connection Successful",
|
||||||
"email-service-unresponsive": "Email Service Url did not respond.",
|
"email-service-unresponsive": "Email Service Url did not respond.",
|
||||||
"refresh-covers-queued": "Refresh covers queued for {{name}}",
|
"refresh-covers-queued": "Refresh covers queued for {{name}}",
|
||||||
"library-file-analysis-queued": "Library file analysis queued for {{name}}",
|
"library-file-analysis-queued": "Library file analysis queued for {{name}}",
|
||||||
|
30
openapi.json
30
openapi.json
@ -9455,6 +9455,36 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/api/Server/email-version": {
|
||||||
|
"get": {
|
||||||
|
"tags": [
|
||||||
|
"Server"
|
||||||
|
],
|
||||||
|
"summary": "Returns the KavitaEmail version for non-default instances",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Success",
|
||||||
|
"content": {
|
||||||
|
"text/plain": {
|
||||||
|
"schema": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"text/json": {
|
||||||
|
"schema": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/api/Settings/base-url": {
|
"/api/Settings/base-url": {
|
||||||
"get": {
|
"get": {
|
||||||
"tags": [
|
"tags": [
|
||||||
|
Loading…
x
Reference in New Issue
Block a user