Skip to content

Commit

Permalink
Whenever there is an installid mismatch on the K+ backend, prompt the…
Browse files Browse the repository at this point in the history
… user to overwrite it (aka reset then register). This should make for a much better experience.
  • Loading branch information
majora2007 committed Jan 4, 2025
1 parent 5f176ed commit 3f1f42a
Show file tree
Hide file tree
Showing 8 changed files with 223 additions and 153 deletions.
1 change: 0 additions & 1 deletion API/Controllers/SettingsController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -389,7 +389,6 @@ private bool UpdateSchedulingSettings(ServerSetting setting, ServerSettingDto up
{
if (setting.Key == ServerSettingKey.TaskBackup && updateSettingsDto.TaskBackup != setting.Value)
{
//if (updateSettingsDto.TotalBackup)
setting.Value = updateSettingsDto.TaskBackup;
_unitOfWork.SettingsRepository.Update(setting);

Expand Down
31 changes: 31 additions & 0 deletions API/Extensions/VersionExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using System;

namespace API.Extensions;

public static class VersionExtensions
{
public static bool CompareWithoutRevision(this Version v1, Version v2)
{
if (v1.Major != v2.Major)
return v1.Major == v2.Major;
if (v1.Minor != v2.Minor)
return v1.Minor == v2.Minor;
if (v1.Build != v2.Build)
return v1.Build == v2.Build;
return true;
}


/// <summary>
/// v0.8.2.3 is within v0.8.2 (v1). Essentially checks if this is a Nightly of a stable release
/// </summary>
/// <param name="v1"></param>
/// <param name="v2"></param>
/// <returns></returns>
public static bool IsWithinStableRelease(this Version v1, Version v2)
{
return v1.Major == v2.Major && v1.Minor != v2.Minor && v1.Build != v2.Build;
}


}
14 changes: 10 additions & 4 deletions API/Services/Plus/LicenseService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -266,10 +266,16 @@ public async Task<bool> ResetLicense(string license, string email)
.WithKavitaPlusHeaders(encryptedLicense.Value)
.GetJsonAsync<LicenseInfoDto>();

// Fill out the ValidVersion
var releases = await versionUpdaterService.GetAllReleases(3);
response.IsValidVersion = releases.Where(r => !r.IsPrerelease)
.Any(r => r.UpdateVersion == BuildInfo.Version.ToString());
// This indicates a mismatch on installid or no active subscription
if (response == null) return null;

// Ensure that current version is within the 3 version limit. Don't count Nightly releases or Hotfixes
var releases = await versionUpdaterService.GetAllReleases();
response.IsValidVersion = releases
.Where(r => !r.UpdateTitle.Contains("Hotfix")) // We don't care about Hotfix releases
.Where(r => !r.IsPrerelease || BuildInfo.Version.IsWithinStableRelease(new Version(r.UpdateVersion))) // Ensure we don't take current nightlies within the current/last stable
.Take(3)
.All(r => new Version(r.UpdateVersion) <= BuildInfo.Version);

response.HasLicense = hasLicense;

Expand Down
30 changes: 14 additions & 16 deletions API/Services/Tasks/VersionUpdaterService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using API.DTOs.Update;
using API.Extensions;
using API.SignalR;
using Flurl.Http;
using Kavita.Common.EnvironmentInfo;
Expand Down Expand Up @@ -121,6 +122,7 @@ private async Task EnrichWithNightlyInfo(List<UpdateNotificationDto> dtos)
IsDocker = true, // Nightlies are always Docker Only
IsReleaseEqual = IsVersionEqualToBuildVersion(Version.Parse(nightly.Version)),
IsReleaseNewer = true, // Since we already filtered these in GetNightlyReleases
IsPrerelease = true, // All Nightlies are considered prerelease
Added = sections.TryGetValue("Added", out var added) ? added : [],
Changed = sections.TryGetValue("Changed", out var changed) ? changed : [],
Fixed = sections.TryGetValue("Fixed", out var bugfixes) ? bugfixes : [],
Expand Down Expand Up @@ -286,6 +288,12 @@ public async Task<IList<UpdateNotificationDto>> GetAllReleases(int count = 0)
var cachedReleases = await TryGetCachedReleases();
if (cachedReleases != null)
{
if (count > 0)
{
// NOTE: We may want to allow the admin to clear Github cache
return cachedReleases.Take(count).ToList();
}

return cachedReleases;
}

Expand All @@ -295,11 +303,6 @@ public async Task<IList<UpdateNotificationDto>> GetAllReleases(int count = 0)
.OrderByDescending(d => d!.PublishDate)
.Select(d => d!);

if (count > 0)
{
query = query.Take(count);
}

var updateDtos = query.ToList();

// If we're on a nightly build, enrich the information
Expand Down Expand Up @@ -328,6 +331,11 @@ public async Task<IList<UpdateNotificationDto>> GetAllReleases(int count = 0)
await CacheReleasesAsync(updateDtos);
}

if (count > 0)
{
return updateDtos.Take(count).ToList();
}

return updateDtos;
}

Expand Down Expand Up @@ -364,19 +372,9 @@ private async Task CacheReleasesAsync(IList<UpdateNotificationDto> updates)
private static bool IsVersionEqualToBuildVersion(Version updateVersion)
{
return updateVersion == BuildInfo.Version || (updateVersion.Revision < 0 && BuildInfo.Version.Revision == 0 &&
CompareWithoutRevision(BuildInfo.Version, updateVersion));
BuildInfo.Version.CompareWithoutRevision(updateVersion));
}

private static bool CompareWithoutRevision(Version v1, Version v2)
{
if (v1.Major != v2.Major)
return v1.Major == v2.Major;
if (v1.Minor != v2.Minor)
return v1.Minor == v2.Minor;
if (v1.Build != v2.Build)
return v1.Build == v2.Build;
return true;
}

public async Task<int> GetNumberOfReleasesBehind()
{
Expand Down
128 changes: 61 additions & 67 deletions UI/Web/src/app/admin/license/license.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,11 @@
<p>{{t('kavita+-desc-part-1')}} <a [href]="WikiLink.KavitaPlus" target="_blank" rel="noreferrer nofollow">{{t('kavita+-desc-part-2')}}</a> {{t('kavita+-desc-part-3')}}</p>
</div>

@if (hasLicense) {
<div class="alert alert-warning">
{{t('reset-license-alert')}}
</div>
}

<form [formGroup]="formGroup">
<div class="mt-2">
<app-setting-item [title]="t('title')" (editMode)="updateEditMode($event)" [isEditMode]="!isViewMode">
<app-setting-item [title]="t('title')" (editMode)="updateEditMode($event)" [isEditMode]="!isViewMode" [showEdit]="licenseInfo?.hasLicense || false">
<ng-template #titleExtra>
<button class="btn btn-icon btn-sm" (click)="validateLicense(true)">
<button class="btn btn-icon btn-sm" (click)="loadLicenseInfo(true)">
@if (isChecking) {
<app-loading [loading]="isChecking" size="spinner-border-sm"></app-loading>
} @else if (hasLicense) {
Expand All @@ -37,7 +31,7 @@
<span class="visually-hidden">{{t('loading')}}</span>
</div>
} @else {
@if (hasValidLicense) {
@if (licenseInfo?.isActive) {
<i [ngbTooltip]="t('license-valid')" class="fa-solid fa-check-circle successful-validation ms-1">
<span class="visually-hidden">{{t('license-valid')}}</span>
</i>
Expand All @@ -47,6 +41,10 @@
</i>
}
}
@if (hasLicense && !licenseInfo) {
<div><span class="error">{{t('license-mismatch')}}</span></div>
}

} @else {
{{t('no-license-key')}}
}
Expand Down Expand Up @@ -101,7 +99,7 @@

<ng-template #titleActions>
@if (hasLicense) {
@if (hasValidLicense) {
@if (licenseInfo?.isActive) {
<a class="btn btn-primary-outline btn-sm me-1" [href]="manageLink" target="_blank" rel="noreferrer nofollow">{{t('manage')}}</a>
} @else {
<a class="btn btn-primary-outline btn-sm me-1"
Expand All @@ -120,67 +118,64 @@



@if (hasLicense) {
@if (hasLicense && licenseInfo) {
<div class="setting-section-break"></div>

<div class="row g-0 mt-3">
@if (licenseInfo) {
<h3 class="container-fluid">{{t('info-title')}}</h3>
<div class="mb-2 col-md-6 col-sm-12">
<app-setting-item [canEdit]="false" [showEdit]="false" [title]="t('license-active-label')">
<ng-template #view>
@if (isChecking) {
{{null | defaultValue}}
} @else {
<i class="fas {{hasValidLicense ? 'fa-check-circle' : 'fa-circle-xmark error'}}">
<span class="visually-hidden">{{hasValidLicense ? t('valid') : t('invalid')}]</span>
</i>
}
</ng-template>
</app-setting-item>
</div>

<div class="mb-2 col-md-6 col-sm-12">
<app-setting-item [canEdit]="false" [showEdit]="false" [title]="t('supported-version-label')">
<ng-template #view>
<i class="fas {{licenseInfo.isVersionValid ? 'fa-check-circle' : 'fa-circle-xmark error'}}">
<span class="visually-hidden">{{isVersionValid ? t('valid') : t('invalid')}]</span>
<h3 class="container-fluid">{{t('info-title')}}</h3>
<div class="mb-2 col-md-6 col-sm-12">
<app-setting-item [canEdit]="false" [showEdit]="false" [title]="t('license-active-label')">
<ng-template #view>
@if (isChecking) {
{{null | defaultValue}}
} @else {
<i class="fas {{licenseInfo.isActive ? 'fa-check-circle' : 'fa-circle-xmark error'}}">
<span class="visually-hidden">{{licenseInfo.isActive ? t('valid') : t('invalid')}]</span>
</i>
</ng-template>
</app-setting-item>
</div>

<div class="mb-2 col-md-6 col-sm-12">
<app-setting-item [canEdit]="false" [showEdit]="false" [title]="t('expiration-label')">
<ng-template #view>
{{licenseInfo.expirationDate | utcToLocalTime | defaultValue}}
</ng-template>
</app-setting-item>
</div>

<div class="mb-2 col-md-6 col-sm-12">
<app-setting-item [canEdit]="false" [showEdit]="false" [title]="t('total-subbed-months-label')">
<ng-template #view>
{{licenseInfo.totalMonthsSubbed | number}}
</ng-template>
</app-setting-item>
</div>

<div class="col-md-6 col-sm-12">
<app-setting-item [canEdit]="false" [showEdit]="false" [title]="t('email-label')">
<ng-template #view>
<span (click)="toggleEmailShow()" class="col-12 clickable">
@if (showEmail) {
{{licenseInfo.registeredEmail}}
} @else {
***************
}
</span>
</ng-template>
</app-setting-item>
</div>
}
}
</ng-template>
</app-setting-item>
</div>

<div class="mb-2 col-md-6 col-sm-12">
<app-setting-item [canEdit]="false" [showEdit]="false" [title]="t('supported-version-label')">
<ng-template #view>
<i class="fas {{licenseInfo.isVersionValid ? 'fa-check-circle' : 'fa-circle-xmark error'}}">
<span class="visually-hidden">{{isVersionValid ? t('valid') : t('invalid')}]</span>
</i>
</ng-template>
</app-setting-item>
</div>

<div class="mb-2 col-md-6 col-sm-12">
<app-setting-item [canEdit]="false" [showEdit]="false" [title]="t('expiration-label')">
<ng-template #view>
{{licenseInfo.expirationDate | utcToLocalTime | defaultValue}}
</ng-template>
</app-setting-item>
</div>

<div class="mb-2 col-md-6 col-sm-12">
<app-setting-item [canEdit]="false" [showEdit]="false" [title]="t('total-subbed-months-label')">
<ng-template #view>
{{licenseInfo.totalMonthsSubbed | number}}
</ng-template>
</app-setting-item>
</div>

<div class="col-md-6 col-sm-12">
<app-setting-item [canEdit]="false" [showEdit]="false" [title]="t('email-label')">
<ng-template #view>
<span (click)="toggleEmailShow()" class="col-12 clickable">
@if (showEmail) {
{{licenseInfo.registeredEmail}}
} @else {
***************
}
</span>
</ng-template>
</app-setting-item>
</div>

<div class="setting-section-break"></div>

Expand All @@ -200,7 +195,6 @@ <h3 class="container-fluid">{{t('actions-title')}}</h3>
<a class="btn btn-primary btn-sm mt-1" [href]="manageLink" target="_blank" rel="noreferrer nofollow">{{t('manage')}}</a>
</app-setting-button>
</div>

</div>
}
</ng-container>
Loading

0 comments on commit 3f1f42a

Please sign in to comment.