mirror of
https://github.com/Ombi-app/Ombi.git
synced 2025-08-19 12:59:39 -07:00
#1513 Added the update available icon
This commit is contained in:
parent
057683d97a
commit
f689af82f0
14 changed files with 104 additions and 160 deletions
|
@ -1,111 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.IO;
|
|
||||||
using System.IO.Compression;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Net.Http;
|
|
||||||
using System.Net.Http.Headers;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Ombi.Core.Update
|
|
||||||
{
|
|
||||||
public class UpdateEngine
|
|
||||||
{
|
|
||||||
public async Task Update(UpdateOptions options)
|
|
||||||
{
|
|
||||||
if (options.Status == UpdateStatus.UptoDate)
|
|
||||||
{
|
|
||||||
// We don't need to update...
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Download zip into temp location
|
|
||||||
var path = await Download(options);
|
|
||||||
|
|
||||||
Extract(path);
|
|
||||||
|
|
||||||
var location = System.Reflection.Assembly.GetEntryAssembly().Location;
|
|
||||||
var current = Path.GetDirectoryName(location);
|
|
||||||
// TODO Run the Update.exe and pass in the args
|
|
||||||
var start = new ProcessStartInfo
|
|
||||||
{
|
|
||||||
UseShellExecute = false,
|
|
||||||
CreateNoWindow = true,
|
|
||||||
FileName = Path.Combine(current, "Ombi.Updater.exe")
|
|
||||||
};
|
|
||||||
using (var proc = new Process { StartInfo = start })
|
|
||||||
{
|
|
||||||
proc.Start();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Extract(string path)
|
|
||||||
{
|
|
||||||
using (var zip = ZipFile.OpenRead(path))
|
|
||||||
{
|
|
||||||
path = Path.GetDirectoryName(path);
|
|
||||||
foreach (var entry in zip.Entries.Skip(1))
|
|
||||||
{
|
|
||||||
var fullname = string.Empty;
|
|
||||||
if (entry.FullName.Contains("publish/")) // Don't extract the publish folder, we are already in there
|
|
||||||
{
|
|
||||||
fullname = entry.FullName.Replace("publish/", string.Empty);
|
|
||||||
}
|
|
||||||
|
|
||||||
var fullPath = Path.Combine(path, fullname);
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(entry.Name))
|
|
||||||
{
|
|
||||||
Directory.CreateDirectory(fullPath);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
entry.ExtractToFile(fullPath, true);
|
|
||||||
Console.WriteLine("Restored {0}", entry.FullName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Downloads the specified zip from the options and returns the zip path.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="options">The options.</param>
|
|
||||||
/// <returns></returns>
|
|
||||||
private async Task<string> Download(UpdateOptions options)
|
|
||||||
{
|
|
||||||
|
|
||||||
// Create temp path
|
|
||||||
var location = System.Reflection.Assembly.GetEntryAssembly().Location;
|
|
||||||
var current = Path.GetDirectoryName(location);
|
|
||||||
var tempDir = Directory.CreateDirectory(Path.Combine(current, "UpdateTemp"));
|
|
||||||
var tempZip = Path.Combine(tempDir.FullName, "Ombi.zip");
|
|
||||||
|
|
||||||
if (File.Exists(tempZip))
|
|
||||||
{
|
|
||||||
return tempZip;
|
|
||||||
}
|
|
||||||
|
|
||||||
using (var httpClient = new HttpClient())
|
|
||||||
using (var contentStream = await httpClient.GetStreamAsync(options.DownloadUrl))
|
|
||||||
using (var fileStream = new FileStream(tempZip, FileMode.Create, FileAccess.Write, FileShare.None, 1048576, true))
|
|
||||||
{
|
|
||||||
await contentStream.CopyToAsync(fileStream);
|
|
||||||
}
|
|
||||||
|
|
||||||
return tempZip;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public UpdateOptions CheckForUpdate()
|
|
||||||
{
|
|
||||||
return new UpdateOptions
|
|
||||||
{
|
|
||||||
Status = UpdateStatus.Available,
|
|
||||||
DownloadUrl = "https://ci.appveyor.com/api/buildjobs/t500indclt3etd50/artifacts/Ombi_windows.zip",
|
|
||||||
UpdateDate = DateTime.Now,
|
|
||||||
Version = "3.0.0"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,12 +0,0 @@
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace Ombi.Core.Update
|
|
||||||
{
|
|
||||||
public class UpdateOptions
|
|
||||||
{
|
|
||||||
public UpdateStatus Status { get; set; }
|
|
||||||
public string DownloadUrl { get; set; }
|
|
||||||
public string Version { get; set; }
|
|
||||||
public DateTime UpdateDate { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,8 +0,0 @@
|
||||||
namespace Ombi.Core.Update
|
|
||||||
{
|
|
||||||
public enum UpdateStatus
|
|
||||||
{
|
|
||||||
Available,
|
|
||||||
UptoDate
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -6,6 +6,6 @@ namespace Ombi.Helpers
|
||||||
{
|
{
|
||||||
public static class CacheKeys
|
public static class CacheKeys
|
||||||
{
|
{
|
||||||
public const string RadarrCacher = nameof(RadarrCacher);
|
public const string Update = nameof(Update);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
54
src/Ombi.Helpers/MemoryCacheHelper.cs
Normal file
54
src/Ombi.Helpers/MemoryCacheHelper.cs
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
#region Copyright
|
||||||
|
// /************************************************************************
|
||||||
|
// Copyright (c) 2017 Jamie Rees
|
||||||
|
// File: MemoryCacheHelper.cs
|
||||||
|
// Created By: Jamie Rees
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
// a copy of this software and associated documentation files (the
|
||||||
|
// "Software"), to deal in the Software without restriction, including
|
||||||
|
// without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
// permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
// the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be
|
||||||
|
// included in all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
// ************************************************************************/
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using Microsoft.Extensions.Caching.Memory;
|
||||||
|
|
||||||
|
namespace Ombi.Helpers
|
||||||
|
{
|
||||||
|
public static class MemoryCacheHelper
|
||||||
|
{
|
||||||
|
public static IMemoryCache TryAdd(this IMemoryCache cache, object cacheObject, TimeSpan slidingExpiration)
|
||||||
|
{
|
||||||
|
object cachedObject;
|
||||||
|
if (!cache.TryGetValue(CacheKeys.Update, out cachedObject))
|
||||||
|
{
|
||||||
|
// Key not in cache, so get data.
|
||||||
|
|
||||||
|
// Set cache options.
|
||||||
|
var cacheEntryOptions = new MemoryCacheEntryOptions()
|
||||||
|
.SetSlidingExpiration(slidingExpiration);
|
||||||
|
|
||||||
|
// Save data in cache.
|
||||||
|
cache.Set(CacheKeys.Update, cacheObject, cacheEntryOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
return cache;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,6 +10,7 @@
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="EasyCrypto" Version="3.3.2" />
|
<PackageReference Include="EasyCrypto" Version="3.3.2" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="2.0.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="2.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="2.0.0" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="10.0.3" />
|
<PackageReference Include="Newtonsoft.Json" Version="10.0.3" />
|
||||||
<PackageReference Include="System.Security.Claims" Version="4.3.0" />
|
<PackageReference Include="System.Security.Claims" Version="4.3.0" />
|
||||||
|
|
|
@ -33,7 +33,14 @@
|
||||||
|
|
||||||
|
|
||||||
<ul class="nav navbar-nav navbar-right">
|
<ul class="nav navbar-nav navbar-right">
|
||||||
<li *ngIf="hasRole('Admin') " [routerLinkActive]="['active']"><a [routerLink]="['/Settings/About']"><i class="fa fa-cog"></i> Settings</a></li>
|
<li *ngIf="hasRole('Admin') " [routerLinkActive]="['active']"><a [routerLink]="['/Settings/About']">
|
||||||
|
|
||||||
|
<i *ngIf="!updateAvailable" class="fa fa-cog"></i>
|
||||||
|
<i *ngIf="updateAvailable" class="fa fa-warning" style="color:#f57f17" pTooltip="Update Available!" tooltipPosition="left" [tooltipZIndex]="999999"></i>
|
||||||
|
|
||||||
|
Settings
|
||||||
|
|
||||||
|
</a></li>
|
||||||
<li [routerLinkActive]="['active']" class="dropdown">
|
<li [routerLinkActive]="['active']" class="dropdown">
|
||||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false"><i class="fa fa-user"></i> Welcome {{user.name}} <span class="caret"></span></a>
|
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false"><i class="fa fa-user"></i> Welcome {{user.name}} <span class="caret"></span></a>
|
||||||
<ul class="dropdown-menu" role="menu">
|
<ul class="dropdown-menu" role="menu">
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { Router } from "@angular/router";
|
||||||
import { AuthService } from "./auth/auth.service";
|
import { AuthService } from "./auth/auth.service";
|
||||||
import { ILocalUser } from "./auth/IUserLogin";
|
import { ILocalUser } from "./auth/IUserLogin";
|
||||||
import { NotificationService } from "./services";
|
import { NotificationService } from "./services";
|
||||||
import { SettingsService } from "./services";
|
import { JobService, SettingsService } from "./services";
|
||||||
|
|
||||||
import { ICustomizationSettings } from "./interfaces";
|
import { ICustomizationSettings } from "./interfaces";
|
||||||
|
|
||||||
|
@ -17,8 +17,13 @@ export class AppComponent implements OnInit {
|
||||||
public customizationSettings: ICustomizationSettings;
|
public customizationSettings: ICustomizationSettings;
|
||||||
public user: ILocalUser;
|
public user: ILocalUser;
|
||||||
public showNav: boolean;
|
public showNav: boolean;
|
||||||
|
public updateAvailable: boolean;
|
||||||
|
|
||||||
constructor(public notificationService: NotificationService, public authService: AuthService, private router: Router, private settingsService: SettingsService) { }
|
constructor(public notificationService: NotificationService,
|
||||||
|
public authService: AuthService,
|
||||||
|
private readonly router: Router,
|
||||||
|
private readonly settingsService: SettingsService,
|
||||||
|
private readonly jobService: JobService) { }
|
||||||
|
|
||||||
public ngOnInit() {
|
public ngOnInit() {
|
||||||
this.user = this.authService.claims();
|
this.user = this.authService.claims();
|
||||||
|
@ -29,6 +34,8 @@ export class AppComponent implements OnInit {
|
||||||
this.user = this.authService.claims();
|
this.user = this.authService.claims();
|
||||||
this.showNav = this.authService.loggedIn();
|
this.showNav = this.authService.loggedIn();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.jobService.getCachedUpdate().subscribe(x => this.updateAvailable = x);
|
||||||
}
|
}
|
||||||
|
|
||||||
public hasRole(role: string): boolean {
|
public hasRole(role: string): boolean {
|
||||||
|
|
|
@ -17,6 +17,10 @@ export class JobService extends ServiceAuthHelpers {
|
||||||
return this.http.get(`${this.url}update/`).map(this.extractData);
|
return this.http.get(`${this.url}update/`).map(this.extractData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getCachedUpdate(): Observable<boolean> {
|
||||||
|
return this.http.get(`${this.url}updateCached/`).map(this.extractData);
|
||||||
|
}
|
||||||
|
|
||||||
public runPlexImporter(): Observable<boolean> {
|
public runPlexImporter(): Observable<boolean> {
|
||||||
return this.http.post(`${this.url}plexUserImporter/`, { headers: this.headers }).map(this.extractData);
|
return this.http.post(`${this.url}plexUserImporter/`, { headers: this.headers }).map(this.extractData);
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ export class AboutComponent implements OnInit {
|
||||||
|
|
||||||
public ngOnInit() {
|
public ngOnInit() {
|
||||||
this.settingsService.about().subscribe(x => this.about = x);
|
this.settingsService.about().subscribe(x => this.about = x);
|
||||||
this.jobService.checkForNewUpdate().subscribe(x => {
|
this.jobService.getCachedUpdate().subscribe(x => {
|
||||||
if (x === true) {
|
if (x === true) {
|
||||||
this.newUpdate = true;
|
this.newUpdate = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,14 +21,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>-->
|
</div>-->
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<div class="form-group">
|
|
||||||
<div class="checkbox">
|
|
||||||
<input type="checkbox" id="allowExternalUsersToAuthenticate" allowExternalUsersToAuthenticate="allowExternalUsersToAuthenticate" formControlName="allowExternalUsersToAuthenticate"
|
|
||||||
pTooltip="This will allow Plex Friends and Emby users to log into Ombi.">
|
|
||||||
<label for="allowExternalUsersToAuthenticate">Allow media server users to authenticate</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="baseUrl" class="control-label">Base Url</label>
|
<label for="baseUrl" class="control-label">Base Url</label>
|
||||||
<div>
|
<div>
|
||||||
|
|
|
@ -1,8 +1,11 @@
|
||||||
using System.Threading.Tasks;
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using Hangfire;
|
using Hangfire;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.Extensions.Caching.Memory;
|
||||||
using Ombi.Api.Service;
|
using Ombi.Api.Service;
|
||||||
using Ombi.Attributes;
|
using Ombi.Attributes;
|
||||||
|
using Ombi.Helpers;
|
||||||
using Ombi.Schedule.Jobs.Plex;
|
using Ombi.Schedule.Jobs.Plex;
|
||||||
using Ombi.Schedule.Ombi;
|
using Ombi.Schedule.Ombi;
|
||||||
|
|
||||||
|
@ -13,14 +16,17 @@ namespace Ombi.Controllers
|
||||||
[Produces("application/json")]
|
[Produces("application/json")]
|
||||||
public class JobController : Controller
|
public class JobController : Controller
|
||||||
{
|
{
|
||||||
public JobController(IOmbiAutomaticUpdater updater, IPlexUserImporter userImporter)
|
public JobController(IOmbiAutomaticUpdater updater, IPlexUserImporter userImporter,
|
||||||
|
IMemoryCache mem)
|
||||||
{
|
{
|
||||||
_updater = updater;
|
_updater = updater;
|
||||||
_plexUserImporter = userImporter;
|
_plexUserImporter = userImporter;
|
||||||
|
_memCache = mem;
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly IOmbiAutomaticUpdater _updater;
|
private readonly IOmbiAutomaticUpdater _updater;
|
||||||
private readonly IPlexUserImporter _plexUserImporter;
|
private readonly IPlexUserImporter _plexUserImporter;
|
||||||
|
private readonly IMemoryCache _memCache;
|
||||||
|
|
||||||
[HttpPost("update")]
|
[HttpPost("update")]
|
||||||
public bool ForceUpdate()
|
public bool ForceUpdate()
|
||||||
|
@ -40,6 +46,22 @@ namespace Ombi.Controllers
|
||||||
return updateAvailable;
|
return updateAvailable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpGet("updateCached")]
|
||||||
|
public async Task<bool> CheckForUpdateCached()
|
||||||
|
{
|
||||||
|
var val = await _memCache.GetOrCreateAsync(CacheKeys.Update, async entry =>
|
||||||
|
{
|
||||||
|
entry.AbsoluteExpiration = DateTimeOffset.Now.AddHours(1);
|
||||||
|
var productArray = _updater.GetVersion();
|
||||||
|
var version = productArray[0];
|
||||||
|
var branch = productArray[1];
|
||||||
|
var updateAvailable = await _updater.UpdateAvailable(branch, version);
|
||||||
|
|
||||||
|
return updateAvailable;
|
||||||
|
});
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
[HttpPost("plexuserimporter")]
|
[HttpPost("plexuserimporter")]
|
||||||
public bool PlexUserImporter()
|
public bool PlexUserImporter()
|
||||||
{
|
{
|
||||||
|
|
|
@ -30,8 +30,6 @@ using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Ombi.Core.Settings;
|
using Ombi.Core.Settings;
|
||||||
using Ombi.Core.Settings.Models;
|
|
||||||
using Ombi.Core.Update;
|
|
||||||
using Ombi.Settings.Settings.Models;
|
using Ombi.Settings.Settings.Models;
|
||||||
|
|
||||||
namespace Ombi.Controllers
|
namespace Ombi.Controllers
|
||||||
|
@ -72,15 +70,5 @@ namespace Ombi.Controllers
|
||||||
|
|
||||||
return new { Result = settings?.Wizard ?? false};
|
return new { Result = settings?.Wizard ?? false};
|
||||||
}
|
}
|
||||||
|
|
||||||
[AllowAnonymous]
|
|
||||||
[HttpGet("Update")]
|
|
||||||
public async Task Update()
|
|
||||||
{
|
|
||||||
var u = new UpdateEngine();
|
|
||||||
var result = u.CheckForUpdate();
|
|
||||||
await u.Update(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -99,8 +99,7 @@ namespace Ombi
|
||||||
options.User.AllowedUserNameCharacters =
|
options.User.AllowedUserNameCharacters =
|
||||||
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 -._@+";
|
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 -._@+";
|
||||||
});
|
});
|
||||||
|
|
||||||
services.AddDataProtection();
|
|
||||||
services.AddMemoryCache();
|
services.AddMemoryCache();
|
||||||
|
|
||||||
services.AddJwtAuthentication(Configuration);
|
services.AddJwtAuthentication(Configuration);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue